Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
STM32F1 系列使用 PM 组件的教程
发布于 2020-05-15 21:13:43 浏览:3095
订阅该版
* 本帖最后由 sunwan 于 2020-5-15 21:30 编辑 * 首先保证BSP能正常运行 本教程使用 stm32f103-atk-nano 的BSP。 下载修改的 PM 组件 由于针对 STM32F 系列的 PM 组件还没有 PR,先用 Git 下载最新的 PM 组件: [pm-ports-stm32-new 分支](https://gitee.com/sunwancn/rt-thread/tree/pm-ports-stm32-new/) 针对 SMT32F1 系列,由于改变了秒时基,需要修改后的 `stm32f1xx_hal_rtc.c` 才能正确的读写时间和日期,此分支里的这个文件是修改后的。 启用 RTC 外设 由于 STM32F1 系列只能使用 RTC 的 ALARM 来唤醒,使用 STM32CubeMX 配置 RTC,启用 RTC 外设,并根据 BSP 选择 RTC 的时钟源 LSE 或 LSI,其余的 RTC 选项不用配置。 ![开启RTC.jpg](/uploads/202005/15/205217g543tc4031cgc1b4.jpg) 然后生成配置代码,重新拷贝 `board/CubeMX_Config/Src/main.c` 文件中的 `SystemClock_Config()` 函数到 board.c 中。 配置选项 当我们使用 PM 组件时需要在 `rtconfig.h` 中定义如下宏定义(STM32F1 只使用以下一个宏定义): [table=50%] [tr][td] 宏定义[/td][td] 描述[/td][/tr] [tr][td] RT_USING_PM[/td][td] 开启 PM 组件[/td][/tr] [/table] 上面配置选项可以直接在 `rtconfig.h` 文件中添加使用,也可以通过组件包管理工具 ENV 配置选项加入,ENV 工具中具体配置路径如下: ```RT-Thread Components ---> Device Drivers ---> (*) Using Power Management device drivers ----``` 另外,**要适当地增加 `IDLE_THREAD_STACK_SIZE` 的值**,如增大到 1024。 配置完成可以通过 scons 命令重新生成功能,完成 PM 组件的添加。 使用方式 **头文件定义** STM32F1 系列使用 PM 组件,需要包含如下头文件: ```#include
``` API 介绍请参看文档中心的[电源管理](https://www.rt-thread.org/document/site/programming-manual/pm/pm/#api)一节。 应用示例 此程序主要实现开机后恢复日期,经过10秒后进入 STOP 模式,再经过10秒进入 SLEEP 模式,如此循环往复,同时经过一个循环后,切换 MCU 的运行频率,验证运行的稳定性。 ```#include
#include
#include
#include
/* 这里LED指示灯设置成你自己的 */ #define LED0_PIN GET_PIN(C, 0) static RTC_HandleTypeDef hrtc; static RTC_DateTypeDef curdate = {0}; static RTC_TimeTypeDef curtime = {0}; static RTC_TimeTypeDef alarmtime = {0}; static RTC_AlarmTypeDef alarm = {0}; static rt_uint8_t sleep_mode = PM_SLEEP_MODE_DEEP; /* STOP 模式 */ //static rt_uint8_t sleep_mode = PM_SLEEP_MODE_STANDBY; /* 休眠模式 */ static rt_uint32_t last_seconds = 0; static rt_uint8_t run_mode = PM_RUN_MODE_NORMAL_SPEED; static rt_uint32_t get_interval(void); static void RTC_TimeShow(void); static rt_uint8_t mode_loop(void); int main(void) { rt_uint32_t date = 0; hrtc.Instance = RTC; /* RTC_BKP_DR1保存年月,RTC_BKP_DR2保存日星期,RTC_BKP_DR3已写入RTC时间标志 0xa5a5 */ date = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1); hrtc.DateToUpdate.Year = (rt_uint8_t)(date >> 8); hrtc.DateToUpdate.Month = (rt_uint8_t)date; date = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR2); hrtc.DateToUpdate.Date = (rt_uint8_t)(date >> 8); hrtc.DateToUpdate.WeekDay = (rt_uint8_t)date; /* 更新时间及日期 */ HAL_RTC_GetTime(&hrtc, &curtime, RTC_FORMAT_BIN); if (__HAL_PWR_GET_FLAG(PWR_FLAG_SB) != RESET || HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR3) == 0xa5a5) { /* 休眠复位或已写入RTC时间 */ RTC_TimeShow(); } else { /* 2020/01/01 01:00:00 */ curdate.Year = 20; curdate.Month = 1; curdate.Date = 1; curtime.Hours = 1; curtime.Minutes = 0; curtime.Seconds = 0; HAL_RTC_SetTime(&hrtc, &curtime, RTC_FORMAT_BCD); HAL_RTC_SetDate(&hrtc, &curdate, RTC_FORMAT_BCD); HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR3, 0xa5a5); } /* 日期写入备份寄存器 */ date = ((rt_uint32_t)hrtc.DateToUpdate.Year << 8) | (rt_uint32_t)hrtc.DateToUpdate.Month; HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, date); date = ((rt_uint32_t)hrtc.DateToUpdate.Date << 8) | (rt_uint32_t)hrtc.DateToUpdate.WeekDay; HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR2, date); /* set LED0 pin mode to output */ rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT); rt_pin_write(LED0_PIN, PIN_LOW); rt_thread_mdelay(10000); #ifdef RT_USING_PM /* 申请低功耗模式 */ rt_pm_request(sleep_mode); #endif RTC_TimeShow(); last_seconds = LL_RTC_TIME_Get(hrtc.Instance); if (sleep_mode == PM_SLEEP_MODE_STANDBY) { HAL_RTC_GetTime(&hrtc, &curtime, RTC_FORMAT_BIN); /* 设置休眠,闹钟 20 秒后唤醒,简化版闹钟,只支持 1分钟内有效 */ alarmtime.Hours = curtime.Hours; alarmtime.Minutes = curtime.Minutes; alarmtime.Seconds = curtime.Seconds + 20; if (alarmtime.Seconds >= 60) { alarmtime.Seconds -= 60; alarmtime.Minutes ++; } alarm.Alarm = RTC_ALARM_A; alarm.AlarmTime = alarmtime; /* 开启闹钟 */ HAL_RTC_SetAlarm_IT(&hrtc, &alarm, RTC_FORMAT_BIN); } while (1) { rt_pin_write(LED0_PIN, PIN_HIGH); /* 开始进入低功耗模式 */ rt_thread_mdelay(10000); /* 退出低功耗模式 */ rt_kprintf("Sleep %d ms
", get_interval()); RTC_TimeShow(); #ifdef RT_USING_PM rt_pm_request(PM_SLEEP_MODE_LIGHT); #endif rt_pin_write(LED0_PIN, PIN_LOW); rt_thread_mdelay(10000); rt_kprintf("Wakeup %d ms
", get_interval()); RTC_TimeShow(); /* 运行模式切换 */ rt_pm_run_enter(mode_loop()); #ifdef RT_USING_PM rt_pm_release(PM_SLEEP_MODE_LIGHT); #endif } return RT_EOK; } rt_uint32_t get_interval(void) { rt_uint32_t period, seconds; seconds = LL_RTC_TIME_Get(hrtc.Instance); period = seconds - last_seconds; last_seconds = seconds; return period * 1000 / (RTC->PRLL + 1U); } /* Display the current time */ static void RTC_TimeShow(void) { /* Get the RTC current Time */ HAL_RTC_GetTime(&hrtc, &curtime, RTC_FORMAT_BIN); /* Get the RTC current Date */ HAL_RTC_GetDate(&hrtc, &curdate, RTC_FORMAT_BCD); /* Display curtime Format yy/MM/dd hh:mm:ss */ rt_kprintf("%02d/%02d/%02d %02d:%02d:%02d
", curdate.Year, curdate.Month, curdate.Date, curtime.Hours, curtime.Minutes, curtime.Seconds); } rt_uint8_t mode_loop(void) { rt_uint8_t mode = 1; run_mode++; switch (run_mode) { case 0: case 1: case 2: case 3: mode = run_mode; break; case 4: mode = 2; break; case 5: mode = 1; break; case 6: mode = run_mode = 0; break; } return mode; }``` 自定义提高篇 **运行频率** 新的 PM 组件针对 STM32 提供4组运行频率,分别为 高速、普通、中低速、低速,STM32F1 系列默认的四组频率为: [table=50%] [tr][td] 运行模式[/td][td] 频率[/td][/tr] [tr][td] 高速[/td][td] 器件的最高频率[/td][/tr] [tr][td] 普通[/td][td] SystemClock_Config()配置的频率[/td][/tr] [tr][td] 中低速[/td][td] 器件的最高频率/3[/td][/tr] [tr][td] 低速[/td][td] 2MHz[/td][/tr] [/table] 要自定义运行频率,在 board.c 添加以下,如对于 STM32F103 : ```/* * 根据自己的BSP运行频率设置 */ rt_uint16_t pm_run_freq[PM_RUN_MODE_MAX][2] = { /* 真实的频率是 1/除数 MHz, 除数只能是 1 或 1000 */ /* {HCLK 频率, 除数} */ {72, 1}, /* 高速,72/1 = 72 MHz */ {72, 1}, /* 普通,72/1 = 72 MHz */ {16, 1}, /* 中低速,16/1 = 16 MHz */ {500, 1000}, /* 低速,500/1000 = 0.5 MHz = 500KHz */ };``` **当低速的频率小于2MHz时,要注意以下2点:** * 串口波特率如果设置过高,将不能正常通信 * 在时钟频率很低时,要适当减小 `RT_TICK_PER_SECOND` 值,不然由于 `tick` 相对过短,某些线程将不能完成任务,从而不能进入低功耗模式 **自定义初始化 RTC** 新的PM组件重新设置了STM32F1系列的RTC预分频器,默认的秒时基从 1S 改变到了 1ms,这将会稍微增大 RTC 的功耗,如果不需要高精度的睡眠时间,可以适当增大预分频值,在 board.c 添加自定义的 RTC 初始化,如: ```int PM_TimerInit(void) { RTC_HandleTypeDef hrtc = {0}; hrtc.Instance = RTC; /* * 在32768Hz的RTC时钟下,提供 5ms 的时基 */ hrtc.Init.AsynchPrediv = 159; hrtc.Init.OutPut = RTC_OUTPUTSOURCE_NONE; if (HAL_RTC_Init(&hrtc) != HAL_OK) { return -1; } NVIC_ClearPendingIRQ(RTC_Alarm_IRQn); NVIC_SetPriority(RTC_Alarm_IRQn, 0); NVIC_EnableIRQ(RTC_Alarm_IRQn); return 0; }``` **自定义运行级别时钟树配置函数** 新的 PM 组件在给定运行频率时,已经尽量自动最优化配置时钟树,但有时外设时钟还是没有达到自己想要的频率,这时可以自己配置时钟树,在 board.c 添加以下单个或所有函数,代码可参考 `SystemClock_Config()` 函数: ```void pm_system_clock_high(void) { /* 添加代码,配置高频运行时的时钟树 */ }``` ```void pm_system_clock_normal(void) { /* 添加代码,配置普通速度运行时的时钟树 */ }``` ```void pm_system_clock_medium(void) { /* 添加代码,配置中低频运行时的时钟树 */ }``` ```void pm_system_clock_low(void) { /* 添加代码,配置低频运行时的时钟树 */ }``` ![STM32F1使用PM组件的教程.md](/uploads/202005/15/212940eolzg2heashhpe62.attach)
查看更多
16
个回答
默认排序
按发布时间排序
sunwan
2020-05-15
这家伙很懒,什么也没写!
示例代码中的 get_interval() 函数拷贝错了,应该是下面的: ```c rt_uint32_t get_interval(void) { rt_uint32_t period, seconds; seconds = LL_RTC_TIME_Get(hrtc.Instance); period = seconds - last_seconds; last_seconds = seconds; return period * 1000 / (HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_RTC) / (RTC->PRLL + 1U)); } ```
jerry2cool
2020-05-21
这家伙很懒,什么也没写!
这么好的分享,一定要顶一下,感谢。。。。
403463275
2020-05-28
啥都不懂
真的好强啊
jerry4cool
2020-05-29
这家伙很懒,什么也没写!
大佬,请教一下,我按照你的流程做的,编译的时候提示函数:`LL_RTC_ALARM_SetCounter(RTC, Last_timecounter + reload);`未定义,如下图所示,这个函数时在哪个文件里呀?多谢了。。。。
sunwan
2020-05-29
这家伙很懒,什么也没写!
>大佬,请教一下,我按照你的流程做的,编译的时候提示函数:LL_RTC_ALARM_SetCounter(RTC, Last_timecounte ... --- 你是使用的studio吗?studio的话还需要拷贝覆盖 stm32f1xx_hal_rtc.c 和 stm32f1xx_ll_rtc.c,并且添加 USE_FULL_LL_DRIVER 预处理: [attach]15609[/attach]
jerry4cool
2020-05-30
这家伙很懒,什么也没写!
>你是使用的studio吗?studio的话还需要拷贝覆盖 stm32f1xx_hal_rtc.c 和 stm32f1xx_ll_rtc.c,并且添加 U ... --- 多谢大佬回复。 我用的不是stadio,用的env,就是按照您这个帖子一步一步来做的。
sunwan
2020-05-30
这家伙很懒,什么也没写!
>多谢大佬回复。 > >我用的不是stadio,用的env,就是按照您这个帖子一步一步来做的。 ... --- 那很大可能你GIT时弄错分支里,应该是 pm-ports-stm32-new ,不是master。
jerry4cool
2020-05-30
这家伙很懒,什么也没写!
>那很大可能你GIT时弄错分支里,应该是 pm-ports-stm32-new ,不是master。 --- 多谢。能从哪个文件里看出我是不是弄错分支了呢?看哪些函数?
jerry4cool
2020-06-01
这家伙很懒,什么也没写!
老大,我看了一下,我看了一下我下载的包,就是`“sunwancn-rt-thread-pm-ports-stm32-new”`,应该是没有下载错分支吧?是不是只是把包里 `“sunwancn-rt-thread-pm-ports-stm32-new\rt-thread\bsp\stm32\libraries\HAL_Drivers”` 这个文件夹下的东西都复制替换到我本地就行? 再次感谢。。。
sunwan
2020-06-01
这家伙很懒,什么也没写!
>老大,我看了一下,我看了一下我下载的包,就是“sunwancn-rt-thread-pm-ports-stm32-new”,应该是没有下 ... 对F1系列,须下载覆盖下面的文件: ```c bsp/stm32/libraries/HAL_Drivers/SConscript bsp/stm32/libraries/HAL_Drivers/drv_pm.c bsp/stm32/libraries/HAL_Drivers/drv_pmhw.h bsp/stm32/libraries/HAL_Drivers/drv_pmtim.c bsp/stm32/libraries/HAL_Drivers/drv_pmhw/drv_pmhw_f1.c bsp/stm32/libraries/STM32F1xx_HAL/SConscript bsp/stm32/libraries/STM32F1xx_HAL/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_rtc.c ```
撰写答案
登录
注册新账号
关注者
0
被浏览
3.1k
关于作者
sunwan
这家伙很懒,什么也没写!
提问
10
回答
91
被采纳
0
关注TA
发私信
相关问题
推荐文章
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组件
最新文章
1
【24嵌入式设计大赛】基于RT-Thread星火一号的智慧家居系统
2
RT-Thread EtherKit开源以太网硬件正式发布
3
如何在master上的BSP中添加配置yml文件
4
使用百度AI助手辅助编写一个rt-thread下的ONVIF设备发现功能的功能代码
5
RT-Thread 发布 EtherKit开源以太网硬件!
热门标签
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
WIZnet_W5500
UART
ota在线升级
PWM
cubemx
freemodbus
flash
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
Debug
编译报错
msh
SFUD
keil_MDK
rt_mq_消息队列_msg_queue
MicroPython
ulog
C++_cpp
本月问答贡献
踩姑娘的小蘑菇
7
个答案
3
次被采纳
a1012112796
16
个答案
2
次被采纳
张世争
9
个答案
2
次被采纳
rv666
6
个答案
2
次被采纳
用户名由3_15位
13
个答案
1
次被采纳
本月文章贡献
程序员阿伟
9
篇文章
2
次点赞
hhart
3
篇文章
4
次点赞
大龄码农
1
篇文章
5
次点赞
RTT_逍遥
1
篇文章
2
次点赞
ThinkCode
1
篇文章
1
次点赞
回到
顶部
发布
问题
分享
好友
手机
浏览
扫码手机浏览
投诉
建议
回到
底部