STM32 PM组件 Standby 模式的使用(RT-Thread操作系统)


相关文章
1 STM32F4 RTC-Alarm 的使用
2 STM32F4 PM组件 DeepSleep 模式的使用
3 STM32F4 PM组件 StandBy 模式的使用


  本次测试使用的开发板为正点原子的 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组件的移植主要参考了官方文档 电源管理组件 和文章 RT-Thread进阶之低功耗PM组件应用笔记STM32 PM组件 DeepSleep 模式的使用,在移植时主要是在 RT-Thread Settings 里面的设备驱动程序中使能 PM(电源管理)设备驱动程序,并设置空闲任务线程的堆栈空间不小于 1024Bytes,移植步骤在此不再叙述。
在这里插入图片描述

3 源码的修改

  为了测试方便将 RTC 的唤醒时钟选择配置为 1Hz,修改方式如下

// 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 组件移植后,参考官方文档 电源管理组件 进行测试用例的编写,本次主要测试待机模式,因为待机模式唤醒之后相当于程序复位,所以测试起来比较简单,编写的测试用例如下。

4.1 main.c

  main.c 的代码 如下,首先打印一下系统时钟信息,看一下和 CubeMX 配置的 180MHz 是否一致,以判断 PM 组件是否工作在 Normal 模式,然后初始化 LED 和 按键中断,设置 PM 组件的回调函数。

// main.c

#include <rtthread.h>

#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

#include "led.h"
#include <time.h>

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,其中一个作为心跳灯,在正常工作模式下心跳灯闪烁,在待机模式下心跳灯不会闪烁。

// led.c

#include <rtthread.h>
#include "rtdevice.h"
#include "board.h"

#define DBG_TAG "led.c"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

// 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 组件的状态。

// wakeup_test.c

#include <board.h>
#include <rtthread.h>
#include <rtdevice.h>
#include <time.h>

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 秒表明测试结果和预期吻合。

  \ | /
- 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 <date> 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 <date> command to config.
now time: Sat Jan  1 06:50:54 2000             // 唤醒后打印当前的系统时间
msh >

6 其他

6.1 RTC Alarm 唤醒

  RTC Alarm 唤醒待机模式,可以参考文章 STM32F4 RTC-Alarm 的使用,使用时在 CubeMX 开启 RTC 和 Alarm 及其中断的功能即可,测试用例如下,使用时先开启 RTC Alarm 的功能, 然后再进入 standby 模式便可进行测试,即在控制台分别依次输入命令 alarm_samplestandby_mode_test

// wakeup_tese.c

#include <board.h>
#include <rtthread.h>
#include <rtdevice.h>

#define DBG_TAG "wakeup_test.c"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

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) 引脚做唤醒源时的使用方法。

在这里插入图片描述

  根据 STM32F4xx 的芯片手册(如上图所示),当开启了 RTC 相关中断后,必须先关闭 RTC 中断,再清中断标志位,然后重新设置 RTC 中断,再进入待机模式才可以正常唤醒,否则会有问题。所以需要添加对 RTC 中断的处理,本次将相关代码添加在文件 drv_pmtim.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 中对进入待机模式的代码进行修改,修改后的代码如下:

    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) 按键,唤醒开发板。

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);
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
STM32是意法半导体推出的一款微控制器产品系列,主要应用于嵌入式系统中。其中的Standby和IWDG是STM32中的两个重要概念。 StandbySTM32提供的一种低功耗模式。当系统不需要运行时,可以将STM32控制器设置为Standby模式,以达到节省能源的目的。在Standby模式下,控制器的大部分电路都会被关闭,只保留少部分关键电路供其维持功能,从而大幅减少了功耗。这种模式不仅适用于电池供电的系统,也适用于需要长时间待机的应用场景。 IWDG是STM32中集成的一种独立看门狗(Independent Watchdog)模块。看门狗是一种用于监控系统正常运行的机制,当系统出现死锁或死循环等异常情况时,看门狗会自动重启系统,以恢复系统的正常运行。IWDG模块独立于CPU核心,由内部时钟驱动,可以在系统发生故障时独立工作。 在STM32中,可以使用Standby模式和IWDG模块相结合来实现失控保护机制。当系统运行正常时,可以通过喂狗操作来重置IWDG计数器,防止看门狗溢出而导致系统复位。而当系统出现异常时(例如死循环等情况),CPU停止工作,无法喂狗,IWDG计数器会溢出,触发看门狗重启,从而使系统得以恢复。 综上所述,STM32Standby和IWDG是两个相互配合的功能,可以实现系统的低功耗待机和失控保护。通过合理使用这两个功能,可以提高系统的可靠性和能效。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值