一、项目简介
硬禾学堂为高校学生学习嵌入式系统,基于非常通用、学习资源非常多的STM32F103制作了一款嵌入式系统学习平台。此次是基于STM32F103R8T6的芯片,搭配了一个240 * 240的LCD彩屏,提供良好的人机交互,网上相关学习资料很多,主要参考正点原子mini开发板代码。
该项目实现了模拟时钟的功能,可设置时间、整点报时,整点的时候可以通过板上的蜂鸣器以声音报时;转动板子,姿态传感器MPU6050感应,LCD屏上的时钟自动跟着旋转,整个时钟就会发生相应的改变。
二、项目开发具体流程
1、配置引脚和时钟
使用stm32CubeMX进行配置并生成项目工程,根据给出的STM32_Pocket参考文档,对所需要的引脚配置,根据按键原理图,按键是硬件下拉,初始化是不用修改,时钟配置时用不到外部时钟,只需要配置内部时钟,选择最大72MHZ,四个GPIO初始化就行,也不需要改动,如下两张图所示。
2、模拟时钟的钟表指针的绘制
void gui_circle(int xc, int yc,uint16_t c,int r, int fill)该函数用来绘制钟表的圆心,
void LCD_DrawLine(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2),该函数用来在两点间画一条线,即钟表的指针。
这两个函数里还调用了其它函数,具体过程可以下载代码查看。
void gui_circle(int xc, int yc,uint16_t c,int r, int fill)//用来绘制模拟钟表的圆心
{
int x = 0, y = r, yi, d;
d = 3 - 2 * r;
while (x <= y) {
_draw_circle_8(xc, yc, x, y, c);
if (d < 0) {
d = d + 4 * x + 6;
} else {
d = d + 4 * (x - y) + 10;
y--;
}
x++;
}
}
void LCD_DrawLine(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)//画线
{
uint16_t t;
int xerr=0,yerr=0,delta_x,delta_y,distance;
int incx,incy,uRow,uCol;
delta_x=x2-x1; //计算坐标增量
delta_y=y2-y1;
uRow=x1;
uCol=y1;
if(delta_x>0)incx=1; //设置单步方向
else if(delta_x==0)incx=0;//垂直线
else {
incx=-1;
delta_x=-delta_x;
}
if(delta_y>0)incy=1;
else if(delta_y==0)incy=0;//水平线
else {
incy=-1;
delta_y=-delta_y;
}
if( delta_x>delta_y)distance=delta_x; //选取基本增量坐标轴
else distance=delta_y;
for(t=0; t<=distance+1; t++ ) //画线输出
{
LCD_DrawPoint(uRow,uCol);//画点
xerr+=delta_x ;
yerr+=delta_y ;
if(xerr>distance)
{
xerr-=distance;
uRow+=incx;
}
if(yerr>distance)
{
yerr-=distance;
uCol+=incy;
}
}
}
3、姿态传感器
姿态传感器MPU6050采用的是I2C接口,一开始也无法下手,于是在网上开始查资料,发现官方给出了例程,这个挺不错的。
4、蜂鸣器
这里也是用的官方例程,在中断里操作,另外,设置整点蜂鸣器响,下面函数可以实现。
if(GET_TIME.Minutes==0&&GET_TIME .Seconds <2)
{
HAL_TIM_Base_Start_IT(&htim3);
}
else if(GET_TIME.Minutes==0&&GET_TIME .Seconds >4)
{
HAL_TIM_Base_Stop_IT(&htim3);
}
五、实现的功能
将代码烧入后,可以通过按键进入设置模式,即修改时间,还有一个按键实现退出功能(不保存结果),另外两个按键实现加一或减一,如果要修改成功,进入设置模式后,要经历过时、分、秒三个设置后才能成功(只是经历过,可以不用修改)。
三、遇到的困难
拿到开发板后,遇到的困难主要是lcd显示屏不知道如何显示图片,于是开始根据正点原子视频学习,正点原子MiniSTM32开发板是使用的STM32F103RCT6芯片,对照着学习发现给出了示例代码,但毕竟开发板不一样,对着修改很难受。
下面展示的部分函数实现将240*240的图片显示在lcd上,具体代码可以下载后查看,在考虑flash大小,图片转化时设置为单色
下图为该模拟时钟所采用的钟表样图,现在手表流行简约风格,故采用该图片。
void Gui_Drawbmp1(uint16_t x,uint16_t y,const unsigned char *p) //显示图片 画表
{
int i;
uint8_t data,j;
unsigned char picH,picL;
LCD_SetWindows(x,y,x+240-1,y+240-1);//窗口设置
for(i=0; i<240*240/8; i++)
{
data=*(p+i);
for(j=0; j<8; j++)
{
if(data&0x80)Lcd_WriteData_16Bit(0xffff);
else Lcd_WriteData_16Bit(0x0000);
data =data<<1;
}
}
LCD_SetWindows(0,0,lcddev.width-1,lcddev.height-1);//恢复显示窗口为全屏
}
void LCD_SetWindows(uint16_t xStar, uint16_t yStar,uint16_t xEnd,uint16_t yEnd)
{
LCD_WR_REG(lcddev.setxcmd);
LCD_WR_DATA((xStar+lcddev.xoffset)>>8);
LCD_WR_DATA(xStar+lcddev.xoffset);
LCD_WR_DATA((xEnd+lcddev.xoffset)>>8);
LCD_WR_DATA(xEnd+lcddev.xoffset);
LCD_WR_REG(lcddev.setycmd);
LCD_WR_DATA((yStar+lcddev.yoffset)>>8);
LCD_WR_DATA(yStar+lcddev.yoffset);
LCD_WR_DATA((yEnd+lcddev.yoffset)>>8);
LCD_WR_DATA(yEnd+lcddev.yoffset);
LCD_WriteRAM_Prepare(); //开始写入GRAM
}
void LCD_WR_REG(uint8_t data)
{
LCD_CS_CLR;
LCD_RS_CLR;
SPIv_WriteData(data);
LCD_CS_SET;
}
四、未来的计划
考虑到这次模拟时钟的钟表图片未能实现彩色,并且这块开发板有SD外设。另外,对于老式座钟,到了几点就会打几下钟,我想看看能不能实现到了几点,蜂鸣器就响几下,这个我也尝试过,但实现的话要修改代码,比较麻烦难实现,未来深入学习后再来实现这两个想法。