基于51单片机的多功能LCD电子钟

1 设计总体目标

1.1 功能目标

1.实现显示实时时钟、闹铃、日期(秒、分、时、日、月、星期)。

2.利用按键可对闹铃、时间、日期、星期、进行设置,并可显示闹铃时间。当闹铃时间到蜂鸣器发出声响,按停止键使可使闹铃声停止。

3.显示实时温度

4.使所显示内容滚动;

5.温度报警;

image-20240306103553555

LCD功能分区

image-20240306103603181

1.2 总体结构

image-20240306103613466

图1 总体结构

2硬件设计

1、晶振电路:

MCS-51 内部有一个用于构成振荡器的高增益反相放大器,引脚 XTAL1 和 XTAL2 分别是此放大器 的输入端和输出端。

MCS-51 可由内部方式或外部方式产生。内部方式时钟电路如下图所示。外接晶体以及电容 C1、C2 构成并联谐振电路,接在放大器的反 馈回路中,内部振荡器产生自激振荡,一般晶振可在 2-12MHz 之间任选。对外接电容值虽然没有严格的 要求,但电容的大小多少会影响振荡频率的高低、振荡器的稳定性、起振的快速性和温度和稳定性。外 接晶体振荡器时, C1、和 C2 通常选 30pF 左右。在设计印刷线路板时,晶体和电容应尽可能安装得与 单片机芯片靠近,以保证稳定可靠

image-20240306103626330

2、复位电路

(1).上电自动复位方式 复位电路的基本功能是:系统上电时提供复位信号,直至系统电源稳定后,撤销复位信号。为可靠起见,电源稳定后还要经一定的延时才撤销复位信号,以防电源开关或电源插头分—合过程中引起的抖 动而影响复位。对于 MCS-51 单片机,只要在 RST 复位端接一个电容至 VCC 和一个电阻至VSS即可。 上电复位电路如下图所示。在加电瞬间,RST 端出现一定时间的高电平,只要高电平保持时间足够长,就可以使MCS-51复位。

(2).人工复位 除了上电复位外,有时还需要人工复位,图 2-11(b) 是实用的上电复位与人工复位电路,KG 为手动 复位开关,并联于上电自动复位电路,增加电容Ch可避免高频谐波对电路的干扰,增加二极管D在电 源电压瞬间下降时使电容迅速放电,一定宽度的电源毛刺也可令系统可靠复位。按一下开关 KG 就会在 RST 端出现一段时间的高电平,使单片机可靠复位。

image-20240306103636492

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

image-20240306103645881 image-20240306103653412

4、按键输入

单片机的P3.2~P3.5分别接入按键,用来检测按键是否被按下(按下则为低电平)

image-20240306103702644

5、DS18B20

温度传感器DS18B20是DALLAS公司生产的单总线数字温度传感器芯片,具有3引脚TO-92小体积封装形式;温度测量范围为-55℃~+125℃;可编程为9~12位A/D转换精度;用户可自设定非易失性的报警上下限值;被测温度用16位补码方式串行输出;测温分辨率可达0.0625℃;其工作电源既可在远端引入,也可采用寄生电源方式产生;多个DS18B20可以并联到3根或两根线上,CPU只需一根端口线就能与诸多DS18B20通信,占用微处理器的端口较少。

image-20240306103744156

image-20240306103806645

6、蜂鸣器

有源自激型蜂鸣器的工作发声原理是:直流电源输入经过振荡系统的放大取样电路在谐振装置作用下产生声音信号,当BUZZER引脚为低电平时,蜂鸣器通电,发出声音。

image-20240306103818671

7、串行通信电路

MAX232是一种双组驱动器/接收器,片内含有一个电容性电压发生器以便在单5V电源供电时提供EIA/TIA-232-E电平。当用单片机和PC机通过串口进行通信,尽管单片机有串行通信的功能,但单片机提供的信号电平和RS232的标准不一样,因此要通max232这种类似的芯片进行电平转换。MAX232芯片的作用:是将单片机输出的TTL电平转换成PC机能接收的232电平或将PC机输出的232电平转换成单片机能接收的TTL电平。

image-20240306103828520

3软件设计

image-20240306103842758

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)、流程图:

image-20240306103851040

2、按键输入调节模块

(1)、设计思想:按键一为功能选择键(uchar select_function;)每按下一次相应的全局变量加1若全局变量不为0的话使计时器停止计数,全局变量不同的值对应不同的功能入口(1为调节当前时间秒、2为调节当前时间分……),当全局变量达到最大值时再次按下功能按键(或者按下退出键k4)则全局变量清零退出调节功能并使计时器开始计数。具有按键消抖功能(判断—延时—再判断);while (!k1)检测按键是否松开,保证按一次加一次!

(2)、流程图:

image-20240306103900443

image-20240306103909244

3、温度读入模块

(1)设计思想:

image-20240306103919695

<1>、DS18B20初始化步骤如下:将总线拉低,持续时

480us到960us之间、将总线拉高、等待DS18B20的应答,若初始化成功,会在15-60us之后产生一个低电平信号,该信号会持续60us到240us之后DS18B20会主动释放总线,总线电平会被拉高;

image-20240306103951845

<2>、读时序:分为读“0”时序和读“1”时序两个过程。当要读取DS18B20的数据时,将总线拉低,并保持1ms的时间,然后将总线拉高,此时需尽快读取。从拉低到读取引脚状态的时间不能超过15ms。

image-20240306104001305

<3>、写时序分为写“0”时序和写“1”时序两个过程。DS18B20写“0”时序和写“1”时序的要求不同,当要写“0”时,单总线要被拉低至少60ms,以保证DS18B20能够在15ms到45ms之间正确地采样I/O总线上的“0”电平;当要写“1”时,单总线被拉低之后,在15ms之内就得释放单总线。

(2)、流程图:

image-20240306104012501

4、闹铃模块

(1)设计思想:当前时间和闹钟时间相等蜂鸣器响(P2.4=0),按下按键停止蜂鸣

(2)、流程图:

image-20240306104027573

5、温度报警模块:

(1)设计思想:温度达到预设值后蜂鸣器响,同时LED亮;温度低于预

设值后蜂鸣器停止响,同时LED灭;

(2)、流程图:

image-20240306104036718

6、LCD显示模块

(1)设计思想:对要显示的相应的值的不同位进行分离,通过数组索引(uchar str[] = {“0123456789”}, wk[] ={“MoTuWeThFrSaSu”})的方式找到对应字符并显示;开机或同时按下2、3键滚动显示,滚动显示用一for循环实现。

(2)、流程图:

image-20240306104044506

4 实验结果

LCD上显示时间、日期、温度、星期、闹钟。当前温度为28.3度,大于预设值25度,LED指示灯亮

image-20240306104059218image-20240306104114979

LCD滚动功能

image-20240306104140626image-20240306104148659

Proteus仿真:

image-20240306104159406

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 int

sbit 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);  //消除抖动 一般大约10msif (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);  //消除抖动 一般大约10msif (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();//初始化LCDinittimer();//初始化定时器while (1) {keypros();Alarm_();ReadTemperature();Temp_Alarm_();display();if (i == 0 | (k3 == 0 && k2 == 0)) {rolldisplay();//开始时和按键2、3同时按下滚动显示

​           i = 1;}}

汇编目前完成了时钟计时功能:

RS     EQU       P2.6

RW    EQU       P3.6

E      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 ENABLE

MOV p0,#0CH;显示开关控制

CALL ENABLE

MOV p0,#06H;显示光标右移加1

CALL ENABLE

RET

DD2: MOV p0,#80H;第一行的开始位置

CALL ENABLE

CALL WRITE1;到TABLE1取码

RET

ENABLE:

CLR RS;送命令

CLR RW

CLR   E

CALL DELAY

SETB E

RET

WRITE1: 

MOV R1,#08H

MOV    R0,#20H

A1: 

MOV A,@R0; 到table取码

MOVC A,@A+DPTR

CALL WRITE2; 显示到LED

INC R0

DJNZ R1,A1

RET

WRITE2: MOV p0,A;显示

SETB RS

CLR RW

CLR E

CALL DELAY

SETB E

RET

DELAY:

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   

}
  • 34
    点赞
  • 112
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
### 回答1: 电子闹钟设计单片机内置ds1802芯片有很多优点。首先,ds1802芯片是一款数字音量控制芯片,可以通过单片机来控制闹钟的音量大小。这样,用户可以根据需要调节闹钟的音量,既满足了个人喜好,也可以根据不同环境调整,例如在夜晚调低音量,避免影响他人睡眠。 其次,ds1802芯片具有低功耗特性,可以加长闹钟的使用时间。由于ds1802芯片的工作电流很低,所以闹钟可以使用较小容量的电池或者可以使用更长时间的电池来工作。这对于那些常常需要外出或者没有电源接口的场合十分方便。 另外,ds1802芯片具有高精度的数字音量调节能力。单片机通过控制ds1802芯片可以进行数字精确调节,避免了传统旋钮式音量控制的精度不高的问题。这样用户可以更准确地选择合适的音量大小,避免音量过大或者过小的情况发生。 最后,ds1802芯片具有可编程性,可以根据用户的需求进行定制化设计单片机能够通过编程来控制ds1802芯片的各种参数,比如音量预设、音量平滑过渡等等。这使得电子闹钟的功能更加灵活多样化,满足用户的个性化需求。 综上所述,电子闹钟设计单片机内置ds1802芯片是一种高性能的选择。它不仅可以实现精确的音量控制,还能节省功耗并满足用户个性化需求。这样的设计无疑会提高电子闹钟的实用性和用户体验。 ### 回答2: 电子闹钟设计中使用单片机内置DS1802芯片有以下几个优点: 首先,DS1802芯片是一款专为电子闹钟设计的数字控制电位器。其内置了数字控制电位器功能,可以通过单片机对该芯片进行控制,实现对音量、亮度等参数的调节。这使得电子闹钟具备了更加便捷的用户调节和控制功能。 其次,DS1802芯片具备高精度和稳定性。这使得电子闹钟在长时间运行过程中可以保持准确的时间和稳定的性能。同时,DS1802芯片还具备抗干扰能力强的特点,能够有效地防止来自外部环境的干扰信号对电子闹钟正常运行的影响。 此外,DS1802芯片还具备低功耗和小尺寸的特点。它能够在较低的工作电压下正常工作,从而节省电力和延长电池的使用寿命。芯片本身的体积小且引脚较少,这意味着电子闹钟的设计可以更加紧凑和简洁,方便携带和放置。 最后,由于DS1802芯片内置在单片机中,可以减少电子闹钟设计中所需的外部元件数量并简化电路结构。这不仅可以节约成本,还可以提高产品的可靠性和可维护性。 综上所述,电子闹钟设计中使用单片机内置DS1802芯片可以提供调节方便、性能稳定、低功耗小尺寸以及简化电路结构等诸多优点。这将为用户带来更好的使用体验和方便,同时也简化了生产过程和降低了成本。 ### 回答3: 电子闹钟设计中使用单片机内置DS1802芯片可以实现时间显示和闹钟功能。DS1802芯片是一种数字式电位器,可以用于模拟电控制和可变电源控制。在电子闹钟设计中,单片机通过与DS1802芯片的通信控制,既可以设置闹钟时间和警报时间,又可以实时显示时间。 首先,单片机通过与DS1802芯片的通信,可以获取电位器的输出电压,然后将其转换为数字信号。通过根据转换后的数字信号,单片机可以实时显示当前的时间。这样,用户就可以方便地看到当前的时间,而无需其他设备。 此外,单片机还可以通过DS1802芯片设置闹钟时间和警报时间。用户可以通过按键或其他输入设备与单片机进行交互,设置闹钟的时间和警报的时间。单片机接收到用户的设置后,将这些数据传递给DS1802芯片,将设置的时间存储在芯片中。每当闹钟时间或警报时间到达时,DS1802芯片会向单片机发送一个警报信号,然后单片机可以通过其他模块(如蜂鸣器)发出响声,提醒用户。 综上所述,电子闹钟设计中使用单片机内置DS1802芯片可以实现时间显示和闹钟功能。通过与芯片的通信,单片机可以实时显示时间,并可以设置闹钟时间和警报时间。这样,用户可以方便地使用电子闹钟,准确掌握时间并及时响应闹钟或警报。这种设计简单高效,有助于人们管理时间和提高生活效率。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值