Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
PM电源管理
stm32F4
STM32F4 PM组件 StandBy 模式的使用
发布于 2022-06-10 17:17:55 浏览:1198
订阅该版
文章目录 [toc] ----------------------------------- 相关文章 1 [RT-Thread 电源管理组件官方文档](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/pm/pm) 2 [RT-Thread 进阶之低功耗 PM 组件应用笔记](https://blog.csdn.net/sinat_31039061/article/details/106356068) 3 [PM 组件适配 STM32F0/F1/F2/F3/F4/F7/G0/G4/L0/L1/L4/H7](https://club.rt-thread.org/ask/question/ceccda97ff72daa3.html) 4 [RT-Thread PM 组件 TICKLESS 原理分析](https://club.rt-thread.org/ask/article/a93494016df18951.html) 5 [RT-Thread 精通 PM 功耗调优 - Tickless篇](https://club.rt-thread.org/ask/article/1b39bec9d1fd3818.html) 6 [STM32F4 RTC-Alarm 的使用](https://club.rt-thread.org/ask/article/79d84bfce8d71c4b.html "STM32F4 RTC-Alarm 的使用") 7 [STM32F4 PM 组件 DeepSleep 模式的使用](https://club.rt-thread.org/ask/article/c79667d4d1dd0310.html) 8 [STM32F4 PM 组件 StandBy 模式的使用](https://club.rt-thread.org/ask/article/bae6eba8c15a2376.html "STM32F4 PM组件 StandBy 模式的使用") 9 [STM32 PM组件适配源码 gitee-sunwancn](https://gitee.com/sunwancn/rt-thread/tree/pm-ports-stm32-new) ------ 本次测试使用的开发板为正点原子的 STM32F429IGT6 阿波罗开发板,该款芯片支持睡眠、停止和待机三种低功耗模式。本文以 StandBy 模式也就是待机模式为例进行组件的使用分析,唤醒方式采用 RTC WakeUP 的方式进行唤醒。 # 1 STM32F4xx 待机模式介绍 STM32F4xx 待机模式的各种特性如下所示。在待机模式下除了关闭所有的时钟,还把 1.2V 区域的电源也完全关闭了,也就是说,从待机模式唤醒后,由于没有之前代码的运行记录,只能对芯片复位,重新检测 boot 条件,从头开始执行程序。 | 特性 | 说明 | | -------- | ------------------------------------------------------------ | | 进入方式 | 内核寄存器的 SLEEPDEEP =1,PWR_CR 寄存器中的 PDDS=1,PWR_CR 寄存器中的唤醒状态位 WUF=0,然后 调用 WFI或 WFE 指令即可进入待机模式 ; | | 唤醒方式 | WKUP 引脚上升沿、RTC 闹钟(闹钟 A 和闹钟 B)、RTC 唤醒事件、RTC入侵事件、RTC 时间戳事件、NRST 引脚外部复位 和 IWDG 复位 | | 待机时 | 内核停止,片上外设也停止;内核寄存器、内存的数据会丢失;除复位引脚、RTC_AF1 引脚及 WKUP 引脚,其它 I/O 口均工作在高阻态。 | | 唤醒延迟 | 芯片复位的时间 | | 唤醒后 | 相当于芯片复位,从头开始执行代码。 | # 2 PM 组件的移植 PM组件的移植主要参考了官方文档 [电源管理组件](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/pm/pm) 和文章 [RT-Thread进阶之低功耗PM组件应用笔记](https://blog.csdn.net/sinat_31039061/article/details/106356068) 及 [STM32 PM组件 DeepSleep 模式的使用](https://blog.csdn.net/qq_36310253/article/details/125221801?spm=1001.2014.3001.5501),在移植时主要是在 RT-Thread Settings 里面的设备驱动程序中使能 PM(电源管理)设备驱动程序,并设置空闲任务线程的堆栈空间不小于 1024Bytes,移植步骤在此不再叙述。 ![111.png](https://oss-club.rt-thread.org/uploads/20220610/17f1e4a7b528ad68388b4c54f578ce54.png.webp "111.png") # 3 源码的修改 为了测试方便将 RTC 的唤醒时钟选择配置为 1Hz,修改方式如下 ```c // drv_pmtim.c 函数 stm32_pmtim_start() HAL_RTCEx_SetWakeUpTimer_IT(&RtcHandle, reload, RTC_WAKEUPCLOCK_RTCCLK_DIV16); // 修改前 HAL_RTCEx_SetWakeUpTimer_IT(&RtcHandle, reload, RTC_WAKEUPCLOCK_CK_SPRE_16BITS); // 修改后 ``` # 4 测试用例 依照上文中参考文档对 PM 组件移植后,参考官方文档 [电源管理组件](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/pm/pm) 进行测试用例的编写,本次主要测试待机模式,因为待机模式唤醒之后相当于程序复位,所以测试起来比较简单,编写的测试用例如下。 ## 4.1 main.c main.c 的代码 如下,首先打印一下系统时钟信息,看一下和 CubeMX 配置的 180MHz 是否一致,以判断 PM 组件是否工作在 Normal 模式,然后初始化 LED 和 按键中断,设置 PM 组件的回调函数。 ```c // main.c #include
#define DBG_TAG "main" #define DBG_LVL DBG_LOG #include
#include "led.h" #include
int main(void) { time_t now; now = time(RT_NULL); // 获取当前的系统时间 rt_kprintf("now time: %.*s", 25, ctime(&now)); // 打印系统时间 led_init(); while(1) { rt_thread_mdelay(1000); } return RT_EOK; } ``` ## 4.2 led.c 该文件初始化了两个 LED,其中一个作为心跳灯,在正常工作模式下心跳灯闪烁,在待机模式下心跳灯不会闪烁。 ```c // led.c #include
#include "rtdevice.h" #include "board.h" #define DBG_TAG "led.c" #define DBG_LVL DBG_LOG #include
// LED 引脚定义 #define LED0_RTT_PIN (GET_PIN(B, 1)) #define LED1_RTT_PIN (GET_PIN(B, 0)) #define LED_ON (0) // 低电平亮 #define LED_OFF (1) // 高电平熄灭 void led0_on(void) { rt_pin_write(LED0_RTT_PIN, LED_ON); } void led0_off(void) { rt_pin_write(LED0_RTT_PIN, LED_OFF); } void led1_on(void) { rt_pin_write(LED1_RTT_PIN, LED_ON); } void led1_off(void) { rt_pin_write(LED1_RTT_PIN, LED_OFF); } void led_thread_entry(void *parameter) { while(1) { led0_on(); rt_thread_mdelay(500); led0_off(); rt_thread_mdelay(500); } } void start_led_thread(void) { rt_thread_t tid; tid = rt_thread_create("led_thread", led_thread_entry, RT_NULL, 1024, 10, 20); rt_thread_startup(tid); } /* 初始化 LED */ void led_init(void) { rt_pin_mode(LED0_RTT_PIN, PIN_MODE_OUTPUT); // 配置为输出模式 rt_pin_mode(LED1_RTT_PIN, PIN_MODE_OUTPUT); // 配置为输出模式 led0_off(); led1_off(); start_led_thread(); } ``` ## 4.4 wakeup_test.c 该文件写了进入 standby 模式的测试代码,并打印进入前后的 PM 组件的状态。 ```c // wakeup_test.c #include
#include
#include
#include
int rtc_wakeup_test(void) { time_t now; stm32_pmtim_start(5); // 5秒后唤醒 now = time(RT_NULL); // 获取当前的系统时间 rt_kprintf("%.*s", 25, ctime(&now)); // 打印系统时间 return 0; } MSH_CMD_EXPORT(rtc_wakeup_test, rtc_wakeup_test); int standby_mode_test(void) { rtc_wakeup_test(); // 设置 RTC wakeup 时间 rt_pm_request(PM_SLEEP_MODE_STANDBY); rt_pm_dump_status(); rt_pm_release(PM_SLEEP_MODE_NONE); rt_pm_dump_status(); return 0; } MSH_CMD_EXPORT(standby_mode_test, standby_mode_test); ``` # 5 测试结果 测试结果的日志如下所示,对于运行结果的解释在结果中以注释的方式呈现。从结果中可以看到成功完成了 standby 模式的进入和 RTC wakeup 唤醒的过程。可以看到进入 standby 模式的系统时间 和 唤醒后打印的系统时间之间相差了 6 秒,考虑到必须等到空闲线程才能真正进入到 standby 模式,加上系统初始化耗费的时间,相差 6 秒表明测试结果和预期吻合。 ```c \ | / - RT - Thread Operating System / | \ 4.0.4 build Jun 10 2022 16:37:41 2006 - 2021 Copyright by rt-thread team [I/drv.rtc] RTC hasn't been configured, please use
command to config. now time: Sat Jan 1 06:50:44 2000 // 打印进入 standby 模式时的系统时间 msh > msh >standby_mode_test // standby 模式测试 Sat Jan 1 06:50:48 2000 | Power Management Mode | Counter | Timer | +-----------------------+---------+-------+ | None Mode | 1 | 0 | | Idle Mode | 0 | 0 | | LightSleep Mode | 0 | 0 | | DeepSleep Mode | 0 | 0 | | Standby Mode | 1 | 0 | // 请求 standby 模式成功 | Shutdown Mode | 0 | 0 | +-----------------------+---------+-------+ pm current sleep mode: None Mode // 当前处于 None 模式 pm current run mode: Normal Speed // 当前运行在 Normal Speed | module | busy | start time | timeout | +--------+------+------------+-----------+ | 0001 | 0 | 0x00000000 | 0x00000000 | +--------+------+------------+-----------+ | Power Management Mode | Counter | Timer | +-----------------------+---------+-------+ | None Mode | 0 | 0 | // 释放 None 模式成功 | Idle Mode | 0 | 0 | | LightSleep Mode | 0 | 0 | | DeepSleep Mode | 0 | 0 | | Standby Mode | 1 | 0 | | Shutdown Mode | 0 | 0 | +-----------------------+---------+-------+ pm current sleep mode: Standby Mode // 当前处于 standby 模式,待运行到 IDLE 线程后,系统正式进入 standby 模式 pm current run mode: Normal Speed // 当前处运行在 Normal Speed | module | busy | start time | timeout | +--------+------+------------+-----------+ | 0001 | 0 | 0x00000000 | 0x00000000 | +--------+------+------------+-----------+ msh > \ | / - RT - Thread Operating System / | \ 4.0.4 build Jun 10 2022 16:37:41 2006 - 2021 Copyright by rt-thread team [I/drv.rtc] RTC hasn't been configured, please use
command to config. now time: Sat Jan 1 06:50:54 2000 // 唤醒后打印当前的系统时间 msh > ``` # 6 其他 # 6.1 RTC Alarm 唤醒 RTC Alarm 唤醒待机模式,可以参考文章 [STM32F4 RTC-Alarm 的使用](https://club.rt-thread.org/ask/article/79d84bfce8d71c4b.html),使用时在 CubeMX 开启 RTC 和 Alarm 及其中断的功能即可,测试用例如下,使用时先开启 RTC Alarm 的功能, 然后再进入 standby 模式便可进行测试,即在控制台分别依次输入命令 `alarm_sample` 和 `standby_mode_test` 。 ```c // wakeup_tese.c #include
#include
#include
#define DBG_TAG "wakeup_test.c" #define DBG_LVL DBG_LOG #include
static rt_alarm_t alarm = RT_NULL; /* 闹钟的用户回调函数 */ void user_alarm_callback(rt_alarm_t alarm, time_t timestamp) { struct tm p_tm; localtime_r(timestamp, &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; // 将闹钟的时间设置为当前时间的往后的 5 秒 localtime_r(&curr_time, &p_tm); // 将时间戳转换为本地时间,localtime_r 是线程安全的 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); // 打印当前时间,其中秒应该减去 5,因为前面加了 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); int rtc_wakeup_test(void) { time_t now; stm32_pmtim_start(5); // 5秒后唤醒 now = time(RT_NULL); // 获取当前的系统时间 rt_kprintf("%.*s", 25, ctime(&now)); // 打印系统时间 return 0; } MSH_CMD_EXPORT(rtc_wakeup_test, rtc_wakeup_test); ``` ## 6.2 wakeup 引脚唤醒(PA0) StandBy 模式还可以由 WAKEUP(PA0) 引脚唤醒,本小节讲述当使用 WAKEUP(PA0) 引脚做唤醒源时的使用方法。 ![111.png](https://oss-club.rt-thread.org/uploads/20220613/9302dc2814a2d4fa6e450550d0a14371.png.webp "111.png") 根据 STM32F4xx 的芯片手册(如上图所示),当开启了 RTC 相关中断后,必须先关闭 RTC 中断,再清中断标志位,然后重新设置 RTC 中断,再进入待机模式才可以正常唤醒,否则会有问题。所以需要添加对 RTC 中断的处理,本次将相关代码添加在文件 drv_pmtim.c 中,代码如下: ```c void clear_rtc_irq(void) { __HAL_RCC_AHB1_FORCE_RESET(); // 复位所有IO口 __HAL_RCC_PWR_CLK_ENABLE(); // 使能PWR时钟 //__HAL_RCC_BACKUPRESET_FORCE(); // 复位备份区域 //HAL_PWR_EnableBkUpAccess(); // 后备区域访问使能 // STM32F4,当开启了RTC相关中断后,必须先关闭RTC中断,再清中断标志位,然后重新设置RTC中断, // 再进入待机模式才可以正常唤醒,否则会有问题. __HAL_PWR_CLEAR_FLAG(PWR_FLAG_SB); __HAL_RTC_WRITEPROTECTION_DISABLE(&hrtc); // 关闭RTC写保护 // 关闭RTC相关中断,可能在RTC实验打开了 __HAL_RTC_WAKEUPTIMER_DISABLE_IT(&hrtc, RTC_IT_WUT); __HAL_RTC_TIMESTAMP_DISABLE_IT(&hrtc, RTC_IT_TS); __HAL_RTC_ALARM_DISABLE_IT(&hrtc, RTC_IT_ALRA | RTC_IT_ALRB); // 清除RTC相关中断标志位 __HAL_RTC_ALARM_CLEAR_FLAG(&hrtc, RTC_FLAG_ALRAF | RTC_FLAG_ALRBF); __HAL_RTC_TIMESTAMP_CLEAR_FLAG(&hrtc, RTC_FLAG_TSF); __HAL_RTC_WAKEUPTIMER_CLEAR_FLAG(&hrtc, RTC_FLAG_WUTF); __HAL_RTC_WAKEUPTIMER_ENABLE_IT(&hrtc, RTC_IT_WUT); __HAL_RTC_TIMESTAMP_ENABLE_IT(&hrtc, RTC_IT_TS); __HAL_RTC_ALARM_ENABLE_IT(&hrtc, RTC_IT_ALRA | RTC_IT_ALRB); //__HAL_RCC_BACKUPRESET_RELEASE(); // 备份区域复位结束 __HAL_RTC_WRITEPROTECTION_ENABLE(&hrtc); // 使能RTC写保护 } ``` 在文件 drv_pmhw_f4.c 中对进入待机模式的代码进行修改,修改后的代码如下: ```c case PM_SLEEP_MODE_STANDBY: case PM_SLEEP_MODE_SHUTDOWN: clear_rtc_irq(); // 清除 RTC 中断 __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU); // 清除 Wake_UP 标志 HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1); // 设置 WKUP 引脚用于唤醒 (PA0) HAL_PWR_EnterSTANDBYMode(); // 进入待机模式 break; ``` 待机模式的测试代码如下,测试时在命令行输入 `standby_mode_test`使开发板处于待机模式,然后按下 WAKEUP(PA0) 按键,唤醒开发板。 ```c int standby_mode_test(void) { rt_pm_request(PM_SLEEP_MODE_STANDBY); rt_pm_dump_status(); rt_pm_release(PM_SLEEP_MODE_NONE); rt_pm_dump_status(); return 0; } MSH_CMD_EXPORT(standby_mode_test, standby_mode_test); ```
1
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
crystal266
嵌入式
文章
14
回答
547
被采纳
161
关注TA
发私信
相关文章
1
STM32F103的低功耗
2
最近用rtthread系统下AD采样并低功耗,中断响应不及时
3
rt-thread低功耗休眠应用问题请教
4
关于RTT对低功耗的支持
5
RT-Thread怎么休眠实现低功耗。
6
RT-Thread V3.0支持的低功耗,OS会自行进入吗?
7
关于RTThread3.0低功耗休眠模式
8
RTT3.0的bsp包中哪些MCU自带低功耗定时器?
9
关于低功耗上次说针对L4出个BSP的,怎么迟迟不见呀
10
低功耗问题。
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
UART
WIZnet_W5500
ota在线升级
PWM
cubemx
flash
freemodbus
BSP
packages_软件包
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
编译报错
Debug
rt_mq_消息队列_msg_queue
SFUD
keil_MDK
msh
ulog
MicroPython
C++_cpp
本月问答贡献
出出啊
1517
个答案
342
次被采纳
小小李sunny
1444
个答案
290
次被采纳
张世争
812
个答案
177
次被采纳
crystal266
547
个答案
161
次被采纳
whj467467222
1222
个答案
148
次被采纳
本月文章贡献
出出啊
1
篇文章
2
次点赞
小小李sunny
1
篇文章
1
次点赞
张世争
1
篇文章
2
次点赞
crystal266
2
篇文章
2
次点赞
whj467467222
2
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部