本人是新人,剛學51單片機,準備學好51后再轉ARM.通過這段時間的學習和參閱論壇各大神們的51代碼。在自己的理解基礎上,做了這么一個小鬧鐘。
過程是苦惱的。但是也加深了對51的理解。特別是時序、指令、指針。還有就是LCD1602的移屏、光標顯示、對CGRAM區,RAM區的一些操作。對DS1302的數據格式、指令、外存儲存器的操作有了一定的認識。因為是自學的。所在在代碼編寫上肯定還有一些不足,對于優化代碼更是談不上。所以也就不怕獻丑,把代碼發上論壇。讓各位老師們批評與指導。讓我能在學習中有進步。謝謝了。這里放上有Keil 5寫的代碼和Proteus 8 Professional仿真程序。Proteus 8 Professional仿真程序中不能稱之為電路圖了。只能算是原理圖吧。呵呵。
這個時鐘具有LCD1602不帶字庫。通過對CGRAM的操作來顯示漢字。能過對LCD1602的移屏指令來實現菜單功能、通過對DS1302的外存儲器的操作來實現鬧鐘掉電能保持、DS1302第一次通電可初始化。主機掉電繼續運行,簡單的定時鬧鈴功能。,通過SETKEY按鍵來移動光標。
仿真原理圖如下(proteus仿真工程文件可到本帖附件中下載)
捕獲.PNG (98.6 KB, 下載次數: 126)
下載附件
2018-9-14 09:27 上傳
制作出來的實物圖如下:
4.jpg (2.32 MB, 下載次數: 133)
下載附件
2018-9-14 09:35 上傳
3.jpg (2.44 MB, 下載次數: 122)
下載附件
2018-9-14 09:35 上傳
2.jpg (2.45 MB, 下載次數: 143)
下載附件
2018-9-14 09:35 上傳
1.jpg (2.23 MB, 下載次數: 110)
下載附件
2018-9-14 09:35 上傳
這部份是代碼
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的賦值后?芍匦聦憰r間到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)
2018-9-14 09:38 上傳
點擊文件名下載附件
下載積分: 黑幣 -5
Time.rar
(93.8 KB, 下載次數: 364)
2018-9-14 09:38 上傳
點擊文件名下載附件
下載積分: 黑幣 -5
|