STM32F103 stop模式 RTC闹钟唤醒
·
F103没有HAL_RTCEx_SetWakeUpTimer_IT()怎么办
芯片进入stop模式后,F103系列不具备RTC唤醒功能,而L0系列具备。使用L0芯片实现周期唤醒,可以直接调用库函数HAL_RTCEx_SetWakeUpTimer_IT()来实现,但F103没有这个函数。

RTC闹钟唤醒stop模式
查芯片手册可知,在这种情况下,只能通过外部中断唤醒。但幸运的是,F103的RTC闹钟中断由EXTI17管理,因此,可以通过RTC闹钟触发外部中断,实现内部唤醒(本质上还是EXTI17外部中断唤醒)。



RTC闹钟原理
根据STM32中文参考手册:
-
若 STM32 原本处于待机状态,可由闹钟事件或 WKUP 事件 (外部唤醒事件,属于 EXTI 模块,不属于 RTC) 使它退出待机模式。闹钟事件是在 * 计数器 RTC_CNT* 的值等于闹钟寄存器 RTC_ALR 的值时触发的。
- 在备份域中所有寄存器都是 16 位的,RTC 控制相关的寄存器也不例外。它的计数器RTC_CNT 的32 位由 RTC_CNTL 和 RTC_CNTH 两个寄存器组成,分别保存定时计数值的低 16 位和高 16 位。
-
在配置 RTC 模块的时钟时,通常把输入的 32768Hz 的 RTCCLK 进行 32768 分频得到实际驱动计 数器的时钟 TR_CLK = RTCCLK/32768= 1 Hz,计时周期为 1 秒,计时器在TR_CLK 的驱动下计 数,即每秒计数器 RTC_CNT 的值加 1。
因此,原理就很清楚了,当RTC_CNT与闹钟寄存器 RTC_ALR 相等时,触发闹钟中断。RTC_ALR我们会提前设置,所以我们控制RTC_CNT的累加速度,就可以控制闹钟中断触发的快慢了。例如,TR_CLK =2Hz时,计时周期为 500ms,即RTC_CNT的值每500ms加 1。所以我们实现了500ms后触发闹钟中断。
cubeMX配置

最后自己生成工程文件就行了。
代码示例
//函数入口
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_RTC_Init();
while (1)
{
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_8);//指示灯闪烁
Enter_StopMode();
}
}
//BCD转二进制
uint8_t BYTE_2_BCD(uint8_t BYTE)
{
uint8_t temp = 0;
temp = (BYTE/10)<<4;
temp |= BYTE%10;
return temp;
}
//二进制转BCD
uint8_t BCD_2_BYTE(uint8_t BCD)
{
uint8_t temp = 0;
temp = (BCD>>4)*10;
temp += BCD&0XFU;
return temp;
}
void Enter_StopMode(void)
{
//获取当前时间
RTC_TimeTypeDef RTime = {0};
HAL_RTC_GetTime(&hrtc,&RTime,RTC_FORMAT_BCD);
uint8_t h = RTime.Hours;
uint8_t m = RTime.Minutes;
uint8_t s = RTime.Seconds;
//设置唤醒时间(下一秒)
h = BCD_2_BYTE(h);
m = BCD_2_BYTE(m);
s = BCD_2_BYTE(s)+1;
if(s>59){ s = 0; m += 1;}
if(m>59){ m = 0; h += 1;}
if(h>23) h = 0;
//设置闹钟
RTC_AlarmTypeDef sAlarm;
sAlarm.Alarm = RTC_ALARM_A;
sAlarm.AlarmTime.Hours = BYTE_2_BCD(h);
sAlarm.AlarmTime.Minutes = BYTE_2_BCD(m);
sAlarm.AlarmTime.Seconds = BYTE_2_BCD(s);
HAL_RTC_SetAlarm_IT(&hrtc,&sAlarm,RTC_FORMAT_BCD);
__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON,PWR_STOPENTRY_WFI);
SystemClock_Config();//唤醒后重新初始化系统时钟
}
//RTC初始化
void MX_RTC_Init(void)
{
RTC_TimeTypeDef sTime = {0};
RTC_DateTypeDef DateToUpdate = {0};
//RTC初始化
hrtc.Instance = RTC;
hrtc.Init.AsynchPrediv = 8192;// 将32768分频,得到4Hz的计数频率,即每250ms计数器RTC_CNT累加1
hrtc.Init.OutPut = RTC_OUTPUTSOURCE_ALARM;
if (HAL_RTC_Init(&hrtc) != HAL_OK)
{
Error_Handler();
}
//设置时分秒
sTime.Hours = 0x23;
sTime.Minutes = 0x57;
sTime.Seconds = 0x0;
if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD) != HAL_OK)
{
Error_Handler();
}
//设置年月日周
DateToUpdate.WeekDay = RTC_WEEKDAY_TUESDAY;
DateToUpdate.Month = RTC_MONTH_SEPTEMBER;
DateToUpdate.Date = 0x16;
DateToUpdate.Year = 0x25;
if (HAL_RTC_SetDate(&hrtc, &DateToUpdate, RTC_FORMAT_BCD) != HAL_OK)
{
Error_Handler();
}
}
void HAL_RTC_MspInit(RTC_HandleTypeDef* rtcHandle)
{
if(rtcHandle->Instance==RTC)
{
//HAL_PWR_EnableBkUpAccess();//访问备份寄存器时,再解除屏蔽
//__HAL_RCC_BKP_CLK_ENABLE();
__HAL_RCC_RTC_ENABLE();
HAL_NVIC_SetPriority(RTC_Alarm_IRQn, 0, 0);//设置闹钟中断优先级
HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn); //使能中断
}
}
void HAL_RTC_MspDeInit(RTC_HandleTypeDef* rtcHandle)
{
if(rtcHandle->Instance==RTC)
{
__HAL_RCC_RTC_DISABLE();
HAL_NVIC_DisableIRQ(RTC_Alarm_IRQn);
}
}
//编写中断函数
void RTC_Alarm_IRQHandler(void)
{
HAL_RTC_AlarmIRQHandler(&hrtc);
}
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_10);//闪灯提示触发了闹钟中断
}
疑惑
通过以上内容可知,RTC_CNT == RTC_ALR 时,触发闹钟中断。
HAL_RTC_SetAlarm_IT()设置RTC_ALR 。
RTC时钟源32768Hz,分频系数16384,分频后得到2Hz,即每秒计数2次(RTC_CNT每500ms加1)。
在理论上,如果将唤醒时间始终设置为当前时间的下一秒,即RTC_ALR = RTC_CNT +1。而RTC_CNT却每500ms加1,所以实际上,只隔了500ms就触发了闹钟中断,最终实现了500ms的中断周期。
以上是理论逻辑,但在实际操作时,需要将分频系数设为8192(分频后得到4Hz,每秒计数4次),才能得到500ms的中断周期。
也就是说,设置250ms的周期,得到的却是500ms周期;设置500ms周期,得到的却是1000ms周期。
这让我难以理解,希望各位高手略微出手,帮我解答一下疑惑。
总结
F103可以通过RTC闹钟实现周期唤醒stop模式。
更多推荐



所有评论(0)