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

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

15.6 DS1302 通信時序介紹

DS1302 我們前邊也有提起過,是三根線,分別是 CE、I/O 和 SCLK,其中 CE 是使能線,SCLK 是時鐘線,I/O 是數(shù)據(jù)線。前邊我們介紹過了 SPI 通信,同學們發(fā)現(xiàn)沒發(fā)現(xiàn),這個 DS1302 的通信線定義和 SPI 怎么這么像呢?

事實上,DS1302 的通信是 SPI 的變異種類,它用了 SPI 的通信時序,但是通信的時候沒有完全按照 SPI 的規(guī)則來,下面我們一點點解剖 DS1302 的變異 SPI 通信方式。先看一下單字節(jié)寫入操作,如圖15-11所示。

http://wiki.jikexueyuan.com/project/mcu-tutorial-three/images/21.png" alt="" />

圖15-11 DS1302 單字節(jié)寫操作

然后我們再對比一下 CPOL=0/CPHA=0 情況下的 SPI 的操作時序,如圖15-12所示。

http://wiki.jikexueyuan.com/project/mcu-tutorial-three/images/22.png" alt="" />

圖15-12 CPOL=0/CPHA=0 通信時序

圖15-11和圖15-12的通信時序,其中 CE 和 SSEL 的使能控制是反的,對于通信寫數(shù)據(jù),都是在 SCK 的上升沿,從機進行采樣,下降沿的時候,主機發(fā)送數(shù)據(jù)。DS1302 的時序里,單片機要預先寫一個字節(jié)指令,指明要寫入的寄存器的地址以及后續(xù)的操作是寫操作,然后再寫入一個字節(jié)的數(shù)據(jù)。

對于單字節(jié)讀操作,我就不做對比了,把 DS1302 的時序圖貼出來,大家自己看一下即可,如圖15-13所示。

http://wiki.jikexueyuan.com/project/mcu-tutorial-three/images/23.png" alt="" />

圖15-13 DS1302 單字節(jié)讀操作

讀操作有兩處需要特別注意的地方。第一,DS1302 的時序圖上的箭頭都是針對 DS1302 來說的,因此讀操作的時候,先寫第一個字節(jié)指令,上升沿的時候 DS1302 來鎖存數(shù)據(jù),下降沿我們用單片機發(fā)送數(shù)據(jù)。到了第二個字數(shù)據(jù),由于我們這個時序過程相當于 CPOL=0/CPHA=0,前沿發(fā)送數(shù)據(jù),后沿讀取數(shù)據(jù),第二個字節(jié)是 DS1302 下降沿輸出數(shù)據(jù),我們的單片機上升沿來讀取,因此箭頭從 DS1302 角度來說,出現(xiàn)在了下降沿。

第二個需要注意的地方就是,我們的單片機沒有標準的 SPI 接口,和 I2C 一樣需要用 IO 口來模擬通信過程。在讀 DS1302 的時候,理論上 SPI 是上升沿讀取,但是程序是用 IO 口模擬的,所以數(shù)據(jù)的讀取和時鐘沿的變化不可能同時了,必然就有一個先后順序。通過實驗發(fā)現(xiàn),如果先讀取 IO 線上的數(shù)據(jù),再拉高 SCLK 產(chǎn)生上升沿,那么讀到的數(shù)據(jù)一定是正確的,而顛倒順序后數(shù)據(jù)就有可能出錯。這個問題產(chǎn)生的原因還是在于 DS1302 的通信協(xié)議與標準 SPI 協(xié)議存在的差異造成的,如果是標準 SPI 的數(shù)據(jù)線,數(shù)據(jù)會一直保持到下一個周期的下降沿才會變化,所以讀取數(shù)據(jù)和上升沿的先后順序就無所謂了;但 DS1302 的 IO 線會在時鐘上升沿后被 DS1302 釋放,也就是撤銷強推挽輸出變?yōu)槿跸吕瓲顟B(tài),而此時在51單片機引腳內部上拉的作用下,IO 線上的實際電平會慢慢上升,從而導致在上升沿產(chǎn)生后再讀取 IO 數(shù)據(jù)的話就可能會出錯。因此這里的程序我們按照先讀取 IO 數(shù)據(jù),再拉高 SCLK 產(chǎn)生上升沿的順序。

下面我們就寫一個程序,先將2013年10月8號星期二12點30分00秒這個時間寫到 DS1302 內部,讓 DS1302 正常運行,然后再不停的讀取 DS1302 的當前時間,并顯示在我們的液晶屏上。 /*Lcd1602.c 文件程序源代碼***/ (此處省略,可參考之前章節(jié)的代碼)

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

sbit DS1302_CE = P1^7;
sbit DS1302_CK = P3^5;
sbit DS1302_IO = P3^4;
bit flag200ms = 0; //200ms 定時標志
unsigned char T0RH = 0; //T0 重載值的高字節(jié)
unsigned char T0RL = 0; //T0 重載值的低字節(jié)

void ConfigTimer0(unsigned int ms);
void InitDS1302();
unsigned char DS1302SingleRead(unsigned char reg);
extern void InitLcd1602();
extern void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str);

void main(){
    unsigned char i;
    unsigned char psec=0xAA; //秒備份,初值 AA 確保首次讀取時間后會刷新顯示
    unsigned char time[8]; //當前時間數(shù)組
    unsigned char str[12]; //字符串轉換緩沖區(qū)
    EA = 1; //開總中斷
    ConfigTimer0(1); //T0 定時 1ms
    InitDS1302(); //初始化實時時鐘
    InitLcd1602(); //初始化液晶

    while (1){
        if (flag200ms){ //每 200ms 讀取一次時間
            flag200ms = 0;
            for (i=0; i<7; i++){ //讀取 DS1302 當前時間
                time[i] = DS1302SingleRead(i);
            }
            if (psec != time[0]){ //檢測到時間有變化時刷新顯示
                str[0] = '2'; //添加年份的高 2 位:20
                str[1] = '0';
                str[2] = (time[6] >> 4) + '0'; //“年”高位數(shù)字轉換為 ASCII 碼
                str[3] = (time[6]&0x0F) + '0'; //“年”低位數(shù)字轉換為 ASCII 碼
                str[4] = '-'; //添加日期分隔符
                str[5] = (time[4] >> 4) + '0'; //“月”
                str[6] = (time[4]&0x0F) + '0';
                str[7] = '-';
                str[8] = (time[3] >> 4) + '0'; //“日”
                str[9] = (time[3]&0x0F) + '0';
                str[10] = '\0';
                LcdShowStr(0, 0, str); //顯示到液晶的第一行
                str[0] = (time[5]&0x0F) + '0'; //“星期”
                str[1] = '\0';
                LcdShowStr(11, 0, "week");
                LcdShowStr(15, 0, str); //顯示到液晶的第一行
                str[0] = (time[2] >> 4) + '0'; //“時”
                str[1] = (time[2]&0x0F) + '0';
                str[2] = ':'; //添加時間分隔符
                str[3] = (time[1] >> 4) + '0'; //“分”
                str[4] = (time[1]&0x0F) + '0';
                str[5] = ':';
                str[6] = (time[0] >> 4) + '0'; //“秒”
                str[7] = (time[0]&0x0F) + '0';
                str[8] = '\0';
                LcdShowStr(4, 1, str); //顯示到液晶的第二行
                psec = time[0]; //用當前值更新上次秒數(shù)
            }
        }
    }
}
/* 發(fā)送一個字節(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; //然后拉高時鐘
        DS1302_CK = 0; //再拉低時鐘,完成一個位的操作
    }
    DS1302_IO = 1; //最后確保釋放 IO 引腳
}
/* 由 DS1302 通信總線上讀取一個字節(jié) */
unsigned char DS1302ByteRead(){
    unsigned char mask;
    unsigned char dat = 0;

    for (mask=0x01; mask!=0; mask<<=1){ //低位在前,逐位讀取
        if (DS1302_IO != 0){ //首先讀取此時的 IO 引腳,并設置 dat 中的對應位
            dat |= mask;
        }
        DS1302_CK = 1; //然后拉高時鐘
        DS1302_CK = 0; //再拉低時鐘,完成一個位的操作
    }
    return dat; //最后返回讀到的字節(jié)數(shù)據(jù)
}
/* 用單次寫操作向某一寄存器寫入一個字節(jié),reg-寄存器地址,dat-待寫入字節(jié) */
void DS1302SingleWrite(unsigned char reg, unsigned char dat){
    DS1302_CE = 1; //使能片選信號
    DS1302ByteWrite((reg<<1)|0x80); //發(fā)送寫寄存器指令
    DS1302ByteWrite(dat); //寫入字節(jié)數(shù)據(jù)
    DS1302_CE = 0; //除能片選信號
}
/* 用單次讀操作從某一寄存器讀取一個字節(jié),reg-寄存器地址,返回值-讀到的字節(jié) */
unsigned char DS1302SingleRead(unsigned char reg){
    unsigned char dat;
    DS1302_CE = 1; //使能片選信號
    DS1302ByteWrite((reg<<1)|0x81); //發(fā)送讀寄存器指令
    dat = DS1302ByteRead()//讀取字節(jié)數(shù)據(jù)
    DS1302_CE = 0; //除能片選信號
    return dat;
}
/* DS1302 初始化,如發(fā)生掉電則重新設置初始時間 */
void InitDS1302(){
    unsigned char i;
    unsigned char code InitTime[] = { //2013 年 10 月 8 日 星期二 12:30:00
        0x00,0x30,0x12, 0x08, 0x10, 0x02, 0x13
    };

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

    if ((i & 0x80) != 0){ //由秒寄存器最高位 CH 的值判斷 DS1302 是否已停止
        DS1302SingleWrite(7, 0x00); //撤銷寫保護以允許寫入數(shù)據(jù)
        for (i=0; i<7; i++){ //設置 DS1302 為默認的初始時間
            DS1302SingleWrite(i, InitTime[i]);
        }
    }
}
/* 配置并啟動 T0,ms-T0 定時時間 */
void ConfigTimer0(unsigned int ms){
    unsigned long tmp; //臨時變量
    tmp = 11059200 / 12; //定時器計數(shù)頻率
    tmp = (tmp * ms) / 1000; //計算所需的計數(shù)值
    tmp = 65536 - tmp; //計算定時器重載值
    tmp = tmp + 12; //補償中斷響應延時造成的誤差
    T0RH = (unsigned char)(tmp>>8); //定時器重載值拆分為高低字節(jié)
    T0RL = (unsigned char)tmp;
    TMOD &= 0xF0; //清零 T0 的控制位
    TMOD |= 0x01; //配置 T0 為模式 1
    TH0 = T0RH; //加載 T0 重載值
    TL0 = T0RL;
    ET0 = 1; //使能 T0 中斷
    TR0 = 1; //啟動 T0
}
/* T0 中斷服務函數(shù),執(zhí)行 200ms 定時 */
void InterruptTimer0() interrupt 1{
    static unsigned char tmr200ms = 0;
    TH0 = T0RH; //重新加載重載值
    TL0 = T0RL;
    tmr200ms++;
    if (tmr200ms >= 200){ //定時 200ms
        tmr200ms = 0;
        flag200ms = 1;
    }
}

前邊學習了 I2C 和 EEPROM 的底層讀寫時序,那么 DS1302 的底層讀寫時序程序的實現(xiàn)方法是與之類似的,這里就不過多解釋了,大家自己認真揣摩一下。