APM32F402实现RTC定时唤醒STOP模式
# APM32F402实现RTC定时唤醒STOP模式#申请原创# [@21小跑堂](https://bbs.21ic.com/space-uid-760190.html)
## 低功耗模式
按功耗由高到低排列,APM32F402具有运行、睡眠、停止和待机四种工作模式。上电复位后APM32F402处于运行状态时,当内核不需要继续运行,就可以选择进入后面的三种低功耗模式降低功耗,这三种模式中,功耗消耗不同、唤醒时间不同、唤醒源不同,用户需要根据应用需求,选择最佳的低功耗模式;三种低功耗的模式说明见下表。
!(data/attachment/forum/202506/30/222034ums2hxjs7m5ms9xm.png "image.png")
!(data/attachment/forum/202506/30/222102ytc8zb8znbcttiee.png "image.png")
### SLEEP模式
SLEEP模式又叫睡眠模式,在睡眠模式中,仅关闭了内核时钟,内核停止运行,但其片上外设,内核的外设全都还照常运行。睡眠模式有两种方式进入睡眠模式, 它的进入方式也决定了从睡眠模式唤醒的方式,分别是WFI(等待中断)和WFE(等待事件);睡眠模式的各种特性见下表。
!(data/attachment/forum/202506/30/220735t2jlt2cjmxtqx8ll.png "image.png")
在APM32F402中我们可以通过PMU_EnterSleepMode函数来进入睡眠模式,其参数PMU_SLEEPENTRY_WFI和PMU_SLEEPENTRY_WFE决定我们怎么进入和唤醒睡眠模式,分别代表中断和事件。
### STOP模式
STOP模式又叫停止模式,在停止模式中,在睡眠模式的基础上进一步关闭了其它所有的时钟,因此其所有的外设都停止了工作,但由于其1.2V区域的部分电源没有关闭,还保留了内核的寄存器、内存的信息,所以从停止模式唤醒,并重新开启时钟后,还可以从上次停止处继续执行代码。停止模式可以由任意一个外部中断(EINT)唤醒,在停止模式中可以选择电压调节器为正常模式或低功耗模式;停止模式的各种特性见下表。
!(data/attachment/forum/202506/30/221251nf7kxbss36uxusft.png "image.png")
!(data/attachment/forum/202506/30/221312tinnndnt15tnam3i.png "image.png")
在APM32F402中我们可以通过PMU_EnterSTOPMode函数来进入停止模式,其参数1决定调压器处于正常模式(PMU_REGULATOR_ON)还是低功耗模式(PMU_REGULATOR_LOWPOWER),其参数2决定是中断进入停止模式还是事件进入停止模式,分别是PMU_STOP_ENTRY_WFI&PMU_STOP_ENTRY_WFE。
### STANDBY模式
STANDBY模式又叫待机模式,待机模式除了关闭所有的时钟,还把1.2V区域的电源也完全关闭了,也就是说,从待机模式唤醒后,没有之前代码的运行记录,只能对芯片复位,重新检测boot条件,从头开始执行程序。它有四种唤醒方式,分别是WKUP(PA0)引脚的上升沿,RTC闹钟事件,NRST引脚的复位和IWDG(独立看门狗)复位;待机模式的各种特性见下表。
!(data/attachment/forum/202506/30/221810t5w3xvuc3ajx3uic.png "image.png")
## RTC实现定时唤醒停止模式
上面的停止模式中描述有说,停止模式可以通过任意中断唤醒,当然前提是中断进入的停止模式哈,在EINT章节中我们可以看到RTC Alarm 事件映射到EINT 17线,那么我们也可以通过RTC闹钟来实现定时唤醒停止模式。
### RTC闹钟配置
在RTC闹钟配置中,我们使用内部的LSI配置为RTC时钟源,配置5s的闹钟,并且把RTC Alarm和EINT 17想关联起来
```
void RTC_Config_Init(void)
{
EINT_Config_T EINT_Configure;
/* 使能BKP和PWR时钟 */
RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_PMU | RCM_APB1_PERIPH_BAKR);
/* 允许访问备份域 */
PMU_EnableBackupAccess();
/* 备份域复位 */
BAKPR_Reset();
/* 使能LSI */
RCM_EnableLSI();
/* 等待直到LSI就绪 */
while(RCM_ReadStatusFlag(RCM_FLAG_LSIRDY) == RESET);
/* 选择LSI作为RTC时钟源 */
RCM_ConfigRTCCLK(RCM_RTCCLK_LSI);
/* 使能RTC时钟 */
RCM_EnableRTCCLK();
/* 等待RTC寄存器同步 */
RTC_WaitForSynchro();
/* 等待RTC寄存器上的最后一次写操作完成 */
RTC_WaitForLastTask();
/* 使能RTC闹钟中断 */
RTC_EnableInterrupt(RTC_INT_ALR);
/* 等待RTC寄存器上的最后一次写操作完成 */
RTC_WaitForLastTask();
/* 设置RTC分频值: 设置RTC周期为1s */
RTC_ConfigPrescaler(40000);
/* 等待RTC寄存器上的最后一次写操作完成 */
RTC_WaitForLastTask();
/* 设置RTC计数器值为0 */
RTC_ConfigCounter(0U);
/* 等待RTC寄存器上的最后一次写操作完成 */
RTC_WaitForLastTask();
/* 设置RTC闹钟值为5s */
RTC_ConfigAlarm(ALARM_TIME_INTERVAL);
/* 等待RTC寄存器上的最后一次写操作完成 */
RTC_WaitForLastTask();
/* EXTI配置 */
EINT_Reset();
EINT_Configure.line = EINT_LINE_17;
EINT_Configure.lineCmd = ENABLE;
EINT_Configure.mode = EINT_MODE_INTERRUPT;
EINT_Configure.trigger = EINT_TRIGGER_RISING;
EINT_Config(&EINT_Configure);
/* 标志清除 */
RTC_ClearStatusFlag(RTC_FLAG_ALR);
EINT_ClearIntFlag(EINT_LINE_17);
/* NVIC配置 */
NVIC_EnableIRQRequest(RTC_Alarm_IRQn, 1, 1);
}
```
### RTC闹钟中断配置
在RTC闹钟中断服务函数,我们检测到RTC闹钟中断,先清除闹钟标志和EINT 17的标志,然后我们在重新设置RTC的计数器的值和闹钟值,实现再配置5sRTC闹钟
```
void RTC_Alarm_IRQHandler(void)
{
if(RTC_ReadIntFlag(RTC_INT_ALR) == SET)
{
/* 清除RTC闹钟和EXTI_Line17中断标志 */
RTC_ClearIntFlag(RTC_INT_ALR);
EINT_ClearIntFlag(EINT_LINE_17);
/* 等待RTC寄存器同步 */
RTC_WaitForSynchro();
/* 等待RTC寄存器上的最后一次写操作完成 */
RTC_WaitForLastTask();
/* 设置RTC计数器值为0 */
RTC_ConfigCounter(0U);
/* 等待RTC寄存器上的最后一次写操作完成 */
RTC_WaitForLastTask();
/* 设置RTC闹钟值为5s */
RTC_ConfigAlarm(ALARM_TIME_INTERVAL);
/* 等待RTC寄存器上的最后一次写操作完成 */
RTC_WaitForLastTask();
}
}
```
### 进入STOP模式前IO的配置
进入停止模式前把所有未使用的IO配置模拟输入,这样子功耗可以到达最低。
```
void GPIO_ALL_Init(void)
{
GPIO_Config_T GPIO_Configure;
/* 使能所有GPIO时钟 */
RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_GPIOA | RCM_APB2_PERIPH_GPIOB | RCM_APB2_PERIPH_GPIOC | RCM_APB2_PERIPH_GPIOD);
/* IO配置模拟输入,为了达到STOP模式功耗最低,保留PA0中断唤醒 */
GPIO_Configure.mode = GPIO_MODE_ANALOG;
GPIO_Configure.pin = GPIO_PIN_ALL&(~GPIO_PIN_0);
GPIO_Config(GPIOA, &GPIO_Configure);
GPIO_Configure.pin = GPIO_PIN_ALL;
GPIO_Config(GPIOB, &GPIO_Configure);
GPIO_Config(GPIOC, &GPIO_Configure);
GPIO_Config(GPIOD, &GPIO_Configure);
/* 失能所有GPIO时钟 */
RCM_DisableAPB2PeriphClock(RCM_APB2_PERIPH_GPIOA | RCM_APB2_PERIPH_GPIOB | RCM_APB2_PERIPH_GPIOC | RCM_APB2_PERIPH_GPIOD);
}
```
### 进入停止模式
```
void System_Enter_StopMode(void)
{
/* 使能PWR时钟 */
RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_PMU);
/* 清除唤醒标志 */
PMU_ClearStatusFlag(PMU_FLAG_WUE);
/* 进入Stop模式 */
PMU_EnterSTOPMode(PMU_REGULATOR_LOWPOWER, PMU_STOP_ENTRY_WFI);
}
```
## RTC闹钟定时唤醒STOP模式验证
在主函数里面实现延时5s后进入STOP模式,然后RTC闹钟5s之后就会再唤醒MCU,然后在重新配置主频和其他外设,最后进入while循环。
```
//RTC闹钟值的宏
#define ALARM_TIME_INTERVAL(5U)
void System_Enter_StopMode(void);
void GPIO_ALL_Init(void);
void RTC_Config_Init(void);
int main(void)
{
NVIC_ConfigPriorityGroup(NVIC_PRIORITY_GROUP_2);
BSP_HSI64_SysclkConfig();
BSP_Systick_Init(64);
BSP_LED_Init(GPIOB, GPIO_PIN_8);
BSP_LED_Init(GPIOB, GPIO_PIN_9);
BSP_KEY2_Init(BUTTON_MODE_EXTI);
BSP_USART1_Init(115200);
printf("SYSCLKFreq = %d\r\n", RCM_ReadSYSCLKFreq());
//延时5s再进入Standby模式,方便复位的时候擦除和重新下载程序
delay_ms(1000);
delay_ms(1000);
delay_ms(1000);
delay_ms(1000);
delay_ms(1000);
printf("After a 5-second delay, enter Stop mode\r\n");
GPIO_ALL_Init();
RTC_Config_Init();
System_Enter_StopMode();
//唤醒需要重新配置时钟
BSP_HSI64_SysclkConfig();
BSP_Systick_Init(64);
BSP_LED_Init(GPIOB, GPIO_PIN_8);
BSP_LED_Init(GPIOB, GPIO_PIN_9);
BSP_KEY2_Init(BUTTON_MODE_EXTI);
BSP_USART1_Init(115200);
printf("Exti Stop mode\r\n");
while (1)
{
printf("RUNNING\r\n");
delay_ms(200);
}
}
```
串口工具查看是否实现RTC闹钟5s唤醒STOP模式,通过串口工具查看确实是已经实现RTC闹钟定时唤醒STOP模式。
!(data/attachment/forum/202506/30/225915r66htzusjidh69dh.png "image.png")
这个RTC的电力消耗算哪里的啊? 本帖最后由 heyanmei 于 2025-7-2 15:45 编辑
夜幕叙事曲 发表于 2025-7-1 10:22
这个RTC的电力消耗算哪里的啊?
RTC算备份域的,如果VDD断电,VBAT供电,备份域就是VBAT供电的,如果VDD没断电,备份域就是VDD供电 这个实验要做一做。低功耗必备的方案与策略。 heyanmei 发表于 2025-7-1 19:44
RTC算备份域的,如果VDD断电,VBAT供电,备份域就是VBAT供电的,如果VDD没断电,备份域就是VDD供电 ...
也是啊!
即使我vbat没有电了,理论上,我连接着VDD,这块备份区我也能操作。 这个帖子详细介绍了APM32F402如何通过RTC定时器实现从STOP模式唤醒,非常实用!
页:
[1]