LCD1602 电子时钟(时间日期温度显示)
写在前面
这个工程大致综合了前段时间学习到的大多数内容,用状态机实现的按键检测、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 电子时钟(时间日期温度显示)相关推荐
- CORE-ESP32C3|eink|墨水屏日历+时间日期+温度显示|I2C软件模拟| LuatOS-SOC接口|官方demo|学习(14):墨水屏动态日历+oled日期显示+ AHT10测温模组
目录 参考博文 源于网友oled+eink+aht10项目 源代码修改及复现说明 主要修改 显示效果 编辑硬件准备 软件版本 日志及soc下载工具 软件使用 接线说明 天气显示屏 硬件接线 温度采集 ...
- 基于matlab的电子时钟,基于LCD1602电子时钟毕业设计报告.doc
PAGE PAGE 32 PAGE \* MERGEFORMAT 0 信息与电子工程学院课程设计报告 课 程单片机技术应用设计题目基于LCD1602电子时钟专 业班 级成 员姓 名学 号分工成 绩指导 ...
- keil c语言 1602时钟代码,基于单片机的LCD1602电子时钟设计.doc
基于单片机的LCD1602电子时钟设计 设计任务和目的 1.1.设计任务 (1):用单片机设计基于LCD1602的电子时钟,显示时间和日期: (2):误差精度控制在1s/天: (3):具有时间和日期的 ...
- 基于51单片机LCD1602电子时钟实物制作
之前写了51单片机的LCD1602的程序,现在画了电路图,焊接了电路板,做出了基于51单片机LCD1602电子时钟的实物. 注意事项: 焊接过程中,注意提前布局电路. 对应好端口(避免接线错误),我由 ...
- STC15单片机+DS18B20+LCD1602+PCF8574转IIC接口温度显示
STC15单片机+DS18B20+LCD1602+PCF8574转IIC接口温度显示
- 51单片机DS18B20+DS1302+LCD12864实时显示时间日期温度
文章目录 介绍 Proteus仿真运行效果 源程序 主函数 DS1302时钟函数 DS18B20函数 LCD12864(不带字库) sharing函数 mine头文件 介绍 DS18B20采集温度 D ...
- 八位一体共阴极数码管显示电子时钟+闹铃+温度检测
老师一枚,服务于学生,制作了电子时钟融合温度检测的系统. 首先在焊接式前一定要仿真,更要注意仿真引脚和实际引脚的不同. 下面这个链接是常用元器件实物引脚图和解释. CSDNhttps://mp.csd ...
- 基于51单片机的LCD1602电子时钟
摘要: 51系列单片机是各单片机中最为典型和最有代表性的一种.由RAM ,ROM,CPU构成,定时,计数和多种接口于一体的微控制器.本次设计的数字电子时钟采用了STC89C52芯片进行控制,使用DS1 ...
- C51单片机的电子时钟(数码管显示)
C51单片机的电子时钟 硬件环境: 相关开发板原理图解析 正式代码: 效果预览图 硬件环境: 普中科技51单片机开发板STC89C52 相关开发板原理图解析 查看这两个原理图,我们可以发现控制这8个数 ...
最新文章
- 列名必须是一个字符串或者数组_我爱Julia之入门-078(字符串08)
- SAP相关会计科目的设置OBYC
- swoole client php,Swoole Client
- Go丨语言学习笔记--func
- robotframework自动化测试修炼宝典_自动化测试之框架Cucumber和RobotFramework的实战对比...
- 云服务下的安全特点及基础防护
- EditPlus 3 实现将JSON字符串格式化、排版成JSON格式数据
- 下载视频 用IDM下载的视频花屏
- 【数据处理】——利用Excel VBA批量将详细地址转换成省市区三级行政区划
- Python爬虫系列之poizon爬虫newSign、sign、data加解密算法
- 如何查看本机路由表并进行分析?
- Android系统版本(截止2022年2月)
- 阿里服务器微信发不了图片,为什么微信发不了图片?这四招教你解决难题
- windows安装linux
- http服务器的实现
- 数据库三大范式的理解
- 开启tomcat服务后,如何解决浏览器访问不到tomcat中图片或文件的问题,以及如何设置访问图片路径
- 求AUC 95%置信区间
- 关于SSD 4K对齐
- 全球最大游戏外挂案件告破!案值达5亿,销往上百个国家和地区