本人是新人,刚学51单片机,准备学好51后再转ARM.通过这段时间的学习和参阅论坛各大神们的51代码。在自己的理解基础上,做了这么一个小闹钟。
过程是苦恼的。但是也加深了对51的理解。特别是时序、指令、指针。还有就是LCD1602的移屏、光标显示、对CGRAM区,RAM区的一些操作。对DS1302的数据格式、指令、外存储存器的操作有了一定的认识。因为是自学的。所在在代码编写上肯定还有一些不足,对于优化代码更是谈不上。所以也就不怕献丑,把代码发上论坛。让各位老师们批评与指导。让我能在学习中有进步。谢谢了。这里放上有Keil 5写的代码和Proteus 8 Professional仿真程序。Proteus 8 Professional仿真程序中不能称之为电路图了。只能算是原理图吧。呵呵。
这个时钟具有LCD1602不带字库。通过对CGRAM的操作来显示汉字。能过对LCD1602的移屏指令来实现菜单功能、通过对DS1302的外存储器的操作来实现闹钟掉电能保持、DS1302第一次通电可初始化。主机掉电继续运行,简单的定时闹铃功能。,通过SETKEY按键来移动光标。
仿真原理图如下(proteus仿真工程文件可到本帖附件中下载)
制作出来的实物图如下:
这部份是代码
main.c
********************************************************
- #include "main.h"
- void main(void)
- {
- LCMInit(); //初始化LCD1602
- WriteCGRAM(); //CGRAM区存入自定义字符
- DS1302_Init(); //初始化DS1302
- while(1)
- {
- if(flag==0)
- {
- Read_RTC(); //读取DS1302时间日期数据
- Disp_Write_Lcd(); //将时间日期数据显示到LCD1602
- ShowAlarmBit(); //显示闹钟标志并执行闹钟动作
- bebuzz(); //蜂鸣器简单出声
- }
- Setkeyscan(); //扫描设置按钮
- OKkeyscan(); //扫描确认按钮
- Backkeyscan(); //扫描返回按钮
- Addkeyscan(); //扫描增加按钮
- Subkeyscan(); //扫描减少按钮
- }
- }
- ************************************************************
- main.h
- **************************************#ifndef __MAIN_H__
- #define __MAIN_H__
- #include <reg52.h>
- #include <string.h>
- #include "LCD1602.H"
- #include "DS1302.H"
- #include "keybutton.h"
- #include "bell.h"
- #include <intrins.h>
- extern bit Moveflag,SETMoveflag,AlarmMoveflag,flag,ShowAlarm,ibell; //移屏显示标志
- extern uchar setn,usetn,msetn; //设置按键计数
- extern uchar My_tmpdate[7]; //初始时间
- extern uchar code write_add[7]; //写入DS1302数据地址
- extern uchar code read_add[7]; //读取DS1302数据地址
- extern uchar code time0[]; //显示日期字符串
- extern uchar code time1[]; //显示时间字符串
- #endif
- ************************************
- LCD1602.C
- *********************************************
- #include "main.h"
- uchar code mychar[8][8]={
- 0x08,0x0F,0x12,0x0F,0x0A,0x1F,0x02,0x02, //年 0x00
- 0x0F,0x09,0x0F,0x09,0x0F,0x09,0x13,0x00, //月 0x01
- 0x1F,0x11,0x11,0x1F,0x11,0x11,0x1F,0x00, //日 0x02
- 0x10,0x06,0x09,0x08,0x08,0x08,0x09,0x06, // ℃ 0x03
- 0x01,0x03,0x1D,0x11,0x1D,0x03,0x01,0x00, //小喇叭标记 0x04
- 0x1F,0x0A,0x0A,0x1F,0x0A,0x0A,0x1A,0x00, // 开 0x05
- 0x11,0x0A,0x1F,0x04,0x1F,0x0A,0x11,0x00, // 关 0x06
- 0x04,0x0E,0x0E,0x0E,0x1F,0x1F,0x04,0x00, // 闹钟标记 0x07
- };
- /****************延时函数************************/
- void delay(uint i)
- {
- while(--i); //自减 当i为1时,跳出循环
- }
- /****************侧忙函数************************/
- bit ReadStatusLCD()
- {
- bit result;
- LCD_RS = 0;
- LCD_RW = 1;
- LCD_E = 1;
- delay(50);
- result = (bit)(LCD_Data&0x80); //当LCD_DATA&0x80不为0时result=1,否则result=0
- LCD_E = 0;
- return result;
- }
- /****************LCD1602写指令函数************************/
- /*
- /*写指令数据到LCD
- /*RS=L,RW=L,E=高脉冲,D0-D7=指令码。
- /*
- /*********************************************************/
- void WriteCommandLCM(uchar WDLCD)
- {
- while(ReadStatusLCD());
- LCD_RS = 0; //表示向LCM写入指令
- LCD_RW = 0; //表示向LCM写入指令
- /****在高脉冲下命令输入*****/
- LCD_E = 0;
- delay(50);
- LCD_Data = WDLCD; //指令代码
- delay(50);;
- LCD_E = 1;
- delay(50);
- LCD_E = 0;
- }
- /****************LCD1602写数据函数************************/
- /*
- /*写显示数据到LCD
- /*RS=H,RW=L,E=高脉冲,D0-D7=数据。
- /*
- /*******************************************************************/
- void WriteDataLCM(uchar WDLCD)
- {
- while(ReadStatusLCD());
- LCD_RS = 1;//写数据使能
- LCD_RW = 0;
- /****在高脉冲下数据输入*****/
- LCD_E = 0;
- LCD_Data = WDLCD; //数据内容
- delay(50);
- LCD_E = 1;
- delay(50);
- LCD_E = 0;
- }
- /****************CGRAM区写入自定义的字符************************/
- void WriteCGRAM(void)
- {
- uchar x,y;
- WriteCommandLCM(0x40);
- for(y = 0; y < 8; y++)
- {
- for(x = 0; x < 8; x++)
- {
- WriteDataLCM(mychar[y][x]); //写入自定义汉字字模,作为临时字库。掉电会清除。
- }
- }
- }
- /****************初始化LCD1602函数************************/
- void LCMInit(void)
- {
- uchar i;
- delay(50);
- WriteCommandLCM(0x38);//显示模式设置
- delay(50);
- WriteCommandLCM(0x38);
- delay(50);
- WriteCommandLCM(0x38);
- WriteCommandLCM(0x08);//显示关闭
- delay(50);
- WriteCommandLCM(0x01);//清除DDRAM的所有单元,光标被移动到屏幕左上角,也就是清除屏幕内容
- WriteCommandLCM(0x06);//光标移动设置整屏不移动
- WriteCommandLCM(0x0c);//屏幕开、光标不闪(0)不显示(0)
- delay(50);
- WriteCommandLCM(0x80); //指定第一行
- for(i = 0;i < 16;i++)
- {
- WriteDataLCM(time0[i]); //显示字符 日期
- delay(5);
- switch(i)
- {
- case 4 : WriteDataLCM(0x00);break; //显示自定义汉字 年
- case 6 : WriteDataLCM(0x01);break; //显示自定义汉字 月
- case 8 : WriteDataLCM(0x02);break; //显示自定义汉字 日
- }
- }
- WriteCommandLCM(0x80+0x40); //指定第二行
- for(i = 0;i < 12;i++)
- {
- WriteDataLCM(time1[i]); //显示字符 时间
- delay(5);
- }
- }
- **************************
- LCD1602.H
- *******************************
- /*****************************************************************************
- 接口定义是为配合本人的普中HC6800 V2.8开发板上的接口而定义。可以自行改动
- *****************************************************************************
- DS1302 命令字节表
- 1602使用三条控制线:EN、RW、RS。其中EN起到类似片选和时钟线的作用,RW和RS指示了读、写的方向和内容。
- 在读数据(或者Busy标志)期间,EN线必须保持高电平;而在写指令(或者数据)过程中,EN线上必须送出一个正脉冲。
- RW、RS的组合一共有四种情况,分别对应四种操作:
- RS=0、RW=0——表示向LCM写入指令
- RS=0、RW=1——表示读取Busy标志
- RS=1、RW=0——表示向LCM写入数据
- RS=1、RW=1——表示从LCM读取数据
- LCD在使用的过程中,可以在RS=0、RW=0的情况下,向LCM写入一个字节的控制指令。使用的控制指令一共八个类别。有的类别又有几条不同的指令。具体的情况罗列在下:
- 指令功能 RS R?W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
- 清屏 0 0 0 0 0 0 0 0 0 1
- ①01H:清除DDRAM的所有单元,光标被移动到屏幕左上角。
- ②02H:DDRAM所有单元的内容不变,光标移至左上角。
- ③输入方式设置(EnterModeSet),这些指令规定了两个方面:一是写入一个DDRAM单元后,地址指针如何改变(加一还是减一);二是屏幕上的内容是否滚动。
- 04H:写入DDRAM后,地址指针减一,比如第一个字符写入8FH,则下一个字符会写入8EH;屏幕上的内容不滚动。
- 05H:写入DDRAM后,地址指针减一,同上一种情况;每一个字符写入以后,屏幕上的内容向右滚动一个字符位。
- 06H:写入DDRAM后,地址指针加一,比如第一个字符写入80H,则下一个字符会写入81H;屏幕上的内容也是不滚动。这应该是最常用的一种显示方式。
- 07H:写入DDRAM后,地址指针加一,同上一种情况;每一个字符写入以后,屏幕上的内容向左滚动一个字符位。
- ④屏幕开关、光标开关、闪烁开关。
- 08H、09H、0AH、0BH:关闭显示屏,实质上是不把DDRAM中的内容对应显示在屏幕上,对DDRAM的操作还是在进行的,执行这条指令,
- 接着对 DDRAM进行写入,屏幕上没有任何内容,但是接着执行下面的某条指令,就能看到刚才屏幕关闭期间,对DDRAM操作的效果了。
- 0cH:打开显示屏,不显示光标,光标所在位置的字符不闪烁。
- 0dH:打开显示屏,不显示光标,光标所在位置的字符闪烁。
- 0eH:打开显示屏,显示光标,光标所在位置的字符不闪烁。
- 0fH:打开显示屏,显示光标,光标所在位置的字符闪烁。
- 关于光标的位置:光标所在的位置指示了下一个被写入的字符所处的位置,加入在写入下一个字符前没有通过指令设置DDRAM的地址,那么这个字符就应该显示在光标指定的地方。
- ⑤设置光标移动(本质就是AC的增加还是减少)、整体画面是否滚动。
- 指令功能 RS R?W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
- 进入设置模式 0 0 0 0 0 0 0 1 I?D s
- I?D 0= 写入新数据后光标左移 1=写入新数据后光标右移
- s 0= 写入新数据后显示屏不移 1=写入新数据后屏幕整体右移1个字符
- 例如: WriteCommandLCM(0x06); 也就是 I?D = 1 S = 0 DB2固定不变 110 转成16进制就为0X06
- 指令功能 RS R?W DB7 DB6 DB5 DB4 DB3 BDB2 DB1 DB0
- 进入设置模式 0 0 0 0 0 1 S?C R?L X X
- S?C =0 R?L=0 每输入一次该指令,AC(地址计数器)就减一,对应了光标向左移动一格。整体的画面不滚动。 10100 转成16进制就为0X10
- S?C =0 R?L=1 每输入一次该指令,AC(地址计数器)就加一,对应了光标向右移动一格。整体的画面不滚动 10100转成16进制就为0X14
- S?C =1 R?L=0 每输入一次该指令,整体的画面就向左滚动一个字符位。11000 转成16进制就为0X18
- S?C =1 R?L=1 每输入一次该指令,整体的画面就向右滚动一个字符位 11100 转成16进制就为0X1C
- 例如: WriteCommandLCM(0x10); 也就是 S?C = 0 R?L = 1 DB1固定不变为0 DB0固定不变为0 10100 转成16进制就为0X10
- 10H:每输入一次该指令,AC(地址计数器)就减一,对应了光标向左移动一格。整体的画面不滚动。
- 14H:每输入一次该指令,AC就加一,对应了光标向右移动一格。整体的画面不滚动。
- 18H:每输入一次该指令,整体的画面就向左滚动一个字符位。
- 1CH:每输入一次该指令,整体的画面就向右滚动一个字符位。画面在滚动的时候,每行的首尾是连在一起的,也就是每行的第一个字符,若左移25次,就会显示在该行的最后一格。
- 在画面滚动的过程中,AC的值也是变化的。
- ⑥显示模式设定指令,设定了显示几行,显示什么样的点阵字符,数据总线占用几位。
- 20H:4位总线,单行显示,显示5×7的点阵字符。
- 24H:4位总线,单行显示,显示5×10的点阵字符。
- 28H:4位总线,双行显示,显示5×7的点阵字符。
- 2CH:4位总线,双行显示,显示5×10的点阵字符。
- 30H:8位总线,单行显示,显示5×7的点阵字符。
- 34H:8位总线,单行显示,显示5×10的点阵字符。
- 38H:8位总线,双行显示,显示5×7的点阵字符。这是最常用的一种模式。
- 3CH:8位总线,双行显示,显示5×10的点阵字符。
- 作者:Anksy 发表与51黑电子论坛 转载注明
- ********************************************************************************************************************/
- #ifndef __LCD1602_H__ //防止一个头文件被include两次
- #define __LCD1602_H__
- #define LCD_Data P0 //宏定义单片机P0口为LCD1062,后面的LCD_Data都为P0。
- #define uchar unsigned char //把 unsigned char 宏定义为 uchar 以后只要写uchar 就是unsigned char 的意思。方便程序编写。 无符号字节型(0~255)
- #define uint unsigned int //把 unsigned int 宏定义为 uint 以后只要写uint 就是unsigned int 的意思。方便程序编写。无符号整型,最高位不表示符号
-
- sbit LCD_RS = P2^6; //外部位声明定义单片机P2.6口为LM1602的数据命令/选择口
- sbit LCD_RW = P2^5; //外部位声明定义单片机P2.5口为LM1602的数据读/写选口
- sbit LCD_E = P2^7; //外部位声明定义单片机P2.7口为LM1602的使能口
- /****************延时函数************************/
- void delay(uint i);
- /****************侧忙函数************************/
- bit ReadStatusLCM(void);
- /****************LCD1602写指令函数************************/
- void WriteCommandLCM(uchar WDLCD);
- /****************LCD1602写数据函数************************/
- void WriteDataLCM(uchar WDLCD);
- /****************初始化LCD1602函数************************/
- void LCMInit();
- /****************CGRAM区写入自定义的字符************************/
- void WriteCGRAM(void);
- #endif
- ********************************************************************
- DS1302.C
- #include "main.h"
- uchar code time0[]=" 20 ";
- uchar code time1[]=" : : ";
- uchar code time2[]=" : ";
- uchar My_tmpdate[7] = {10,47,23,11,9,2,18};//秒分时日月周年// 可随时更改
- uchar code Set_tmpdate[] = "> SET Time&Day ";//显示时间日期设置
- uchar code Set_tmpdate1[] = "> SET Alarm Time";
- uchar code read_add[7] = {0x81,0x83,0x85,0x87,0x89,0x8b,0x8d}; //秒分时日月周年 读的寄存器地址
- uchar code write_add[7] = {0x80,0x82,0x84,0x86,0x88,0x8a,0x8c}; //秒分时日月周年 写的寄存器地址
- uchar data str[3] = " ";
- /****************向DS1302写入一字节数据************************/
- void DS1302_Write_Byte(uchar dat)
- {
- uchar i;
- for (i = 0;i < 8;i++) //循环8次写入数据
- {
- SCK = 0;
- SDA = dat&0x01; //每次传输低字节
- dat >>= 1; //右移一位
- SCK = 1;
- }
- }
- /****************向DS1302时间寄存器写入数据************************/
- void Write_Ds1302(uchar address,uchar dat )
- {
- RST=0;
- _nop_();
- SCK = 0;
- _nop_();
- RST = 1;//启动
- _nop_();
- DS1302_Write_Byte(address); //发送地址
- DS1302_Write_Byte(dat); //发送数据
- RST = 0; //恢复
- }
- /****************从DS1302读出一字节数据***********************/
- uchar DS1302_Read_Byte()
- {
- uchar i,temp = 0x00;
- for (i = 0;i < 8;i++) //循环8次 读取数据
- {
- if(SDA)
- temp |= 0x80; //每次传输低字节
- SCK = 1; // 这里的时序如果是反的。真机能通过,可正常显示,但是Proteus 8 Professional显是乱码
- SCK = 0; //
- temp >>= 1; //右移一位
- }
- return(temp);
- }
- /****************从DS1302任意地址读出一字节数据***********************/
- uchar Read_DS1302_Byte(uchar addr)
- {
- uchar i;
- uchar temp;
- RST = 1;
- addr = addr | 0x01; //最低RD,有效为高电平
- for (i = 0; i < 8; i ++)
- {
- if (addr & 0x01)
- {
- SDA = 1;
- }
- else
- {
- SDA = 0;
- }
- SCK = 1; //这里的时序如果是反的。真机能通过,可正常显示,但是Proteus 8 Professional显是乱码
- SCK = 0;
- addr = addr >> 1;
- }
- //输出数据:temp
- for (i = 0; i < 8; i ++)
- {
- temp = temp >> 1;
- if (SDA)
- {
- temp |= 0x80;
- }
- else
- {
- temp &= 0x7F;
- }
- SCK = 1; //这里的时序如果是反的。真机能通过,可正常显示,但是Proteus 8 Professional显是乱码
- SCK = 0;
- }
- RST = 0; //停止DS1302总线
- return temp;
- }
- /*************************从DS1302指定时间寄存器地址读数据**************************************/
- uchar Read_Ds1302(uchar address)
- {
- uchar date;
- RST = 0;
- _nop_();
- SCK = 0;
- _nop_();
- RST = 1;
- _nop_();
- DS1302_Write_Byte(address); //发送地址
- date = DS1302_Read_Byte(); //返回一字节数据
- RST = 0;
- _nop_(); //以下为DS1302复位的稳定时间
- RST = 0;
- SCK = 1; //这里的时序如果是反的。真机能通过,可正常显示,但是Proteus 8 Professional显是乱码
- _nop_();
- SCK = 0;
- _nop_();
- SDA = 0;
- _nop_();
- SDA = 1;
- _nop_();
- return (date);
- }
- /*************从DS1302读取日历数据*********************/
- void Read_RTC(void)
- {
- uchar i,*p;
- p = read_add; //读的地址传递
- for(i = 0;i < 7;i++) //分7次读取 秒分时日月周年
- {
- My_tmpdate[i] = Read_Ds1302(*p);
- p++;
- }
- }
- /***************往DS1302写入日历数据*********************/
- void Set_RTC(void)
- {
- uchar i,*p,tmp;
- for(i=0;i<7;i++)
- { //BCD处理,将十六进制转换成十进制
- tmp = My_tmpdate[i] / 10;
- My_tmpdate[i] = My_tmpdate[i] % 10;
- My_tmpdate[i] = My_tmpdate[i] + tmp*16;
- }
- Write_Ds1302(0x8e,0x00);//关闭写保护
- p = write_add;//传要写的寄存器地址
- for(i=0;i<7;i++) //7次写入秒分时日月周年
- {
- Write_Ds1302(*p,My_tmpdate[i]);
- p++;
- }
- Write_Ds1302(0x8E,0x80);//打开写保护
- }
- /****************DS1302初始化函数************************/
- void DS1302_Init(void)
- {
- uchar i,x; //定义一个标志,第一次写入DS1302的RAM,改动时钟。修改i的赋值后。可重新写时间到DS1302
- i = 0x0a;
- RST = 0; //RST脚置低
- SCK = 0; //SCK脚置低
- Write_Ds1302(0x80,0x00); //让DS1302工作(强制起振)
- if(Read_DS1302_Byte(0xc1) != i) //如RAM C1标志不是原来的,则写入时钟,达到掉电继续走时。
- {
- Write_Ds1302(0x8e,0x00);//关闭写保护,允许写入数据
- Write_Ds1302(0xc0,i); //写入标志
- for(x=0;x<3;x++)
- {
- Write_Ds1302(0xc2 + x*2,0x12);
- }
- Write_Ds1302(0x8e,0x80); //打开写保护,禁止写入数据
- Set_RTC(); //写入时间
- }
- }
- /****************星期数字转星期字母************************/
- void AutoWeek(void)
- {
- switch(My_tmpdate[5]&0x0f) //星期数据
- {
- case 7:
- {
- str[0] = 'S';
- str[1] = 'u';
- str[2] = 'n';
- break;
- }
- case 1:
- {
- str[0] = 'M';
- str[1] = 'o';
- str[2] = 'n';
- break;
- }
- case 2:
- {
- str[0] = 'T';
- str[1] = 'u';
- str[2] = 'e';
- break;
- }
- case 3:
- {
- str[0] = 'W';
- str[1] = 'e';
- str[2] = 'd';
- break;
- }
- case 4:
- {
- str[0] = 'T';
- str[1] = 'h';
- str[2] = 'u';
- break;
- }
- case 5:
- {
- str[0] = 'F';
- str[1] = 'r';
- str[2] = 'i';
- break;
- }
- case 6:
- {
- str[0] = 'S';
- str[1] = 'a';
- str[2] = 't';
- break;
- }
- default:
- break;
- }
- }
- /**********************发送到LCD1602显示出来*******************************/
- void Disp_Write_Lcd(void)
- {
- static uchar i,temp[2];
- for (i = 0; i<7;i++)
- {
- switch(i)
- {
- case 0: WriteCommandLCM(0x80 + 0x40 + 0x0a);break;//秒显示坐标
- case 1: WriteCommandLCM(0x80 + 0x40 + 7);break;//分显示坐标
- case 2: WriteCommandLCM(0x80 + 0x40 + 4);break;//时显示坐标
- case 3: WriteCommandLCM(0x80 + 0x9);break;//日显示坐标
- case 4: WriteCommandLCM(0x80 + 6);break;//月显示坐标
- case 5:
- {
- WriteCommandLCM(0x80 + 0x0d);//星期显示坐标
- AutoWeek(); //转换星期数字为英文
- WriteDataLCM(str[0]); //显示出来
- WriteDataLCM(str[1]);
- WriteDataLCM(str[2]);
- break;
- }
- case 6: WriteCommandLCM(0x80 + 3);break;//年显示坐标
- }
- temp[0] = My_tmpdate[i]/16;//数据的转换
- temp[1] = My_tmpdate[i]&0x0f;
- WriteDataLCM(0x30 + temp[0]); //显示出来
- WriteDataLCM(0x30 + temp[1]);
- }
- }
- /**********************发送副屏到LCD1602显示出来*******************************/
- void Move_Write_Lcd(void)
- {
- static uchar i,temp[2];
- WriteCommandLCM(0x02);
- WriteCommandLCM(0x80 + 0x10); //指定第一行
- for(i = 0;i < 16;i++)
- {
- WriteDataLCM(time0[i]); //显示字符 日期
- delay(5);
- switch(i)
- {
- case 4 : WriteDataLCM(0x00);break; //显示自定义汉字 年
- case 6 : WriteDataLCM(0x01);break; //显示自定义汉字 月
- case 8 : WriteDataLCM(0x02);break; //显示自定义汉字 日
- }
- }
- WriteCommandLCM(0x80+0x40+0x10); //指定第二行
- for(i = 0;i < 12;i++)
- {
- WriteDataLCM(time1[i]); //显示字符 时间
- delay(5);
- }
- for (i = 0; i<7;i++)
- {
- switch(i)
- {
- case 0: WriteCommandLCM(0x80 + 0x40 + 0x1a);break;//秒显示坐标
- case 1: WriteCommandLCM(0x80 + 0x40 + 0x17);break;//分显示坐标
- case 2: WriteCommandLCM(0x80 + 0x40 + 0x14);break;//时显示坐标
- case 3: WriteCommandLCM(0x80 + 0x19);break;//日显示坐标
- case 4: WriteCommandLCM(0x80 + 0x16);break;//月显示坐标
- case 5: WriteCommandLCM(0x80 + 0x1d);break;//星期显示坐标
- case 6: WriteCommandLCM(0x80 + 0x13);break;//年显示坐标
- }
- temp[0] = My_tmpdate[i] / 16;//数据的转换
- temp[1] = My_tmpdate[i]&0x0f;
- WriteDataLCM(0x30 + temp[0]);
- WriteDataLCM(0x30 + temp[1]);
- }
- for (i = 0;i <16;i++)
- {
- WriteCommandLCM(0x18); //移动屏幕
- delay(10000);
- }
- Moveflag = 1;
- }
- /**********************发送设置副屏到LCD1602显示出来*******************************/
- void Move_SET_Write_Lcd(void)
- {
- static uchar i;
- for(i = 0;i < 16;i++)
- {
- WriteCommandLCM(0x80 + i); //设置日期时间字符坐标
- WriteDataLCM(Set_tmpdate[i]); //将日期时间菜单显示出来
- WriteCommandLCM(0x80 + 0x40 + i); //设置闹钟字符坐标
- WriteDataLCM(Set_tmpdate1[i]); //将闹钟菜单显示出来
- }
- for (i = 0;i <16;i++)
- {
- WriteCommandLCM(0x18); //移动屏幕
- delay(10000);
- }
- WriteCommandLCM(0x02);
- SETMoveflag =1;
- }
- /**********************发送设置副屏到LCD1602显示出来*******************************/
- void Move_Alarm_Write_Lcd(void)
- {
- static uchar i,temp[2];
- WriteCommandLCM(0x02);
- WriteCommandLCM(0x80+0x10); //指定第二行
- for(i = 0;i<16;i++)
- {
- WriteDataLCM(0x20);
- }
- WriteCommandLCM(0x80+0x40+0x10); //指定第二行
- for(i = 0;i < 16;i++)
- {
- WriteDataLCM(time2[i]); //显示字符 时间
- delay(5);
- }
- for (i = 0; i<2;i++)
- {
- switch(i)
- {
- case 0: WriteCommandLCM(0x80+ 0x40 + 0x17);break;//分显示坐标
- case 1: WriteCommandLCM(0x80+ 0x40 + 0x14);break;//时显示坐标
- }
- temp[0] = Read_DS1302_Byte(0xc5+i*2)/10; //16进制转10进制取整
- temp[1] = Read_DS1302_Byte(0xc5+i*2)%10; //16进制转10进制取余
- WriteDataLCM(0x30 + temp[0]);
- WriteDataLCM(0x30 + temp[1]);
- }
- if(Read_DS1302_Byte(0xc9)== 0x00)
- {
- WriteCommandLCM(0x80+0x40+0x1e); //指定第二行
- WriteDataLCM(0x06); //写入自定义字符 关
- }
- if(Read_DS1302_Byte(0xc9)== 0x01)
- {
- WriteCommandLCM(0x80+0x40+0x1e); //指定第二行
- WriteDataLCM(0x05); //写入自定义字符 开
- }
- for (i = 0;i <16;i++)
- {
- WriteCommandLCM(0x18); //移动屏幕
- delay(10000);
- }
- AlarmMoveflag=1;
- }
- /****************显示闹钟标志************************/
- void ShowAlarmBit(void) //读GCRAM区标志来判断
- {
- if(Read_DS1302_Byte(0xc9)== 0x01)
- {
- WriteCommandLCM(0x80+0x40); //指定第二行
- WriteDataLCM(0x07);
- if (HEX_DEC(Read_DS1302_Byte(0xc5))==change(My_tmpdate[1])&HEX_DEC(Read_DS1302_Byte(0xc7))==change(My_tmpdate[2]))
- {
- ibell = 1; //打开蜂鸣器
- WriteCommandLCM(0x80+0x40); //指定第二行
- WriteCommandLCM(0x0d);//不显示光标字符闪烁
- }
- else
- {
- ibell = 0; //关闭蜂鸣器
- WriteCommandLCM(0x80+0x40); //指定第二行
- WriteCommandLCM(0x0c);//不显示光标并且不闪烁
- }
- }
- if(Read_DS1302_Byte(0xc9)== 0x00)
- {
- WriteCommandLCM(0x80+0x40); //指定第二行
- WriteDataLCM(0x20);
- }
- }
复制代码
*************************************************
DS1302.H
************************************************
/*****************************************************************************
单片机IO接口定义是为配合本人的普中HC6800 V2.8开发板上的接口而定义。可以自行改动
DS1302 命令字节表
********************************************************************************************************************
READ I WRITE I BIT7 I BIT6 I BIT5 I BIT4 I BIT3 I BIT2 I BIT1 I BIT0 RANGE
81H I 80H I CH I 10 Seconds I Seconds I 00-59
83H I 81H I I 10 Minutes I Minutes I 00-59
85H I 84H I 12-24 I 0 I AM-PM I Hour I Hour I 1-12 0-23
87H I 86H I 0 I 0 I 10Date I Date I 1-31
89H I 88H I 0 I 0 I 0 I10 MonthI Month I I 1-12
8BH I 8AH I 0 I 0 I 0 I 0 I 0 I Day I 1-7
8DH I 8CH I 10 Year I Year I I 00-99
8FH I 8EH I WP I 0 I 0 I 0 I 0 I 0 I 0 I I --
91H I 90H I TCS I TCS I TCS I TCS I TCS I TCS I TCS I I --
********************************************************************************************************************/
/*
DS1302时钟的寄存器,其中8个和时钟有关的,5位地址分别是00000一直到00111这8个地址,还有一个寄存器的地址是01000,这是涓流充电所用的寄存器,我们这里不讲。
在DS1302的数据手册里的地址,直接把第七位、第六位和第零位值给出来了,所以指令就成了80H、81H那些了,最低位是1,那么表示读,最低位是0表示写.
寄存器一:最高位CH是一个时钟停止标志位。如果我们的时钟电路有备用电源部分,上电后,我们要先检测一下这一位,如果这一位是0,那说明我们的时钟在系
统掉电后,由于备用电源的供给,时钟是持续正常运行的;如果这一位是1,那么说明我们的时钟在系统掉电后,时钟部分不工作了。若我们的Vcc1悬
空或者是电池没电了,当我们下次重新上电时,读取这一位,那这一位就是1,我们可以通过这一位判断时钟在单片机系统掉电后是否持续运行。
剩下的7位高3位是秒的十位,低4位是秒的个位,这里注意再提一次,DS1302内部是BCD码,而秒的十位最大是5,所以3个二进制位就够了。
寄存器二:bit7没意义,剩下的7位高3位是分钟的十位,低4位是分钟的个位。
寄存器三:bit7是1的话代表是12小时制,是0的话代表是24小时制,bit6固定是0,bit5在12小时制下0代表的是上午,1代表的是下午,
在24小时制下和bit4一起代表了小时的十位,低4位代表的是小时的个位。
寄存器四:高2位固定是0,bit5和bit4是日期的十位,低4位是日期的个位。
寄存器五:高3位固定是0,bit4是月的十位,低4位是月的个位。
寄存器六:高5位固定是0,低3位代表了星期。
寄存器七:高4位代表了年的十位,低4位代表了年的个位。这里特别注意,这里的00到99年指的是2000年到2099年。
寄存器八:bit7是一个保护位,如果这一位是1,那么是禁止给任何其他的寄存器或者那31个字节的RAM写数据的。因此在写数据之前,这一位必须先写成0。
******************************************************************************************************
外存储器(RAM)
读地址 写地址 数据范围
C1 C0 00-FF 这个地址在本程序中用于判断DS1302是否是第一次使用。用于初始时间设定
C3 C2 00-FF
C5 C4 00-FF C2\3,C4\C5,C6\C7,C8\C9 在本程序中定义为闹钟分钟的读和写的设定时间。好处是只要DS1302有后备电源则时间不丢。
.. .. 00-FF 闹钟打开与关闭的标志
.. .. 00-FF
.. .. 00-FF 作者:Anksy 发表与51黑电子论坛 转载注明
.. .. 00-FF
FD FC 00-FF
*/
#ifndef __DS1302_H__ //防止一个头文件被include两次
#define __DS1302_H__
#define uchar unsigned char //把 unsigned char 宏定义为 uchar 以后只要写uchar 就是unsigned char 的意思。方便程序编写。 无符号字节型(0~255)
#define uint unsigned int //把 unsigned int 宏定义为 uint 以后只要写uint 就是unsigned int 的意思。方便程序编写。无符号整型,最高位不表示符号
sbit SCK = P3^6; //外部位声明定义单片机P3.6口为DS1302时钟线
sbit SDA = P3^4; //外部位声明定义单片机P3.4口为DS1302数据线
sbit RST = P3^5; //外部位声明定义单片机P3.5口为DS1302复位线
/****************从DS1302任意地址读出一字节数据***********************/
uchar Read_DS1302_Byte(uchar addr);
/****************向DS1302时间寄存器写入一字节数据************************/
void DS1302_Write_Byte(uchar dat);
/*****************向DS1302写入数据************************/
void Write_Ds1302(uchar address,uchar dat );
/****************从DS1302时间寄存器读出一字节数据***********************/
uchar DS1302_Read_Byte();
/*************************从DS1302指定时间寄存器地址读数据**************************************/
uchar Read_Ds1302(uchar address);
/************************读取日历*********************************/
void Read_RTC(void);
/************************设定日历******************************/
void Set_RTC(void);
/**********************发送到LCD1602显示出来*******************************/
void Disp_Write_Lcd(void);
/**********************发送副屏到LCD1602显示出来*******************************/
void Move_Write_Lcd(void);
/**********************发送设置副屏到LCD1602显示出来*******************************/
void Move_SET_Write_Lcd(void);
/**********************发送设置副屏到LCD1602显示出来*******************************/
void Move_Alarm_Write_Lcd(void);
/****************DS1302初始化函数************************/
void DS1302_Init(void);
/****************星期数字转星期字母************************/
void AutoWeek(void);
/****************显示闹钟标志************************/
void ShowAlarmBit(void);
#endif
********************************************************
keybutton.c
***********************#include "main.h"
bit flag;//是否读取时间的标志位
uchar setn=0; // 复位键按下次数
uchar msetn; // 确认按键按下次数
uchar usetn; // 调时间日期时 复位键按下次数
bit Moveflag,SETMoveflag,AlarmMoveflag,ShowAlarm,alterTime,alterAlarm; //一些移动按键 的标志
/****************十进制转换成十六进制************************/
uchar change(uchar x)//十进制转换成十六进制
{
uchar tep,y;
y=x;
tep=y/16;
y=y%16;
y=y+tep*10;
return y;
}
/****************十六进制转换成十进制************************/
uchar HEX_DEC(uchar x)
{
uchar tep,y;
tep= x/10; //数据转换 16进制转10进制 十位
y = x%10; //数据转换 16进制转10进制 十位
y = tep*10 + y;
return y;
}
/****************扫描设置按键动作************************/
void Setkeyscan(void)
{
if(SET_KEY==0)
{
flag=1;//时间停止
delay(10000);//去键盘按下抖动
if(SET_KEY==0)
{
delay(10000);
while(!SET_KEY);
delay(10000);//去键盘抬起时抖动
while(!SET_KEY);
delay(10000);
if(SETMoveflag == 0)
{
Move_SET_Write_Lcd(); //显示设置菜单界面
}
switch(setn)
{
case 0:
{
WriteCommandLCM(0x80 + 0x00);//将光标移动到时间日期设置
WriteCommandLCM(0x0f);//显示光标并且闪烁
//SETMoveflag = 1;
msetn = 0;
break;
}
case 1:
{
WriteCommandLCM(0x80 + 0x40+0x00);//将光标移动到闹钟设置
WriteCommandLCM(0x0f);//显示光标并且闪烁
//SETMoveflag = 1;
msetn = 1;
break;
}
}
setn++;
if(setn == 2)
{
setn = 0; //菜单处光标循环数值归零
}
/*********************以下循环闪烁要改变日期时间数值处的光标****************/
if(Moveflag==1&SETMoveflag==1)
{
usetn++;
if (usetn == 7)
{
usetn = 0; //改变数值光标归零
}
switch(usetn)
{
case 0 :
{
WriteCommandLCM(0x80+0x40+27);//将光标移动到秒个位
WriteCommandLCM(0x0f);//显示光标并且闪烁
break;
}
case 1:
{
WriteCommandLCM(0x80+0x40+24);//将光标移动到分个位
WriteCommandLCM(0x0f);//显示光标并且闪烁
break;
}
case 2:
{
WriteCommandLCM(0x80+0x40+21);//将光标移动到时个位
WriteCommandLCM(0x0f);//显示光标并且闪烁
break;
}
case 3:
{
WriteCommandLCM(0x80+ 30);//将光标移动到星期个位
WriteCommandLCM(0x0f);//显示光标并且闪烁
break;
}
case 4:
{
WriteCommandLCM(0x80+ 26);//将光标移动到天个位
WriteCommandLCM(0x0f);//显示光标并且闪烁
break;
}
case 5:
{
WriteCommandLCM(0x80+ 23);//将光标移动到月个位
WriteCommandLCM(0x0f);//显示光标并且闪烁
break;
}
case 6:
{
WriteCommandLCM(0x80+ 20);//将光标移动到年个位
WriteCommandLCM(0x0f);//显示光标并且闪烁
break;
}
}
}
/*********************以下循环闪烁要改变闹钟时间数值处的光标****************/
if(AlarmMoveflag==1)
{
usetn++;
if (usetn == 3)
{
usetn = 0; //改变数值光标归零
}
switch(usetn)
{
/*case 0 :
{
WriteCommandLCM(0x80+0x40+27);//将光标移动到秒个位
WriteCommandLCM(0x0f);//显示光标并且闪烁
break;
}*/
case 0:
{
WriteCommandLCM(0x80+0x40+24);//将光标移动到分个位
WriteCommandLCM(0x0f);//显示光标并且闪烁
break;
}
case 1:
{
WriteCommandLCM(0x80+0x40+21);//将光标移动到时个位
WriteCommandLCM(0x0f);//显示光标并且闪烁
break;
}
case 2:
{
WriteCommandLCM(0x80+0x40 +30);//将光标移动到 开
WriteCommandLCM(0x0f);//显示光标并且闪烁
break;
}
}
}
}
}
}
/****************扫描确认按键动作************************/
void OKkeyscan(void)
{
if(OK_KEY == 0)
{
delay(10000);//去键盘按下抖动
if(OK_KEY == 0)
{
delay(10000);
while(!OK_KEY);
delay(10000);//去键盘抬起时抖动
while(!OK_KEY);
delay(10000);
if(Moveflag == 0&SETMoveflag ==1&AlarmMoveflag==0) //防止反复进入调日期界面
{
switch(msetn)
{
case 0:
{
Move_Write_Lcd(); //进入调日期界面
WriteCommandLCM(0x80+0x40+27);//将光标移动到秒个位
WriteCommandLCM(0x0f);//关闭光标
usetn = 0;
alterTime = 1;
alterAlarm = 0;
break;
}
case 1:
{
OK_KEY = 1;
Move_Alarm_Write_Lcd(); //进入调日期界面
WriteCommandLCM(0x80+0x40+24);//将光标移动到秒个位
WriteCommandLCM(0x0f);//关闭光标
usetn = 0;
alterTime = 0;
alterAlarm = 1;
break;
}
}
}
}
}
}
/****************扫描返回按键动作************************/
void Backkeyscan(void)
{
if(Back_KEY==0)
{
delay(10000);
while(!SET_KEY);
delay(10000);//去键盘抬起时抖动
while(!SET_KEY);
delay(10000);
LCMInit();
flag = 0;//时钟正常运行
Moveflag = 0;
SETMoveflag = 0;
AlarmMoveflag = 0;
setn = 0;
msetn = 0;
usetn = 0;
WriteCommandLCM(0x0c);//光标停止闪烁
}
}
/****************扫描增加键动作************************/
void Addkeyscan(void)
{
uchar tem[2],tp,AlarmTemp[2];
if (Add_KEY == 0)
{
delay(5000);
if (Add_KEY == 0 )
{
delay(5000);
while(!Add_KEY);
delay(5000);
while(!Add_KEY);
if (SETMoveflag == 1&alterTime== 1)
{
switch(usetn) //case 序号为LCD1602显示顺序。My_tmpdate[0]中括号内序号为DS1302时间格式顺序
{
case 0: //调秒
{
My_tmpdate[0] = change(My_tmpdate[0]);
My_tmpdate[0]++;
if(My_tmpdate[0] == 60)
{
My_tmpdate[0] = 0;
}
tp = My_tmpdate[0]/10;
My_tmpdate[0] = My_tmpdate[0]%10;
My_tmpdate[0] = My_tmpdate[0]+tp*16;
Write_Ds1302(0x8e,0x00);//关闭写保护
Write_Ds1302(write_add[0],My_tmpdate[0]);
Write_Ds1302(0x8e,0x80);//打开写保护
WriteCommandLCM(0x80+0x40+26);
tem[0]=My_tmpdate[0]/16;//数据的转换
tem[1]=My_tmpdate[0]&0x0f;
WriteDataLCM(0x30+tem[0]); //显示出来
WriteDataLCM(0x30+tem[1]);
WriteCommandLCM(0x80+0x40+27);
break;
}
case 1: //调分钟
{
My_tmpdate[1] = change(My_tmpdate[1]);
My_tmpdate[1]++;
if(My_tmpdate[1] == 60)
{
My_tmpdate[1] = 0;
}
tp = My_tmpdate[1]/10;
My_tmpdate[1] = My_tmpdate[1]%10;
My_tmpdate[1] = My_tmpdate[1]+tp*16;
Write_Ds1302(0x8e,0x00);//关闭写保护
Write_Ds1302(write_add[1],My_tmpdate[1]);
Write_Ds1302(0x8e,0x80);//打开写保护
WriteCommandLCM(0x80+0x40+23);
tem[0]=My_tmpdate[1]/16;//数据的转换
tem[1]=My_tmpdate[1]&0x0f;
WriteDataLCM(0x30+tem[0]);
WriteDataLCM(0x30+tem[1]);
WriteCommandLCM(0x80+0x40+24);
break;
}
case 2: //调小时
{
My_tmpdate[2] = change(My_tmpdate[2]);
My_tmpdate[2]++;
if(My_tmpdate[2] >= 24)
{
My_tmpdate[2] = 0;
}
tp = My_tmpdate[2]/10;
My_tmpdate[2] = My_tmpdate[2]%10;
My_tmpdate[2] = My_tmpdate[2]+tp*16;
Write_Ds1302(0x8e,0X00);//关闭写保护
Write_Ds1302(write_add[2],My_tmpdate[2]);
Write_Ds1302(0x8e,0x80);//打开写保护
WriteCommandLCM(0x80+0x40+20);
tem[0]=My_tmpdate[2]/16;//数据的转换
tem[1]=My_tmpdate[2]&0x0f;
WriteDataLCM(0x30+tem[0]);
WriteDataLCM(0x30+tem[1]);
WriteCommandLCM(0x80+0x40+21);
break;
}
case 3: //调星期
{
My_tmpdate[5] = change(My_tmpdate[5]);
My_tmpdate[5]++;
if(My_tmpdate[5] == 8)
{
My_tmpdate[5] = 1;
}
tp = My_tmpdate[5]/10;
My_tmpdate[5] = My_tmpdate[5]%10;
My_tmpdate[5] = My_tmpdate[5]+tp*16;
Write_Ds1302(0x8e,0x00);//关闭写保护
Write_Ds1302(write_add[5],My_tmpdate[5]);
Write_Ds1302(0x8e,0x80);//打开写保护
WriteCommandLCM(0x80+29);
tem[0]=My_tmpdate[5]/16;//数据的转换
tem[1]=My_tmpdate[5]&0x0f;
WriteDataLCM(0x30+tem[0]);
WriteDataLCM(0x30+tem[1]);
WriteCommandLCM(0x80+30);
break;
}
case 4: //调天
{
My_tmpdate[3] = change(My_tmpdate[3]);
My_tmpdate[3]++;
if(My_tmpdate[3] == 32)
{
My_tmpdate[3] = 1;
}
tp = My_tmpdate[3]/10;
My_tmpdate[3] = My_tmpdate[3]%10;
My_tmpdate[3] = My_tmpdate[3]+tp*16;
Write_Ds1302(0x8e,0x00);//关闭写保护
Write_Ds1302(write_add[3],My_tmpdate[3]);
Write_Ds1302(0x8e,0x80);//打开写保护
WriteCommandLCM(0x80+25);
tem[0]=My_tmpdate[3]/16;//数据的转换
tem[1]=My_tmpdate[3]&0x0f;
WriteDataLCM(0x30+tem[0]);
WriteDataLCM(0x30+tem[1]);
WriteCommandLCM(0x80+26);
break;
}
case 5: //调月
{
My_tmpdate[4] = change(My_tmpdate[4]);
My_tmpdate[4]++;
if(My_tmpdate[4] == 13)
{
My_tmpdate[4] = 1;
}
tp = My_tmpdate[4]/10;
My_tmpdate[4] = My_tmpdate[4]%10;
My_tmpdate[4] = My_tmpdate[4]+tp*16;
Write_Ds1302(0x8e,0x00);//关闭写保护
Write_Ds1302(write_add[4],My_tmpdate[4]);
Write_Ds1302(0x8e,0x80);//打开写保护
WriteCommandLCM(0x80+22);
tem[0]=My_tmpdate[4]/16;//数据的转换
tem[1]=My_tmpdate[4]&0x0f;
WriteDataLCM(0x30+tem[0]);
WriteDataLCM(0x30+tem[1]);
WriteCommandLCM(0x80+23);
break;
}
case 6: //调年
{
My_tmpdate[6] = change(My_tmpdate[6]);
My_tmpdate[6]++;
if(My_tmpdate[6] == 100)
{
My_tmpdate[6] = 0;
}
tp = My_tmpdate[6]/10;
My_tmpdate[6] = My_tmpdate[6]%10;
My_tmpdate[6] = My_tmpdate[6]+tp*16;
Write_Ds1302(0x8e,0x00);//关闭写保护
Write_Ds1302(write_add[5],My_tmpdate[6]);
Write_Ds1302(0x8e,0x80);//打开写保护
WriteCommandLCM(0x80+19);
tem[0]=My_tmpdate[6]/16;//数据的转换
tem[1]=My_tmpdate[6]&0x0f;
WriteDataLCM(0x30+tem[0]);
WriteDataLCM(0x30+tem[1]);
WriteCommandLCM(0x80+20);
break;
}
}
}
/*************************************以下是修改闹钟时间***************************************************************/
if(AlarmMoveflag==1&alterAlarm==1)
{
switch(usetn) //case 序号为LCD1602显示顺序。My_tmpdate[0]中括号内序号为DS1302时间格式顺序
{
case 0: //调分钟
{
AlarmTemp[0] = Read_DS1302_Byte(0xc5);
AlarmTemp[0]++;
if(AlarmTemp[0] == 60)
{
AlarmTemp[0] = 0;
}
Write_Ds1302(0x8e,0x00);//关闭写保护
Write_Ds1302(0xc4,AlarmTemp[0]);
Write_Ds1302(0x8e,0x80);//打开写保护
WriteCommandLCM(0x80+0x40+23);
tem[0]=AlarmTemp[0]/10;//数据的转换
tem[1]=AlarmTemp[0]%10;
WriteDataLCM(0x30+tem[0]);
WriteDataLCM(0x30+tem[1]);
WriteCommandLCM(0x80+0x40+24); //显示出来
break;
}
case 1: //调小时
{
AlarmTemp[1] = Read_DS1302_Byte(0xc7);
AlarmTemp[1]++;
if(AlarmTemp[1] == 24)
{
AlarmTemp[1] = 0;
}
Write_Ds1302(0x8e,0x00);//关闭写保护
Write_Ds1302(0xc6,AlarmTemp[1]);
Write_Ds1302(0x8e,0x80);//打开写保护
WriteCommandLCM(0x80+0x40+20);
tem[0]=AlarmTemp[1]/10;//数据的转换
tem[1]=AlarmTemp[1]%10;
WriteDataLCM(0x30+tem[0]);
WriteDataLCM(0x30+tem[1]);
WriteCommandLCM(0x80+0x40+21);
break;
}
case 2:
{
ShowAlarm = 1; //调闹钟关
ScanShowAlarm();
WriteCommandLCM(0x80+0x40+30);//将光标移动到 关
WriteCommandLCM(0x0f);//显示光标并且闪烁
break;
}
}
}
}
}
}
/****************扫描减少键动作************************/
void Subkeyscan(void) //case 序号为LCD1602显示顺序。My_tmpdate[0]中括号内序号为DS1302时间格式顺序
{
uchar tem[2],tp,AlarmTemp[2];
if (Sub_KEY == 0)
{
delay(5000);
if (Sub_KEY == 0)
{
delay(5000);
while(!Sub_KEY);
delay(5000);
while(!Sub_KEY);
if (SETMoveflag == 1&alterTime==1)
{
switch(usetn)
{
case 0: //调秒
{
My_tmpdate[0] = change(My_tmpdate[0]);
if (My_tmpdate[0] !=0)
My_tmpdate[0]--;
if(My_tmpdate[0] == 0)
{
My_tmpdate[0] = 59;
}
tp = My_tmpdate[0]/10;
My_tmpdate[0] = My_tmpdate[0]%10;
My_tmpdate[0] = My_tmpdate[0]+tp*16;
Write_Ds1302(0x8e,0x00);//关闭写保护
Write_Ds1302(write_add[0],My_tmpdate[0]);
Write_Ds1302(0x8e,0x80);//打开写保护
WriteCommandLCM(0x80+0x40+26);
tem[0]=My_tmpdate[0]/16;//数据的转换
tem[1]=My_tmpdate[0]&0x0f;
WriteDataLCM(0x30+tem[0]);
WriteDataLCM(0x30+tem[1]);
WriteCommandLCM(0x80+0x40+27); //显示出来
break;
}
case 1: //调分钟
{
My_tmpdate[1] = change(My_tmpdate[1]);
if (My_tmpdate[1] !=0)
My_tmpdate[1]--;
if(My_tmpdate[1] == 0)
{
My_tmpdate[1] = 59;
}
tp = My_tmpdate[1]/10;
My_tmpdate[1] = My_tmpdate[1]%10;
My_tmpdate[1] = My_tmpdate[1]+tp*16;
Write_Ds1302(0x8e,0x00);//关闭写保护
Write_Ds1302(write_add[1],My_tmpdate[1]);
Write_Ds1302(0x8e,0x80);//打开写保护
WriteCommandLCM(0x80+0x40+23);
tem[0]=My_tmpdate[1]/16;//数据的转换
tem[1]=My_tmpdate[1]&0x0f;
WriteDataLCM(0x30+tem[0]);
WriteDataLCM(0x30+tem[1]);
WriteCommandLCM(0x80+0x40+24);
break;
}
case 2: //调小时
{
My_tmpdate[2] = change(My_tmpdate[2]);
if (My_tmpdate[2] !=0)
My_tmpdate[2]--;
if(My_tmpdate[2] == 0)
{
My_tmpdate[2] = 23;
}
tp = My_tmpdate[2]/10;
My_tmpdate[2] = My_tmpdate[2]%10;
My_tmpdate[2] = My_tmpdate[2]+tp*16;
Write_Ds1302(0x8e,0x00);//关闭写保护
Write_Ds1302(write_add[2],My_tmpdate[2]);
Write_Ds1302(0x8e,0x80);//打开写保护
WriteCommandLCM(0x80+0x40+20);
tem[0]=My_tmpdate[2]/16;//数据的转换
tem[1]=My_tmpdate[2]&0x0f;
WriteDataLCM(0x30+tem[0]);
WriteDataLCM(0x30+tem[1]);
WriteCommandLCM(0x80+0x40+21);
break;
}
case 3: //调星期
{
My_tmpdate[5] = change(My_tmpdate[5]);
if (My_tmpdate[5] !=0)
My_tmpdate[5]--;
if(My_tmpdate[5] == 0)
{
My_tmpdate[5] = 7;
}
tp = My_tmpdate[5]/10;
My_tmpdate[5] = My_tmpdate[5]%10;
My_tmpdate[5] = My_tmpdate[5]+tp*16;
Write_Ds1302(0x8e,0x00);//关闭写保护
Write_Ds1302(write_add[5],My_tmpdate[5]);
Write_Ds1302(0x8e,0x80);//打开写保护
WriteCommandLCM(0x80+29);
tem[0]=My_tmpdate[5]/16;//数据的转换
tem[1]=My_tmpdate[5]&0x0f;
WriteDataLCM(0x30+tem[0]);
WriteDataLCM(0x30+tem[1]);
WriteCommandLCM(0x80+30);
break;
}
case 4: //调天
{
My_tmpdate[3] = change(My_tmpdate[3]);
if (My_tmpdate[3] !=0)
My_tmpdate[3]--;
if(My_tmpdate[3] == 0)
{
My_tmpdate[3] = 31;
}
tp = My_tmpdate[3]/10;
My_tmpdate[3] = My_tmpdate[3]%10;
My_tmpdate[3] = My_tmpdate[3]+tp*16;
Write_Ds1302(0x8e,0x00);//关闭写保护
Write_Ds1302(write_add[3],My_tmpdate[3]);
Write_Ds1302(0x8e,0x80);//打开写保护
WriteCommandLCM(0x80+25);
tem[0]=My_tmpdate[3]/16;//数据的转换
tem[1]=My_tmpdate[3]&0x0f;
WriteDataLCM(0x30+tem[0]);
WriteDataLCM(0x30+tem[1]);
WriteCommandLCM(0x80+26);
break;
}
case 5: //调月
{
My_tmpdate[4] = change(My_tmpdate[4]);
if (My_tmpdate[4] !=0)
My_tmpdate[4]--;
if(My_tmpdate[4] == 0)
{
My_tmpdate[4] = 12;
}
tp = My_tmpdate[4]/10;
My_tmpdate[4] = My_tmpdate[4]%10;
My_tmpdate[4] = My_tmpdate[4]+tp*16;
Write_Ds1302(0x8e,0x00);//关闭写保护
Write_Ds1302(write_add[4],My_tmpdate[4]);
Write_Ds1302(0x8e,0x80);//打开写保护
WriteCommandLCM(0x80+22);
tem[0]=My_tmpdate[4]/16;//数据的转换
tem[1]=My_tmpdate[4]&0x0f;
WriteDataLCM(0x30+tem[0]);
WriteDataLCM(0x30+tem[1]);
WriteCommandLCM(0x80+23);
break;
}
case 6: //调年
{
My_tmpdate[6] = change(My_tmpdate[6]);
if (My_tmpdate[6] !=0)
My_tmpdate[6]--;
if(My_tmpdate[6] == 0)
{
My_tmpdate[6] = 99;
}
tp = My_tmpdate[6]/10;
My_tmpdate[6] = My_tmpdate[6]%10;
My_tmpdate[6] = My_tmpdate[6]+tp*16;
Write_Ds1302(0x8e,0x00);//关闭写保护
Write_Ds1302(write_add[5],My_tmpdate[6]);
Write_Ds1302(0x8e,0x80);//打开写保护
WriteCommandLCM(0x80+19);
tem[0]=My_tmpdate[6]/16;//数据的转换
tem[1]=My_tmpdate[6]&0x0f;
WriteDataLCM(0x30+tem[0]);
WriteDataLCM(0x30+tem[1]);
WriteCommandLCM(0x80+20);
break;
}
}
}
/*************************************以下是修改闹钟时间***************************************************************/
if(AlarmMoveflag==1&alterAlarm== 1)
{
switch(usetn) //case 序号为LCD1602显示顺序。My_tmpdate[0]中括号内序号为DS1302时间格式顺序
{
case 0: //调分钟
{
AlarmTemp[0] = Read_DS1302_Byte(0xc5);
AlarmTemp[0]--;
if (AlarmTemp[0]== 0)
{
AlarmTemp[0] = 59;
}
Write_Ds1302(0x8e,0x00);//关闭写保护
Write_Ds1302(0xc4,AlarmTemp[0]);
Write_Ds1302(0x8e,0x80);//打开写保护
WriteCommandLCM(0x80+0x40+23);
tem[0]=AlarmTemp[0]/10;//数据的转换
tem[1]=AlarmTemp[0]%10;
WriteDataLCM(0x30+tem[0]);
WriteDataLCM(0x30+tem[1]);
WriteCommandLCM(0x80+0x40+24); //显示出来
break;
}
case 1: //调小时
{
AlarmTemp[1] = Read_DS1302_Byte(0xc7);
AlarmTemp[1]--;
if (AlarmTemp[1]== 0)
{
AlarmTemp[1] = 23;
}
Write_Ds1302(0x8e,0x00);//关闭写保护
Write_Ds1302(0xc6,AlarmTemp[1]);
Write_Ds1302(0x8e,0x80);//打开写保护
WriteCommandLCM(0x80+0x40+20);
tem[0]=AlarmTemp[1]/10;//数据的转换
tem[1]=AlarmTemp[1]%10;
WriteDataLCM(0x30+tem[0]);
WriteDataLCM(0x30+tem[1]);
WriteCommandLCM(0x80+0x40+21);
break;
}
case 2:
{
ShowAlarm = 0; //调闹钟关
ScanShowAlarm();
WriteCommandLCM(0x80+0x40+30);//将光标移动到 关
WriteCommandLCM(0x0f);//显示光标并且闪烁
break;
}
}
}
}
}
}
/****************扫描闹钟开与关动作************************/
void ScanShowAlarm(void)
{
uchar i;
if(ShowAlarm == 0) //闹钟标志显示与不显示值
{
WriteCommandLCM(0x80+0x40+30);//将光标移动到 关
WriteDataLCM(0x06); //写入自定义字符 关
Write_Ds1302(0x8e,0x00);//关闭写保护
Write_Ds1302(0xc8,0x00); //写入不显示闹钟标志
Write_Ds1302(0x8e,0x80);//打开写保护
WriteCommandLCM(0x80+0x10); //指定第一行
for(i = 0;i<16;i++)
{
WriteDataLCM(0x20);
delay(5);
}
}
if(ShowAlarm == 1)
{
WriteCommandLCM(0x80+0x40+30);//将光标移动到 开
WriteDataLCM(0x05); //写入自定义字符 开
Write_Ds1302(0x8e,0x00);//关闭写保护
Write_Ds1302(0xc8,0x01); //写入显示闹钟标志
Write_Ds1302(0x8e,0x80);//打开写保护
WriteCommandLCM(0x80+0x10); //指定第一行
for(i = 0;i<16;i++)
{
WriteDataLCM(0x20);
delay(5);
}
}
}
**************************************
keybutton.h
****************************************
#ifndef __KEYBUTTON_H__
#define __KEYBUTTON_H__
#define uchar unsigned char
#define uint unsigned int
sbit SET_KEY = P1^4; //外部位声明定义单片机P1.4口为设置按钮
sbit Add_KEY = P1^5; //外部位声明定义单片机P1.5口为增加按钮
sbit Sub_KEY = P1^6; //外部位声明定义单片机P1.6口为减少按钮
sbit OK_KEY = P1^7; //外部位声明定义单片机P1.7口为确认按钮
sbit Back_KEY = P1^3; //外部位声明定义单片机P1.4口为返回按钮
/****************十六进制转换成十进制************************/
uchar HEX_DEC(uchar x);
/****************BCD转码************************/
uchar change(uchar x);
/****************扫描设置按键动作************************/
void Setkeyscan(void);
/****************扫描确认按键动作************************/
void OKkeyscan(void);
/****************扫描返回按键动作************************/
void Backkeyscan(void);
/****************扫描增加键动作************************/
void Addkeyscan(void);
/****************扫描减少键动作************************/
void Subkeyscan(void);
/****************扫描闹钟开与关动作************************/
void ScanShowAlarm(void);
#endif
********************************
bell.c
**************************************
#include "main.h"
bit ibell=0;
/****************蜂鸣器简单出声***********************/
void bebuzz(void)
{
uchar i;
if (ibell == 1)
{
for(i=0;i<200;i++)//喇叭发声的时间循环,改变大小可以改变发声时间长短
{
delay(80);//参数决定发声的频率,估算值
buzz=!buzz;
}
buzz=1; //喇叭停止工作,间歇的时间,可更改
delay(20000);
}
}
**************************
bell.h
****************************************************
#ifndef __BELL_H__
#define __BELL_H__
#define uchar unsigned char
#define uint unsigned int
sbit buzz = P3^0; //外部位声明定义单片机P3.0口为蜂鸣器
/****************蜂鸣器简单出声***********************/
void bebuzz(void);
#endif
下面是源代码与仿真程序
仿真Timer.rar
(69.62 KB, 下载次数: 309)
|