zoukankan      html  css  js  c++  java
  • 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("
    ");
    }
    
    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("
    ");
            DS1302_ReadBurst(DS1302_R_CLK_BURST, 8, BUF);
            DS1302_printBuf(8);
            DS1302_ReadBurst(DS1302_R_RAM_BURST, 8, BUF);
            DS1302_printBuf(8);
            sleep(491);
        }
    }
    
    

    参考

  • 相关阅读:
    Mysql多表关系
    Linux权限
    Linux安装python环境脚本
    ZJNU 2136
    ZJNU 2135
    ZJNU 2133
    ZJNU 1699
    ZJNU 1542
    ZJNU 1538
    ZJNU 1535
  • 原文地址:https://www.cnblogs.com/milton/p/15192778.html
Copyright © 2011-2022 走看看