相关文章 1 STM32F4 RTC-Alarm 的使用 2 STM32F4 PM组件 DeepSleep 模式的使用 3 STM32F4 PM组件 StandBy 模式的使用
??本次测试使用的开发板为正点原子的 STM32F429IGT6 阿波罗开发板,RT-Thread 的 RTC 驱动没有实现闹钟设置的部分,在测试时花了一些时间查找修改办法,在此记录一下。
1 工程的创建和配置
??本次测试创建的工程是基于芯片创建的 RT-Thread 的标准工程,使用的开发环境是 RT-Thread Studio,各软件包的版本如下
类型 | 对应的版本 |
---|
内核 | 4.0.4 | 芯片支持包 | 0.2.2 | RTT Studio | 2.2.3 | STM32 CubeMX | 6.2.1 |
1.1 CubeMX 的配置
??创建工程后首先对时钟和RTC的功能进行配置,在这里我们使用 CubeMX 进行图形化的配置,双击工程中的“CubeMX Settings”打开图形化配置软件 CubeMX 进行配置。
1.1.1 时钟源的选择
??RT-Thread Studio创建好的工程默认使用的是内部的低速晶振作为时钟源,因此我们首先进行时钟源的选择配置,本次使用的开发板用的是无源的晶振,HSE为25MHz,LSE为32.768KHZ。在配置界面选择 BYPASS Clock Source 表示使用的是有源晶振,选择 Crystal 表示使用的是无源晶振。时钟源选择之后在右侧的引脚描述图中可以看到对应的引脚已被配置,配置完成的界面如下图所示。
1.1.2 Debug 引脚配置
??本次使用四线的STLINK进行程序的烧写和调试,相应的引脚按照下图所示进行配置。需要注意的是在裸机代码中如果Debug引脚不进行配置的话,烧写一次程序后板子将不能进行第二次烧写,原因是Debug引脚没有进行配置复用成其他功能了,此时应该调整BOOT0和BOOT1引脚的电平,使用SRAM启动,然后烧写一次程序后再将BOOT0接地,就能正常烧写程序。
1.1.3 控制台串口的配置
??控制台串口的配置如下,我们使用的是串口1作为控制台串口,串口工作模式是异步通信,串口的收发引脚是PA9和PA10。配置好串口后可以在右侧的引脚图中看到PA9和PA10已经复用为串口1的功能。串口的通讯参数按照默认的115200 8n1进行配置。 ??因为CubeMX配置后的工程使用的是文件 cubemx/Inc/stm32f4xx_hal_conf.h 里面的宏定义来确定外设的使能,所以在这里需要配置一个串口,以便自动打开宏 HAL_UART_MODULE_ENABLED ,否则编译会报错。
1.1.4 RTC的配置
??本次测试的目的是测试RTC的闹钟功能,因此需要对RTC进行配置。F429有Alarm A和Alarm B两个闹钟,本次以Alarm A为例进行测试,配置界面如下图所示。初始的时间可以按照实际的进行设置,也可以不进行设置。
??RTC外设没有独立的中断,是通过触发EXTI来产生RTC外设中断。因此我们还需要对中断进行配置,配置界面如下所示,我们使能了RTC Alarm中断,中断优先级测试时没有对其进行修改,按照默认进行配置。
1.1.5 时钟树配置
??配置完了时钟源和外设,我们需要对时钟树进行配置,如下图所示。
1.1.6 代码生成
??所有选项配置好之后我们点击CubeMX右上角的“GENERATE CODE”生成配置代码,生成之后关闭CubeMX软件,然后查看Studio软件,弹出如下图所示的对话框表示生成成功。
1.2 RT-Thread Settings 的配置
??我们需要配置 RT-Thread 的设备驱动,使能 RTC 外设的驱动和 Alarm 的功能,如下所示。
??除此之外还能进行时区的设置,如下所示,默认为东八区
1.3 board.h 的修改
??上述配置完成之后需要在文件 drivers/board.h 中开启宏 BSP_USING_ONCHIP_RTC ,以使能 RTC 硬件配置,如下所示。
2 编译及错误修改
??上述的配置全部完成之后,点击编译按钮,对配置好的功能进行编译。??RTT Source Code v4.0.4 版本的 RTC 在移植时会有函数重复定义和缺少头文件的问题,需要注意一下。错误信息如下
../drivers/drv_rtc.c:38:8: error: unknown type name 'time_t'
static time_t get_rtc_timestamp(void)
../drivers/drv_rtc.c:211:17: error: conflicting types for 'rt_hw_rtc_register'
static rt_err_t rt_hw_rtc_register(rt_device_t device, const char *name, rt_uint32_t flag)
^
In file included from F:\HotNet_Project\HN1000_GCU\hn1000_gcu_app\rt-thread\components\drivers\include/rtdevice.h:33:0,
from ../drivers/drv_rtc.c:13:
F:\HotNet_Project\HN1000_GCU\hn1000_gcu_app\rt-thread\components\drivers\include/drivers/rtc.h:42:10: note: previous declaration of 'rt_hw_rtc_register' was here
rt_err_t rt_hw_rtc_register(rt_rtc_dev_t *rtc,
??解决方法为 ??1. 在 drivers/drv_rtc.c 中增加头文件 #include <sys/time.h> ??2. 文件 rt-thread/components/drivers/include/drivers/rtc.h 中注释掉 rt_hw_rtc_register() 函数的声明,如下图所示。
3 源码的修改
??使用时发现源码有一些问题导致不能正确的产生 RTC 闹钟的中断,将修改的源码部分记录如下。
3.1 alarm.c 的修改
??alarm.c 文件位于路径 rt-thread/components/drivers/rtc/alarm.c。参考本文的第 6.1 小节的解释,将 alarm.c 中的所有的 gmtime_r 替换为 localtime_r 。
3.2 drv_rtc.c 的修改
??drv_rtc.c 文件位于路径drivers/drv_rtc.c。本部分内容参考文章 drv_rtc.c alarm 的实现。在 RT-Thread 源码提供的drv_rtc.c文件是缺少 RT_DEVICE_CTRL_RTC_SET_ALARM 和 RT_DEVICE_CTRL_RTC_GET_ALARM 的实现,参考论坛中的 @mii 的文章对其进行修改,主要添加了函数 set_rtc_alarm_stamp() 和 get_rtc_alarm_stamp() ,修改的详细步骤参考上面的文章链接,修改后的 drv_rtc.c 如下,添加的内容均有 2022-06-08 时间标记。
#include "board.h"
#include<rtthread.h>
#include<rtdevice.h>
#ifdef BSP_USING_ONCHIP_RTC
#ifndef HAL_RTCEx_BKUPRead
#define HAL_RTCEx_BKUPRead(x1, x2) (~BKUP_REG_DATA)
#endif
#ifndef HAL_RTCEx_BKUPWrite
#define HAL_RTCEx_BKUPWrite(x1, x2, x3)
#endif
#ifndef RTC_BKP_DR1
#define RTC_BKP_DR1 RT_NULL
#endif
#define LOG_TAG "drv.rtc"
#include <drv_log.h>
#define BKUP_REG_DATA 0xA5A5
static struct rt_device rtc;
static RTC_HandleTypeDef RTC_Handler;
static time_t get_rtc_timestamp(void)
{
RTC_TimeTypeDef RTC_TimeStruct = { 0 };
RTC_DateTypeDef RTC_DateStruct = { 0 };
struct tm tm_new;
HAL_RTC_GetTime(&RTC_Handler, &RTC_TimeStruct, RTC_FORMAT_BIN);
HAL_RTC_GetDate(&RTC_Handler, &RTC_DateStruct, RTC_FORMAT_BIN);
tm_new.tm_sec = RTC_TimeStruct.Seconds;
tm_new.tm_min = RTC_TimeStruct.Minutes;
tm_new.tm_hour = RTC_TimeStruct.Hours;
tm_new.tm_mday = RTC_DateStruct.Date;
tm_new.tm_mon = RTC_DateStruct.Month - 1;
tm_new.tm_year = RTC_DateStruct.Year + 100;
LOG_D("get rtc time.");
return mktime(&tm_new);
}
static rt_err_t set_rtc_time_stamp(time_t time_stamp)
{
RTC_TimeTypeDef RTC_TimeStruct = { 0 };
RTC_DateTypeDef RTC_DateStruct = { 0 };
struct tm *p_tm;
p_tm = localtime(&time_stamp);
if (p_tm->tm_year < 100)
{
return -RT_ERROR;
}
RTC_TimeStruct.Seconds = p_tm->tm_sec;
RTC_TimeStruct.Minutes = p_tm->tm_min;
RTC_TimeStruct.Hours = p_tm->tm_hour;
RTC_DateStruct.Date = p_tm->tm_mday;
RTC_DateStruct.Month = p_tm->tm_mon + 1;
RTC_DateStruct.Year = p_tm->tm_year - 100;
RTC_DateStruct.WeekDay = p_tm->tm_wday + 1;
if (HAL_RTC_SetTime(&RTC_Handler, &RTC_TimeStruct, RTC_FORMAT_BIN) != HAL_OK)
{
return -RT_ERROR;
}
if (HAL_RTC_SetDate(&RTC_Handler, &RTC_DateStruct, RTC_FORMAT_BIN) != HAL_OK)
{
return -RT_ERROR;
}
LOG_D("set rtc time.");HAL_RTCEx_BKUPWrite(&RTC_Handler, RTC_BKP_DR1, BKUP_REG_DATA);
return RT_EOK;
}
static rt_err_t set_rtc_alarm_stamp(struct rt_rtc_wkalarm wkalarm)
{
RTC_AlarmTypeDef sAlarm = { 0 };
if (wkalarm.enable == RT_FALSE)
{
if (HAL_RTC_DeactivateAlarm(&RTC_Handler, RTC_ALARM_A) != HAL_OK)
{
return -RT_ERROR;
}
LOG_I("stop rtc alarm.");
}
else
{
sAlarm.AlarmTime.Hours = wkalarm.tm_hour;
sAlarm.AlarmTime.Minutes = wkalarm.tm_min;
sAlarm.AlarmTime.Seconds = wkalarm.tm_sec;
sAlarm.AlarmTime.SubSeconds = 0x0;
sAlarm.AlarmTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
sAlarm.AlarmTime.StoreOperation = RTC_STOREOPERATION_RESET;
sAlarm.AlarmMask = RTC_ALARMMASK_DATEWEEKDAY;
sAlarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_ALL;
sAlarm.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_DATE;
sAlarm.AlarmDateWeekDay = 1;
sAlarm.Alarm = RTC_ALARM_A;
if (HAL_RTC_SetAlarm_IT(&RTC_Handler, &sAlarm, RTC_FORMAT_BIN) != HAL_OK)
{
return -RT_ERROR;
}
LOG_I("set rtc alarm.");
}
return RT_EOK;
}
struct rt_rtc_wkalarm get_rtc_alarm_stamp(void)
{
RTC_AlarmTypeDef sAlarm = { 0 };
struct rt_rtc_wkalarm wkalarm;
if (HAL_RTC_GetAlarm(&RTC_Handler, &sAlarm, RTC_ALARM_A, RTC_FORMAT_BIN) != HAL_OK)
{
LOG_E("get rtc alarm fail!.");
}
wkalarm.tm_sec = sAlarm.AlarmTime.Seconds;
wkalarm.tm_min = sAlarm.AlarmTime.Minutes;
wkalarm.tm_hour = sAlarm.AlarmTime.Hours;
LOG_I("get rtc alarm.");
return wkalarm;
}
void RTC_Alarm_IRQHandler(void)
{
HAL_RTC_AlarmIRQHandler(&RTC_Handler);
}
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
rt_alarm_update(&rtc, 1);
}
static void rt_rtc_init(void)
{
#ifndef SOC_SERIES_STM32H7
__HAL_RCC_PWR_CLK_ENABLE();
#endif
RCC_OscInitTypeDef RCC_OscInitStruct = { 0 };
#ifdef BSP_RTC_USING_LSI
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
RCC_OscInitStruct.LSEState = RCC_LSE_OFF;
RCC_OscInitStruct.LSIState = RCC_LSI_ON;
#else
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
RCC_OscInitStruct.LSEState = RCC_LSE_ON;
RCC_OscInitStruct.LSIState = RCC_LSI_OFF;
#endif
HAL_RCC_OscConfig(&RCC_OscInitStruct);
}
static rt_err_t rt_rtc_config(struct rt_device *dev)
{
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = { 0 };
HAL_PWR_EnableBkUpAccess();
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;
#ifdef BSP_RTC_USING_LSI
PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;
#else
PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
#endif
HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);
__HAL_RCC_RTC_ENABLE();
RTC_Handler.Instance = RTC;
if (HAL_RTCEx_BKUPRead(&RTC_Handler, RTC_BKP_DR1) != BKUP_REG_DATA)
{
LOG_I("RTC hasn't been configured, please use <date> command to config.");
#if defined(SOC_SERIES_STM32F1)
RTC_Handler.Init.OutPut = RTC_OUTPUTSOURCE_NONE;
RTC_Handler.Init.AsynchPrediv = RTC_AUTO_1_SECOND;
#elif defined(SOC_SERIES_STM32F0)
#ifdef BSP_RTC_USING_LSI
RTC_Handler.Init.AsynchPrediv = 0XA0;
RTC_Handler.Init.SynchPrediv = 0xFA;
#else
RTC_Handler.Init.AsynchPrediv = 0X7F;
RTC_Handler.Init.SynchPrediv = 0x0130;
#endif
RTC_Handler.Init.HourFormat = RTC_HOURFORMAT_24;
RTC_Handler.Init.OutPut = RTC_OUTPUT_DISABLE;
RTC_Handler.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
RTC_Handler.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
#elif defined(SOC_SERIES_STM32F2) || defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32F7) || defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32H7)
#ifdef BSP_RTC_USING_LSI
RTC_Handler.Init.AsynchPrediv = 0X7D;
#else
RTC_Handler.Init.AsynchPrediv = 0X7F;
#endif
RTC_Handler.Init.SynchPrediv = 0XFF;
RTC_Handler.Init.HourFormat = RTC_HOURFORMAT_24;
RTC_Handler.Init.OutPut = RTC_OUTPUT_DISABLE;
RTC_Handler.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
RTC_Handler.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
#endif
if (HAL_RTC_Init(&RTC_Handler) != HAL_OK)
{
return -RT_ERROR;
}
}
return RT_EOK;
}
static rt_err_t rt_rtc_control(rt_device_t dev, int cmd, void *args)
{
rt_err_t result = RT_EOK;
RT_ASSERT(dev != RT_NULL);
switch (cmd)
{
case RT_DEVICE_CTRL_RTC_GET_TIME:
*(rt_uint32_t *) args = get_rtc_timestamp();
LOG_D("RTC: get rtc_time %x\n", *(rt_uint32_t * )args);
break;
case RT_DEVICE_CTRL_RTC_SET_TIME:
if (set_rtc_time_stamp(*(rt_uint32_t *) args))
{
result = -RT_ERROR;
}
LOG_D("RTC: set rtc_time %x\n", *(rt_uint32_t * )args);
break;
case RT_DEVICE_CTRL_RTC_SET_ALARM:
if (set_rtc_alarm_stamp(*(struct rt_rtc_wkalarm *) args))
{
result = -RT_ERROR;
}
LOG_D("RTC: set rtc_alarm tme : hour: %d , min: %d , sec: %d \n", *(struct rt_rtc_wkalarm * )args->tm_hour,
*(struct rt_rtc_wkalarm * )args->tm_min, *(struct rt_rtc_wkalarm * )args->tm_sec);
break;
case RT_DEVICE_CTRL_RTC_GET_ALARM:
*(struct rt_rtc_wkalarm *) args = get_rtc_alarm_stamp();
LOG_D("RTC: get rtc_alarm time : hour: %d , min: %d , sec: %d \n", *(struct rt_rtc_wkalarm * )args->tm_hour,
*(struct rt_rtc_wkalarm * )args->tm_min, *(struct rt_rtc_wkalarm * )args->tm_sec);
break;
}
return result;
}
#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops rtc_ops =
{
RT_NULL,
RT_NULL,
RT_NULL,
RT_NULL,
RT_NULL,
rt_rtc_control
};
#endif
static rt_err_t rt_hw_rtc_register(rt_device_t device, const char *name, rt_uint32_t flag)
{
RT_ASSERT(device != RT_NULL);
rt_rtc_init();
if (rt_rtc_config(device) != RT_EOK)
{
return -RT_ERROR;
}
#ifdef RT_USING_DEVICE_OPS
device->ops = &rtc_ops;
#else
device->init = RT_NULL;
device->open = RT_NULL;
device->close = RT_NULL;
device->read = RT_NULL;
device->write = RT_NULL;
device->control = rt_rtc_control;
#endif
device->type = RT_Device_Class_RTC;
device->rx_indicate = RT_NULL;
device->tx_complete = RT_NULL;
device->user_data = RT_NULL;
return rt_device_register(device, name, flag);
}
int rt_hw_rtc_init(void)
{
rt_err_t result;
result = rt_hw_rtc_register(&rtc, "rtc", RT_DEVICE_FLAG_RDWR);
if (result != RT_EOK)
{
LOG_E("rtc register err code: %d", result);
return result;
}
LOG_D("rtc init success");
return RT_EOK;
}
INIT_DEVICE_EXPORT(rt_hw_rtc_init);
#endif
4 测试用例和测试结果
4.1 测试用例
??测试用例如下,本次定义了一个执行闹钟测试命令 5 秒的闹钟,然后通过日志信息来查看闹钟的响应,测试代码如下。
static rt_alarm_t alarm = RT_NULL;
void user_alarm_callback(rt_alarm_t alarm, time_t timestamp)
{
struct tm p_tm;
time_t now = timestamp;
localtime_r(&now, &p_tm);
LOG_D("user alarm callback function.");
LOG_D("curr time: %04d-%02d-%02d %02d:%02d:%02d", p_tm.tm_year + 1900, p_tm.tm_mon + 1, p_tm.tm_mday, p_tm.tm_hour, p_tm.tm_min, p_tm.tm_sec);
}
void alarm_sample(void)
{
time_t curr_time;
struct tm p_tm;
struct rt_alarm_setup setup;
curr_time = time(NULL) + 5;
localtime_r(&curr_time, &p_tm);
LOG_D("now time: %04d-%02d-%02d %02d:%02d:%02d", p_tm.tm_year + 1900, p_tm.tm_mon + 1, p_tm.tm_mday, p_tm.tm_hour,
p_tm.tm_min, p_tm.tm_sec - 5);
setup.flag = RT_ALARM_ONESHOT;
setup.wktime.tm_year = p_tm.tm_year;
setup.wktime.tm_mon = p_tm.tm_mon;
setup.wktime.tm_mday = p_tm.tm_mday;
setup.wktime.tm_wday = p_tm.tm_wday;
setup.wktime.tm_hour = p_tm.tm_hour;
setup.wktime.tm_min = p_tm.tm_min;
setup.wktime.tm_sec = p_tm.tm_sec;
alarm = rt_alarm_create(user_alarm_callback, &setup);
if (RT_NULL != alarm)
{
rt_alarm_start(alarm);
}
else
{
LOG_E("rtc alarm create failed");
}
rt_alarm_dump();
}
MSH_CMD_EXPORT(alarm_sample, alarm sample);
void delete_alarm_sample(void)
{
rt_alarm_delete(alarm);
rt_alarm_dump();
}
MSH_CMD_EXPORT(delete_alarm_sample, delete alarm sample);
4.2 测试结果
??测试结果如下,测试结果的分析在下面的结果中以注释的内容呈现。
msh >alarm_sample
[D/main] now time: 2022-06-08 22:26:32
[I/drv.rtc] set rtc alarm.
[I/drv.rtc] get rtc alarm.
| id | YYYY-MM-DD hh:mm:ss | week | flag | en |
+----+---------------------+------+------+----+
| 0 | 2022-06-08 22:26:37 | 3 | O | 1 |
+----+---------------------+------+------+----+
msh >
msh >[I/drv.rtc] stop rtc alarm.
[D/main] user alarm callback function.
[D/main] curr time: 2022-06-08 22:26:37
[I/drv.rtc] stop rtc alarm.
msh >rt_alarm_dump
| id | YYYY-MM-DD hh:mm:ss | week | flag | en |
+----+---------------------+------+------+----+
| 0 | 2022-06-08 22:26:37 | 3 | O | 0 |
+----+---------------------+------+------+----+
msh >
msh >alarm_sample
[D/main] now time: 2022-06-08 22:26:51
[I/drv.rtc] set rtc alarm.
[I/drv.rtc] get rtc alarm.
| id | YYYY-MM-DD hh:mm:ss | week | flag | en |
+----+---------------------+------+------+----+
| 0 | 2022-06-08 22:26:56 | 3 | O | 1 |
| 1 | 2022-06-08 22:26:37 | 3 | O | 0 |
+----+---------------------+------+------+----+
msh >
msh >[I/drv.rtc] stop rtc alarm.
[D/main] user alarm callback function.
[D/main] curr time: 2022-06-08 22:26:56
[I/drv.rtc] stop rtc alarm.
msh >
msh >rt_alarm_dump
| id | YYYY-MM-DD hh:mm:ss | week | flag | en |
+----+---------------------+------+------+----+
| 0 | 2022-06-08 22:26:56 | 3 | O | 0 |
| 1 | 2022-06-08 22:26:37 | 3 | O | 0 |
+----+---------------------+------+------+----+
msh >
msh >delete_alarm_sample
[I/drv.rtc] stop rtc alarm.
| id | YYYY-MM-DD hh:mm:ss | week | flag | en |
+----+---------------------+------+------+----+
| 0 | 2022-06-08 22:26:37 | 3 | O | 0 |
+----+---------------------+------+------+----+
msh >
5 源码分析
5.1 RTC设备初始化源码分析
??RTC 设备注册和初始化的过程如下所示。
rt_hw_rtc_init
|-> rt_hw_rtc_register
|-> rt_rtc_init
|-> rt_rtc_config
|-> HAL_RTC_Init
|-> HAL_RTC_MspInit
|-> rt_device_register
|-> rt_object_init
??从函数的调用流程和分析我们可以得出,其实在 CubeMX 配置 RTC 的参数时只有生成的函数 HAL_RTC_MspInit(cubemx/Src/stm32f4xx_hal_msp.c) 被调用了,该函数的具体内容如下,也就是说只有配置的 RTC 的中断初始化被调用。 ??在 CubeMX 中配置的分频因子,时间日志和闹钟的初始值等参数均没有被使用,因此可以不用在 CubeMX 中配置相关的参数。分频因子的配置由 RT-Thread 源码默认设定了,在函数 rt_rtc_config 里面实现,开发板的时间需要手动的使用 date 命令或者 set_time() 函数进行设置。
void HAL_RTC_MspInit(RTC_HandleTypeDef* hrtc)
{
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
if(hrtc->Instance==RTC)
{
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;
PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
{
Error_Handler();
}
__HAL_RCC_RTC_ENABLE();
HAL_NVIC_SetPriority(RTC_Alarm_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn);
}
}
5.2 RTC闹钟源码分析
??RTC闹钟源码调用过程如下。 ??在闹钟的启动函数中我们可以看到该函数调用了在文件 drv_rtc.c 中添加的函数 set_rtc_alarm_stamp() 将设定的闹钟值写入到对应的寄存器中 ??闹钟的处理流程可以概括为创建一个名字为 alarmsvc 的线程,该线程一直在等待一个闹钟事件集,该事件集有闹钟的中断处理函数中进行发送。alarmsvc 线程获取到该闹钟事件集后就更新闹钟事件,然后进行调用函数 alarm_wakeup() 进行闹钟的唤醒操作,最终调用了用户使用函数 rt_alarm_create() 在创建闹钟时设定的回调函数,执行用户设定的操作。
rt_alarm_start
|-> alarm_setup
|-> alarm_set
|-> rt_device_control(RT_DEVICE_CTRL_RTC_SET_ALARM)
|-> device_control
|-> rt_rtc_control
|-> set_rtc_alarm_stamp
|-> HAL_RTC_SetAlarm_IT
rt_alarm_system_init
|-> rt_thread_create("alarmsvc", rt_alarmsvc_thread_init)
rt_alarmsvc_thread_init
|-> rt_event_recv(&_container.event)
|-> alarm_update
|-> alarm_wakeup
|-> switch (alarm->flag & 0xFF00)
|-> alarm->callback(alarm, timestamp);
RTC_Alarm_IRQHandler
|-> HAL_RTC_AlarmIRQHandler
|-> HAL_RTC_AlarmAEventCallback
|-> rt_alarm_update
|-> rt_event_send
6 问题记录
6.1 时区问题
??参考官方文档 时间函数 说底层驱动不应当使用带有时区的时间,而应该使用格林威治时间,即 UTC+0,但是在使用函数 set_time(rt-thread/components/drivers/rtc/rtc.c) 设置RTC的时间时却使用了函数 localtime_r ,也就是说写入到寄存器的时间确实是带时区的时间,查看相应的寄存器写入的也是带时区的时间,这部分代码和文档的说法有矛盾。 ??在 alarm.c 的函数 alarm_update() 获取的却是不带时区的时间,然后在函数 alarm_wakeup() 中对不带时区的现在时间和带时区的闹钟时间进行对比,结果就是 sec_now 永远小于 sec_alarm,导致变量 wakeup 的值永远为 FALSE,也就是永远不能执行用户定义的alarm回调函数 alarm->callback 。解决办法是将 alarm.c 中的所有的 gmtime_r 替换为 localtime_r 。
|