本文主要是介绍基于51单片机的多功能LCD电子钟,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
1 设计总体目标
1.1 功能目标
1.实现显示实时时钟、闹铃、日期(秒、分、时、日、月、星期)。
2.利用按键可对闹铃、时间、日期、星期、进行设置,并可显示闹铃时间。当闹铃时间到蜂鸣器发出声响,按停止键使可使闹铃声停止。
3.显示实时温度
4.使所显示内容滚动;
5.温度报警;
LCD功能分区
1.2 总体结构
图1 总体结构
2硬件设计
1、晶振电路:
MCS-51 内部有一个用于构成振荡器的高增益反相放大器,引脚 XTAL1 和 XTAL2 分别是此放大器 的输入端和输出端。
MCS-51 可由内部方式或外部方式产生。内部方式时钟电路如下图所示。外接晶体以及电容 C1、C2 构成并联谐振电路,接在放大器的反 馈回路中,内部振荡器产生自激振荡,一般晶振可在 2-12MHz 之间任选。对外接电容值虽然没有严格的 要求,但电容的大小多少会影响振荡频率的高低、振荡器的稳定性、起振的快速性和温度和稳定性。外 接晶体振荡器时, C1、和 C2 通常选 30pF 左右。在设计印刷线路板时,晶体和电容应尽可能安装得与 单片机芯片靠近,以保证稳定可靠
2、复位电路
(1).上电自动复位方式 复位电路的基本功能是:系统上电时提供复位信号,直至系统电源稳定后,撤销复位信号。为可靠起见,电源稳定后还要经一定的延时才撤销复位信号,以防电源开关或电源插头分—合过程中引起的抖 动而影响复位。对于 MCS-51 单片机,只要在 RST 复位端接一个电容至 VCC 和一个电阻至VSS即可。 上电复位电路如下图所示。在加电瞬间,RST 端出现一定时间的高电平,只要高电平保持时间足够长,就可以使MCS-51复位。
(2).人工复位 除了上电复位外,有时还需要人工复位,图 2-11(b) 是实用的上电复位与人工复位电路,KG 为手动 复位开关,并联于上电自动复位电路,增加电容Ch可避免高频谐波对电路的干扰,增加二极管D在电 源电压瞬间下降时使电容迅速放电,一定宽度的电源毛刺也可令系统可靠复位。按一下开关 KG 就会在 RST 端出现一段时间的高电平,使单片机可靠复位。
3、LCD1602
LCD(Liquid Crystal Display,液晶显示器)是一种被动式的显示器,利用液晶能改变光线通过方向的特性,来达到显示的目的。
LCD具有功耗低、抗干扰能力强的优点,广泛应用于仪器仪表、控制系统以及笔记本计算机和手持电子产品。
JD51使用1602液晶,5V电压驱动,带背光,可显示两行,每行16个字符,不能显示汉字,内置含128个字符的ASCII字符集字库,只有并行接口,无串行接口,是2´16字符型液晶显示模块
1602的数据接口接到单片机的P1口,同时接上拉电阻。RS、E、RW分别接P2.6、P2.7、P3.6
4、按键输入
单片机的P3.2~P3.5分别接入按键,用来检测按键是否被按下(按下则为低电平)
5、DS18B20
温度传感器DS18B20是DALLAS公司生产的单总线数字温度传感器芯片,具有3引脚TO-92小体积封装形式;温度测量范围为-55℃~+125℃;可编程为9~12位A/D转换精度;用户可自设定非易失性的报警上下限值;被测温度用16位补码方式串行输出;测温分辨率可达0.0625℃;其工作电源既可在远端引入,也可采用寄生电源方式产生;多个DS18B20可以并联到3根或两根线上,CPU只需一根端口线就能与诸多DS18B20通信,占用微处理器的端口较少。
6、蜂鸣器
有源自激型蜂鸣器的工作发声原理是:直流电源输入经过振荡系统的放大取样电路在谐振装置作用下产生声音信号,当BUZZER引脚为低电平时,蜂鸣器通电,发出声音。
7、串行通信电路
MAX232是一种双组驱动器/接收器,片内含有一个电容性电压发生器以便在单5V电源供电时提供EIA/TIA-232-E电平。当用单片机和PC机通过串口进行通信,尽管单片机有串行通信的功能,但单片机提供的信号电平和RS232的标准不一样,因此要通max232这种类似的芯片进行电平转换。MAX232芯片的作用:是将单片机输出的TTL电平转换成PC机能接收的232电平或将PC机输出的232电平转换成单片机能接收的TTL电平。
3软件设计
1、时钟计时模块:
(1)、设计思想:单片机内部定时器0,方式1,允许中断;TMOD = 0x01;
初值为TH0=(65536-5000)/256、TL0=(65536-5000)%256;定义全局变量存放相应单元;计数5000次(计数一次约为1us)进入中断服务程序;中断服务程序对进中断计数,计数两百次秒单元加1(20050001us=1s),秒单元满60清零,分单元加1;分单元满60清零,时单元加1;时单元满24清零,日单元加一,星期单元加1;日单元满28、29、30、31置1,月单位加一;星期日到置1;月单元满12置1;
(2)、流程图:
2、按键输入调节模块
(1)、设计思想:按键一为功能选择键(uchar select_function;)每按下一次相应的全局变量加1若全局变量不为0的话使计时器停止计数,全局变量不同的值对应不同的功能入口(1为调节当前时间秒、2为调节当前时间分……),当全局变量达到最大值时再次按下功能按键(或者按下退出键k4)则全局变量清零退出调节功能并使计时器开始计数。具有按键消抖功能(判断—延时—再判断);while (!k1)检测按键是否松开,保证按一次加一次!
(2)、流程图:
3、温度读入模块
(1)设计思想:
<1>、DS18B20初始化步骤如下:将总线拉低,持续时
480us到960us之间、将总线拉高、等待DS18B20的应答,若初始化成功,会在15-60us之后产生一个低电平信号,该信号会持续60us到240us之后DS18B20会主动释放总线,总线电平会被拉高;
<2>、读时序:分为读“0”时序和读“1”时序两个过程。当要读取DS18B20的数据时,将总线拉低,并保持1ms的时间,然后将总线拉高,此时需尽快读取。从拉低到读取引脚状态的时间不能超过15ms。
<3>、写时序分为写“0”时序和写“1”时序两个过程。DS18B20写“0”时序和写“1”时序的要求不同,当要写“0”时,单总线要被拉低至少60ms,以保证DS18B20能够在15ms到45ms之间正确地采样I/O总线上的“0”电平;当要写“1”时,单总线被拉低之后,在15ms之内就得释放单总线。
(2)、流程图:
4、闹铃模块
(1)设计思想:当前时间和闹钟时间相等蜂鸣器响(P2.4=0),按下按键停止蜂鸣
(2)、流程图:
5、温度报警模块:
(1)设计思想:温度达到预设值后蜂鸣器响,同时LED亮;温度低于预
设值后蜂鸣器停止响,同时LED灭;
(2)、流程图:
6、LCD显示模块
(1)设计思想:对要显示的相应的值的不同位进行分离,通过数组索引(uchar str[] = {“0123456789”}, wk[] ={“MoTuWeThFrSaSu”})的方式找到对应字符并显示;开机或同时按下2、3键滚动显示,滚动显示用一for循环实现。
(2)、流程图:
4 实验结果
LCD上显示时间、日期、温度、星期、闹钟。当前温度为28.3度,大于预设值25度,LED指示灯亮
LCD滚动功能
Proteus仿真:
5 心得
本次单片机课设的设计和LCD具体的功能分区自己的确花了很长时间去思考,去合理的设计,去充分利用,在程序编写的过程中遇到很多麻烦,自己能参考的就只有MOOC的课程和相关PPT,花了很长时间调试了很久,单片机硬件本身也出现过很多问题,单片机插上电源后电源指示灯不亮,但不知道为什么后来自己好了,可能是因为单片机在电流或电压过大时会自我保护吧!总之,在自己的亲自动手下学到了很多东西!!!
还有在Proteus仿真的过程中也出现了很多问题,仿真时LCD只是亮而不显示数据,而且P0口引脚既不是蓝色也不是红色,而是灰色,这就离谱了,经分析原来是P0口数据传输要接上拉电阻才能保证正确的传输;但是在我接好上拉电阻的时候还是不行,LCD只是亮而不显示数据,又经过长时间的分析和查验,一遍一遍地改,甚至重新换了Proteus的版本,到最后想放弃,因为实在找不到什么原因,而且自己也花了很多时间在上面。最后,我听别人说有极大可能是程序的问题,于是我又仔细检查了一遍程序,发现是LCD的判忙函数出了问题,但在实物演示中去没有此问题,这就玄乎了。通过仿真,让我明白了仿真软件只是提供一个平台,他的结果绝不是实际中的结果,他只是提供一个参考而已;仿真和实际还是有很大区别的,就跟理论与实际一样,偏差还是有的;所以,像单片机这样实践性很强的课程还是要多动手、多动脑。
学校大老远把单片机寄来给我们,感觉大家都不容易;用单片机做实验,除了要有丰富的单片机基础知识外,还要对相关的硬件扩展的使用十分熟悉,并掌握JD51单片机开发平台的电路图,引脚功能等;
本次课设我充分考虑了LCD1602的显示分区(2*16个字符),并对其进行合理的功能分区,尽可能发挥LCD1604的显示能力;对不同的功能模块进行了深入细致的研究;本次设计的单片机LCD电了钟,其误差主要来源包括晶体频率误差,定时器溢出误差,延迟误差。晶体频率产生震荡,容易产生走时误差(时间变慢);定时器溢出的时间误差,本应这一次溢出,但却在下一移溢出,造成走时误差时间变慢);延迟时间过长或过短,都会造成与基准时间产生偏差,造成走时误差(时间变慢)。该电子钟的计时误差主要是由于晶振频率造成的,由于一个机器周期等于12个振荡器脉冲周期,因此计数频率为振荡频率的1/12。该单片机采用11.0592MHz晶体,因此计数器加1的时间是0.9216us;但问题来了,为什么单片机不用12MHz的晶体呢(12M的晶振,振荡频率明显高于11.0592M的,按理说12MHz的晶振可以提高单片机的性能;而且计数处理起来也比较方便)我对此仔细调研分析了一番:
51单片机串口工作在方式1,上时,给串口使用的时钟频率要先除2,再除16,为什么要除2呢?因为实际上对于单片机的串口及外部的通信模块来说,单片机的晶振频率即使在12分频后,依然太快,所以先除2,降低串口模块所使用的的时钟频率。为什么要除16呢?因为在串口通信中为了保证所接收的数据的正确性,先对每位信号采集16次,再取其中的7、8、9次,如果有两次是高电平,就认定这一位是1,如果有2位是低电平,就认定这一位是0,所以,公式中频率要除16,。至于为什么要除12是因为公式中的频率Fosc是晶振频率,但是单片机所使用的的频率是经过了12分频的。所以对单片机而言一个机器周期等于12个时钟周期,为了理解上诉公式,有以下知识点需了解:
1个时钟周期=1/晶振周期 = 1/Fosc
1个机器周期=12*(1/Fosc)
定时器T1的计数值每经过一个机器周期加1,即每经过12*(1/Fosc)秒,TL1加1,当TL1等于256就溢出,TH1将值重新赋给TL1,TL1开始重新计数。
如此我们设经过Y个机器周期TL1溢出,则:
Y*(12/Fosc)= (1/Baud)/2/16
Y =Fosc/Baud/32/12
由于使用T1的模式2,所以,设自动重装载值为RELOAD,则:
RELOAD= 256 – Y = 256- (Fosc/Baud/32/12)
Baud =Fosc/(256 - RELOAD)/32/12
而误差等于实际值减理论值,再除理论值,再乘以100%,所以误差计算公式为:
Error= ((Baud – Baud0)/Baud0 )*100%
Baud0= 标准波特率
Baud =实际波特率
综上所述,可以得到以下公式:
RELAOD= 256 – INF(Fosc/Baud0/32/12 + 0.5)
Baud =Fosc/(256 - RELOAD)/32/12
Error= (Baud – Baud0)/Baud0 * 100%
RELOAD= 自动重装载值
Baud0= 标准波特率
Baud =实际波特率
Fosc =晶振频率
Error = 偏差
INF()表示取整运算(RELOAD只能为整数),即去掉小数,0.5可以达到四舍五入的目的。
RELOAD= 256 – INF(Fosc/Baud0/32/12 + 0.5)= 256 - 3= 253
Buad =Fosc/(256 - RELOAD)/32/12= 12 * 10^6/3/32/12= 10416.67
Error= (Buad – Buad0)/Buad0 * 100%=8.51%
当Baud0 =9600时,使用11.059200MHz晶振:
RELOAD= 256 – INF(Fosc/Baud0/32/12 + 0.5)= 253
Buad =Fosc/(256 -RELOAD)/32/12= 9600
Error = 0
可以看出:使用11.0592MHz的晶振可以使串口通信的误差为0;而12MHz的晶振误差为8.51%,比较大;
6源程序清单
\#include "reg52.h"\#define uchar unsigned char\#define uint unsigned intsbit RS = P2 ^ 6;sbit RW = P3 ^ 6;sbit E = P2 ^ 7;sbit k1 = P3 ^ 2; //定义P31口是k1 功能选择键sbit k2 = P3 ^ 3; //增加键sbit k3 = P3 ^ 4; //减少键sbit k4 = P3 ^ 5; //闹铃停止键sbit DQ = P3 ^ 7; //温度脚sbit ALA = P2 ^ 4; //闹铃脚sbit Temp=P1 ^ 0 ;uchar count = 0; //中断计数uchar select_function; //按键功能选择uchar hour = 12, min = 59, sec = 30, shi = 0, feng = 1, miao = 30, day = 29, month = 2, year = 2020, week = 7;//当前时间时、分、秒;闹钟时、分、秒uchar temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8, temp9, temp10, temp11, temp12, temp13, temp14, temp15;//中间变量uchar str[] = { "0123456789" }, wk[] = { "MoTuWeThFrSaSu" }, temper[2] = { 0,0 }, transtemper[3] = { 0,0,0 };// 延时子程序typedef bit BOOL;void delay(uchar ms) { uchar i; while (ms--) { for (i = 0; i < 250; i++); }}//长延时void longdelay(uchar s) { while (s--) { delay(60); }}/**********************定时时钟模块********************/void inittimer() { TMOD = 0x01; TH0 = (65536 - 5000) / 256; TL0 = (65536 - 5000) % 256; ET0 = 1; EA = 1; TR0 = 1;}void timer0_isr() interrupt 1 { TH0 = (65536 - 5000) / 256; //重装初值 TL0 = (65536 - 5000) % 256; count++; if (count == 200) { sec++; count = 0; } if (sec == 60) { min++; sec = 0; } if (min == 60) { hour++; min = 0; } if (hour == 24) { day++; if (week == 7) { week = 1; } else { week++; } hour = 0; } if (day == 32 && (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12)) { month++; day = 1; } if (day == 31 && (month == 4 || month == 6 || month == 9 || month == 11)) { month++; day = 1; } if (day == 30 && (month == 2 && (year % 400 == 0 || (year % 4 == 0 && year % 100 != 0)))) { month++; day = 1; } if (day == 29 && (month == 2 && !(year % 400 == 0 || (year % 4 == 0 && year % 100 != 0)))) { month++; day = 1; } if (month == 12) { month = 1; }}/******************按键输入调节模块*********************/void keypros() { if (k1 == 0) { //检测按键K1是否按下 delay(10); //消除抖动 一般大约10ms if (k1 == 0) { //再次判断按键是否按下 select_function++; //不同值对应当前时间时分秒、闹钟时分秒、日月、星期 if (select_function == 10)select_function = 0; if (select_function != 0)TR0 = 0;//停止计时开始调整 } while (!k1); //检测按键是否松开 if (select_function == 0)TR0 = 1; } if (select_function == 1) { //调节当前时间秒 if (k2 == 0) { sec++; if (sec >= 60)sec = 0; } while (!k2); if (k3 == 0) { if (sec == 0)sec = 60; sec--; } while (!k3); } if (select_function == 2) { //调节当前时间分 if (k2 == 0) { min++; if (min >= 60)min = 0; } while (!k2); if (k3 == 0) { if (min == 0)min = 60; min--; } while (!k3); } if (select_function == 3) { //调节当前时间时 if (k2 == 0) { hour++; if (hour >= 24)hour = 0; } while (!k2); if (k3 == 0) { if (hour == 0)hour = 24; hour--; } while (!k3); } if (select_function == 4) { //调节闹钟时间秒 if (k2 == 0) { miao++; if (miao >= 60)miao = 0; } while (!k2); if (k3 == 0) { if (miao == 0)miao = 60; miao--; } while (!k3); } if (select_function == 5) { //调节闹钟时间分 if (k2 == 0) { feng++; if (feng >= 60)feng = 0; } while (!k2); if (k3 == 0) { if (feng == 0)feng = 60; feng--; } while (!k3); } if (select_function == 6) { //调节闹钟时间时 if (k2 == 0) { shi++; if (shi >= 24)shi = 0; } while (!k2); if (k3 == 0) { if (shi == 0)shi = 24; shi--; } while (!k3); } if (select_function == 7) { //调节当前时间日 if (k2 == 0) { day++; if (day == 32 && (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12)) { day = 1; } if (day == 31 && (month == 4 || month == 6 || month == 9 || month == 11)) { day = 1; } if (day == 30 && (month == 2 && (year % 400 == 0 || (year % 4 == 0 && year % 100 != 0)))) { day = 1; } if (day == 29 && (month == 2 && !(year % 400 == 0 || (year % 4 == 0 && year % 100 != 0)))) { day = 1; } } while (!k2); if (k3 == 0) { day--; if (day == 0 && (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12)) { day = 31; } if (day == 0 && (month == 4 || month == 6 || month == 9 || month == 11)) { day = 30; } if (day == 0 && (month == 2 && (year % 400 == 0 || (year % 4 == 0 && year % 100 != 0)))) { day = 29; } if (day == 0 && (month == 2 && !(year % 400 == 0 || (year % 4 == 0 && year % 100 != 0)))) { day = 28; } while (!k3); } } if (select_function == 8) { //调节当前时间月 if (k2 == 0) { month++; if (month >= 13)month = 1; } while (!k2); if (k3 == 0) { month--; if (month <= 0)month = 12; } while (!k3); } if (select_function == 9) { //调节当前时间星期 if (k2 == 0) { week++; if (week >= 8)week = 1; } while (!k2); if (k3 == 0) { week--; if (week <= 0)week = 7; } while (!k3); } temp0 = month / 10; temp1 = month % 10; temp2 = day / 10; temp3 = day % 10; temp4 = hour / 10; temp5 = hour % 10; temp6 = min / 10; temp7 = min % 10; temp8 = sec / 10; temp9 = sec % 10; temp10 = shi / 10; temp11 = shi % 10; temp12 = feng / 10; temp13 = feng % 10; temp14 = miao / 10; temp15 = miao % 10;}/***********************温度读入模块************************/void delay_18B20(unsigned int i) { //18B20延时 for (; i > 0; i--);}//初始化void Init_DS18B20(void) { unsigned char x = 0; DQ = 1; //DQ拉高 delay_18B20(8); //稍作延时 DQ = 0; //DQ拉低 delay_18B20(80); //延时大于480us DQ = 1; //拉高总线 delay_18B20(14); x = DQ; //若x=0初始化成功,若x=1初始化失败 delay_18B20(20);}//读时序unsigned char ReadOneChar(void) { unsigned char i = 0; unsigned char dat = 0; for (i = 8; i > 0; i--) { DQ = 0; // 拉低总线 dat >>= 1;//每读取移位向右移移位 DQ = 1; //拉高总线 if (DQ) dat |= 0x80; delay_18B20(4); } return(dat);}//写时序void WriteOneChar(unsigned char dat) { unsigned char i = 0; for (i = 8; i > 0; i--) { DQ = 0; DQ = dat & 0x01; if (DQ) { delay_18B20(1); DQ = 1; } else { delay_18B20(5); DQ = 1; } dat >>= 1; }}//读温度void ReadTemperature(void) { unsigned char a = 0, b = 0, temp = 0; float bacxbit = 0; Init_DS18B20(); WriteOneChar(0xCC); // 跳过读序列号操作 WriteOneChar(0x44); // 启动温度转换 delay_18B20(100); // Init_DS18B20(); WriteOneChar(0xCC); //跳过读序列号操作 WriteOneChar(0xBE); //读取温度寄存器 delay_18B20(100); a = ReadOneChar(); //读温度低位 b = ReadOneChar(); //读温度高位 temper[0] = a & 0x0f; a = a >> 4; temper[1] = b << 4; temper[1] = temper[1] | a; bacxbit = temper[0]; bacxbit = bacxbit * 6.25; temp = bacxbit; transtemper[2] = temp / 10 % 10;//温度小数 transtemper[1] = temper[1] % 10;//温度个位 transtemper[0] = temper[1] / 10 % 10;//温度十位}/*******************************闹钟模块**********************/void Alarm_() { if (hour == shi && min == feng && sec == miao) { ALA = 0; } if (k4 == 0) { //检测按键K1是否按下 delay(10); //消除抖动 一般大约10ms if (k4 == 0) { //再次判断按键是否按下 ALA = 1; } }}/******************* 温度报警模块************************/void Temp_Alarm_() { if( transtemper[0]*10+transtemper[1]+transtemper[2]*0.1>25 ) { Temp=0; ALA = 0; } else { Temp=1; ALA = 1; }}/***************************LCD显示模块************************/// 测试LCD忙碌状态BOOL lcd_bz() { BOOL result; RS = 0; RW = 1; E = 1; result = (BOOL)(P0 & 0x80); //强制类型转换为BOOL,非0则等于1 E = 0; return result;}//写命令void writecom(uchar com) { while (lcd_bz()); RS = 0; RW = 0; E = 0; P0 = com; E = 1; E = 0;}//写数据void writedat(uchar dat) { while (lcd_bz()); RS = 1; RW = 0; E = 0; P0 = dat; E = 1; E = 0;}//lcd初始化void initlcd() { writecom(0x38); delay(1); writecom(0x0c); delay(1); writecom(0x06); delay(1); writecom(0x01); delay(1);}//显示数据void display() { writecom(0x80); writedat(str[temp0]); writedat(str[temp1]); writedat('-'); writedat(str[temp2]); writedat(str[temp3]); writecom(0x80 | 0x06); writedat(wk[(week - 1) * 2]); writecom(0x80 | 0x08); writedat(str[temp4]); writedat(str[temp5]); writedat(':'); writedat(str[temp6]); writedat(str[temp7]); writedat(':'); writedat(str[temp8]); writedat(str[temp9]); writecom(0x80 + 0x40); writedat(str[transtemper[0]]); writedat(str[transtemper[1]]); writedat('.'); writedat(str[transtemper[2]]); writedat('C'); writecom(0x80 + 0x40 | 0x06); writedat(wk[(week - 1) * 2 + 1]); writecom((0x80 + 0x40) | 0x08); writedat(str[temp10]); writedat(str[temp11]); writedat(':'); writedat(str[temp12]); writedat(str[temp13]); writedat(':'); writedat(str[temp14]); writedat(str[temp15]);}//滚动显示void rolldisplay() { uint i = 0, j = 0; writecom(0x01); longdelay(8); for (i = 0; i < 16; i++) { writecom((0x80 | (15 - i))); writedat(str[temp0]); writedat(str[temp1]); writedat('-'); writedat(str[temp2]); writedat(str[temp3]); writedat(' '); writedat(wk[(week - 1) * 2]); writedat(' '); writedat(str[temp4]); writedat(str[temp5]); writedat(':'); writedat(str[temp6]); writedat(str[temp7]); writedat(':'); writedat(str[temp8]); writedat(str[temp9]); writecom((0x80 + 0x40 | (15 - i))); writedat(str[transtemper[0]]); writedat(str[transtemper[1]]); writedat('.'); writedat(str[transtemper[2]]); writedat('C'); writedat(' '); writedat(wk[(week - 1) * 2 + 1]); writedat(' '); writedat(str[temp10]); writedat(str[temp11]); writedat(':'); writedat(str[temp12]); writedat(str[temp13]); writedat(':'); writedat(str[temp14]); writedat(str[temp15]); longdelay(7); }}void main() { uint i=0; initlcd();//初始化LCD inittimer();//初始化定时器 while (1) { keypros(); Alarm_(); ReadTemperature(); Temp_Alarm_(); display(); if (i == 0 | (k3 == 0 && k2 == 0)) { rolldisplay();//开始时和按键2、3同时按下滚动显示 i = 1; } }汇编目前完成了时钟计时功能:RS EQU P2.6RW EQU P3.6E EQU P2.7 ORG 0000H LJMP MAIN ORG 0BH LJMP T1INT ORG 30H MAIN: MOV SP, #6FH MOV TMOD, #00000001B MOV TH0, #3CH MOV TL0, #0D4H MOV IE, #82H MOV 46H, #20 ;1000ms ACALL DD1 ;DD1是LCD初始化SETB EA; LCALL DATAINIT SETB TR0 H:LCALL DATAPRO ACALL DD2;DD2是LCD第一行显示TABLE1 SJMP H DATAPRO: CJNE R5,#60, PRO MOV R5, #0 INC R6 CJNE R6,#60, PRO MOV R6, #0 INC R7 CJNE R7,#24, PRO MOV R7,#0 PRO: MOV A,R7 MOV B, #10 DIV AB MOV 20H, A MOV 21H, B MOV 22H, #16 MOV A,R6 MOV B, #10 DIV AB MOV 23H, A MOV 24H, B MOV 25H, #16 MOV A, R5 MOV B, #10 DIV AB MOV 26H, A MOV 27H, B RET T1INT: CLR TR0 DJNZ 46H, LP4 MOV 46H, #20 INC R5 LP4: MOV TH0, #3CH MOV TL0, #0D4H SETB TR0 RETI DATAINIT: MOV R5, #0 ;sec MOV R6, #0 ;min MOV R7, #0 ;hour RET DD1: MOV p0,#01H;清屏CALL ENABLE MOV p0,#3CH;显示功能CALL ENABLEMOV p0,#0CH;显示开关控制CALL ENABLEMOV p0,#06H;显示光标右移加1CALL ENABLERETDD2: MOV p0,#80H;第一行的开始位置CALL ENABLECALL WRITE1;到TABLE1取码RETENABLE:CLR RS;送命令CLR RWCLR ECALL DELAYSETB ERETWRITE1: MOV R1,#08HMOV R0,#20HA1: MOV A,@R0; 到table取码MOVC A,@A+DPTRCALL WRITE2; 显示到LEDINC R0DJNZ R1,A1RETWRITE2: MOV p0,A;显示SETB RSCLR RWCLR ECALL DELAYSETB ERETDELAY:MOV 41H,#20 LP1: MOV 40H,#50 LP0: DJNZ 40H,LP0 DJNZ 41H,LP1 RET TAB: DB 30H, 31H, 32H, 33H 34H, 35H, 36H, 37H 38H, 39H END }
这篇关于基于51单片机的多功能LCD电子钟的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!