完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
项目说明
用普中51和keil uVision4实现智能手表,功能包括:显示秒表、时间、日期、星期、设置闹钟,读取温度,指示灯亮等。综合的实验主要有独立按键、指示灯亮、温度传感器、定时器、动态数码管显示、蜂鸣器实验,其中本篇博文重要讲解定时器和温度传感器实验,其他结合的实验比较简单,自行查阅资料即可明白。 硬件设计 温度传感器 这个项目我用的是精度较高的外部 DS18B20 数字温度传感器,由于此传感器是单总线接口,所以需要使用 51 单片机的一个 IO 口模拟单总线时序与 DS18B20 通信,将检测的环境温度读取出来。 DS18B20 外观实物如下图所示: **注意:**如果把传感器插反,那么电源将短路,传感器就会发烫,很容易损坏,所以一定要注意传感器方向,通常在开发板上都会标出传感器的凸起出,所以只需要把传感器凸起的方向对着开发板凸起方向插入即可。DS18B20 数字温度传感器内部的一些高速暂存存储器自行查阅资料,这里就跳过了,直接理解它是怎么计算温度值的吧。 计算温度 如果测得的温度大于 0,前5 位为‘ 0’,只要将测到的数值乘以 0.0625(默认精度是 12 位)即可得到实际温度;如果温度小于 0,这 5 位为‘ 1’,测到的数值需要取反加 1 再乘以 0.0625 即可得到实际温度。比如我们要计算+85 度,数据输出十六进制是 0X0550,因为高字节的高 5位为 0,表明检测的温度是正温度,0X0550 对应的十进制为 1360,将这个值乘以 12 位精度 0.0625,所以可以得到+85 度。 读取温度 知道了怎么计算温度,接下来我们就来看看如何读取温度数据,由于DS18B20 是单总线器件,所有的单总线器件都要求采用严格的信号时序,以保证 数据的完整性。DS18B20 时序包括如下几种:初始化时序、写(0 和 1)时序、读(0 和 1)时序。 DS18B20 发送所有的命令和数据都是字节的低位在前。 这里主要有这几个信号的时序:(1) 初始化时序、(2)写时序、(3)读时序。时序的详细介绍自行查阅,其实根据字面意思大致也可以猜到是什么意思的。 DS18B20 的典型温度读取过程为:复位→发 SKIP ROM 命令(0XCC)→发开始转换命令(0X44)→延时→复位→发送 SKIP ROM 命令(0XCC)→发读存储器命令(0XBE)→连续读出两个字节数据(即温度)→结束。 定时器中断 学习定时器前需要明白: ①51 单片机有两组定时器/计数器,因为既可以定时,又可以计数,故称之 为定时器/计数器。 ②定时器/计数器和单片机的 CPU 是相互独立的。定时器/计数器工作的过程是自动完成的,不需要 CPU 的参与。 ③51 单片机中的定时器/计数器是根据机器内部的时钟或者是外部的脉冲信 号对寄存器中的数据加 1。 有了定时器/计数器之后,可以增加单片机的效率,一些简单的重复加 1 的 工作可以交给定时器/计数器处理。CPU 转而处理一些复杂的事情,同时可以实现精确定时作用。 STC89C5X 单片机内有两个可编程的定时/计数器 T0、T1 和一个特殊功能定时器 T2。 工作方式 这里着重讲解定时器常用的工作方式: 计数位数是 16 位,由 TL0 作为低 8 位,TH0 作为高 8 位,组成了16 位加 1 计数器。 计数初值与计数个数的关系为:X=2(16)-N。 定时器配置 这里以定时器 0 为例介绍配置定时器工作方式 1、设定 1ms 初值,开启定时器计数功能以及总中断,如下: void Timer0Init() { TMOD|=0X01;//选择为定时器 0 模式,工作方式 1,仅用 TR0 打开启动 TH0=0XFC; //给定时器赋初值,定时 1ms TL0=0X18; ET0=1;//打开定时器 0 中断允许 EA=1;//打开总中断 TR0=1;//打开定时器 } 硬件实物连接 由于这个线连接的有点绕,所以直接口述还好些吧。 上面就是用到的GPIO口,LSA、LSB、LSC指的就是74HC138模块,k1到k8就是独立按键模块,led到led4指的是LED交通灯模块,beep指蜂鸣器模块,DS18B20我连接的是P3^7接口,动态数码管我连接的是P0口(在普中51单片机上是J22连接到J6,就是上图中的8排杜邦线)。 软件设计 上述DS18B20温度传感器的代码实现如下: temp.h #ifndef _TEMP_H_ #define _TEMP_H_ #include //---重定义关键词---// #ifndef uchar #define uchar unsigned char #endif #ifndef uint #define uint unsigned int #endif //--定义使用的 IO 口--// ***it DSPORT=P3^7; //--声明全局函数--// void Delay1ms(uint ); uchar Ds18b20Init(); void Ds18b20WriteByte(uchar com); uchar Ds18b20ReadByte(); void Ds18b20ChangTemp(); void Ds18b20ReadTempCom(); int Ds18b20ReadTemp(); #endif temp.c #include "temp.h" void Delay1ms(uint y) //延时函数 { uint x; for(;y>0;y--) { for(x=110;x>0;x--); } } uchar Ds18b20Init() //初始化函数 { uchar i; DSPORT = 0; //将总线拉低 480us~960us i = 70; while(i--);//延时 642us DSPORT = 1; //然后拉高总线,如果 DS18B20 做出反应会将在 15us~60us 后总线拉低 i = 0; while(DSPORT) //等待 DS18B20 拉低总线 { Delay1ms(1); i++; if(i>5)//等待>5ms { return 0;//初始化失败 } } return 1;//初始化成功 } void Ds18b20WriteByte(uchar dat) //向 18B20 写入一个字节 { uint i, j; for(j=0; j<8; j++) { DSPORT = 0; //每写入一位数据之前先把总线拉低 1us i++; DSPORT = dat & 0x01; //然后写入一个数据,从最低位开始 i=6; while(i--); //延时 68us,持续时间最少 60us DSPORT = 1; //然后释放总线,至少 1us 给总线恢复时间才能接 着写入第二个数值 dat >>= 1; } } uchar Ds18b20ReadByte() //读取一个字节 { uchar byte, bi; uint i, j; for(j=8; j>0; j--) { DSPORT = 0; i++; DSPORT = 1; i++; i++;//延时 6us 等待数据稳定 bi = DSPORT; //读取数据,从最低位开始读取 /*将 byte 左移一位,然后与上右移 7 位后的 bi,注意移动之后移掉 那位补 0*/ byte = (byte >> 1) | (bi << 7); i = 4; while(i--); } return byte; } void Ds18b20ChangTemp() //让 18b20 开始转换温度 { Ds18b20Init(); Delay1ms(1); Ds18b20WriteByte(0xcc); //跳过 ROM 操作命令 Ds18b20WriteByte(0x44); //温度转换命令 //Delay1ms(100); //等待转换成功,而如果你是一直刷着的话,就不用这个延时了 } void Ds18b20ReadTempCom() //发送读取温度命令 { Ds18b20Init(); Delay1ms(1); Ds18b20WriteByte(0xcc); //跳过 ROM 操作命令 Ds18b20WriteByte(0xbe); //发送读取温度命令 } int Ds18b20ReadTemp() //读取温度 { int temp = 0; uchar tmh, tml; Ds18b20ChangTemp(); //先写入转换命令 Ds18b20ReadTempCom(); //然后等待转换完后发送读取温度命令 tml = Ds18b20ReadByte(); //读取温度值共 16 位,先读低字节 tmh = Ds18b20ReadByte(); //再读高字节 temp = tmh; temp <<= 8; temp |= tml; return temp; } 最后在主函数一并实现定时器中断实验: main.c #include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器 #include "temp.h" typedef unsigned int u16; //对数据类型进行声明定义 typedef unsigned char u8; //--定义使用的 IO 口--// ***it LSA=P2^2; ***it LSB=P2^3; ***it LSC=P2^4; ***it k1=P3^1; ***it k2=P3^2; ***it k3=P3^3; ***it k4=P3^4; ***it k5=P3^5; ***it k6=P3^6; ***it k7=P3^0; ***it led=P2^0; ***it led1=P2^1; ***it led2=P2^5; ***it led3=P2^6; ***it beep=P2^7; ***it k8=P1^0; ***it led4=P1^1; u8 code smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07, 0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//显示0~F的值 u16 hour=15,minute=32,second=30;//自己设置初始时间,全局变量 u16 year=2020,month=6,day=30,week=2;//自己设置初始日期、星期,全局变量 u8 ssec,sec,min; //毫秒,秒,分 //不同功能的数码管显示// u8 DisplayData[8]; u8 DisplayData1[8]; u8 DisplayData2[8]; u8 DisplayData3[8]; u8 DisplayData4[8]; void delay(u16 i) { while(i--); } void keypros() //按键处理函数 { if(k1==0) //检测按键 K1 是否按下 { delay(1000); //消除抖动 一般大约 10ms if(k1==0) //再次判断按键是否按下 { //--只留当前指示灯亮,其他指示灯灭--// led1=1; led=1; led3=1; led4=1; led=~led; //led 状态取反 } while(!k1); //检测按键是否松开 } //--其它按键注释一样,下面就不注释了--// if(k2==0) { delay(1000); if(k2==0) { led=1; led2=1; led3=1; led4=1; led1=~led1; } while(!k2); } if(k3==0) { delay(1000); if(k3==0) { led=1; led1=1; led3=1; led4=1; led2=~led2; } while(!k3); } if(k4==0) { delay(1000); if(k4==0) { led=1; led1=1; led2=1; led4=1; led3=~led3; } while(!k4); } if(k8==0) { delay(1000); if(k8==0) { led=1; led1=1; led2=1; led3=1; led4=~led4; } while(!k8); } } void datapros1(int temp) //温度传感器数码管数字设置 { float tp; if(temp< 0) //当温度值为负数 { DisplayData[0] = 0x40; //因为读取的温度是实际温度的补码,所以减 1,再取反求出原码 temp=temp-1; temp=~temp; tp=temp; temp=tp*0.0625*100+0.5; } else { DisplayData[0] = 0x00; tp=temp;//因为数据处理有小数点所以将温度赋给一个浮点型变量 //如果温度是正的那么,那么正数的原码就是补码它本身 temp=tp*0.0625*100+0.5; //留两个小数点就*100,+0.5 是四舍五入,因为 C 语言浮点数转换为整型的时候把小数点 //后面的数自动去掉,不管是否大于 0.5,而+0.5 之后大于 0.5 的 就是进 1 了,小于 0.5 的就 //算加上 0.5,还是在小数点后面。 } DisplayData1[1] = smgduan[temp / 10000]; DisplayData1[2] = smgduan[temp % 10000 / 1000]; DisplayData1[3] = smgduan[temp % 1000 / 100] | 0x80; DisplayData1[4] = smgduan[temp % 100 / 10]; DisplayData1[5] = smgduan[temp % 10]; } void Timer0Init()//定时器0初始化 { TMOD|=0X01;//选择为定时器0模式,工作方式1,仅用TR0打开启动。 TH0=0Xd8; //给定时器赋初值,定时10ms TL0=0Xf0; ET0=1;//打开定时器0中断允许 EA=1;//打开总中断 TR0=1;//打开定时器 } void DigDisplay1() //温度传感器数码管数字显示 { u8 i; for(i=0;i<6;i++) { switch(i) //位选,选择点亮的数码管, { case(0): LSA=0;LSB=0;LSC=0; break;//显示第 0 位 case(1): LSA=1;LSB=0;LSC=0; break;//显示第 1 位 case(2): LSA=0;LSB=1;LSC=0; break;//显示第 2 位 case(3): LSA=1;LSB=1;LSC=0; break;//显示第 3 位 case(4): LSA=0;LSB=0;LSC=1; break;//显示第 4 位 case(5): LSA=1;LSB=0;LSC=1; break;//显示第 5 位 } P0=DisplayData1;//发送数据 delay(100); //间隔一段时间扫描 P0=0x00;//消隐 } } void DigDisplay2()//时间数码管数字显示 { u8 i; for(i=0;i<8;i++) { switch(i) //位选,选择点亮的数码管, { case(0): LSA=0;LSB=0;LSC=0; break;//显示第0位 case(1): LSA=1;LSB=0;LSC=0; break;//显示第1位 case(2): LSA=0;LSB=1;LSC=0; break;//显示第2位 case(3): LSA=1;LSB=1;LSC=0; break;//显示第3位 case(4): LSA=0;LSB=0;LSC=1; break;//显示第4位 case(5): LSA=1;LSB=0;LSC=1; break;//显示第5位 case(6): LSA=0;LSB=1;LSC=1; break;//显示第6位 case(7): LSA=1;LSB=1;LSC=1; break;//显示第7位 } P0=DisplayData2;//发送段码 delay(100); //间隔一段时间扫描 P0=0x00;//消隐 } } void datapros2()//时间数码管数字设置 { DisplayData2[0]=smgduan[hour/10]; DisplayData2[1]=smgduan[hour%10]; DisplayData2[2]=0x40; DisplayData2[3]=smgduan[minute/10]; DisplayData2[4]=smgduan[minute%10]; DisplayData2[5]=0x40; DisplayData2[6]=smgduan[second/10]; DisplayData2[7]=smgduan[second%10]; } void datapros4()//星期数码管数字设置 { DisplayData4[0]=0x00; DisplayData4[1]=0x00; DisplayData4[2]=0x00; DisplayData4[3]=0x00; DisplayData4[4]=0x00; DisplayData4[5]=0x00; DisplayData4[6]=0x00; DisplayData4[7]=smgduan[week%10]; } void DigDisplay4()//星期数码管数字显示 { u8 i; for(i=0;i<8;i++) { switch(i) //位选,选择点亮的数码管, { case(0): LSA=0;LSB=0;LSC=0; break;//显示第0位 case(1): LSA=1;LSB=0;LSC=0; break;//显示第1位 case(2): LSA=0;LSB=1;LSC=0; break;//显示第2位 case(3): LSA=1;LSB=1;LSC=0; break;//显示第3位 case(4): LSA=0;LSB=0;LSC=1; break;//显示第4位 case(5): LSA=1;LSB=0;LSC=1; break;//显示第5位 case(6): LSA=0;LSB=1;LSC=1; break;//显示第6位 case(7): LSA=1;LSB=1;LSC=1; break;//显示第7位 } P0=DisplayData4;//发送段码 delay(100); //间隔一段时间扫描 P0=0x00;//消隐 } } void DigDisplay3()//日期数码管数字显示 { u8 i; for(i=0;i<8;i++) { switch(i) //位选,选择点亮的数码管, { case(0): LSA=0;LSB=0;LSC=0; break;//显示第0位 case(1): LSA=1;LSB=0;LSC=0; break;//显示第1位 case(2): LSA=0;LSB=1;LSC=0; break;//显示第2位 case(3): LSA=1;LSB=1;LSC=0; break;//显示第3位 case(4): LSA=0;LSB=0;LSC=1; break;//显示第4位 case(5): LSA=1;LSB=0;LSC=1; break;//显示第5位 case(6): LSA=0;LSB=1;LSC=1; break;//显示第6位 case(7): LSA=1;LSB=1;LSC=1; break;//显示第7位 } P0=DisplayData3;//发送段码 delay(100); //间隔一段时间扫描 P0=0x00;//消隐 } } void datapros3()//日期数码管数字设置 { DisplayData3[0]=smgduan[year/1000]; DisplayData3[1]=smgduan[year/100%10]; DisplayData3[2]=smgduan[year/10%10]; DisplayData3[3]=smgduan[year%10]; DisplayData3[4]=smgduan[month/10]; DisplayData3[5]=smgduan[month%10]; DisplayData3[6]=smgduan[day/10]; DisplayData3[7]=smgduan[day%10]; } void DigDisplay()//秒表数码管数字显示 { u8 i; for(i=0;i<8;i++) { switch(i) //位选,选择点亮的数码管, { case(0): LSA=0;LSB=0;LSC=0; break;//显示第0位 case(1): LSA=1;LSB=0;LSC=0; break;//显示第1位 case(2): LSA=0;LSB=1;LSC=0; break;//显示第2位 case(3): LSA=1;LSB=1;LSC=0; break;//显示第3位 case(4): LSA=0;LSB=0;LSC=1; break;//显示第4位 case(5): LSA=1;LSB=0;LSC=1; break;//显示第5位 case(6): LSA=0;LSB=1;LSC=1; break;//显示第6位 case(7): LSA=1;LSB=1;LSC=1; break;//显示第7位 } P0=DisplayData;//发送段码 delay(100); //间隔一段时间扫描 P0=0x00;//消隐 } } void datapros()//秒表数码管数字设置 { DisplayData[0]=smgduan[min/10]; DisplayData[1]=smgduan[min%10]; DisplayData[2]=0x40; DisplayData[3]=smgduan[sec/10]; DisplayData[4]=smgduan[sec%10]; DisplayData[5]=0x40; DisplayData[6]=smgduan[ssec/10]; DisplayData[7]=smgduan[ssec%10]; } void main() { //--初始化指示灯全灭--// led=1; led1=1; led2=1; led3=1; led4=1; Timer0Init(); //定时器0初始化 while(1) { keypros(); while(hour==16 && minute==50)//设置闹钟 { beep=~beep; delay(100); if(led2==0) { datapros2(); DigDisplay2(); } } if(led==0)//秒表功能 { datapros(); DigDisplay(); } if(led1==0)//读取温度 { datapros1(Ds18b20ReadTemp()); //数据处理函数 DigDisplay1();//数码管显示函数 } if(led2==0)//显示时间 { datapros2(); DigDisplay2(); } if(led3==0)//显示日期 { datapros3(); DigDisplay3(); } if(led4==0)//显示星期 { datapros4(); DigDisplay4(); } } } void Timer0() interrupt 1//定时器0中断函数 { if(k5==0 && led2==0)//小时中断 { delay(1000); if(k5==0) { hour++; if(hour==24) { hour=0; week++; day++; } } while(!k5); } if(k6==0 && led2==0)//分钟中断 { delay(1000); if(k6==0) { minute++; if(minute==60) { minute=0; hour++; } } while(!k6); } if(k7==0 && led2==0)//秒中断 { delay(1000); if(k7==0) { second++; if(second==60) { second=0; minute++; } } while(!k7); } if(k5==0 && led3==0)//年中断 { delay(1000); if(k5==0) { year++; if(year==10000) { year=0; } } while(!k5); } if(k6==0 && led3==0)//月中断 { delay(1000); if(k6==0) { month++; if(month==13) { month=1; year++; } } while(!k6); } if(k7==0 && led3==0)//天中断 { delay(1000); if(k7==0) { day++; week++; if(week==8)//星期中断 { week=1; } if(month==1 || month==3 || month==5 || month==7 || month==8 || month==10)//月份对应的天数中断 { if(day==32) { day=1; month++; } } if(month==4 || month==6 || month==9 || month==11) { if(day==31) { day=1; month++; } } //2月中断,判断闰年和平年// if(month==2) { if(year%4==0 && year%100!=0 &&day==30) { day=1; month++; } if(year%400==0 && day==30) { day=1; month++; } if(year%4!=0 && day==29) { day=1; month++; } if(year%4==0 && year%100==0 && year%400!=0 && day==29) { day=1; month++; } } } while(!k7); } TH0=0Xd8; //给定时器赋初值,定时10ms TL0=0Xf0; ssec++; if(ssec>=100) //100*ms=1s { ssec=0; sec++; second++; if(second==60)//秒中断 { second=0; minute++; if(minute==60) { minute=0; hour++; if(hour==24) { hour=0; day++; } //与上面的注释雷同// if(month==1 || month==3 || month==5 || month==7 || month==8 || month==10) { if(day==32) { day=1; month++; } } if(month==4 || month==6 || month==9 || month==11) { if(day==31) { day=1; month++; } } if(month==2) { if(year%4==0 && year%100!=0 &&day==30) { day=1; month++; } if(year%400==0 && day==30) { day=1; month++; } if(year%4!=0 && day==29) { day=1; month++; } if(year%4==0 && year%100==0 && year%400!=0 && day==29) { day=1; month++; } } if(month==12) { month=1; year++; if(year==10000) { year=0; } } } } if(sec>=60) { sec=0; min++; if(min>=60) { min=0; } } } } 实验现象与总结 烧录单片机后,单片机运行: 51单片机实现智能手表 大致实现就这样,觉得本篇博文不错的,也可以给博客点个赞、关注哟!后面也一起学习吧。 |
|
|
|
只有小组成员才能发言,加入小组>>
2561 浏览 0 评论
1158浏览 2评论
761浏览 1评论
514浏览 0评论
271浏览 0评论
447浏览 0评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-26 16:11 , Processed in 1.787934 second(s), Total 80, Slave 62 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (威廉希尔官方网站 图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号