DS1302與STC12的連接電路和驅動實現


簡介

DS1302是低功耗帶RAM的實時時鍾電路, 常見的SOP8封裝體積很小, 它可以對年月日周時分秒進行計時, 具有閏年補償功能, 工作電壓為2.0V-5.5V, 采用三線接口與CPU進行同步通信, 並可采用突發方式一次傳送多個字節的時鍾信號或RAM數據. DS1302內部有一個31byte的用於臨時性存放數據的RAM寄存器. DS1302是DS1202的升級產品, 與DS1202兼容, 但增加了主電源/后備電源雙電源引腳, 同時提供了對后備電源進行涓細電流充電的能力.

幾個需要知道的點:

  • 不帶溫度補償, 所以在溫度變化大的環境, 走時誤差會比較大, 需要有校對機制
  • 三線接口機制與SPI相似, 但不是SPI, 因為它在同一根線上實現的雙向IO
  • 自身不帶電源/電容, 掉電即重置, 需要自己管理好主電備電
  • 備電的充電方式是可以通過寄存器控制的, 分三級控制, 第一級開關, 第二級可選1級或2級二極管降壓, 第三級可選三種阻值
  • 額外的31個byte的存儲可以自由讀寫
  • burst read有兩處, 一處是8個字節的control+時間, 另一處是31字節的ram, 都是一次性全部讀取, 但是可以編程控制讀到第幾個字節就停止.
  • 讀取的結果是BCD碼, 要轉換

pin腳結構

對於SOP8封裝, 將字面朝自己, 小圓點朝左上方, 從小圓點開始逆時針數分別為1至8腳

Left Right
1:Vcc2 8:Vcc1
2:X1 7:SCLK
3:X2 6:I/O
4:GND 5:RST/CE
  • X1,X2: X1和X2是振盪源, 可以直接外接 32.768 kHz 晶振
  • RST: 重置
  • I/O: 數據IO, 串行數據輸入輸出端(雙向)
  • SCLK: 串口時鍾
  • Vcc1: 備用電
  • Vcc2: 供電

Vcc1和Vcc2

DS1302的引腳中Vcc2為主電源, Vcc1為后備電源. 在主電源關閉的情況下, 也能保持時鍾的連續運行. DS1302由Vcc1或Vcc2兩者中的較大者供電, 當Vcc2大於Vcc1+0.2V時, Vcc2給DS1302供電, 當Vcc2小於Vcc1時, DS1302由Vcc1供電.

注意: 供電不要低於3.6V, 否則在秒進位時容易出現時間清零. 如果是在VCC端串聯電阻限流, 電阻不要超過3K.

RST腳

RST是復位/片選線, 通過把RST輸入驅動置高電平來啟動所有的數據傳送.

  • 當RST為高電平時, 允許地址/命令序列送入移位寄存器, 所有的數據傳送被初始化, 允許對DS1302進行操作
  • 提供終止單字節或多字節數據傳送的方法, 如果在傳送過程中RST置為低電平,則會終止此次數據傳送,I/O引腳變為高阻態
  • 上電運行時, 在Vcc>2.0V之前RST必須保持低電平. 只有在SCLK為低電平時, 才能將RST置為高電平.

寫入邏輯:

  1. 將RST先拉低
  2. 將SCLK拉低(如果不拉低, 且本來是高電平, 則瞬間就發出了一個bit, 這時還沒放數據)
  3. 將RST拉高, 形成一個上升沿
  4. 在IO口放入一個bit (字節發送順序:從低位到高位)
  5. 將SCLK拉高, 形成上升沿, 這時候DS1302就會將IO口的數據移入它的寄存器
  6. 拉低SCLK, 為下一個字節做准備
  7. IO口再放一個bit, 再拉高, 直到發送完所有的16個字節
  8. 最后拉低RST, 完成寫入過程

寄存器地址

與時間相關的寄存器從0x80到0x8F, 其中偶數為寫地址, 奇數為讀地址, 共控制8個字節, 這8個字節可以通過burst方式一次性讀出

#define DS1302_W_SECOND     0x80
#define DS1302_W_MINUTE     0x82
#define DS1302_W_HOUR       0x84
#define DS1302_W_DAY        0x86
#define DS1302_W_MONTH      0x88
#define DS1302_W_WEEK       0x8A
#define DS1302_W_YEAR       0x8C
#define DS1302_W_PROTECT    0x8E

#define DS1302_R_SECOND     0x81
#define DS1302_R_MINUTE     0x83
#define DS1302_R_HOUR       0x85
#define DS1302_R_DAY        0x87
#define DS1302_R_MONTH      0x89
#define DS1302_R_WEEK       0x8B
#define DS1302_R_YEAR       0x8D
#define DS1302_R_PROTECT    0x8F

與控制相關的寄存器為如下幾個, 也是偶數寫奇數讀, 這些只能單個讀寫

#define DS1302_W_TK_CHARGER 0x90
#define DS1302_W_CLK_BURST  0xBE
#define DS1302_W_RAM_BURST  0xFE

#define DS1302_R_TK_CHARGER 0x91
#define DS1302_R_CLK_BURST  0xBF
#define DS1302_R_RAM_BURST  0xFF

片內的31個字節RAM起始地址為0xC0, 這31個字節可以通過burst方式一次性讀出

#define DS1302_RAM_SIZE     0x31 // Ram Size in bytes
#define DS1302_RAM_START    0xC0 // First byte Address

使用STC12 MCU讀取DS1302

接線圖, 測試中可以只接Vcc2或Vcc1, 可以串電阻或串二極管限流, 代碼中使用的管腳與圖中不一樣, 具體看代碼中的說明

實際的測試板, 接DS1302使用的是SOP8的測試座, 避免焊接, 中間的萬能板僅僅是為了用一個IC座外接晶振, 可以忽略其他元件

測試座里的DS1302

代碼例子, 包含單個讀, 單個寫, burst讀時間, burst讀RAM, 基於 HML_FwLib_STC12 封裝庫. 因為是非標准SPI, 所以實際上只用到了串口打印, 直接把這個方法拎出來也行, 就不需要引入這個封裝庫了.

/*****************************************************************************/
/** 
 * 連接方式
 *               |           |               |
 *               | --------- | ------------  |
 *               |1:Vcc2 VCC | 8:Vcc1        |
 *               |2:X1   OCS | 7:SCLK   P1_0 |
 *               |3:X2   OCS | 6:I/O    P1_1 |
 *               |4:GND  GND | 5:RST/CE P1_2 |
 *
 *****************************************************************************/
#include "hml/hml.h"
#include <stdio.h>

#define DS1302_W_SECOND     0x80
#define DS1302_W_MINUTE     0x82
#define DS1302_W_HOUR       0x84
#define DS1302_W_DAY        0x86
#define DS1302_W_MONTH      0x88
#define DS1302_W_WEEK       0x8A
#define DS1302_W_YEAR       0x8C
#define DS1302_W_PROTECT    0x8E
#define DS1302_W_TK_CHARGER 0x90
#define DS1302_W_CLK_BURST  0xBE
#define DS1302_W_RAM_BURST  0xFE

#define DS1302_R_SECOND     0x81
#define DS1302_R_MINUTE     0x83
#define DS1302_R_HOUR       0x85
#define DS1302_R_DAY        0x87
#define DS1302_R_MONTH      0x89
#define DS1302_R_WEEK       0x8B
#define DS1302_R_YEAR       0x8D
#define DS1302_R_PROTECT    0x8F
#define DS1302_R_TK_CHARGER 0x91
#define DS1302_R_CLK_BURST  0xBF
#define DS1302_R_RAM_BURST  0xFF

#define DS1302_RAM_SIZE     0x31 // Ram Size in bytes
#define DS1302_RAM_START    0xC0 // First byte Address

#define HEX2BCD(v)  ((v) % 10 + (v) / 10 * 16)
#define BCD2HEX(v)  ((v) % 16 + (v) / 16 * 10)

#define DS1302_SCK P1_0
#define DS1302_IO  P1_1
#define DS1302_RST P1_2

const uint8_t READ_RTC_ADDR[7]  = {0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d};
const uint8_t WRITE_RTC_ADDR[7] = {0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c};
uint8_t BUF[8] = { 0 };

// Write one byte to DS1302
void DS1302_WriteByte(uint8_t writeByte)
{
    for(uint8_t i=0; i < 8; i++)
    {
        if(1 == (writeByte & 0x01)){
            DS1302_IO = 1;
        }else{
            DS1302_IO = 0;
        }
        DS1302_SCK = 1;
        DS1302_SCK = 0;
        writeByte >>= 1; // From low to high
    }
}
 
// Read one byte from DS1302
uint8_t DS1302_ReadByte(void)
{
    uint8_t dat, readByte = 0;
    for(uint8_t i = 0; i < 8; i++)
    {
        //dat = DS1302_IO;
        //readByte = (readByte>>1) | (dat << 7); // From low to high
        readByte >>= 1;
    if(DS1302_IO)
        {
      readByte |= 0x80;
    }
        DS1302_SCK = 1;
        DS1302_SCK = 0;
    }
    return readByte;
}

void DS1302_WriteReg(uint8_t addr, uint8_t value)
{
    DS1302_RST = 0;
    DS1302_SCK = 0;
    DS1302_RST = 1;
    DS1302_WriteByte(addr);
    DS1302_WriteByte(value);
    DS1302_SCK = 1;
    DS1302_RST = 0;
}
 
uint8_t DS1302_ReadReg(uint8_t addr)
{
    uint8_t readByte = 0;
    DS1302_RST = 0;
    DS1302_SCK = 0;
    DS1302_RST = 1;
    DS1302_WriteByte(addr);
    readByte = DS1302_ReadByte();
    DS1302_SCK = 1;
    DS1302_RST = 0;
    return readByte;
}

void DS1302_ReadBurst(uint8_t cmd, uint8_t len, uint8_t *buf) 
{
    uint8_t readByte = 0;
    DS1302_RST = 0;
    DS1302_SCK = 0;
    DS1302_RST = 1;
    DS1302_WriteByte(cmd);
    while(len--)
        *buf++ = DS1302_ReadByte();
    DS1302_SCK = 1;
    DS1302_RST = 0;
}

void DS1302_Init(void)
{
    DS1302_WriteReg(DS1302_W_PROTECT, 0x00); // write unprotect
    DS1302_WriteReg(DS1302_W_TK_CHARGER, 0x01); // stop charger
    DS1302_WriteReg(0XC0, 0x00);
    DS1302_WriteReg(0XC2, 0x01);
    DS1302_WriteReg(0XC4, 0x02);
    DS1302_WriteReg(0XC6, 0x03);
    DS1302_WriteReg(DS1302_W_PROTECT, 0x80); // write protect
}

void DS1302_print(uint8_t dat)
{
    printf_tiny("%x%x ", dat >> 4, dat & 0x0F);
}

void DS1302_printBuf(uint8_t len)
{
    for(uint8_t i = 0; i < len; i++)
    {
        printf_tiny("%x%x ", BUF[i] >> 4, BUF[i] & 0x0F);
    }
    printf_tiny("\n");
}

void main(void)
{
    UTIL_enablePrintf();
    DS1302_Init();
    while(1)
    {
        uint8_t dat = DS1302_ReadReg(DS1302_R_SECOND);
        DS1302_print(dat);
        dat = DS1302_ReadReg(DS1302_R_MINUTE);
        DS1302_print(dat);
        dat = DS1302_ReadReg(DS1302_R_HOUR);
        DS1302_print(dat);
        printf_tiny("\n");
        DS1302_ReadBurst(DS1302_R_CLK_BURST, 8, BUF);
        DS1302_printBuf(8);
        DS1302_ReadBurst(DS1302_R_RAM_BURST, 8, BUF);
        DS1302_printBuf(8);
        sleep(491);
    }
}

參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM