在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ 數(shù)據(jù)庫(kù)/ 15.9 單片機(jī)電子時(shí)鐘程序設(shè)計(jì)
18. RS485 通信與 Modbus 協(xié)議
17.5 A/D 差分輸入信號(hào)
15.8 C 語(yǔ)言復(fù)合數(shù)據(jù)類型(結(jié)構(gòu)體,共用體,枚舉類型)
16.3 NEC 協(xié)議紅外遙控器
13.1 單片機(jī)通信時(shí)序解析
14.4 單片機(jī) EEPROM 單字節(jié)讀寫操作時(shí)序
13.3 多個(gè) .c 文件的初步認(rèn)識(shí)
18.2 Modbus 通信協(xié)議介紹
15.1 BCD 碼介紹
18.3 單片機(jī) Modbus 多機(jī)通信程序設(shè)計(jì)
18.1 單片機(jī) RS485 通信接口、控制線、原理圖及程序?qū)嵗?/span>
15. 實(shí)時(shí)時(shí)鐘 DS1302
14.7 單片機(jī) I2C 和 EEPROM 的綜合編程
17. 模數(shù)轉(zhuǎn)換與數(shù)模轉(zhuǎn)換
16.2 紅外遙控通信原理
13.2 1602 液晶整屏移動(dòng)程序
17.6 D/A 輸出
17.7 單片機(jī)信號(hào)發(fā)生器程序
16.4 溫度傳感器 DS18B20
14.6 單片機(jī)EEPROM的頁(yè)寫入
13.4 單片機(jī)計(jì)算器程序設(shè)計(jì)[詳細(xì)]
17.2 A/D(模數(shù)轉(zhuǎn)換)的主要指標(biāo)
17.4 PCF8591 應(yīng)用程序
17.1 A/D 和 D/A 的基本概念
17.3 PCF8591硬件接口(電路圖引腳圖)
14.3 單片機(jī) EEPROM 簡(jiǎn)介
13.5 單片機(jī)串口通信原理和控制程序
15.5 DS1302 寄存器介紹
15.2 單片機(jī) SPI 通信接口
15.6 DS1302 通信時(shí)序介紹
14.5 單片機(jī) EEPROM 多字節(jié)讀寫操作時(shí)序
16. 紅外通信與 DS18B20 溫度傳感器
14.1 單片機(jī) I2C 時(shí)序介紹
15.3 實(shí)時(shí)時(shí)鐘芯片 DS1302 介紹
15.9 單片機(jī)電子時(shí)鐘程序設(shè)計(jì)
16.1 紅外光的基本原理
15.4 DS1302 的硬件信息
15.7 DS1302 的 BURST 模式
14.2 單片機(jī) I2C 尋址模式
14. 單片機(jī) I2C 總線與 EEPROM
13. 單片機(jī) 1602 液晶與串口的應(yīng)用實(shí)例

15.9 單片機(jī)電子時(shí)鐘程序設(shè)計(jì)

共用體除非必要,否則我們不推薦使用,枚舉的用法比較簡(jiǎn)單,在本書19章的項(xiàng)目實(shí)踐中有很好的示例,這節(jié)課我們先來(lái)練習(xí)一下結(jié)構(gòu)體的使用。下邊這個(gè)程序的功能是一個(gè)帶日期的電子鐘,相當(dāng)于一個(gè)簡(jiǎn)易萬(wàn)年歷了,并且加入了按鍵調(diào)時(shí)功能。學(xué)有余力的同學(xué)看到這里,不妨先不看我們提供的代碼,自己寫寫試試。如果能夠獨(dú)立寫一個(gè)按鍵可調(diào)的萬(wàn)年歷程序,單片機(jī)可以說(shuō)基本入門了。如果自己還不能夠獨(dú)立完成這個(gè)程序,那么還是老規(guī)矩,先抄并且理解,而后自己獨(dú)立默寫出來(lái),并且要邊默寫邊理解。

本例直接忽略了星期這項(xiàng)內(nèi)容,通過(guò)上、下、左、右、回車、ESC 這6個(gè)按鍵可以調(diào)整時(shí)間。這也是一個(gè)具有綜合練習(xí)性質(zhì)的實(shí)例,雖然在功能實(shí)現(xiàn)上沒(méi)有多少難度,但要進(jìn)行的操作卻比較多而且煩瑣,同學(xué)們可以從中體會(huì)到把繁雜的功能實(shí)現(xiàn)分解為一步步函數(shù)操作的必要性以及方便靈活性。簡(jiǎn)單說(shuō)一下這個(gè)程序的幾個(gè)要點(diǎn),方便大家閱讀理解程序。

  1. 把 DS1302 的底層操作封裝為一個(gè) DS1302.c 文件,對(duì)上層應(yīng)用提供基本的實(shí)時(shí)時(shí)間的操作接口,這個(gè)文件也是我們的又一個(gè)功能模塊了,我們的積累也越來(lái)越多了。
  2. 定義一個(gè)結(jié)構(gòu)體類型 sTime 用來(lái)封裝日期時(shí)間的各個(gè)元素,又用該結(jié)構(gòu)體定義了一個(gè)時(shí)間緩沖區(qū)變量 bufTime 來(lái)暫存從 DS1302 讀出的時(shí)間和設(shè)置時(shí)間時(shí)的設(shè)定值。需要注意的是在其它文件中要使用這個(gè)結(jié)構(gòu)體變量時(shí),必須首先再聲明一次 sTime 類型;
  3. 定義一個(gè)變量 setIndex 來(lái)控制當(dāng)前是否處于設(shè)置時(shí)間的狀態(tài),以及設(shè)置時(shí)間的哪一位,該值為0就表示正常運(yùn)行,1~12分別代表可以修改日期時(shí)間的12個(gè)位;
  4. 由于這節(jié)課的程序功能要進(jìn)行時(shí)間調(diào)整,用到了 1602 液晶的光標(biāo)功能,添加了設(shè)置光標(biāo)的函數(shù),我們要改變哪一位的數(shù)字,就在 1602 對(duì)應(yīng)位置上進(jìn)行光標(biāo)閃爍,所以 Lcd1602.c 在之前文件的基礎(chǔ)上添加了兩個(gè)控制光標(biāo)的函數(shù);
  5. 時(shí)間的顯示、增減、設(shè)置移位等上層功能函數(shù)都放在 main.c 中來(lái)實(shí)現(xiàn),當(dāng)按鍵需要這些函數(shù)時(shí)則在按鍵文件中做外部聲明,這樣做是為了避免一組功能函數(shù)分散在不同的文件內(nèi)而使程序顯得凌亂。
/***************************DS1302.c 文件程序源代碼*****************************/
#include <reg52.h>

sbit DS1302_CE = P1^7;
sbit DS1302_CK = P3^5;
sbit DS1302_IO = P3^4;

struct sTime { //日期時(shí)間結(jié)構(gòu)體定義
    unsigned int year; //年
    unsigned char mon; //月
    unsigned char day; //日
    unsigned char hour; //時(shí)
    unsigned char min; //分
    unsigned char sec; //秒
    unsigned char week; //星期
};

/* 發(fā)送一個(gè)字節(jié)到 DS1302 通信總線上 */
void DS1302ByteWrite(unsigned char dat){
    unsigned char mask;
    for (mask=0x01; mask!=0; mask<<=1){ //低位在前,逐位移出
        if ((mask&dat) != 0){ //首先輸出該位數(shù)據(jù)
            DS1302_IO = 1;
        }else{
            DS1302_IO = 0;
        }
        DS1302_CK = 1; //然后拉高時(shí)鐘
        DS1302_CK = 0; //再拉低時(shí)鐘,完成一個(gè)位的操作
    }
    DS1302_IO = 1; //最后確保釋放 IO 引腳
}
/* 由 DS1302 通信總線上讀取一個(gè)字節(jié) */
unsigned char DS1302ByteRead(){
    unsigned char mask;
    unsigned char dat = 0;

    for (mask=0x01; mask!=0; mask<<=1){ //低位在前,逐位讀取
        if (DS1302_IO != 0){ //首先讀取此時(shí)的 IO 引腳,并設(shè)置 dat 中的對(duì)應(yīng)位
            dat |= mask;
        }
        DS1302_CK = 1; //然后拉高時(shí)鐘
        DS1302_CK = 0; //再拉低時(shí)鐘,完成一個(gè)位的操作
    }
    return dat; //最后返回讀到的字節(jié)數(shù)據(jù)
}
/* 用單次寫操作向某一寄存器寫入一個(gè)字節(jié),reg-寄存器地址,dat-待寫入字節(jié) */
void DS1302SingleWrite(unsigned char reg, unsigned char dat){
    DS1302_CE = 1; //使能片選信號(hào)
    DS1302ByteWrite((reg<<1)|0x80); //發(fā)送寫寄存器指令
    DS1302ByteWrite(dat); //寫入字節(jié)數(shù)據(jù)
    DS1302_CE = 0; //除能片選信號(hào)
}
/* 用單次讀操作從某一寄存器讀取一個(gè)字節(jié),reg-寄存器地址,返回值-讀到的字節(jié) */
unsigned char DS1302SingleRead(unsigned char reg){
    unsigned char dat;
    DS1302_CE = 1; //使能片選信號(hào)
    DS1302ByteWrite((reg<<1)|0x81); //發(fā)送讀寄存器指令
    dat = DS1302ByteRead(); //讀取字節(jié)數(shù)據(jù)
    DS1302_CE = 0; //除能片選信號(hào)
    return dat;
}
/* 用突發(fā)模式連續(xù)寫入 8 個(gè)寄存器數(shù)據(jù),dat-待寫入數(shù)據(jù)指針 */
void DS1302BurstWrite(unsigned char *dat){
    unsigned char i;

    DS1302_CE = 1;
    DS1302ByteWrite(0xBE); //發(fā)送突發(fā)寫寄存器指令
    for (i=0; i<8; i++){ //連續(xù)寫入 8 字節(jié)數(shù)據(jù)
        DS1302ByteWrite(dat[i]);
    }
    DS1302_CE = 0;
}
/* 用突發(fā)模式連續(xù)讀取 8 個(gè)寄存器的數(shù)據(jù),dat-讀取數(shù)據(jù)的接收指針 */
void DS1302BurstRead(unsigned char *dat){
    unsigned char i;

    DS1302_CE = 1;
    DS1302ByteWrite(0xBF); //發(fā)送突發(fā)讀寄存器指令

    for (i=0; i<8; i++){ //連續(xù)讀取 8 個(gè)字節(jié)
        dat[i] = DS1302ByteRead();
    }
    DS1302_CE = 0;
}
/* 獲取實(shí)時(shí)時(shí)間,即讀取 DS1302 當(dāng)前時(shí)間并轉(zhuǎn)換為時(shí)間結(jié)構(gòu)體格式 */
void GetRealTime(struct sTime *time){
    unsigned char buf[8];

    DS1302BurstRead(buf);
    time->year = buf[6] + 0x2000;
    time->mon = buf[4];
    time->day = buf[3];
    time->hour = buf[2];
    time->min = buf[1];
    time->sec = buf[0];
    time->week = buf[5];
}
/* 設(shè)定實(shí)時(shí)時(shí)間,時(shí)間結(jié)構(gòu)體格式的設(shè)定時(shí)間轉(zhuǎn)換為數(shù)組并寫入 DS1302 */
void SetRealTime(struct sTime *time){
    unsigned char buf[8];

    buf[7] = 0;
    buf[6] = time->year;
    buf[5] = time->week;
    buf[4] = time->mon;
    buf[3] = time->day;
    buf[2] = time->hour;
    buf[1] = time->min;
    buf[0] = time->sec;
    DS1302BurstWrite(buf);
}
/* DS1302 初始化,如發(fā)生掉電則重新設(shè)置初始時(shí)間 */
void InitDS1302(){
    unsigned char dat;
    struct sTime code InitTime[] = { //2013 年 10 月 8 日 12:30:00 星期二
        0x2013,0x10,0x08, 0x12,0x30,0x00, 0x02
    };

    DS1302_CE = 0; //初始化 DS1302 通信引腳
    DS1302_CK = 0;
    dat = DS1302SingleRead(0); //讀取秒寄存器

    if ((dat & 0x80) != 0){ //由秒寄存器最高位 CH 的值判斷 DS1302 是否已停止
        DS1302SingleWrite(7, 0x00); //撤銷寫保護(hù)以允許寫入數(shù)據(jù)
        SetRealTime(&InitTime); //設(shè)置 DS1302 為默認(rèn)的初始時(shí)間
    }
}

DS1302.c 最終向外提供出與具體時(shí)鐘芯片寄存器位置無(wú)關(guān)的、由時(shí)間結(jié)構(gòu)類型 sTime 作為接口的實(shí)時(shí)時(shí)間的讀取和設(shè)置函數(shù),如此處理體現(xiàn)了我們前面提到過(guò)的層次化編程的思想。應(yīng)用層可以不關(guān)心底層實(shí)現(xiàn)細(xì)節(jié),底層實(shí)現(xiàn)的改變也不會(huì)對(duì)應(yīng)用層造成影響,比如說(shuō)日后你可能需要換一款時(shí)鐘芯片,而它與 DS1302 的操作和時(shí)間寄存器順序是不同的,那么你需要做的也僅是針對(duì)這款新的時(shí)鐘芯片設(shè)計(jì)出底層操作函數(shù),最終提供出同樣的以 sTime 為接口的操作函數(shù)即可,應(yīng)用層無(wú)需做任何的改動(dòng)。

/***************************Lcd1602.c 文件程序源代碼*****************************/
#include <reg52.h>
#define LCD1602_DB P0

sbit LCD1602_RS = P1^0;
sbit LCD1602_RW = P1^1;
sbit LCD1602_E = P1^5;

/* 等待液晶準(zhǔn)備好 */
void LcdWaitReady(){
    unsigned char sta;

    LCD1602_DB = 0xFF;
    LCD1602_RS = 0;
    LCD1602_RW = 1;
    do {
        LCD1602_E = 1;
        sta = LCD1602_DB; //讀取狀態(tài)字
        LCD1602_E = 0;
    } while (sta & 0x80); //bit7 等于 1 表示液晶正忙,重復(fù)檢測(cè)直到其等于 0 為止
}
/* 向 LCD1602 液晶寫入一字節(jié)命令,cmd-待寫入命令值 */
void LcdWriteCmd(unsigned char cmd){
    LcdWaitReady();
    LCD1602_RS = 0;
    LCD1602_RW = 0;
    LCD1602_DB = cmd;
    LCD1602_E = 1;
    LCD1602_E = 0;
}
/* 向 LCD1602 液晶寫入一字節(jié)數(shù)據(jù),dat-待寫入數(shù)據(jù)值 */
void LcdWriteDat(unsigned char dat){
    LcdWaitReady();
    LCD1602_RS = 1;
    LCD1602_RW = 0;
    LCD1602_DB = dat;
    LCD1602_E = 1;
    LCD1602_E = 0;
}
/* 設(shè)置顯示 RAM 起始地址,亦即光標(biāo)位置,(x,y)-對(duì)應(yīng)屏幕上的字符坐標(biāo) */
void LcdSetCursor(unsigned char x, unsigned char y){
    unsigned char addr;
    if (y == 0){ //由輸入的屏幕坐標(biāo)計(jì)算顯示 RAM 的地址
        addr = 0x00 + x; //第一行字符地址從 0x00 起始
    }else{
        addr = 0x40 + x; //第二行字符地址從 0x40 起始
    }
    LcdWriteCmd(addr | 0x80); //設(shè)置 RAM 地址
}
/* 在液晶上顯示字符串,(x,y)-對(duì)應(yīng)屏幕上的起始坐標(biāo),str-字符串指針 */
void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str){
    LcdSetCursor(x, y); //設(shè)置起始地址
    while (*str != '\0'){ //連續(xù)寫入字符串?dāng)?shù)據(jù),直到檢測(cè)到結(jié)束符
        LcdWriteDat(*str++);
    }
}
/* 打開(kāi)光標(biāo)的閃爍效果 */
void LcdOpenCursor(){
    LcdWriteCmd(0x0F);
}
/* 關(guān)閉光標(biāo)顯示 */
void LcdCloseCursor(){
    LcdWriteCmd(0x0C);
}
/* 初始化 1602 液晶 */
void InitLcd1602(){
    LcdWriteCmd(0x38); //16*2 顯示,5*7 點(diǎn)陣,8 位數(shù)據(jù)接口
    LcdWriteCmd(0x0C); //顯示器開(kāi),光標(biāo)關(guān)閉
    LcdWriteCmd(0x06); //文字不動(dòng),地址自動(dòng)+1
    LcdWriteCmd(0x01); //清屏
}

為了本例的具體需求,在之前文件的基礎(chǔ)上添加兩個(gè)控制光標(biāo)效果打開(kāi)和關(guān)閉的函數(shù),雖然函數(shù)都很簡(jiǎn)單,但為了保持程序整體上良好的模塊化和層次化,還是應(yīng)該在液晶驅(qū)動(dòng)文件內(nèi)以函數(shù)的形式提供,而不是由應(yīng)用層代碼直接來(lái)調(diào)用具體的液晶寫命令操作。 /***keyboard.c 文件程序源代碼****/ (此處省略,可參考之前章節(jié)的代碼)

/*****************************main.c 文件程序源代碼******************************/
#include <reg52.h>

struct sTime { //日期時(shí)間結(jié)構(gòu)體定義
    unsigned int year;
    unsigned char mon;
    unsigned char day;
    unsigned char hour;
    unsigned char min;
    unsigned char sec;
    unsigned char week;
};

bit flag200ms = 1; //200ms 定時(shí)標(biāo)志
struct sTime bufTime; //日期時(shí)間緩沖區(qū)
unsigned char setIndex = 0; //時(shí)間設(shè)置索引
unsigned char T0RH = 0; //T0 重載值的高字節(jié)
unsigned char T0RL = 0; //T0 重載值的低字節(jié)

void ConfigTimer0(unsigned int ms);
void RefreshTimeShow();
extern void InitDS1302();
extern void GetRealTime(struct sTime *time);
extern void SetRealTime(struct sTime *time);
extern void KeyScan();
extern void KeyDriver();
extern void InitLcd1602();
extern void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str);
extern void LcdSetCursor(unsigned char x, unsigned char y);
extern void LcdOpenCursor();
extern void LcdCloseCursor();

void main(){
    unsigned char psec=0xAA; //秒備份,初值 AA 確保首次讀取時(shí)間后會(huì)刷新顯示
    EA = 1; //開(kāi)總中斷
    ConfigTimer0(1); //T0 定時(shí) 1ms
    InitDS1302(); //初始化實(shí)時(shí)時(shí)鐘
    InitLcd1602(); //初始化液晶

    //初始化屏幕上固定不變的內(nèi)容
    LcdShowStr(3, 0, "20 - - ");
    LcdShowStr(4, 1, " : : ");

    while (1){
        KeyDriver(); //調(diào)用按鍵驅(qū)動(dòng)
        if (flag200ms && (setIndex == 0)){ //每隔 200ms 且未處于設(shè)置狀態(tài)時(shí),
            flag200ms = 0;
            GetRealTime(&bufTime); //獲取當(dāng)前時(shí)間
            if (psec != bufTime.sec){ //檢測(cè)到時(shí)間有變化時(shí)刷新顯示
                RefreshTimeShow();
                psec = bufTime.sec; //用當(dāng)前值更新上次秒數(shù)
            }
        }
    }
}
/* 將一個(gè) BCD 碼字節(jié)顯示到屏幕上,(x,y)-屏幕起始坐標(biāo),bcd-待顯示 BCD 碼 */
void ShowBcdByte(unsigned char x, unsigned char y, unsigned char bcd){
    unsigned char str[4];

    str[0] = (bcd >> 4) + '0';
    str[1] = (bcd&0x0F) + '0';
    str[2] = '\0';
    LcdShowStr(x, y, str);
}
/* 刷新日期時(shí)間的顯示 */
void RefreshTimeShow(){
    ShowBcdByte(5, 0, bufTime.year);
    ShowBcdByte(8, 0, bufTime.mon);
    ShowBcdByte(11, 0, bufTime.day);
    ShowBcdByte(4, 1, bufTime.hour);
    ShowBcdByte(7, 1, bufTime.min);
    ShowBcdByte(10, 1, bufTime.sec);
}
/* 刷新當(dāng)前設(shè)置位的光標(biāo)指示 */
void RefreshSetShow(){
    switch (setIndex){
        case 1: LcdSetCursor(5, 0); break;
        case 2: LcdSetCursor(6, 0); break;
        case 3: LcdSetCursor(8, 0); break;
        case 4: LcdSetCursor(9, 0); break;
        case 5: LcdSetCursor(11, 0); break;
        case 6: LcdSetCursor(12, 0); break;
        case 7: LcdSetCursor(4, 1); break;
        case 8: LcdSetCursor(5, 1); break;
        case 9: LcdSetCursor(7, 1); break;
        case 10: LcdSetCursor(8, 1); break;
        case 11: LcdSetCursor(10, 1); break;
        case 12: LcdSetCursor(11, 1); break;
        default: break;
    }
}
/* 遞增一個(gè) BCD 碼的高位 */
unsigned char IncBcdHigh(unsigned char bcd){
    if ((bcd&0xF0) < 0x90){
        bcd += 0x10;
    }else{
        bcd &= 0x0F;
    }
    return bcd;
}
/* 遞增一個(gè) BCD 碼的低位 */
unsigned char IncBcdLow(unsigned char bcd){
    if ((bcd&0x0F) < 0x09){
        bcd += 0x01;
    }else{
        bcd &= 0xF0;
    }
    return bcd;
}
/* 遞減一個(gè) BCD 碼的高位 */
unsigned char DecBcdHigh(unsigned char bcd){
    if ((bcd&0xF0) > 0x00){
        bcd -= 0x10;
    }else{
        bcd |= 0x90;
    }
    return bcd;
}
/* 遞減一個(gè) BCD 碼的低位 */
unsigned char DecBcdLow(unsigned char bcd){
    if ((bcd&0x0F) > 0x00){
        bcd -= 0x01;
    }else{
        bcd |= 0x09;
    }
    return bcd;
}
/* 遞增時(shí)間當(dāng)前設(shè)置位的值 */
void IncSetTime(){
    switch (setIndex){
        case 1: bufTime.year = IncBcdHigh(bufTime.year); break;
        case 2: bufTime.year = IncBcdLow(bufTime.year); break;
        case 3: bufTime.mon = IncBcdHigh(bufTime.mon); break;
        case 4: bufTime.mon = IncBcdLow(bufTime.mon); break;
        case 5: bufTime.day = IncBcdHigh(bufTime.day); break;
        case 6: bufTime.day = IncBcdLow(bufTime.day); break;
        case 7: bufTime.hour = IncBcdHigh(bufTime.hour); break;
        case 8: bufTime.hour = IncBcdLow(bufTime.hour); break;
        case 9: bufTime.min = IncBcdHigh(bufTime.min); break;
        case 10: bufTime.min = IncBcdLow(bufTime.min); break;
        case 11: bufTime.sec = IncBcdHigh(bufTime.sec); break;
        case 12: bufTime.sec = IncBcdLow(bufTime.sec); break;
        default: break;
    }
    RefreshTimeShow();
    RefreshSetShow();
}
/* 遞減時(shí)間當(dāng)前設(shè)置位的值 */
void DecSetTime(){
    switch (setIndex){
        case 1: bufTime.year = DecBcdHigh(bufTime.year); break;
        case 2: bufTime.year = DecBcdLow(bufTime.year); break;
        case 3: bufTime.mon = DecBcdHigh(bufTime.mon); break;
        case 4: bufTime.mon = DecBcdLow(bufTime.mon); break;
        case 5: bufTime.day = DecBcdHigh(bufTime.day); break;
        case 6: bufTime.day = DecBcdLow(bufTime.day); break;
        case 7: bufTime.hour = DecBcdHigh(bufTime.hour); break;
        case 8: bufTime.hour = DecBcdLow(bufTime.hour); break;
        case 9: bufTime.min = DecBcdHigh(bufTime.min); break;
        case 10: bufTime.min = DecBcdLow(bufTime.min); break;
        case 11: bufTime.sec = DecBcdHigh(bufTime.sec); break;
        case 12: bufTime.sec = DecBcdLow(bufTime.sec);
        default: break;
    }
    RefreshTimeShow();
    RefreshSetShow();
}
/* 右移時(shí)間設(shè)置位 */
void RightShiftTimeSet(){
    if (setIndex != 0){
        if (setIndex < 12){
            setIndex++;
        }else{
            setIndex = 1;
        }
        RefreshSetShow();
    }
}
/* 左移時(shí)間設(shè)置位 */
void LeftShiftTimeSet(){
    if (setIndex != 0){
        if (setIndex > 1){
            setIndex--;
        }else{
            setIndex = 12;
        }
        RefreshSetShow();
    }
}
/* 進(jìn)入時(shí)間設(shè)置狀態(tài) */
void EnterTimeSet(){
    setIndex = 2; //把設(shè)置索引設(shè)置為 2,即可進(jìn)入設(shè)置狀態(tài)
    LeftShiftTimeSet(); //再利用現(xiàn)成的左移操作移到位置 1 并完成顯示刷新
    LcdOpenCursor(); //打開(kāi)光標(biāo)閃爍效果
}
/* 退出時(shí)間設(shè)置狀態(tài),save-是否保存當(dāng)前設(shè)置的時(shí)間值 */
void ExitTimeSet(bit save){
    setIndex = 0; //把設(shè)置索引設(shè)置為 0,即可退出設(shè)置狀態(tài)
    if (save){ //需保存時(shí)即把當(dāng)前設(shè)置時(shí)間寫入 DS1302
        SetRealTime(&bufTime);
    }
    LcdCloseCursor(); //關(guān)閉光標(biāo)顯示
}
/* 按鍵動(dòng)作函數(shù),根據(jù)鍵碼執(zhí)行相應(yīng)的操作,keycode-按鍵鍵碼 */
void KeyAction(unsigned char keycode){
    if ((keycode>='0') && (keycode<='9')){ //本例中不響應(yīng)字符鍵
    }else if (keycode == 0x26){ //向上鍵,遞增當(dāng)前設(shè)置位的值
        IncSetTime();
    }else if (keycode == 0x28){ //向下鍵,遞減當(dāng)前設(shè)置位的值
        DecSetTime();
    }else if (keycode == 0x25){ //向左鍵,向左切換設(shè)置位
        LeftShiftTimeSet();
    }else if (keycode == 0x27){ //向右鍵,向右切換設(shè)置位
        RightShiftTimeSet();
    }else if (keycode == 0x0D){ //回車鍵,進(jìn)入設(shè)置模式/啟用當(dāng)前設(shè)置值
        if (setIndex == 0){ //不處于設(shè)置狀態(tài)時(shí),進(jìn)入設(shè)置狀態(tài)
            EnterTimeSet();
        }else{ //已處于設(shè)置狀態(tài)時(shí),保存時(shí)間并退出設(shè)置狀態(tài)
            ExitTimeSet(1);
        }
    }else if (keycode == 0x1B){ //Esc 鍵,取消當(dāng)前設(shè)置
        ExitTimeSet(0);
    }
}
/* 配置并啟動(dòng) T0,ms-T0 定時(shí)時(shí)間 */
void ConfigTimer0(unsigned int ms){
    unsigned long tmp; //臨時(shí)變量
    tmp = 11059200 / 12; //定時(shí)器計(jì)數(shù)頻率
    tmp = (tmp * ms) / 1000; //計(jì)算所需的計(jì)數(shù)值
    tmp = 65536 - tmp; //計(jì)算定時(shí)器重載值
    tmp = tmp + 28; //補(bǔ)償中斷響應(yīng)延時(shí)造成的誤差
    T0RH = (unsigned char)(tmp>>8); //定時(shí)器重載值拆分為高低字節(jié)
    T0RL = (unsigned char)tmp;
    TMOD &= 0xF0; //清零 T0 的控制位
    TMOD |= 0x01; //配置 T0 為模式 1
    TH0 = T0RH; //加載 T0 重載值
    TL0 = T0RL;
    ET0 = 1; //使能 T0 中斷
    TR0 = 1; //啟動(dòng) T0
}
/* T0 中斷服務(wù)函數(shù),執(zhí)行按鍵掃描和 200ms 定時(shí) */
void InterruptTimer0() interrupt 1{
    static unsigned char tmr200ms = 0;
    TH0 = T0RH; //重新加載重載值
    TL0 = T0RL;
    KeyScan(); //按鍵掃描
    tmr200ms++;
    if (tmr200ms >= 200){ //定時(shí) 200ms
        tmr200ms = 0;
        flag200ms = 1;
    }
}

main.c 主文件,負(fù)責(zé)所有應(yīng)用層的功能實(shí)現(xiàn),文件比較長(zhǎng),還是那句話“不難但比較煩瑣”,希望對(duì)具體問(wèn)題分析細(xì)化能力還不太強(qiáng)的同學(xué)們把這個(gè)文件多練習(xí)幾遍,學(xué)習(xí)一下其中把具體問(wèn)題逐步細(xì)化并一步步實(shí)現(xiàn)出來(lái)的編程思想,多進(jìn)行此類練習(xí),鍛煉程序思維能力,將來(lái)遇到具體項(xiàng)目設(shè)計(jì)需求的時(shí)候,你很快就可以找到方法并實(shí)現(xiàn)它們了。