开场白:
DS1302有两路独立电源输入,咱们只需在其中一路电源上挂一个扣子电池就能够完成掉电时钟持续跑的功用,扣子电池作为备用电源有必要比主电源的电压低一点。DS1302还给咱们预留了一片RAM区,咱们能够把一些数据存入到DS1302,只需DS1302的电池有电,那么它就相当于一个EEPROM。这个RAM区有什么用呢?由于RAM区的数据只需一掉电,一切的数据都会变成0x00或许0xff,也便是数据掉电会丢掉,咱们能够使用这个特色,能够在里面存入标志位数据,一旦发现这个数据改变了,就知道时钟的数据需求从头设置过,或许阐明电池没电了。
在移植DS1302驱动程序中,有一个当地最简单犯错,便是DS1302芯片的数据线DIO。咱们编程时要特别留心这个IO口什么时分作为数据输入,什么时分作为数据输出,以便及时更改方向寄存器。关于51单片机,IO口在读取数据之前,要先置1。
这一节要教会咱们六个知识点:
榜首个:DS1302做实时时钟时,批改时刻和读取时刻的常见程序结构。
第二个:怎么编写监控备用电池电量耗尽的监控程序。
第三个:在往DS1302写入数据批改时刻之前,有必要留心调整一下“日”的变量,由于每个月的最大天数是不一样的,有的一个月28天,有的一个月29天,有的一个月30天,有的一个月31天。
第四个:本程序榜首次呈现了电平按键,跟之前讲的下降沿按键不一样,请留心我是何如用软件滤波的,以及它详细的完成代码。
第五个:本程序榜首次呈现了一个按键按下去后,假如不松手就会触发两次事情,榜首次是短按,第2次是长按3秒。请留心我是怎么在之前的按键上略做批改就完成此功用的详细代码。
第六个:持续加深了解按键与显现是怎么严密相关起来的程序结构。
详细内容,请看源代码解说。
(1) 硬件渠道.
依据朱兆祺51单片机学习板。
旧版的朱兆祺51学习板在硬件上有一个bug,DS1302芯片邻近的R43,R42两个电阻应该去掉,而且把R41的电阻换成0欧姆的电阻,或许直接短接起来。新版的朱兆祺51学习板现已改正来了。
(2)完成功用:
本程序有2两个窗口。
第1个窗口显现日期。显现格局“年-月-日”。留心中心有“-”分隔。
第2个窗口显现时刻。显现格局“时 分 秒”。留心中心没“-”,只需空格分隔。
体系上电后,默许显现第2个窗口,实时显现动态的“时 分 秒”时刻。此刻按下S13按键不松手就会切换到显现日期的第1个窗口。松手后主动切换回第2个显现动态时刻的窗口。
需求更改时刻的时分,长按S9按键不松手超越3秒后,体系将进入批改时刻的状况,切换到第1个日期窗口,而且显现“年”的两位数码管会闪耀,此刻能够按S1或许S5加减按键批改年的参数,批改完年后,持续短按S9按键,会切换到“月”的参数闪耀状况,只需顺次不断按下S9按键,就会顺次切换年,月,日,时,分,秒的参数闪耀状况,最终批改完秒的参数后,体系会主动把咱们批改设置的日期时刻一次性写入DS1302芯片内部,到达批改日期时刻的意图。
S13是电平改变按键,用来切换窗口的,专门用来查看当时日期。按下S13按键时显现日期窗口,松手后返回到显现实时时刻的窗口。
本程序在使用过程中的留心事项:
(a)榜首次上电时,蜂鸣器会报警,只需DS1302芯片的备用电池电量足够,这个时分断电再重启一次,就不会报警了。
(b)榜首次上电时,时刻没有走动,需求从头设置一下日期时刻才能够。长按S9按键能够进入批改日期时刻的状况。
(3)源代码解说如下:
- #include “REG52.H”
- #define const_dpy_time_half200//数码管闪耀时刻的半值
- #define const_dpy_time_all 400//数码管闪耀时刻的全值 必定要比const_dpy_time_half 大
- #define const_voice_short40 //蜂鸣器短叫的持续时刻
- #define const_key_time120 //按键去颤动延时的时刻
- #define const_key_time220 //按键去颤动延时的时刻
- #define const_key_time320 //按键去颤动延时的时刻
- #define const_key_time420 //按键去颤动延时的时刻
- #define const_key_time171200//长按超越3秒的时刻
- #define const_ds1302_0_5s200 //大约0.5秒的时刻
- #define const_ds1302_sampling_time 360 //累计主循环次数的时刻,每次改写采样时钟芯片的时刻
- #define WRITE_SECOND 0x80 //DS1302内部的相关地址
- #define WRITE_MINUTE 0x82
- #define WRITE_HOUR 0x84
- #define WRITE_DATE 0x86
- #define WRITE_MONTH 0x88
- #define WRITE_YEAR 0x8C
- #define WRITE_CHECK 0xC2//用来查看芯片的备用电池是否用完了的地址
- #define READ_CHECK 0xC3//用来查看芯片的备用电池是否用完了的地址
- #define READ_SECOND 0x81
- #define READ_MINUTE 0x83
- #define READ_HOUR 0x85
- #define READ_DATE 0x87
- #define READ_MONTH 0x89
- #define READ_YEAR 0x8D
- #define WRITE_PROTECT 0x8E
- void initial_myself(void);
- void initial_peripheral(void);
- void delay_short(unsigned int uiDelayShort);
- void delay_long(unsigned int uiDelaylong);
- //驱动数码管的74HC595
- void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01);
- void display_drive(void); //显现数码管字模的驱动函数
- void display_service(void); //显现的窗口菜单服务程序
- //驱动LED的74HC595
- void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01);
- void T0_time(void);//守时刻断函数
- void key_service(void); //按键服务的应用程序
- void key_scan(void);//按键扫描函数 放在守时刻断里
- void ds1302_alarm_service(void); //ds1302犯错报警
- void ds1302_sampling(void); //ds1302采样程序,内部每秒钟收集更新一次
- void Write1302 ( unsigned char addr, unsigned char dat );//批改时刻的驱动
- unsigned char Read1302 ( unsigned char addr );//读取时刻的驱动
- unsigned char bcd_to_number(unsigned char ucBcdTemp);//BCD转原始数值
- unsigned char number_to_bcd(unsigned char ucNumberTemp); //原始数值转BCD
- //日调整 每个月份的日最大取值不同,有的最大28日,有的最大29日,有的最大30,有的最大31
- unsigned char date_adjust(unsigned char ucYearTemp,unsigned char ucMonthTemp,unsigned char ucDateTemp); //日调整
- sbit SCLK_dr =P1^3;
- sbit DIO_dr_sr =P1^4;
- sbit DS1302_CE_dr =P1^5;
- sbit key_sr1=P0^0; //对应朱兆祺学习板的S1键
- sbit key_sr2=P0^1; //对应朱兆祺学习板的S5键
- sbit key_sr3=P0^2; //对应朱兆祺学习板的S9键
- sbit key_sr4=P0^3; //对应朱兆祺学习板的S13键
- sbit key_gnd_dr=P0^4; //模仿独立按键的地GND,因而有必要一向输出低电平
- sbit beep_dr=P2^7; //蜂鸣器的驱动IO口
- sbit eeprom_scl_dr=P3^7; //时钟线
- sbit eeprom_sda_dr_sr=P3^6; //数据的输出线和输入线
- sbit dig_hc595_sh_dr=P2^0; //数码管的74HC595程序
- sbit dig_hc595_st_dr=P2^1;
- sbit dig_hc595_ds_dr=P2^2;
- sbit hc595_sh_dr=P2^3; //LED灯的74HC595程序
- sbit hc595_st_dr=P2^4;
- sbit hc595_ds_dr=P2^5;
- unsigned int uiSampingCnt=0; //收集Ds1302的计时器,每秒钟更新收集一次
- unsigned char ucKeySec=0; //被触发的按键编号
- unsigned intuiKeyTimeCnt1=0; //按键去颤动延时计数器
- unsigned char ucKeyLock1=0; //按键触发后自锁的变量标志
- unsigned intuiKeyTimeCnt2=0; //按键去颤动延时计数器
- unsigned char ucKeyLock2=0; //按键触发后自锁的变量标志
- unsigned intuiKeyTimeCnt3=0; //按键去颤动延时计数器
- unsigned char ucKeyLock3=0; //按键触发后自锁的变量标志
- unsigned int uiKey4Cnt1=0;//在软件滤波中,用到的变量
- unsigned int uiKey4Cnt2=0;
- unsigned char ucKey4Sr=1;//实时反映按键的电平状况
- unsigned char ucKey4SrRecord=0; //记载上一次按键的电平状况
- unsigned intuiVoiceCnt=0;//蜂鸣器鸣叫的持续时刻计数器
- unsigned charucVoiceLock=0;//蜂鸣器鸣叫的原子锁
- unsigned char ucDigShow8;//第8位数码管要显现的内容
- unsigned char ucDigShow7;//第7位数码管要显现的内容
- unsigned char ucDigShow6;//第6位数码管要显现的内容
- unsigned char ucDigShow5;//第5位数码管要显现的内容
- unsigned char ucDigShow4;//第4位数码管要显现的内容
- unsigned char ucDigShow3;//第3位数码管要显现的内容
- unsigned char ucDigShow2;//第2位数码管要显现的内容
- unsigned char ucDigShow1;//第1位数码管要显现的内容
- unsigned char ucDigDot8;//数码管8的小数点是否显现的标志
- unsigned char ucDigDot7;//数码管7的小数点是否显现的标志
- unsigned char ucDigDot6;//数码管6的小数点是否显现的标志
- unsigned char ucDigDot5;//数码管5的小数点是否显现的标志
- unsigned char ucDigDot4;//数码管4的小数点是否显现的标志
- unsigned char ucDigDot3;//数码管3的小数点是否显现的标志
- unsigned char ucDigDot2;//数码管2的小数点是否显现的标志
- unsigned char ucDigDot1;//数码管1的小数点是否显现的标志
- unsigned char ucDigShowTemp=0; //暂时中心变量
- unsigned char ucDisplayDriveStep=1;//动态扫描数码管的过程变量
- unsigned char ucWd=2;//本程序的中心变量,窗口显现变量。相似于一级菜单的变量。代表显现不同的窗口。
- unsigned char ucPart=0;//本程序的中心变量,部分显现变量。相似于二级菜单的变量。代表显现不同的部分。
- unsigned char ucWd1Update=0; //窗口1更新显现标志
- unsigned char ucWd2Update=1; //窗口2更新显现标志
- unsigned char ucWd1Part1Update=0;//在窗口1中,部分1的更新显现标志
- unsigned char ucWd1Part2Update=0; //在窗口1中,部分2的更新显现标志
- unsigned char ucWd1Part3Update=0; //在窗口1中,部分3的更新显现标志
- unsigned char ucWd2Part1Update=0;//在窗口2中,部分1的更新显现标志
- unsigned char ucWd2Part2Update=0; //在窗口2中,部分2的更新显现标志
- unsigned char ucWd2Part3Update=0; //在窗口2中,部分3的更新显现标志
- unsigned charucYear=0; //原始数据
- unsigned charucMonth=0;
- unsigned charucDate=0;
- unsigned charucHour=0;
- unsigned charucMinute=0;
- unsigned charucSecond=0;
- unsigned charucYearBCD=0; //BCD码的数据
- unsigned charucMonthBCD=0;
- unsigned charucDateBCD=0;
- unsigned charucHourBCD=0;
- unsigned charucMinuteBCD=0;
- unsigned charucSecondBCD=0;
- unsigned char ucTemp1=0;//中心过渡变量
- unsigned char ucTemp2=0;//中心过渡变量
- unsigned char ucTemp4=0;//中心过渡变量
- unsigned char ucTemp5=0;//中心过渡变量
- unsigned char ucTemp7=0;//中心过渡变量
- unsigned char ucTemp8=0;//中心过渡变量
- unsigned char ucDelayTimerLock=0; //原子锁
- unsigned intuiDelayTimer=0;
- unsigned char ucCheckDs1302=0;//查看Ds1302芯片是否正常
- unsigned char ucDs1302Error=0; //Ds1302芯片的备用电池是否用完了的报警标志
- unsigned char ucDs1302Lock=0;//原子锁
- unsigned intuiDs1302Cnt=0; //间歇性蜂鸣器报警的计时器
- unsigned char ucDpyTimeLock=0; //原子锁
- unsigned intuiDpyTimeCnt=0;//数码管的闪耀计时器,放在守时刻断里不断累加
- //依据原理图得出的共阴数码管字模表
- code unsigned char dig_table[]=
- {
- 0x3f,//0 序号0
- 0x06,//1 序号1
- 0x5b,//2 序号2
- 0x4f,//3 序号3
- 0x66,//4 序号4
- 0x6d,//5 序号5
- 0x7d,//6 序号6
- 0x07,//7 序号7
- 0x7f,//8 序号8
- 0x6f,//9 序号9
- 0x00,//无 序号10
- 0x40,//- 序号11
- 0x73,//P 序号12
- };
- void main()
- {
- initial_myself();
- delay_long(100);
- initial_peripheral();
- while(1)
- {
- key_service(); //按键服务的应用程序
- ds1302_sampling(); //ds1302采样程序,内部每秒钟收集更新一次
- display_service(); //显现的窗口菜单服务程序
- ds1302_alarm_service(); //ds1302犯错报警
- }
- }
- /* 注释一:
- * 体系不必时时刻刻收集ds1302的内部数据,每隔一段时刻更新收集一次就能够了。
- * 这个间隔时刻应该小于或许等于1秒钟的时刻,不然在数码管上看不到每秒钟的时刻改变。
- */
- void ds1302_sampling(void) //ds1302采样程序,内部每秒钟收集更新一次
- {
- if(ucPart==0)//当体系不是处于设置日期和时刻的情况下
- {
- ++uiSampingCnt;//累计主循环次数的时刻
- if(uiSampingCnt>const_ds1302_sampling_time)//每隔一段时刻就更新收集一次Ds1302数据
- {
- uiSampingCnt=0;
- ucYearBCD=Read1302(READ_YEAR); //读取年
- ucMonthBCD=Read1302(READ_MONTH); //读取月
- ucDateBCD=Read1302(READ_DATE); //读取日
- ucHourBCD=Read1302(READ_HOUR); //读取时
- ucMinuteBCD=Read1302(READ_MINUTE); //读取分
- ucSecondBCD=Read1302(READ_SECOND); //读取秒
- ucYear=bcd_to_number(ucYearBCD);//BCD转原始数值
- ucMonth=bcd_to_number(ucMonthBCD);//BCD转原始数值
- ucDate=bcd_to_number(ucDateBCD);//BCD转原始数值
- ucHour=bcd_to_number(ucHourBCD);//BCD转原始数值
- ucMinute=bcd_to_number(ucMinuteBCD);//BCD转原始数值
- ucSecond=bcd_to_number(ucSecondBCD);//BCD转原始数值
- ucWd2Update=1; //窗口2更新显现时刻
- }
- }
- }
- //批改ds1302时刻的驱动 ,留心,此处写入的是BCD码,
- void Write1302 ( unsigned char addr, unsigned char dat )
- {
- unsigned char i,temp; //单片机驱动DS1302归于SPI通讯方法,依据我的经历,不必封闭间断
- DS1302_CE_dr=0; //CE引脚为低,数据传送间断
- delay_short(1);
- SCLK_dr=0; //清零时钟总线
- delay_short(1);
- DS1302_CE_dr = 1; //CE引脚为高,逻辑操控有用
- delay_short(1);
- //发送地址
- for ( i=0; i<8; i++ ) //循环8次移位
- {
- DIO_dr_sr = 0;
- temp = addr;
- if(temp&0x01)
- {
- DIO_dr_sr =1;
- }
- else
- {
- DIO_dr_sr =0;
- }
- delay_short(1);
- addr >>= 1; //右移一位
- SCLK_dr = 1;
- delay_short(1);
- SCLK_dr = 0;
- delay_short(1);
- }
- //发送数据
- for ( i=0; i<8; i++ ) //循环8次移位
- {
- DIO_dr_sr = 0;
- temp = dat;
- if(temp&0x01)
- {
- DIO_dr_sr =1;
- }
- else
- {
- DIO_dr_sr =0;
- }
- delay_short(1);
- dat >>= 1; //右移一位
- SCLK_dr = 1;
- delay_short(1);
- SCLK_dr = 0;
- delay_short(1);
- }
- DS1302_CE_dr = 0;
- delay_short(1);
- }
- //读取Ds1302时刻的驱动 ,留心,此处读取的是BCD码,
- unsigned char Read1302 ( unsigned char addr )
- {
- unsigned char i,temp,dat1;
- DS1302_CE_dr=0; //单片机驱动DS1302归于SPI通讯方法,依据我的经历,不必封闭间断
- delay_short(1);
- SCLK_dr=0;
- delay_short(1);
- DS1302_CE_dr = 1;
- delay_short(1);
- //发送地址
- for ( i=0; i<8; i++ ) //循环8次移位
- {
- DIO_dr_sr = 0;
- temp = addr;
- if(temp&0x01)
- {
- DIO_dr_sr =1;
- }
- else
- {
- DIO_dr_sr =0;
- }
- delay_short(1);
- addr >>= 1; //右移一位
- SCLK_dr = 1;
- delay_short(1);
- SCLK_dr = 0;
- delay_short(1);
- }
- /* 注释二:
- * 51单片机IO口的特色,在读取数据之前有必要先输出高电平,
- * 假如是PIC,AVR等单片机,这儿应该把IO方向寄存器设置为输入
- */
- DIO_dr_sr =1; //51单片机IO口的特色,在读取数据之前有必要先输出高电平,
- temp=0;
- for ( i=0; i<8; i++ )
- {
- temp>>=1;
- if(DIO_dr_sr==1)
- {
- temp=temp+0x80;
- }
- DIO_dr_sr =1;//51单片机IO口的特色,在读取数据之前有必要先输出高电平
- delay_short(1);
- SCLK_dr = 1;
- delay_short(1);
- SCLK_dr = 0;
- delay_short(1);
- }
- DS1302_CE_dr=0;
- delay_short(1);
- dat1=temp;
- return (dat1);
- }
- unsigned char bcd_to_number(unsigned char ucBcdTemp)//BCD转原始数值
- {
- unsigned char ucNumberResult=0;
- unsigned char ucBcdTemp10;
- unsigned char ucBcdTemp1;
- ucBcdTemp10=ucBcdTemp;
- ucBcdTemp10=ucBcdTemp10>>4;
- ucBcdTemp1=ucBcdTemp;
- ucBcdTemp1=ucBcdTemp1&0x0f;
- ucNumberResult=ucBcdTemp10*10+ucBcdTemp1;
- return ucNumberResult;
- }
- unsigned char number_to_bcd(unsigned char ucNumberTemp) //原始数值转BCD
- {
- unsigned char ucBcdResult=0;
- unsigned char ucNumberTemp10;
- unsigned char ucNumberTemp1;
- ucNumberTemp10=ucNumberTemp;
- ucNumberTemp10=ucNumberTemp10/10;
- ucNumberTemp10=ucNumberTemp10<<4;
- ucNumberTemp10=ucNumberTemp10&0xf0;
- ucNumberTemp1=ucNumberTemp;
- ucNumberTemp1=ucNumberTemp1%10;
- ucBcdResult=ucNumberTemp10|ucNumberTemp1;
- return ucBcdResult;
- }
- //日调整 每个月份的日最大取值不同,有的最大28日,有的最大29日,有的最大30,有的最大31
- unsigned char date_adjust(unsigned char ucYearTemp,unsigned char ucMonthTemp,unsigned char ucDateTemp) //日调整
- {
- unsigned char ucDayResult;
- unsigned int uiYearTemp;
- unsigned int uiYearYu;
- ucDayResult=ucDateTemp;
- switch(ucMonthTemp)//依据不同的月份来批改不同的日最大值
- {
- case 2://二月份要核算是否是闰年
- uiYearTemp=2000+ucYearTemp;
- uiYearYu=uiYearTemp%4;
- if(uiYearYu==0) //闰年
- {
- if(ucDayResult>29)
- {
- ucDayResult=29;
- }
- }
- else
- {
- if(ucDayResult>28)
- {
- ucDayResult=28;
- }
- }
- break;
- case 4:
- case 6:
- case 9:
- case 11:
- if(ucDayResult>30)
- {
- ucDayResult=30;
- }
- break;
- }
- return ucDayResult;
- }
- void ds1302_alarm_service(void) //ds1302犯错报警
- {
- if(ucDs1302Error==1)//备用电池的电量用完了报警提示
- {
- if(uiDs1302Cnt>const_ds1302_0_5s)//大约0.5秒钟蜂鸣器响一次
- {
- ucDs1302Lock=1;//原子锁加锁
- uiDs1302Cnt=0; //计时器清零
- ucDs1302Lock=0;//原子锁解锁
- ucVoiceLock=1;//原子锁加锁,维护主函数与间断函数的同享变量uiVoiceCnt
- uiVoiceCnt=const_voice_short; //蜂鸣器声响触发,滴一声就停。
- ucVoiceLock=0;//原子锁解锁,维护主函数与间断函数的同享变量uiVoiceCnt
- }
- }
- }
- void display_service(void) //显现的窗口菜单服务程序
- {
- switch(ucWd)//本程序的中心变量,窗口显现变量。相似于一级菜单的变量。代表显现不同的窗口。
- {
- case 1: //显现日期窗口的数据数据格局 NN-YY-RR 年-月-日
- if(ucWd1Update==1)//窗口1要悉数更新显现
- {
- ucWd1Update=0;//及时清零标志,防止一向进来扫描
- ucDigShow6=11;//显现一杠”-“
- ucDigShow3=11;//显现一杠”-“
- ucWd1Part1Update=1;//部分年更新显现
- ucWd1Part2Update=1;//部分月更新显现
- ucWd1Part3Update=1;//部分日更新显现
- }
- if(ucWd1Part1Update==1)//部分年更新显现
- {
- ucWd1Part1Update=0;
- ucTemp8=ucYear/10;//年
- ucTemp7=ucYear%10;
- ucDigShow8=ucTemp8; //数码管显现实践内容
- ucDigShow7=ucTemp7;
- }
- if(ucWd1Part2Update==1)//部分月更新显现
- {
- ucWd1Part2Update=0;
- ucTemp5=ucMonth/10;//月
- ucTemp4=ucMonth%10;
- ucDigShow5=ucTemp5; //数码管显现实践内容
- ucDigShow4=ucTemp4;
- }
- if(ucWd1Part3Update==1) //部分日更新显现
- {
- ucWd1Part3Update=0;
- ucTemp2=ucDate/10;//日
- ucTemp1=ucDate%10;
- ucDigShow2=ucTemp2; //数码管显现实践内容
- ucDigShow1=ucTemp1;
- }
- //数码管闪耀
- switch(ucPart)//相当于二级菜单,依据部分变量的值,使对应的参数产生闪耀的动态效果。
- {
- case 0://都不闪耀
- break;
- case 1://年参数闪耀
- if(uiDpyTimeCnt==const_dpy_time_half)
- {
- ucDigShow8=ucTemp8; //数码管显现实践内容
- ucDigShow7=ucTemp7;
- }
- else if(uiDpyTimeCnt>const_dpy_time_all) //const_dpy_time_all必定要比const_dpy_time_half 大
- {
- ucDpyTimeLock=1; //原子锁加锁
- uiDpyTimeCnt=0; //及时把闪耀记时器清零
- ucDpyTimeLock=0;//原子锁解锁
- ucDigShow8=10; //数码管显现空,什么都不显现
- ucDigShow7=10;
- }
- break;
- case 2: //月参数闪耀
- if(uiDpyTimeCnt==const_dpy_time_half)
- {
- ucDigShow5=ucTemp5; //数码管显现实践内容
- ucDigShow4=ucTemp4;
- }
- else if(uiDpyTimeCnt>const_dpy_time_all) //const_dpy_time_all必定要比const_dpy_time_half 大
- {
- ucDpyTimeLock=1; //原子锁加锁
- uiDpyTimeCnt=0; //及时把闪耀记时器清零
- ucDpyTimeLock=0;//原子锁解锁
- ucDigShow5=10; //数码管显现空,什么都不显现
- ucDigShow4=10;
- }
- break;
- case 3: //日参数闪耀
- if(uiDpyTimeCnt==const_dpy_time_half)
- {
- ucDigShow2=ucTemp2; //数码管显现实践内容
- ucDigShow1=ucTemp1;
- }
- else if(uiDpyTimeCnt>const_dpy_time_all) //const_dpy_time_all必定要比const_dpy_time_half 大
- {
- ucDpyTimeLock=1; //原子锁加锁
- uiDpyTimeCnt=0; //及时把闪耀记时器清零
- ucDpyTimeLock=0;//原子锁解锁
- ucDigShow2=10; //数码管显现空,什么都不显现
- ucDigShow1=10;
- }
- break;
- }
- break;
- case 2: //显现时刻窗口的数据数据格局 SS FF MM 时 分 秒
- if(ucWd2Update==1)//窗口2要悉数更新显现
- {
- ucWd2Update=0;//及时清零标志,防止一向进来扫描
- ucDigShow6=10;//显现空
- ucDigShow3=10;//显现空
- ucWd2Part3Update=1;//部分时更新显现
- ucWd2Part2Update=1;//部分分更新显现
- ucWd2Part1Update=1;//部分秒更新显现
- }
- if(ucWd2Part1Update==1)//部分时更新显现
- {
- ucWd2Part1Update=0;
- ucTemp8=ucHour/10;//时
- ucTemp7=ucHour%10;
- ucDigShow8=ucTemp8; //数码管显现实践内容
- ucDigShow7=ucTemp7;
- }
- if(ucWd2Part2Update==1)//部分分更新显现
- {
- ucWd2Part2Update=0;
- ucTemp5=ucMinute/10;//分
- ucTemp4=ucMinute%10;
- ucDigShow5=ucTemp5; //数码管显现实践内容
- ucDigShow4=ucTemp4;
- }
- if(ucWd2Part3Update==1) //部分秒更新显现
- {
- ucWd2Part3Update=0;
- ucTemp2=ucSecond/10;//秒
- ucTemp1=ucSecond%10;
- ucDigShow2=ucTemp2; //数码管显现实践内容
- ucDigShow1=ucTemp1;
- }
- //数码管闪耀
- switch(ucPart)//相当于二级菜单,依据部分变量的值,使对应的参数产生闪耀的动态效果。
- {
- case 0://都不闪耀
- break;
- case 1://时参数闪耀
- if(uiDpyTimeCnt==const_dpy_time_half)
- {
- ucDigShow8=ucTemp8; //数码管显现实践内容
- ucDigShow7=ucTemp7;
- }
- else if(uiDpyTimeCnt>const_dpy_time_all) //const_dpy_time_all必定要比const_dpy_time_half 大
- {
- ucDpyTimeLock=1; //原子锁加锁
- uiDpyTimeCnt=0; //及时把闪耀记时器清零
- ucDpyTimeLock=0;//原子锁解锁
- ucDigShow8=10; //数码管显现空,什么都不显现
- ucDigShow7=10;
- }
- break;
- case 2: //分参数闪耀
- if(uiDpyTimeCnt==const_dpy_time_half)
- {
- ucDigShow5=ucTemp5; //数码管显现实践内容
- ucDigShow4=ucTemp4;
- }
- else if(uiDpyTimeCnt>const_dpy_time_all) //const_dpy_time_all必定要比const_dpy_time_half 大
- {
- ucDpyTimeLock=1; //原子锁加锁
- uiDpyTimeCnt=0; //及时把闪耀记时器清零
- ucDpyTimeLock=0;//原子锁解锁
- ucDigShow5=10; //数码管显现空,什么都不显现
- ucDigShow4=10;
- }
- break;
- case 3: //秒参数闪耀
- if(uiDpyTimeCnt==const_dpy_time_half)
- {
- ucDigShow2=ucTemp2; //数码管显现实践内容
- ucDigShow1=ucTemp1;
- }
- else if(uiDpyTimeCnt>const_dpy_time_all) //const_dpy_time_all必定要比const_dpy_time_half 大
- {
- ucDpyTimeLock=1; //原子锁加锁
- uiDpyTimeCnt=0; //及时把闪耀记时器清零
- ucDpyTimeLock=0;//原子锁解锁
- ucDigShow2=10; //数码管显现空,什么都不显现
- ucDigShow1=10;
- }
- break;
- }
- break;
- }
- }
- void key_scan(void)//按键扫描函数 放在守时刻断里
- {
- if(key_sr1==1)//IO是高电平,阐明按键没有被按下,这时要及时清零一些标志位
- {
- ucKeyLock1=0; //按键自锁标志清零
- uiKeyTimeCnt1=0;//按键去颤动延时计数器清零,此行十分奇妙,是我实战中探索出来的。
- }
- else if(ucKeyLock1==0)//有按键按下,且是榜首次被按下
- {
- uiKeyTimeCnt1++; //累加守时刻断次数
- if(uiKeyTimeCnt1>const_key_time1)
- {
- uiKeyTimeCnt1=0;
- ucKeyLock1=1;//自锁按键置位,防止一向触发
- ucKeySec=1; //触发1号键
- }
- }
- if(key_sr2==1)//IO是高电平,阐明按键没有被按下,这时要及时清零一些标志位
- {
- ucKeyLock2=0; //按键自锁标志清零
- uiKeyTimeCnt2=0;//按键去颤动延时计数器清零,此行十分奇妙,是我实战中探索出来的。
- }
- else if(ucKeyLock2==0)//有按键按下,且是榜首次被按下
- {
- uiKeyTimeCnt2++; //累加守时刻断次数
- if(uiKeyTimeCnt2>const_key_time2)
- {
- uiKeyTimeCnt2=0;
- ucKeyLock2=1;//自锁按键置位,防止一向触发
- ucKeySec=2; //触发2号键
- }
- }
- /* 注释三:
- * 留心,此处把一个按键的短按和长按的功用都完成了。
- */
- if(key_sr3==1)//IO是高电平,阐明按键没有被按下,这时要及时清零一些标志位
- {
- ucKeyLock3=0; //按键自锁标志清零
- uiKeyTimeCnt3=0;//按键去颤动延时计数器清零,此行十分奇妙,是我实战中探索出来的。
- }
- else if(ucKeyLock3==0)//有按键按下,且是榜首次被按下
- {
- uiKeyTimeCnt3++; //累加守时刻断次数
- if(uiKeyTimeCnt3>const_key_time3)
- {
- uiKeyTimeCnt3=0;
- ucKeyLock3=1;//自锁按键置位,防止一向触发
- ucKeySec=3; //短按触发3号键
- }
- }
- else if(uiKeyTimeCnt3
- {
- uiKeyTimeCnt3++; //累加守时刻断次数
- if(uiKeyTimeCnt3==const_key_time17)//等于3秒钟,触发17号长按按键
- {
- ucKeySec=17; //长按3秒触发17号键
- }
- }
- /* 注释四:
- * 留心,此处是电平按键的滤波抗干扰处理
- */
- if(key_sr4==1)//对应朱兆祺学习板的S13键
- {
- uiKey4Cnt1=0; //在软件滤波中,十分要害的句子!!!相似按键去颤动程序的及时清零
- uiKey4Cnt2++; //相似独立按键去颤动的软件抗干扰处理
- if(uiKey4Cnt2>const_key_time4)
- {
- uiKey4Cnt2=0;
- ucKey4Sr=1;//实时反映按键松手时的电平状况
- }
- }
- else
- {
- uiKey4Cnt2=0; //在软件滤波中,十分要害的句子!!!相似按键去颤动程序的及时清零
- uiKey4Cnt1++;
- if(uiKey4Cnt1>const_key_time4)
- {
- uiKey4Cnt1=0;
- ucKey4Sr=0;//实时反映按键按下时的电平状况
- }
- }
- }
- void key_service(void) //按键服务的应用程序
- {
- switch(ucKeySec) //按键服务状况切换
- {
- case 1:// 加按键 对应朱兆祺学习板的S1键
- switch(ucWd)//在不同的窗口下,设置不同的参数
- {
- case 1:
- switch(ucPart) //在不同的部分变量下,相当于二级菜单
- {
- case 1://年
- ucYear++;
- if(ucYear>99)
- {
- ucYear=99;
- }
- ucWd1Part1Update=1;//更新显现
- break;
- case 2: //月
- ucMonth++;
- if(ucMonth>12)
- {
- ucMonth=12;
- }
- ucWd1Part2Update=1;//更新显现
- break;
- case 3: //日
- ucDate++;
- if(ucDate>31)
- {
- ucDate=31;
- }
- ucWd1Part3Update=1;//更新显现
- break;
- }
- break;
- case 2:
- switch(ucPart) //在不同的部分变量下,相当于二级菜单
- {
- case 1://时
- ucHour++;
- if(ucHour>23)
- {
- ucHour=23;
- }
- ucWd2Part1Update=1;//更新显现
- break;
- case 2: //分
- ucMinute++;
- if(ucMinute>59)
- {
- ucMinute=59;
- }
- ucWd2Part2Update=1;//更新显现
- break;
- case 3: //秒
- ucSecond++;
- if(ucSecond>59)
- {
- ucSecond=59;
- }
- ucWd2Part3Update=1;//更新显现
- break;
- }
- break;
- }
- ucVoiceLock=1;//原子锁加锁,维护主函数与间断函数的同享变量uiVoiceCnt
- uiVoiceCnt=const_voice_short; //按键声响触发,滴一声就停。
- ucVoiceLock=0;//原子锁解锁,维护主函数与间断函数的同享变量uiVoiceCnt
- ucKeySec=0;//呼应按键服务处理程序后,按键编号清零,防止共同触发
- break;
- case 2:// 减按键 对应朱兆祺学习板的S5键
- switch(ucWd)//在不同的窗口下,设置不同的参数
- {
- case 1:
- switch(ucPart) //在不同的部分变量下,相当于二级菜单
- {
- case 1://年
- ucYear–;
- if(ucYear>99)
- {
- ucYear=0;
- }
- ucWd1Part1Update=1;//更新显现
- break;
- case 2: //月
- ucMonth–;
- if(ucMonth<1)
- {
- ucMonth=1;
- }
- ucWd1Part2Update=1;//更新显现
- break;
- case 3: //日
- ucDate–;
- if(ucDate<1)
- {
- ucDate=1;
- }
- ucWd1Part3Update=1;//更新显现
- break;
- }
- break;
- case 2:
- switch(ucPart) //在不同的部分变量下,相当于二级菜单
- {
- case 1://时
- ucHour–;
- if(ucHour>23)
- {
- ucHour=0;
- }
- ucWd2Part1Update=1;//更新显现
- break;
- case 2: //分
- ucMinute–;
- if(ucMinute>59)
- {
- ucMinute=0;
- }
- ucWd2Part2Update=1;//更新显现
- break;
- case 3: //秒
- ucSecond–;
- if(ucSecond>59)
- {
- ucSecond=0;
- }
- ucWd2Part3Update=1;//更新显现
- break;
- }
- break;
- }
- ucVoiceLock=1;//原子锁加锁,维护主函数与间断函数的同享变量uiVoiceCnt
- uiVoiceCnt=const_voice_short; //按键声响触发,滴一声就停。
- ucVoiceLock=0;//原子锁解锁,维护主函数与间断函数的同享变量uiVoiceCnt
- ucKeySec=0;//呼应按键服务处理程序后,按键编号清零,防止共同触发
- break;
- case 3://短按设置按键 对应朱兆祺学习板的S9键
- switch(ucWd)//在不同的窗口下,设置不同的参数
- {
- case 1:
- ucPart++;
- if(ucPart>3)
- {
- ucPart=1;
- ucWd=2; //切换到第二个窗口,设置时分秒
- ucWd2Update=1;//窗口2更新显现
- }
- ucWd1Update=1;//窗口1更新显现
- break;
- case 2:
- if(ucPart>0) //在窗口2的时分,要榜首次激活设置时刻,有必要是长按3秒才能够,这儿短按激活不了榜首次
- {
- ucPart++;
- if(ucPart>3)//设置时刻完毕
- {
- ucPart=0;
- /* 注释五:
- * 每个月份的天数最大值是不一样的,在写入ds1302时钟芯片内部数据前,应该做一次调整。
- * 有的月份最大28天,有的月份最大29天,有的月份最大30天,有的月份最大31天,
- */
- ucDate=date_adjust(ucYear,ucMonth,ucDate); //日调整 防止日的数值在某个月份超范围
- ucYearBCD=number_to_bcd(ucYear);//原始数值转BCD
- ucMonthBCD=number_to_bcd(ucMonth); //原始数值转BCD
- ucDateBCD=number_to_bcd(ucDate);//原始数值转BCD
- ucHourBCD=number_to_bcd(ucHour);//原始数值转BCD
- ucMinuteBCD=number_to_bcd(ucMinute);//原始数值转BCD
- ucSecondBCD=number_to_bcd(ucSecond);//原始数值转BCD
- Write1302 (WRITE_PROTECT,0X00); //制止写维护
- Write1302 (WRITE_YEAR,ucYearBCD); //年批改
- Write1302 (WRITE_MONTH,ucMonthBCD); //月批改
- Write1302 (WRITE_DATE,ucDateBCD); //日批改
- Write1302 (WRITE_HOUR,ucHourBCD); //小时批改
- Write1302 (WRITE_MINUTE,ucMinuteBCD); //分钟批改
- Write1302 (WRITE_SECOND,ucSecondBCD); //秒位批改
- Write1302 (WRITE_PROTECT,0x80); //答应写维护
- }
- ucWd2Update=1;//窗口2更新显现
- }
- break;
- }
- ucVoiceLock=1;//原子锁加锁,维护主函数与间断函数的同享变量uiVoiceCnt
- uiVoiceCnt=const_voice_short; //按键声响触发,滴一声就停。
- ucVoiceLock=0;//原子锁解锁,维护主函数与间断函数的同享变量uiVoiceCnt
- ucKeySec=0;//呼应按键服务处理程序后,按键编号清零,防止共同触发
- break;
- case 17://长按3秒设置按键 对应朱兆祺学习板的S9键
- switch(ucWd)//在不同的窗口下,设置不同的参数
- {
- case 2:
- if(ucPart==0) //处于非设置时刻的状况下,要榜首次激活设置时刻,有必要是长按3秒才能够
- {
- ucWd=1;
- ucPart=1;//进入到设置日期的状况下
- ucWd1Update=1;//窗口1更新显现
- }
- break;
- }
- ucVoiceLock=1;//原子锁加锁,维护主函数与间断函数的同享变量uiVoiceCnt
- uiVoiceCnt=const_voice_short; //按键声响触发,滴一声就停。
- ucVoiceLock=0;//原子锁解锁,维护主函数与间断函数的同享变量uiVoiceCnt
- ucKeySec=0;//呼应按键服务处理程序后,按键编号清零,防止共同触发
- break;
- }
- /* 注释六:
- * 留心,此处便是榜首次呈现的电平按键程序,跟以往的下降沿按键不一样。
- * ucKey4Sr是通过软件滤波处理后,直接反响IO口电平状况的变量.当电平产生
- * 改变时,就会切换到不同的显现界面,这儿多用了一个ucKey4SrRecord变量
- * 记载上一次的电平状况,是为了防止一向改写显现。
- */
- if(ucKey4Sr!=ucKey4SrRecord)//阐明S13的切换按键电平状况产生改变
- {
- ucKey4SrRecord=ucKey4Sr;//及时记载当时最新的按键电平状况防止一向进来触发
- if(ucKey4Sr==1) //松手后切换到显现时刻的窗口
- {
- ucWd=2; //显现时分秒的窗口
- ucPart=0;//进入到非设置时刻的状况下
- ucWd2Update=1;//窗口2更新显现
- }
- else//按下去切换到显现日期的窗口
- {
- ucWd=1; //显现年月日的窗口
- ucPart=0;//进入到非设置时刻的状况下
- ucWd1Update=1;//窗口1更新显现
- }
- }
- }
- void display_drive(void)
- {
- //以下程序,假如加一些数组和移位的元素,还能够紧缩容量。可是鸿哥寻求的不是容量,而是明晰的解说思路
- switch(ucDisplayDriveStep)
- {
- case 1://显现第1位
- ucDigShowTemp=dig_table[ucDigShow1];
- if(ucDigDot1==1)
- {
- ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
- }
- dig_hc595_drive(ucDigShowTemp,0xfe);
- break;
- case 2://显现第2位
- ucDigShowTemp=dig_table[ucDigShow2];
- if(ucDigDot2==1)
- {
- ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
- }
- dig_hc595_drive(ucDigShowTemp,0xfd);
- break;
- case 3://显现第3位
- ucDigShowTemp=dig_table[ucDigShow3];
- if(ucDigDot3==1)
- {
- ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
- }
- dig_hc595_drive(ucDigShowTemp,0xfb);
- break;
- case 4://显现第4位
- ucDigShowTemp=dig_table[ucDigShow4];
- if(ucDigDot4==1)
- {
- ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
- }
- dig_hc595_drive(ucDigShowTemp,0xf7);
- break;
- case 5://显现第5位
- ucDigShowTemp=dig_table[ucDigShow5];
- if(ucDigDot5==1)
- {
- ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
- }
- dig_hc595_drive(ucDigShowTemp,0xef);
- break;
- case 6://显现第6位
- ucDigShowTemp=dig_table[ucDigShow6];
- if(ucDigDot6==1)
- {
- ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
- }
- dig_hc595_drive(ucDigShowTemp,0xdf);
- break;
- case 7://显现第7位
- ucDigShowTemp=dig_table[ucDigShow7];
- if(ucDigDot7==1)
- {
- ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
- }
- dig_hc595_drive(ucDigShowTemp,0xbf);
- break;
- case 8://显现第8位
- ucDigShowTemp=dig_table[ucDigShow8];
- if(ucDigDot8==1)
- {
- ucDigShowTemp=ucDigShowTemp|0x80;//显现小数点
- }
- dig_hc595_drive(ucDigShowTemp,0x7f);
- break;
- }
- ucDisplayDriveStep++;
- if(ucDisplayDriveStep>8)//扫描完8个数码管后,从头从榜首个开端扫描
- {
- ucDisplayDriveStep=1;
- }
- }
- //数码管的74HC595驱动函数
- void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01)
- {
- unsigned char i;
- unsigned char ucTempData;
- dig_hc595_sh_dr=0;
- dig_hc595_st_dr=0;
- ucTempData=ucDigStatusTemp16_09;//先送高8位
- for(i=0;i<8;i++)
- {
- if(ucTempData>=0x80)dig_hc595_ds_dr=1;
- else dig_hc595_ds_dr=0;
- dig_hc595_sh_dr=0; //SH引脚的上升沿把数据送入寄存器
- delay_short(1);
- dig_hc595_sh_dr=1;
- delay_short(1);
- ucTempData=ucTempData<<1;
- }
- ucTempData=ucDigStatusTemp08_01;//再先送低8位
- for(i=0;i<8;i++)
- {
- if(ucTempData>=0x80)dig_hc595_ds_dr=1;
- else dig_hc595_ds_dr=0;
- dig_hc595_sh_dr=0; //SH引脚的上升沿把数据送入寄存器
- delay_short(1);
- dig_hc595_sh_dr=1;
- delay_short(1);
- ucTempData=ucTempData<<1;
- }
- dig_hc595_st_dr=0;//ST引脚把两个寄存器的数据更新输出到74HC595的输出引脚上而且锁存起来
- delay_short(1);
- dig_hc595_st_dr=1;
- delay_short(1);
- dig_hc595_sh_dr=0; //拉低,抗干扰就增强
- dig_hc595_st_dr=0;
- dig_hc595_ds_dr=0;
- }
- //LED灯的74HC595驱动函数
- void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01)
- {
- unsigned char i;
- unsigned char ucTempData;
- hc595_sh_dr=0;
- hc595_st_dr=0;
- ucTempData=ucLedStatusTemp16_09;//先送高8位
- for(i=0;i<8;i++)
- {
- if(ucTempData>=0x80)hc595_ds_dr=1;
- else hc595_ds_dr=0;
- hc595_sh_dr=0; //SH引脚的上升沿把数据送入寄存器
- delay_short(1);
- hc595_sh_dr=1;
- delay_short(1);
- ucTempData=ucTempData<<1;
- }
- ucTempData=ucLedStatusTemp08_01;//再先送低8位
- for(i=0;i<8;i++)
- {
- if(ucTempData>=0x80)hc595_ds_dr=1;
- else hc595_ds_dr=0;
- hc595_sh_dr=0; //SH引脚的上升沿把数据送入寄存器
- delay_short(1);
- hc595_sh_dr=1;
- delay_short(1);
- ucTempData=ucTempData<<1;
- }
- hc595_st_dr=0;//ST引脚把两个寄存器的数据更新输出到74HC595的输出引脚上而且锁存起来
- delay_short(1);
- hc595_st_dr=1;
- delay_short(1);
- hc595_sh_dr=0; //拉低,抗干扰就增强
- hc595_st_dr=0;
- hc595_ds_dr=0;
- }
- void T0_time(void) interrupt 1 //守时刻断
- {
- TF0=0;//铲除间断标志
- TR0=0; //关间断
- if(ucVoiceLock==0) //原子锁判别
- {
- if(uiVoiceCnt!=0)
- {
- uiVoiceCnt–; //每次进入守时刻断都自减1,直到等于零中止。才中止鸣叫
- beep_dr=0;//蜂鸣器是PNP三极管操控,低电平就开端鸣叫。
- }
- else
- {
- ; //此处多加一个空指令,想保持跟if括号句子的数量对称,都是两条指令。不加也能够。
- beep_dr=1;//蜂鸣器是PNP三极管操控,高电平就中止鸣叫。
- }
- }
- if(ucDs1302Error>0) //EEPROM犯错
- {
- if(ucDs1302Lock==0)//原子锁判别
- {
- uiDs1302Cnt++;//间歇性蜂鸣器报警的计时器
- }
- }
- if(ucDpyTimeLock==0) //原子锁判别
- {
- uiDpyTimeCnt++;//数码管的闪耀计时器
- }
- key_scan(); //按键扫描函数
- display_drive();//数码管字模的驱动函数
- TH0=0xfe; //重装初始值(65535-500)=65035=0xfe0b
- TL0=0x0b;
- TR0=1;//开间断
- }
- void delay_short(unsigned int uiDelayShort)
- {
- unsigned int i;
- for(i=0;i
- {
- ; //一个分号相当于履行一条空句子
- }
- }
- void delay_long(unsigned int uiDelayLong)
- {
- unsigned int i;
- unsigned int j;
- for(i=0;i
- {
- for(j=0;j<500;j++)//内嵌循环的空指令数量
- {
- ; //一个分号相当于履行一条空句子
- }
- }
- }
- void initial_myself(void)//榜首区 初始化单片机
- {
- key_gnd_dr=0; //模仿独立按键的地GND,因而有必要一向输出低电平
- beep_dr=1; //用PNP三极管操控蜂鸣器,输出高电平时不叫。
- hc595_drive(0x00,0x00);//封闭一切通过别的两个74HC595驱动的LED灯
- TMOD=0x01;//设置守时器0为工作方法1
- TH0=0xfe; //重装初始值(65535-500)=65035=0xfe0b
- TL0=0x0b;
- }
- void initial_peripheral(void) //第二区 初始化外围
- {
- ucDigDot8=0; //小数点悉数不显现
- ucDigDot7=0;
- ucDigDot6=0;
- ucDigDot5=0;
- ucDigDot4=0;
- ucDigDot3=0;
- ucDigDot2=0;
- ucDigDot1=0;
- EA=1; //开总间断
- ET0=1; //答应守时刻断
- TR0=1; //发动守时刻断
- /* 注释七:
- * 查看ds1302芯片的备用电池电量是否用完了。
- * 在上电的时分,在一个特定的地址里把数据读出来,假如发现不等于0x5a,
- * 则是由于备用电池电量用完了,导致保存在ds1302内部RAM数据区的数据被更改,
- * 与此同时,应该从头写入一次0x5a,为下一次通电判别做准备
- */
- ucCheckDs1302=Read1302(READ_CHECK); //判别ds1302内部的数据是否被更改
- if(ucCheckDs1302!=0x5a)
- {
- Write1302 (WRITE_PROTECT,0X00); //制止写维护
- Write1302 (WRITE_CHECK,0x5a); //从头写入标志数据,便利下一次更换新电池后的判别
- Write1302 (WRITE_PROTECT,0x80); //答应写维护
- ucDs1302Error=1;//表明ds1302备用电池没电了,报警提示更换新电池
- }
- }
总结陈词:
下一节开端讲单片机驱动温度传感器芯片的内容,欲知概况,请听下回分解—–使用DS18B20做一个温控器。