1. 项目简介
这是基于STM32设计的指针电子钟 万年历小项目采用3个.5寸的LCD屏幕显示时钟、日历、温度、天气,支持触摸屏调整设置时间、设置闹钟、查看日历等。整个项目的主要技术点是LCD画出屏幕的图形。 时钟的时针绘制、分针、秒针、表盘、日历绘制等等。
时钟时间直接使用STM32本身的RTC采用时钟、室内室温数据DS18B获得20温度传感器,STM32芯片的具体型号是STM32F103ZET6,只要是STM32F一系列开发板,代码可以通用。
LCD正点原子的3.5寸TFT支持8080时序的显示屏,具有自己的触摸屏功能,触摸屏为电阻屏,驱动芯片为XPT2046,SPI接口,通信非常方便。
STM32F103ZET6带有FSMC本项目可输出8080时序LCD屏就采用FSMC高效控制。
https://download.csdn.net/download/xiaolong1126626497/63897554
基于STM指针电子钟和日历设计
2. 项目功能介绍
下面详细说明每个子功能页面。
2.1 实时时钟页面
在LCD表盘、分针、时针显示在屏幕上方 秒针、刻度、改变时钟时间方块,实现分针、时针、秒针的移动,数字时钟同步显示在实时时钟下方。
使用触摸屏功能实现时钟设置功能,单击 ” -设置时钟方块,跳出设置时钟界面,开始设置时钟和日期;单击 ”“-”至设置闹钟方块,跳出设置闹钟界面,即可开始设置闹钟。
2.2 日历页面
在LCD显示屏幕中间的日期、星期、天气和实时温度LCD日历显示在屏幕下方,黄历显示在左右两侧,今天的日期突出在日历上。
3. 实现项目主要程序讲解
3.1 流程图
3.2 ds18b2.c 代码
下面列出DS18B20温度传感器主代码.
#include "ds18b20.h" #include "delay.h" //复位DS18B20 void DS18B20_Rst(void) { DS18B20_IO_OUT(); //SET PG11 OUTPUT DS18B20_DQ_OUT=0; //拉低DQ DelayUs(750); //拉低750us DS18B20_DQ_OUT=1; //DQ=1 DelayUs(15); //15US } //等待DS18B20的回应 //返回1:未检测到DS18B20的存在 //返回0:存在 u8 DS18B20_Check(void) { u8 retry=0; DS18B20_IO_IN(); //SET PG11 INPUT while (DS18B20_DQ_IN&&retry<200) { retry ; DelayUs(1); }; if(retry>=200)return 1; else retry=0; while (!DS18B20_DQ_IN&&retry<240) { retry ; DelayUs(1); }; if(retry>=240)return 1; return 0; } //从DS18B20读取一个位 //返回值:1/0 u8 DS18B20_Read_Bit(void) { u8 data; DS18B20_IO_OUT(); //SET PG11 OUTPUT DS18B20_DQ_OUT=0; DelayUs(2); DS18B20_DQ_OUT=1; DS18B20_IO_IN(); //SET PG11 INPUT DelayUs(12); if(DS18B20_DQ_IN)data=1; else data=0; DelayUs(50); return data; } //从DS18B读一个字节 ///返回值:读取的数据 u8 DS18B20_Read_Byte(void) { u8 i,j,dat; dat=0; for (i=1;i<=8;i ) { j=DS18B20_Read_Bit(); dat=(j<<7)|(dat>>1); } return dat; } ///写字节到DS18B20 //dat:要写的字节 void DS18B20_Write_Byte(u8 dat) { u8 j; u8 testb; DS18B20_IO_OUT(); //SET PG11 OUTPUT; for (j=1;j<=8;j ) { testb=dat&0x01; dat=dat>>1; if (testb) { DS18B20_DQ_OUT=0; // Write 1 DelayUs(2); DS18B20_DQ_OUT=1; DelayUs(60); } else { DS18B20_DQ_OUT=0; // Write 0 DelayUs(60); DS18B20_DQ_OUT=1; DelayUs(2); } } } //开始温度转换 void DS18B20_Start(void) { DS18B20_Rst(); DS18B20_Check(); DS18B20_Write_Byte(0xcc); // skip rom DS18B20_Write_Byte(0x44); // convert } //初始化DS18B20的IO口 DQ 同时检测DS的存在 //返回1:不存在 //返0:存在
u8 DS18B20_Init(void)
{
RCC->APB2ENR|=1<<8; //使能PORTG口时钟
GPIOG->CRH&=0XFFFF0FFF; //PORTG.11 推挽输出
GPIOG->CRH|=0X00003000;
GPIOG->ODR|=1<<11; //输出1
DS18B20_Rst();
return DS18B20_Check();
}
//从ds18b20得到温度值
//精度:0.1C
//返回值:温度值 (-550~1250)
short DS18B20_Get_Temp(void)
{
u8 temp;
u8 TL,TH;
short tem;
DS18B20_Start (); // ds1820 start convert
DS18B20_Rst();
DS18B20_Check();
DS18B20_Write_Byte(0xcc); // skip rom
DS18B20_Write_Byte(0xbe); // convert
TL=DS18B20_Read_Byte(); // LSB
TH=DS18B20_Read_Byte(); // MSB
if(TH>7)
{
TH=~TH;
TL=~TL;
temp=0; //温度为负
}else temp=1; //温度为正
tem=TH; //获得高八位
tem<<=8;
tem+=TL; //获得底八位
tem=(float)tem*0.625; //转换
if(temp)return tem; //返回温度值
else return -tem;
}
3.3 lcd屏图形绘制核心算法
整个项目的功能都是在LCD显示屏上,需要绘制线段、绘制圆、绘制矩形、绘制角度线段、绘制中文、绘制数字等等,下面列出这部分的核心代码。
/*
函数功能:画横直线
函数形参:x,y:坐标
length:长度
*/
void LcdDrawThwartLine(u16 x,u16 y,u16 length,u16 color)
{
u16 i;
for(i=0;i<length;i++)
{
LcdDrawPoint(x+i,y,color);
}
}
/*
函数功能:画竖直线
函数形参:x,y:坐标
length:长度
*/
void LcdDrawVerticalLine(u16 x,u16 y,u16 length,u16 color)
{
u16 i;
for(i=0;i<length;i++)
{
LcdDrawPoint3(x,y+i,color);
}
}
/*
函数功能:画矩形
函数形参:x,y:坐标
length:长
width:宽
*/
void LcdDrawRectangle(u16 x,u16 y,u16 length,u16 width,u16 color)
{
LcdDrawThwartLine(x,y,length,color);
LcdDrawVerticalLine(x,y,width,color);
LcdDrawThwartLine(x,y+width,length,color);
LcdDrawVerticalLine(x+length,y,width,color);
}
//两点画线
//x1,y1:起点坐标
//x2,y2:终点坐标
void LcdDrawLine(u16 x1, u16 y1, u16 x2, u16 y2,u16 color)
{
u16 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++ )//画线输出
{
LcdDrawPoint(uRow,uCol,color);//画点
xerr+=delta_x ;
yerr+=delta_y ;
if(xerr>distance)
{
xerr-=distance;
uRow+=incx;
}
if(yerr>distance)
{
yerr-=distance;
uCol+=incy;
}
}
}
//在指定位置画一个指定大小的圆
//(x,y):中心点
//r :半径
void LcdDraw_Circle(u16 x0,u16 y0,u8 r,u16 color)
{
int a,b;
int di;
a=0;b=r;
di=3-(r<<1); //判断下个点位置的标志
while(a<=b)
{
LcdDrawPoint(x0+a,y0-b,color); //5
LcdDrawPoint(x0+b,y0-a,color); //0
LcdDrawPoint(x0+b,y0+a,color); //4
LcdDrawPoint(x0+a,y0+b,color); //6
LcdDrawPoint(x0-a,y0+b,color); //1
LcdDrawPoint(x0-b,y0+a,color);
LcdDrawPoint(x0-a,y0-b,color); //2
LcdDrawPoint(x0-b,y0-a,color); //7
a++;
//使用Bresenham算法画圆
if(di<0)di +=4*a+6;
else
{
di+=10+4*(a-b);
b--;
}
}
}
/*
函数功能:任意角度画直线
参 数:
w :以圆心开始不要画的长度
len:半径
c :颜色
x,y:坐标
实际长度=len-w
*/
void LcdDrawAngleLine(u32 x,u32 y,float du,u32 len,u32 w,u16 c)
{
int i;
int x0,y0;
float k=du*(3.1415926535/180);
for(i=len-w;i<len;i++)
{
x0=cos(k)*i;
y0=sin(k)*i;
LcdDrawPoint(x+x0,y+y0,c);
}
}
/*
函数功能:矩形颜色填充
参 数:(sx,sy),(ex,ey):矩形对角坐标
color:要填充的颜色
*/
void LcdFill(u16 sx,u16 sy,u16 ex,u16 ey,u16 color)
{
u16 i,j;
u16 xlen=0;
xlen=ex-sx+1;
for(i=sy;i<=ey;i++)
{
LcdSetCursor(sx,i); //设置光标位置
LcdWriteReg(0X2C); //开始写入GRAM
for(j=0;j<xlen;j++)LcdWriteData(color);
}
}
/*
功能:任意角度画线
x0,y0:起始点坐标
a:角度
c:颜色
n:长度
*/
void LcdDrawAngleLine2(u32 x0,u32 y0,double a,u16 n,u16 c,u16 mode)
{
u32 x,y;
u32 i;
double p=a*3.1415926535/180;
for(i=0;i<=n;i++) //n是长度
{
x=i*cos(p)+x0;
y=i*sin(p)+y0;
if(mode==1)LcdDrawPoint(x,y,c); //画点
else if(mode==2) LcdDrawPoint2(x,y,c); //画点
else LcdDrawPoint3(x,y,c); //画点
}
}
/*
函数功能:画点(加粗)
函数形参:x,y:坐标
*/
void LcdDrawPoint2(u16 x,u16 y,u16 color)
{
LcdSetCursor(x,y); //设置光标位置
LcdWriteReg(0X2C); //开始写入GRAM
LcdWriteData(color); //写数据
LcdWriteData(color); //写数据
LcdWriteData(color); //写数据
// LcdWriteData(color); //写数据
// LcdWriteData(color); //写数据
// LcdWriteData(color); //写数据
// LcdWriteData(color); //写数据
}
/*
函数功能:画点(变细)
函数形参:x,y:坐标
*/
void LcdDrawPoint3(u16 x,u16 y,u16 color)
{
LcdSetCursor(x,y); //设置光标位置
LcdWriteReg(0X2C); //开始写入GRAM
LcdWriteData(color); //写数据
// LcdWriteData(color); //写数据
// LcdWriteData(color); //写数据
// LcdWriteData(color); //写数据
// LcdWriteData(color); //写数据
// LcdWriteData(color); //写数据
// LcdWriteData(color); //写数据
}
/*
函数功能:任意角度画线,标准加粗
void LcdDrawAngleLine2(u32 x0,u32 y0,double a,u32 n,u32 c,u32 mode)
*/
void Lcdline_add(u32 x,u32 y,double a,u16 n,u16 c, u8 add )
{
u8 i;
for(i=1;i<add;i++)
{
x-=i;
y-=i;
LcdDrawAngleLine2(x,y,a,n,c,1);
}
for(i=1;i<add;i++)
{
x+=i;
y+=i;
LcdDrawAngleLine2(x,y,a,n,c,1);
}
}