写在前面

  这个工程大致综合了前段时间学习到的大多数内容,用状态机实现的按键检测、LCD1602的读写、ds18b20的读写、LCD1602的自编字符,还有最基础的定时器启停。
  功能为:时间显示、日期显示、星期显示、温度显示及调节
  代码如下:

/**************************************************************************************
*                     LCD1602电子时钟                                                 *
实现现象:下载程序后,LCD屏上第一行会显示出日期,第二行显示时间和当前温度。按下K1,进入时间调节模式,按下K2让光标往左移,按下K3让光标处的数字增1,按下K4让光标处的数字减1
注意事项:
***************************************************************************************/
//--头文件声明,数据类型声明--//
#include <reg52.h>
#include <intrins.h>
typedef unsigned char uint8;
typedef unsigned int uint16;#define key P3
#define no_key 0xff                 //无按键按下
#define key_state0 0                //状态定义
#define key_state1 1
#define key_state2 2
//---------端口声明---------//
sbit lcden = P2^7;
sbit lcdrs = P2^6;
sbit lcdrw = P3^6;sbit key1  = P3^2;
sbit key2  = P3^3;
sbit key3  = P3^4;
sbit key4  = P3^5;sbit dq    = P3^7;                  //DS18B20数据线
//---------函数声明---------//
void lcd_init();                    //LCD屏幕初始化
void wcom();                        //LCD写命令
void wdat();                        //LCD写数据
void display();                     //显示函数
void timeshow();                    //时间显示
void dateshow();                    //日期显示
void weekshow();                    //星期显示
void keyscan();                     //按键扫描
void key_disposal();                //键值处理
void day_jud();                     //日期处理
void ds18b20_init();                //18B20模块初始化
uint8 byte_read();                  //读去18B20一个字节
void byte_write();                  //写入18B20一个字节
void temp_read();                   //温度读取函数
void temp_display(uint8 position,uint16 tvalue1);       //温度显示函数
//---------变量定义---------//
uint8 time_1s,time_500ms;
uint8 second,minute = 59,hour = 23;
uint8 day = 1,month = 1,week = 1;            //设定初始
uint16 year = 2019,tvalue;
//uint8 temp;
uint8 cursor;                               //要调节的位置
uint8 key_value;
bit f_twinkle,f_mode,f_pressing,f_set;uint8 code table1[]={0x10,0x06,0x09,0x08,0x08,0x09,0x06,0x00};           //字符℃//---------长延时---------//
void delay(uint16 k){uint16 i,j;for(i=k;i>0;i--)for(j=110;j>0;j--);
}//---------短延时---------//
void delayus(uint8 time_1us){while(time_1us--);
}//---------定时器初始化---------//
void timer_init(){EA = 0;TR0 = 0;TMOD = 0x01;TH0 = 0x4C;        //50msTL0 = 0x00;ET0 = 1;     //允许Timer0中断ET1 = 1;EA  = 1;TR0 = 1;
}//---------Timer0中断服务函数---------//
void isr_timer0 () interrupt 1 using 0 {TH0 = 0x4C;        //50msTL0 = 0x00;keyscan();if(++time_500ms >= 10){time_500ms = 0;f_twinkle = ~f_twinkle;}if(!f_set){if(++time_1s >= 20){time_1s = 0;second++;}}
}void ds18b20_init(){                       //ds18b20初始化dq = 1;    _nop_();dq = 0;delayus(75);dq = 1;delayus(20);dq = 1;_nop_();
}void wcom(uint8 com){                      //LCD1602写命令lcdrw = 0;lcdrs = 0;P0 = com;lcden = 1;delay(5);lcden = 0;
}void wdat(uint8 dat){                      //LCD1602写数据lcdrw = 0;lcdrs = 1;P0 = dat;lcden = 1;delay(5);lcden = 0;
}void lcd_init(){                           //LCD初始化wcom(0x38);wcom(0x01);wcom(0x06);wcom(0x0c);wcom(0x86);wdat(0x2d);                              //"-"wcom(0x89);wdat(0x2d);                               //"-"wcom(0x80+0x40+0x04);wdat(0x3a);                               //":"wcom(0x80+0x40+0x07);wdat(0x3a);                               //":"
}void timeshow(uint8 position,uint8 time){      //时间显示,position为显示位置uint8 i,j;i = time/10;             //shiweij = time%10;               //geweiwcom(0x80+0x40+position);wdat(0x30+i);                //shiweiwdat(0x30+j);              //gewei
}void dateshow(uint8 position,uint8 date1){     //日期显示(月、日)uint8 i,j;i = date1/10;             //shiweij = date1%10;              //geweiwcom(0x80+position);wdat(0x30+i);              //shiweiwdat(0x30+j);              //gewei
}void yearshow(uint8 position,uint16 year1){        //年份显示uint8 i,j,m,n;i = year1/1000;                //qianweij = year1/100%10;         //baiweim = year1/10%10;n = year1%10;wcom(0x80+position);wdat(0x30+i);              //qianweiwdat(0x30+j);             //baiweiwdat(0x30+m);              //shiweiwdat(0x30+n);              //gewei
}void weekshow(uint8 position,uint8 week1){     //星期显示wcom(0x80+position);switch(week1){case 1:wdat(0x4d);wdat(0x4f);wdat(0x4e);break;case 2:wdat(0x54);wdat(0x55);wdat(0x45);break;case 3:wdat(0x57);wdat(0x45);wdat(0x44);break;case 4:wdat(0x54);wdat(0x48);wdat(0x55);break;case 5:wdat(0x46);wdat(0x52);wdat(0x49);break;case 6:wdat(0x53);wdat(0x41);wdat(0x54);break;case 7:wdat(0x53);wdat(0x55);wdat(0x4e);break;default:break;}
}void day_jud(){                                    //日期处理(秒、分、时、日、月进位)if(second > 59){second = 0;if(++minute > 59){minute = 0;if(++hour > 23){hour = 0;if(++week >= 8)   week = 1;day++;}}}if(minute > 59){minute = 0;if(++hour > 23){hour = 0;if(++week > 7)  week = 1;day++;}}if(hour > 23){hour = 0;if(++week > 7)  week = 1;day++;}if(day > 31 && (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12)){day = 1;month++;if(month >= 12){month = 1;year++;}}else if(day > 30 && (month == 4 || month == 6 || month == 9 || month == 11)){day = 1;month++;}else if(day > 29 && month == 2 && ((year%4 == 0 && year%100 != 0) || year%400 == 0)){day = 1;month++;}else if(day > 28 && month == 2){day = 1;month++;}
}void display(){                                        //显示函数if(!f_mode){                                  //时间显示模式yearshow(2,year);dateshow(7,month);dateshow(0x0a,day);weekshow(0x0d,week);timeshow(2,hour);timeshow(5,minute);timeshow(0x08,second);temp_read();                                //读取温度temp_display(0x0b,tvalue);                    //温度显示}else{                                            //时间调节模式wcom(0x80+0x40+0x0d);wdat(0x53);                                  //"S"wdat(0x45);                                  //"E"wdat(0x54);                                  //"T"if(f_twinkle && !f_pressing){                //闪烁标志位,0.5秒取反一次,为1则相应位置显示空白if(cursor == 0) {                     //按压标志位,按键按下时为1,不闪烁wcom(0x82);                          //要显示空白的位置wdat(0x20);                           //4位空白(实现闪烁效果)wdat(0x20);wdat(0x20);wdat(0x20);}if(cursor == 1) {wcom(0x87);                          //要显示空白的位置wdat(0x20);                           //2位空白wdat(0x20);}if(cursor == 2) {wcom(0x8a);wdat(0x20);wdat(0x20);}if(cursor == 3) {wcom(0x8d);wdat(0x20);wdat(0x20);wdat(0x20);}if(cursor == 4) {wcom(0x80+0x42);wdat(0x20);wdat(0x20);}if(cursor == 5) {wcom(0x80+0x45);wdat(0x20);wdat(0x20);}if(cursor == 6) {wcom(0x80+0x48);wdat(0x20);wdat(0x20);}}else {                                     //正常显示yearshow(2,year);dateshow(7,month);dateshow(0x0a,day);weekshow(0x0d,week);timeshow(2,hour);timeshow(5,minute);timeshow(0x08,second);}}
}void keyscan(){                                        //按键扫描static uint8 key_state;switch(key_state){case key_state0:                         //未按下if(!key1 || !key2 || !key3 || !key4){key_state = key_state1;}break;case key_state1:                           //有键按下f_pressing = 1;if(!key1){f_set = ~f_set;                        //秒走时停止f_mode = ~f_mode;                   //模式切换key_value = 1;key_state = key_state2;}else if(!key2){key_value = 2;key_state = key_state2;}else if(!key3){key_value = 3;key_state = key_state2;}else if(!key4){key_value = 4;key_state = key_state2;}else{key_state = key_state0;f_pressing = 0;                        //如果不清零,假如按键是无效按下,调节模式时就会停止闪烁}break;case key_state2:                            //松开按键if(key1 && key2 && key3 && key4){f_pressing = 0;key_state = key_state0;}break;}
}void key_disposal(){                               //键值处理if(!f_pressing){                              //松开按键才处理if(key_value == 1){key_value = 0;cursor = 0;                               //光标位置清零lcd_init();                             //清屏}else if(key_value == 2){key_value = 0;cursor++;                               //光标+(光标即闪烁的位置,实际上LCD上并无光标)if(cursor > 6){cursor = 0;}}else if((key_value == 3) && f_mode){      //光标所处位置值+key_value = 0;if(cursor == 0){year++;}else if (cursor == 1){if(++month > 12) month = 1;}else if(cursor == 2){if(++day > 31) day = 1;}else if(cursor == 3){if(++week > 7) week = 1;}else if(cursor == 4){if(++hour > 23) hour = 0;}else if(cursor == 5){if(++minute > 59) minute = 0;}else if(cursor == 6){if(++second > 59) second = 0;}}else if((key_value == 4) && f_mode){     //光标所处位置值-key_value = 0;if(cursor == 0 && year > 0){year--;}else if (cursor == 1 && month > 1){month--;}else if(cursor == 2 && day > 1){day--;}else if(cursor == 3 && week > 1){week--;}else if(cursor == 4 && hour > 0){hour--;}else if(cursor == 5 && minute > 0){minute--;}else if(cursor == 6 && second > 0){second--;}}}
}uint8 byte_read(){                                 //ds18b20 读字节uint8 i,j,dat;for(i = 0; i < 8 ; i++){dq = 0;_nop_();dq = 1;_nop_();j = dq;delayus(10);dq = 1;_nop_();dat = (j << 7 | dat >> 1);}return(dat);
}void byte_write(uint8 data1){                      //ds18b20 写字节uint8 i;for(i = 0; i < 8 ; i++){dq = 0;_nop_();dq = data1&0x01;delayus(10);dq = 1;_nop_();data1 >>= 1;}
}void temp_read(){                                  //读温度float data1;uint8 i,j;ds18b20_init();byte_write(0xcc);                             //向DS18B20发跳过读ROM命令byte_write(0x44);                                //启动DS18B20进行温度转换命令,转换结果存入内部RAMdelayus(80);ds18b20_init();byte_write(0xcc);                              //向DS18B20发跳过读ROM命令byte_write(0xbe);                                //读取温度寄存器等(共可读9个寄存器) 前两个就是温度delayus(80);i = byte_read();                             //内部RAM 低位j = byte_read();                             //内部RAM 高位data1 = (j*256 + i) * 6.25;                     //取出温度值tvalue = (uint16)data1;                         //强制转换
}void temp_display(uint8 position,uint16 tvalue1){  //温度显示uint8 i,j,m;i = tvalue1/1000;j = tvalue1%1000/100;m = tvalue1%100/10;                              //取出十位 个位 十分位wcom(0x80 + 0x40 + position);wdat(0x30 + i);wdat(0x30 + j);wdat(0x2e);                                     //"."wdat(0x30 + m);wdat(0x00);                                      //自编字符
}void main(){uint8 i;wcom(0x40);for(i = 0; i < 8 ; i++){wdat(table1[i]);                          //写入自编字符}timer_init();lcd_init();while(1){// if(++temp > 2){// temp = 0;// }// switch(temp){// case 0:day_jud();break;// case 1:key_disposal();break;// case 2:display();break;// }day_jud();key_disposal();display();}
}

显示的效果如下:

开发板的电路图如下,读者可以使用proteus搭建类似环境试验(仿真大概率温度传感器有问题),建议还是找个类似的开发板,把端口对应上,一个个功能逐个实现再整合再一起,上来就全复制,不仅不利于自己的消化学习,出了问题还不知道应该从哪着手去调试。

  这个电子时钟在调节时间日期的模式时,我一直想要做一个光标在要调节的参数底下闪烁的效果,但是由于LCD1602开光标的命令我打开之后,就算给光标设定了位置,光标还是会满屏跑,无法实现,是目前比较遗憾的。想了下如果把显示空白那部分改为让LCD每个大格的最下面一行的小格子亮,也能实现一个伪光标的效果。

uint8 code table2[]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff};          //字符__ 用来实现伪光标效果//主函数中添加wcom(0x48);                                     //声明自编字符第一行的位置for(i = 0; i < 8 ; i++){wdat(table2[i]);                            //写入自编字符}                                               //连续写入数据时,应该是先写入第一行的八位,在写入第二行的八位。//类似wcom(0x48);wdat(table2[0]);wcom(0x49);wdat(table2[1]);//只不过这个wcom(0x49)应该会在写完第一个八位数据即wdat(table2[0])后,//在写第二个八位数据时就自动匹配好了,不需要程序员再写出。//把display()函数中闪烁部分的wdat(0x20)替换为wdat(0x01);即可实现一个伪光标效果,不过这个光标的闪烁跟数字的闪烁是互补的,
//数字闪的时候光标常亮,光标闪的时候数字常亮。忘记实物是怎么样的逻辑了,暂时就先这样凑合吧。
void display(){                                     //显示函数if(!f_mode){                                  //时间显示模式yearshow(2,year);dateshow(7,month);dateshow(0x0a,day);weekshow(0x0d,week);timeshow(2,hour);timeshow(5,minute);timeshow(0x08,second);temp_read();                                //读取温度temp_display(0x0b,tvalue);                    //温度显示}else{                                            //时间调节模式wcom(0x80+0x40+0x0d);wdat(0x53);                                  //"S"wdat(0x45);                                  //"E"wdat(0x54);                                  //"T"if(f_twinkle && !f_pressing){                //闪烁标志位,0.4秒取反一次,为1则相应位置显示空白if(cursor == 0) {                     //按压标志位,按键按下时为1,不闪烁wcom(0x82);                          //要显示空白的位置wdat(0x01);                           //4位最下面一行亮,类似光标,伪光标wdat(0x01);wdat(0x01);wdat(0x01);}if(cursor == 1) {wcom(0x87);                         //要显示空白的位置wdat(0x01);                           //2位空白(实现闪烁效果)wdat(0x01);}if(cursor == 2) {wcom(0x8a);wdat(0x01);wdat(0x01);}if(cursor == 3) {wcom(0x8d);wdat(0x01);wdat(0x01);wdat(0x01);}if(cursor == 4) {wcom(0x80+0x42);wdat(0x01);wdat(0x01);}if(cursor == 5) {wcom(0x80+0x45);wdat(0x01);wdat(0x01);}if(cursor == 6) {wcom(0x80+0x48);wdat(0x01);wdat(0x01);}}else {                                     //正常显示yearshow(2,year);dateshow(7,month);dateshow(0x0a,day);weekshow(0x0d,week);timeshow(2,hour);timeshow(5,minute);timeshow(0x08,second);}}
}

LCD1602 电子时钟(时间日期温度显示)相关推荐

  1. CORE-ESP32C3|eink|墨水屏日历+时间日期+温度显示|I2C软件模拟| LuatOS-SOC接口|官方demo|学习(14):墨水屏动态日历+oled日期显示+ AHT10测温模组

    目录 参考博文 源于网友oled+eink+aht10项目 源代码修改及复现说明 主要修改 显示效果 ​编辑硬件准备 软件版本 日志及soc下载工具 软件使用 接线说明 天气显示屏 硬件接线 温度采集 ...

  2. 基于matlab的电子时钟,基于LCD1602电子时钟毕业设计报告.doc

    PAGE PAGE 32 PAGE \* MERGEFORMAT 0 信息与电子工程学院课程设计报告 课 程单片机技术应用设计题目基于LCD1602电子时钟专 业班 级成 员姓 名学 号分工成 绩指导 ...

  3. keil c语言 1602时钟代码,基于单片机的LCD1602电子时钟设计.doc

    基于单片机的LCD1602电子时钟设计 设计任务和目的 1.1.设计任务 (1):用单片机设计基于LCD1602的电子时钟,显示时间和日期: (2):误差精度控制在1s/天: (3):具有时间和日期的 ...

  4. 基于51单片机LCD1602电子时钟实物制作

    之前写了51单片机的LCD1602的程序,现在画了电路图,焊接了电路板,做出了基于51单片机LCD1602电子时钟的实物. 注意事项: 焊接过程中,注意提前布局电路. 对应好端口(避免接线错误),我由 ...

  5. STC15单片机+DS18B20+LCD1602+PCF8574转IIC接口温度显示

    STC15单片机+DS18B20+LCD1602+PCF8574转IIC接口温度显示

  6. 51单片机DS18B20+DS1302+LCD12864实时显示时间日期温度

    文章目录 介绍 Proteus仿真运行效果 源程序 主函数 DS1302时钟函数 DS18B20函数 LCD12864(不带字库) sharing函数 mine头文件 介绍 DS18B20采集温度 D ...

  7. 八位一体共阴极数码管显示电子时钟+闹铃+温度检测

    老师一枚,服务于学生,制作了电子时钟融合温度检测的系统. 首先在焊接式前一定要仿真,更要注意仿真引脚和实际引脚的不同. 下面这个链接是常用元器件实物引脚图和解释. CSDNhttps://mp.csd ...

  8. 基于51单片机的LCD1602电子时钟

    摘要: 51系列单片机是各单片机中最为典型和最有代表性的一种.由RAM ,ROM,CPU构成,定时,计数和多种接口于一体的微控制器.本次设计的数字电子时钟采用了STC89C52芯片进行控制,使用DS1 ...

  9. C51单片机的电子时钟(数码管显示)

    C51单片机的电子时钟 硬件环境: 相关开发板原理图解析 正式代码: 效果预览图 硬件环境: 普中科技51单片机开发板STC89C52 相关开发板原理图解析 查看这两个原理图,我们可以发现控制这8个数 ...

最新文章

  1. 列名必须是一个字符串或者数组_我爱Julia之入门-078(字符串08)
  2. SAP相关会计科目的设置OBYC
  3. swoole client php,Swoole Client
  4. Go丨语言学习笔记--func
  5. robotframework自动化测试修炼宝典_自动化测试之框架Cucumber和RobotFramework的实战对比...
  6. 云服务下的安全特点及基础防护
  7. EditPlus 3 实现将JSON字符串格式化、排版成JSON格式数据
  8. 下载视频 用IDM下载的视频花屏
  9. 【数据处理】——利用Excel VBA批量将详细地址转换成省市区三级行政区划
  10. Python爬虫系列之poizon爬虫newSign、sign、data加解密算法
  11. 如何查看本机路由表并进行分析?
  12. Android系统版本(截止2022年2月)
  13. 阿里服务器微信发不了图片,为什么微信发不了图片?这四招教你解决难题
  14. windows安装linux
  15. http服务器的实现
  16. 数据库三大范式的理解
  17. 开启tomcat服务后,如何解决浏览器访问不到tomcat中图片或文件的问题,以及如何设置访问图片路径
  18. 求AUC 95%置信区间
  19. 关于SSD 4K对齐
  20. 全球最大游戏外挂案件告破!案值达5亿,销往上百个国家和地区

热门文章

  1. 发布文章出现请勿使用默认标题
  2. HOJ 1276 士兵队列训练问题(stl, 水题)
  3. 基于京东云服务器来搭建javaWeb 运行环境
  4. 【千纸诗书】—— PHP/MySQL二手书网站后台开发之项目设计
  5. 计算机网络微课堂 学习笔记
  6. 暗金色 rgb_杜伽TAURUS K310樱桃RGB红轴体验:做工精良、手感优秀
  7. 怎样在JavaScript添加图片
  8. sketch插件导出html,Sketch插件根据命名导出HTML片段Sketch Emmet
  9. FLOWABLE 流程中的自动跳过
  10. 如何通过js关闭微信浏览器页面