时区陷阱!RT-Thread闹钟失效不响应?

描述

一、前言

RT-Thread 的 alarm 是系统提供的闹钟设备接口,提供了一系列用于管理定时事件的 API。然而,在实际使用过程中,遇到了一些与时间处理相关的问题。本文将围绕这些问题展开分析,并提出对应的解决方案。

设备

二、问题点

在使用先楫平台的 drv_rtc 驱动时,发现两个典型问题:

使用 local_time_r 设置闹钟时间时无法触发回调函数

使用 gmtime_r 设置后虽然能触发回调,但通过 list_alarm 命令打印出的闹钟时间显示不正确。

例如:设置为 1719 的本地时间,实际输出却为 919,存在 8 小时的时间差。

设备

这不单单在先楫平台有所体现,在其他平台比如STM32等都有类似现象。

三、原因分析

在解析原因之前,需要了解下rtthread对于ctime的实现。

(一)ctime相关接口的区别

在rtthread的rtc和alarm驱动中,需要调用ctime的各类时间函数,其中最主要的是时间接口区别是:

local_time和local_time_t是将时间戳(time_t)转换本地时间函数,比如设置时区为东八区,那么相比UTC时间就会多8个小时。

gmtime和gmtime_r是将时间戳转换为UTC时间。

(二)rtthread的时区支持

考虑到嵌入式的场景应用,rtthread采用的是轻量级的时区支持,没有支持完整的时区数据库。

默认的时区是东八区(北京时间)

代码宏是RT_LIBC_USING_LIGHT_TZ_DST,对应的kconfig对应以下,如果需要支持其他时区时间,可以改变以下配置。

设备

(三)rtthread的时间转换支持

在rtthread的时区支持上,主要影响localtime和mktime的行为,localtime_r和localtime会根据时区时间做偏移转换为本地时间。

设备

mktime会根据时区时间偏移将本地时间转换为UTC的时间戳。

设备

在rtthread的alarm实现中,涉及到时间戳转换日历时间的接口都使用了UTC时间的函数接口gmtime_r,而当触发闹钟更新的时候,需要使用闹钟时间与当前时间做判断以便是否到达闹钟触发事件,如果此时闹钟时间使用的是local_time,那么判断将永远不成立,也就无法触发闹钟回调。而如果同样使用的是gmtime,那么时间单位是一致的,就会触发闹钟回调,但打印的闹钟列表是基于UTC时间。

由此可知:在 Alarm 模块的实现中,时间戳转日历时间的操作均使用的是 UTC 时间函数(gmtime_r)。如果用户传入的是基于本地时间的结构体,则因未统一转换而造成比较失败,导致无法触发回调。

三、修复

根据上述原因,问题的根本在于 Alarm 模块未正确支持本地时间的判断逻辑。针对这一问题,社区已在最新主线代码中进行了修复。

设备

在对应的时间戳转换日历时间上加个宏定义来切换为本地时间计算还是UTC时间计算即可。

设备

Kconfig配置上加了RT_ALARM_USING_LOCAL_TIME配置宏,如果打开就是基于本地时间来计算。默认不使能。

RT-Thread Components -> Device Drivers -> Using RTC device drivers-> Using RTC alarm -> Using local time for the alarm calculation

设备

如果使能该配置,那么闹钟就是以本地时间local_time为准

如果禁能该配置,那么闹钟就是以UTC时间gmtime为准。

在使用rt_alarm_create增加闹钟时,配置的闹钟时间需要区分来,即可正常工作

设备

在list_alarm命令中加入了timezone,查找闹钟列表更加直观


 

1、使用UTC时间的alarm

设备

2、使用东八区的本地时间的alarm

设备

四、BSP的drv_rtc驱动注意点

1. 实现 set_timestamp 时注意时间转换

当启用了 RT_ALARM_USING_LOCAL_TIME 宏定义时,必须使用 local_time 进行时间转换,否则可能导致时间偏差。

设备

2、设置闹钟时必须完整填写年月日字段

由于 RT_ALARM_ONESHOT 模式下,系统会将闹钟时间转换为时间戳进行比较,因此必须提供完整的日历时间结构(包括年、月、日)。否则可能因字段缺失导致匹配失败。

设备

而先楫只需要从闹钟寄存器中获取即可。

设备

五、总结

本文围绕 RT-Thread 中的 Alarm(闹钟)设备驱动 展开讨论,结合先楫平台的实际开发经验,深入分析了在使用 rt_alarm_create 设置本地时间闹钟时遇到的问题,并从底层机制出发,解释了问题根源,最终提出了有效的解决方案。

核心结论如下:

Alarm 子系统默认使用 UTC 时间函数(如 gmtime_r)进行时间戳与日历时间的转换;

若传入本地时间结构体但未统一转换,会导致比较失败,从而无法触发闹钟回调;

rtthread社区已在主线中引入 RT_ALARM_USING_LOCAL_TIME 宏定义,支持基于本地时间的闹钟行为;

在使用 Alarm 功能时,开发者需关注系统时区设置以及是否启用本地时间模式,以确保闹钟行为符合预期。

通过本文的分析与实践,希望可以帮助开发者更好地理解和使用 RT-Thread 的 Alarm 子系统,提升嵌入式设备中定时任务管理的灵活性与准确性。

 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分