码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • STC8H开发(十三): I2C驱动DS3231高精度实时时钟芯片


    目录

    • STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解)
    • STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解)
    • STC8H开发(三): 基于FwLib_STC8的模数转换ADC介绍和演示用例说明
    • STC8H开发(四): FwLib_STC8 封装库的介绍和使用注意事项
    • STC8H开发(五): SPI驱动nRF24L01无线模块
    • STC8H开发(六): SPI驱动ADXL345三轴加速度检测模块
    • STC8H开发(七): I2C驱动MPU6050三轴加速度+三轴角速度检测模块
    • STC8H开发(八): NRF24L01无线传输音频(对讲机原型)
    • STC8H开发(九): STC8H8K64U模拟USB HID外设
    • STC8H开发(十): SPI驱动Nokia5110 LCD(PCD8544)
    • STC8H开发(十一): GPIO单线驱动多个DS18B20数字温度计
    • STC8H开发(十二): I2C驱动AT24C08,AT24C32系列EEPROM存储
    • STC8H开发(十三): I2C驱动DS3231高精度实时时钟芯片

    DS3231简介

    DS3231是高精度I2C实时时钟芯片, I2C总线地址为固定的0xD0

    • 内置温度补偿晶体振荡源(TCXO), 降低温度变化造成的晶体频率漂移, 在[-40°C, 85°C]范围内误差 ±0.432s/Day.
    • 秒、分、时、星期、日期、月、年, 闰年补偿, 计数年份区间为[1990, 2190]
    • 两个可编程闹钟, 可以按周或按日重复
    • 方波输出
    • 供电 2.3V – 5.5V (typical: 3.3V)
    • 工作电流 200 – 300 μA
    • 待机电流 110 – 170 μA
    • 电池工作电流 70 – 150 μA
    • 时间保持电池电流 0.84 – 3.5 μA

    DS3231管脚和典型电路

    1. 32KHz - 32.768KHz输出(50%占空比), 漏极开路输出, 需要上拉电阻, 如不使用可保持开路.
    2. VCC
    3. INT/SQW - 低电平有效中断或方波输出(1Hz, 4kHz, 8kHz or 32kHz)
    4. RST - 低电平有效复位引脚
    5. GND
    6. VBAT - 备用电源
    7. SDA - I2C 数据
    8. SCL - I2C 时钟

    ZS-042模块

    在某宝上最常见的DS3231是 ZS-042 模块, 模块集成一个CR2032电池座和一个AT24C32的8K字节EEPROM存储, 后者可以通过同一个I2C总线访问.

    CR2032电池座

    当电源中断时为DS3231提供备用电源

    板载 AT24C32 EEPROM

    存储芯片 AT24C32, 容量 32K Bit = 4K Byte, 地址可通过短路 A0/A1/A2修改, 根据 24C32 的数据手册, 这三个bit对应的是7位I2C地址的第五到第七位

    1 0 1 0  A2 A1 A0 R/W
    

    A0至A2内部电阻上拉, 开路为1, 短路为0, 不同的组合可以产生8个不同的地址, 默认全开路对应的地址为0xAE

    使用STC8H3K驱动DS3231

    接线

    AT24C32的3对触点都保持开路

    P32   -> SCL
    P33   -> SDA
    GND   -> GND
    3.3V  -> VCC
    

    示例代码

    代码下载地址

    • Gitee https://gitee.com/iosetting/fw-lib_-stc8/tree/master/demo/i2c/ds3231
    • GitHub https://github.com/IOsetting/FwLib_STC8/tree/master/demo/i2c/ds3231

    代码会将DS3231时间设置为 2022-07-10 14:21:10, 然后每隔一秒显示一次时间, 数值为十六进制

    20-07-0A 0E:15:1E 00 00␍␊
    20-07-0A 0E:15:1F 00 00␍␊
    20-07-0A 0E:15:20 00 00␍␊
    20-07-0A 0E:15:21 00 00␍␊
    20-07-0A 0E:15:22 00 00␍␊
    

    初始化I2C接口

    使用P32和P33

    void I2C_Init(void)
    {
        // Master mode
        I2C_SetWorkMode(I2C_WorkMode_Master);
        /**
         * I2C clock = FOSC / 2 / (__prescaler__ * 2 + 4)
        */
        I2C_SetClockPrescaler(0x1F);
        // Switch alternative port
        I2C_SetPort(I2C_AlterPort_P32_P33);
        // Start I2C
        I2C_SetEnabled(HAL_State_ON);
    }
    
    void GPIO_Init(void)
    {
        // SDA
        GPIO_P3_SetMode(GPIO_Pin_3, GPIO_Mode_InOut_QBD);
        // SCL
        GPIO_P3_SetMode(GPIO_Pin_2, GPIO_Mode_Output_PP);
    }
    

    基础I2C接口读写方法

    #define DS3231_I2C_ADDR                 0xD0
    
    uint8_t DS3231_Write(uint8_t reg, uint8_t dat)
    {
        return I2C_Write(DS3231_I2C_ADDR, reg, &dat, 1);
    }
    
    uint8_t DS3231_MultipleRead(uint8_t reg, uint8_t *buf, uint8_t len)
    {
        return I2C_Read(DS3231_I2C_ADDR, reg, buf, len);
    }
    

    BCD码与HEX的转换

    uint8_t DS3231_Hex2Bcd(uint8_t hex)
    {
        return (hex % 10) + ((hex / 10) << 4);
    }
    
    uint8_t DS3231_Bcd2Hex(uint8_t bcd)
    {
        return (bcd >> 4) * 10 + (bcd & 0x0F);
    }
    

    读取时间

    读取时间并转换为HEX, 使用一个uint8_t数组, 结构为

    /**
        uint8_t year;
        uint8_t month;
        uint8_t week;
        uint8_t date;
        uint8_t hour;
        uint8_t minute;
        uint8_t second;
        DS3231_HourFormat_t format;
        DS3231_AmPm_t am_pm;
     */
    

    从DS3231中读出时间

    uint8_t DS3231_GetTime(uint8_t *t)
    {
        uint8_t res;
        res = I2C_Read(DS3231_I2C_ADDR, DS3231_REG_SECOND, buff, 7);
        if (res != HAL_OK)
        {
            return res;
        }
        t[0] = DS3231_Bcd2Hex(buff[6]) + ((buff[5] >> 7) & 0x01) * 100; // year
        t[1] = DS3231_Bcd2Hex(buff[5] & 0x1F);                          // month
        t[2] = DS3231_Bcd2Hex(buff[3]); // week
        t[3] = DS3231_Bcd2Hex(buff[4]); // date
        t[7] = (buff[2] >> 6) & 0x01; // 12h/24h
        t[8] = (buff[2] >> 5) & 0x01; // am/pm
        if (t[7] == DS3231_FORMAT_12H)
        {
            t[4] = DS3231_Bcd2Hex(buff[2] & 0x1F); // hour
        }
        else
        {
            t[4] = DS3231_Bcd2Hex(buff[2] & 0x3F); // hour
        }
        t[5] = DS3231_Bcd2Hex(buff[1]); // minute
        t[6] = DS3231_Bcd2Hex(buff[0]); // second
        return HAL_OK;
    }
    
    折叠

    设置时间

    先校验各时间数值, 然后通过地址分别写入

    uint8_t DS3231_SetTime(uint8_t *t)
    {
        uint8_t res, reg;
    
        // Time validation
        if (t[0] > 200) t[0] = 200; // year
    
        if (t[1] == 0) t[1] = 1; // month
        else if (t[1] > 12) t[1] = 12;
    
        if (t[2] == 0) t[2] = 1; // week
        else if (t[2] > 7) t[2] = 7;
    
        if (t[3] == 0) t[3] = 1; // date
        else if (t[3] > 31) t[3] = 31;
    
        if (t[7] == DS3231_FORMAT_12H)
        {
            if (t[4] > 12) t[4] = 12; // hour
        }
        else if (t[7] == DS3231_FORMAT_24H)
        {
            if (t[4] > 23) t[4] = 23; // hour
        }
    
        if (t[5] > 59) t[5] = 59; // minute
        if (t[6] > 59) t[6] = 59; // second
    
        res = DS3231_Write(DS3231_REG_SECOND, DS3231_Hex2Bcd(t[6]));
        if (res != HAL_OK) return res;
    
        res = DS3231_Write(DS3231_REG_MINUTE, DS3231_Hex2Bcd(t[5]));
        if (res != HAL_OK) return res;
    
        if (t[7] == DS3231_FORMAT_12H)
        {
            reg = (uint8_t)((1 << 6) | (t[8] << 5) | DS3231_Hex2Bcd(t[4]));
        }
        else
        {
            reg = (0 << 6) | DS3231_Hex2Bcd(t[4]);
        }
        res = DS3231_Write(DS3231_REG_HOUR, reg);
        if (res != HAL_OK) return res;
    
        res = DS3231_Write(DS3231_REG_WEEK, DS3231_Hex2Bcd(t[2]));
        if (res != HAL_OK) return res;
    
        res = DS3231_Write(DS3231_REG_DATE, DS3231_Hex2Bcd(t[3]));
        if (res != HAL_OK) return res;
    
        if (t[0] >= 100)
        {
            res = DS3231_Write(DS3231_REG_MONTH, DS3231_Hex2Bcd(t[1]) | (1 << 7));
            if (res != HAL_OK) return res;
            return DS3231_Write(DS3231_REG_YEAR, DS3231_Hex2Bcd(t[0] - 100));
        }
        else
        {
            res = DS3231_Write(DS3231_REG_MONTH, DS3231_Hex2Bcd(t[1]));
            if (res != HAL_OK) return res;
            return DS3231_Write(DS3231_REG_YEAR, DS3231_Hex2Bcd(t[0]));
        }
    }
    
    折叠

    读写ZS-042模块中的AT24C32

    参考前面一篇 STC8H开发(十二): I2C驱动AT24C08,AT24C32系列EEPROM存储

    参考

    • DS3231的模块ZS-042的讨论, 分析了5V供电时的电池充电问题和改造 https://forum.arduino.cc/t/zs-042-ds3231-rtc-module/268862/24
    • AT24C 的读写 https://www.likecs.com/show-204385163.html
  • 相关阅读:
    java计算机毕业设计排课系统源码+系统+mysql数据库+lw文档
    负载均衡算法
    typescript对类型的管理和查找规则
    什么?MySQL 8.0 会同时修改两个ib_logfilesN 文件?
    在macOS上运行PSCN-debug
    求解 C++问题 求解 求解
    Netty03——进阶
    详细介绍Unlink的原理及分析
    数据结构与算法拾遗十(一些基本的数据结构)
    【C++】类和对象(中)(万字详解)
  • 原文地址:https://www.cnblogs.com/milton/p/16444961.html
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | Kerberos协议及其部分攻击手法
    0day的产生 | 不懂代码的"代码审计"
    安装scrcpy-client模块av模块异常,环境问题解决方案
    leetcode hot100【LeetCode 279. 完全平方数】java实现
    OpenWrt下安装Mosquitto
    AnatoMask论文汇总
    【AI日记】24.11.01 LangChain、openai api和github copilot
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
正则表达式工具 cron表达式工具 密码生成工具

京公网安备 11010502049817号