[RISC-V MCU 应用开发] 国产risc-v微控制器读取DS3231

[复制链接]
559|1
dirtwillfly 发表于 2025-9-28 13:41 | 显示全部楼层 |阅读模式
本帖最后由 dirtwillfly 于 2025-9-28 20:30 编辑

第一部分:DS3231介绍
1.1 特性
DS3231 是一款 I2C 实时时钟 (RTC),具有内置温度补偿晶体振荡器 (TCXO) 和低成本且异常精确的晶体。当模块的电源中断时,设备有电池输入并保持精确的时间。该器件的长期精度因包含晶体振荡器而得到提高。RTC 跟踪秒、分钟、小时、天、日期、月和年。对于少于 31 天的月份,月末日期会自动修改,包括闰年更正。时钟具有 AM/PM 指示,可在 24 小时或 12 小时模式下工作。包括两个可编程的时间警报,以及一个可编程的方波输出。I2C 双向总线用于串行传输地址和数据。


关键特性:
• 高精确时钟 :具有+/-2ppm(百万分之二)的精度,从0°C到+40°C。
• 温度补偿 :内置温度传感器,能够自动补偿时钟频率偏移。
• 集成备份电池 :当主电源失效时,内置锂电池自动切换,保证时钟继续运行

1.2 参数
  • 0°C至+40°C范围内精度为±2ppm
  • -40°C至+85°C范围内精度为±3.5ppm
  • 为连续计时提供电池备份输入
  • 工作温度范围
    • 商用级:0°C至+70°C
    • 工业级:-40°C至+85°C
  • 低功耗
  • 实时时钟产生秒、分、时、星期、日期、月和年计时,并提供有效期到2100年的闰年补偿
  • 两个日历闹钟
  • 可编程方波输出
  • 高速(400kHz) I²C接口
  • 工作在3.3V
  • 数字温度传感器输出:精度为±3°C
  • 老化修正寄存器
  • /RST输出/按钮复位去抖输入


1.3 电路原理图
使用的模块:
339668d8ccfe41803.png
与ds3231相关的原理图:
3612868d8ccbf691cc.png
其中有一点要特别注意,因为原理图中VCC经过限流电阻和二极管连接到BAT,这是一个BAT的限流充电电路。所以这里的BAT电池必须使用可充电的CR2032。

第二部分:应用开发
2.1 硬件连接方式

使用的HPM5361开发板P1接口有两组I2C接口,这里使用I2C0接口,即PB03和PB02引脚。



这两个引脚,在开发板上已经有了10K的上拉电阻,使用的DS3231上也有上拉电阻,这里可以共存,不需要特殊处理。

2.2 驱动代码

  1. #define     DS3231_ARRD         0x68    /* slave address */

  2. #define     REG_SEC             0x00
  3. #define     REG_MIN             0x01
  4. #define     REG_HOUR            0x02
  5. #define     REG_DAY             0x03
  6. #define     REG_WEEK            0x04
  7. #define     REG_MON             0x05
  8. #define     REG_YEAR            0x06
  9. #define     REG_ALM1_SEC        0x07
  10. #define     REG_ALM1_MIN        0x08
  11. #define     REG_ALM1_HOUR       0x09
  12. #define     REG_ALM1_DAY_DATE   0x0A
  13. #define     REG_ALM2_MIN        0x0B
  14. #define     REG_ALM2_HOUR       0x0C
  15. #define     REG_ALM2_DAY_DATE   0x0D
  16. #define     REG_CONTROL         0x0E
  17. #define     REG_STATUS          0x0F
  18. #define     REG_AGING_OFFSET    0x10
  19. #define     REG_TEMP_MSB        0x11
  20. #define     REG_TEMP_LSB        0x12

  21. #define DS3231_I2C_BUS      "i2c0"      /* i2c linked */
  22. #define DS3231_DEVICE_NAME  "rtc"       /* register device name */

  23. static struct rt_device ds3231_dev; /* ds3231 device */

  24. static unsigned char bcd_to_hex(unsigned char data)
  25. {
  26.     unsigned char temp;

  27.     temp = ((data>>4)*10 + (data&0x0f));
  28.     return temp;
  29. }

  30. static unsigned char hex_to_bcd(unsigned char data)
  31. {
  32.     unsigned char temp;

  33.     temp = (((data/10)<<4) + (data%10));
  34.     return temp;
  35. }

  36. static rt_err_t  ds3231_read_reg(rt_device_t dev, rt_uint8_t reg,rt_uint8_t *data,rt_uint8_t data_size)
  37. {
  38.     struct rt_i2c_msg msg[2];
  39.     struct rt_i2c_bus_device *i2c_bus = RT_NULL;

  40.     RT_ASSERT(dev != RT_NULL);

  41.     i2c_bus = (struct rt_i2c_bus_device*)dev->user_data;
  42.     msg[0].addr  = DS3231_ARRD;
  43.     msg[0].flags = RT_I2C_WR;
  44.     msg[0].len   = 1;
  45.     msg[0].buf   = &reg;
  46.     msg[1].addr  = DS3231_ARRD;
  47.     msg[1].flags = RT_I2C_RD;
  48.     msg[1].len   = data_size;
  49.     msg[1].buf   = data;

  50.     if(rt_i2c_transfer(i2c_bus, msg, 2) == 2)
  51.     {
  52.         return RT_EOK;
  53.     }
  54.     else
  55.     {
  56.         LOG_E("i2c bus read failed!\r\n");
  57.         return -RT_ERROR;
  58.     }
  59. }

  60. static rt_err_t  ds3231_write_reg(rt_device_t dev, rt_uint8_t reg, rt_uint8_t *data, rt_uint8_t data_size)
  61. {
  62.     struct rt_i2c_msg msg[2];
  63.     struct rt_i2c_bus_device *i2c_bus = RT_NULL;

  64.     RT_ASSERT(dev != RT_NULL);

  65.     i2c_bus = (struct rt_i2c_bus_device*)dev->user_data;
  66.     msg[0].addr     = DS3231_ARRD;
  67.     msg[0].flags    = RT_I2C_WR;
  68.     msg[0].len      = 1;
  69.     msg[0].buf      = &reg;
  70.     msg[1].addr     = DS3231_ARRD;
  71.     msg[1].flags    = RT_I2C_WR | RT_I2C_NO_START;
  72.     msg[1].len      = data_size;
  73.     msg[1].buf      = data;
  74.     if(rt_i2c_transfer(i2c_bus, msg, 2) == 2)
  75.     {
  76.         return RT_EOK;
  77.     }
  78.     else
  79.     {
  80.         LOG_E("i2c bus write failed!\r\n");
  81.         return -RT_ERROR;
  82.     }
  83. }

  84. static rt_err_t rt_ds3231_open(rt_device_t dev, rt_uint16_t flag)
  85. {
  86.     if (dev->rx_indicate != RT_NULL)
  87.     {
  88.         /* open interrupt */
  89.     }

  90.     return RT_EOK;
  91. }

  92. static rt_size_t rt_ds3231_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
  93. {
  94.     return RT_EOK;
  95. }

  96. static rt_err_t rt_ds3231_control(rt_device_t dev, int cmd, void *args)
  97. {
  98.     rt_err_t    ret = RT_EOK;
  99.     time_t      *time;
  100.     struct tm   time_temp;
  101.     rt_uint8_t  buff[7];

  102.     RT_ASSERT(dev != RT_NULL);
  103.     rt_memset(&time_temp, 0, sizeof(struct tm));

  104.     switch (cmd)
  105.     {
  106.         /* read time */
  107.         case RT_DEVICE_CTRL_RTC_GET_TIME:
  108.             time = (time_t *)args;
  109.             ret = ds3231_read_reg(dev, REG_SEC,buff,7);
  110.             if(ret == RT_EOK)
  111.             {
  112.                 time_temp.tm_year  = bcd_to_hex(buff[6]) + 2000 - 1900;
  113.                 time_temp.tm_mon   = bcd_to_hex(buff[5]&0x7f) - 1;
  114.                 time_temp.tm_mday  = bcd_to_hex(buff[4]);
  115.                 time_temp.tm_hour  = bcd_to_hex(buff[2]);
  116.                 time_temp.tm_min   = bcd_to_hex(buff[1]);
  117.                 time_temp.tm_sec   = bcd_to_hex(buff[0]);
  118.                 *time = mktime(&time_temp);
  119.             }
  120.         break;

  121.         /* set time */
  122.         case RT_DEVICE_CTRL_RTC_SET_TIME:
  123.         {
  124.             struct tm *time_new;

  125.             time = (time_t *)args;
  126.             time_new = localtime(time);
  127.             buff[6] = hex_to_bcd(time_new->tm_year + 1900 - 2000);
  128.             buff[5] = hex_to_bcd(time_new->tm_mon + 1);
  129.             buff[4] = hex_to_bcd(time_new->tm_mday);
  130.             buff[3] = hex_to_bcd(time_new->tm_wday+1);
  131.             buff[2] = hex_to_bcd(time_new->tm_hour);
  132.             buff[1] = hex_to_bcd(time_new->tm_min);
  133.             buff[0] = hex_to_bcd(time_new->tm_sec);
  134.             ret = ds3231_write_reg(dev, REG_SEC, buff, 7);
  135.         }
  136.         break;
  137.     #ifdef RT_USING_ALARM
  138.         /* get alarm time */
  139.         case RT_DEVICE_CTRL_RTC_GET_ALARM:
  140.         {
  141.             struct rt_rtc_wkalarm *alm_time;

  142.             ret = ds3231_read_reg(dev, REG_ALM1_SEC, buff, 4);
  143.             if(ret == RT_EOK)
  144.             {
  145.                 alm_time = (struct rt_rtc_wkalarm *)args;
  146.                 alm_time->tm_hour  = bcd_to_hex(buff[2]);
  147.                 alm_time->tm_min   = bcd_to_hex(buff[1]);
  148.                 alm_time->tm_sec   = bcd_to_hex(buff[0]);
  149.             }
  150.         }
  151.         break;

  152.         /* set alarm time */
  153.         case RT_DEVICE_CTRL_RTC_SET_ALARM:
  154.         {
  155.             struct rt_rtc_wkalarm *alm_time;

  156.             alm_time = (struct rt_rtc_wkalarm *)args;
  157.             buff[3] = 0x80; /* enable, alarm when hours, minutes, and seconds match */
  158.             buff[2] = hex_to_bcd(alm_time->tm_hour);
  159.             buff[1] = hex_to_bcd(alm_time->tm_min);
  160.             buff[0] = hex_to_bcd(alm_time->tm_sec);
  161.             ret = ds3231_write_reg(dev, REG_ALM1_SEC, buff, 4);
  162.         }
  163.         break;
  164.     #endif
  165.         default:
  166.         break;
  167.     }
  168.     return ret;
  169. }

  170. float ds3231_get_temperature(void)
  171. {
  172.     rt_int8_t buff[2];
  173.     float temp = 0.0f;

  174.     ds3231_read_reg(&ds3231_dev, REG_TEMP_MSB, (rt_uint8_t*)buff, 2);
  175.     if(buff[0]&0x80)
  176.     {/* negative temperature */
  177.         temp = buff[0];
  178.         temp -= (buff[1]>>6)*0.25;  /* 0.25C resolution */
  179.     }
  180.     else
  181.     {/* positive temperature */
  182.         temp = buff[0];
  183.         temp += ((buff[1]>>6)&0x03)*0.25;
  184.     }

  185.     return temp;
  186. }

  187. int rt_hw_ds3231_init(void)
  188. {
  189.     struct rt_i2c_bus_device *i2c_device;
  190.     uint8_t data;

  191.     i2c_device = rt_i2c_bus_device_find(DS3231_I2C_BUS);
  192.     if (i2c_device == RT_NULL)
  193.     {
  194.         LOG_E("i2c bus device %s not found!\r\n", DS3231_I2C_BUS);
  195.         return -RT_ERROR;
  196.     }

  197.     /* register rtc device */
  198.     ds3231_dev.type         = RT_Device_Class_RTC;
  199.     ds3231_dev.init         = RT_NULL;
  200.     ds3231_dev.open         = rt_ds3231_open;
  201.     ds3231_dev.close        = RT_NULL;
  202.     ds3231_dev.read         = rt_ds3231_read;
  203.     ds3231_dev.write        = RT_NULL;
  204.     ds3231_dev.control      = rt_ds3231_control;
  205.     ds3231_dev.user_data    = (void*)i2c_device;    /* save i2cbus */;
  206.     rt_device_register(&ds3231_dev, DS3231_DEVICE_NAME, RT_DEVICE_FLAG_RDWR);

  207.     /* init ds3231 */
  208.     data = 0x04;    /* close clock out */
  209.     ds3231_write_reg(&ds3231_dev, REG_CONTROL, &data, 1);
  210.     LOG_D("the rtc of ds3231 init succeed!");

  211.     return 0;
  212. }
  213. //INIT_DEVICE_EXPORT(rt_hw_ds3231_init);
  214. INIT_COMPONENT_EXPORT(rt_hw_ds3231_init);

  215. #ifdef RT_USING_FINSH
  216. #include <finsh.h>

  217. void list_ds31_temp(void)
  218. {
  219.     float temp = 0.0f;

  220.     temp = ds3231_get_temperature();

  221.     rt_kprintf("ds3231 temperature: [%d.%dC] \n", (int)temp, (int)(temp * 10) % 10);
  222. }
  223. FINSH_FUNCTION_EXPORT(list_ds31_temp, list ds3231 temperature.)
  224. #endif /* RT_USING_FINSH */



2.3 效果验证
7777668d9295ddd073.png
经过验证,可以从ds3231上读取到温度值,说明读取寄存器时正常的。但使用date命令回复的时间没有变化,说明rtthread的rtc框架和ds3231驱动间的衔接有问题。经过排查,发现rtthread的系统代码让先楫改的面目全非,不再支持外部rtc了。
解决方案,不使用rtc框架,直接读取DS3231的寄存器就可以解决。



 楼主| dirtwillfly 发表于 2025-9-28 13:41 | 显示全部楼层
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:欢迎进入TI MCU论坛      21ic TI技术交流1群:61549143(已满),  21ic TI技术交流2群:311421422 我的博客:http://blog.timcu.com/

1199

主题

35121

帖子

1122

粉丝
快速回复 在线客服 返回列表 返回顶部