硬件平台:STM32L431RCT6
RT-Thread版本:4.1.0
目录
一.新建工程
二.配置工程
编辑 三.移植pm驱动
四.配置cubeMX
五.修改驱动文件,干掉报错
六.增加用户低功耗逻辑
1.设置唤醒方式
2.设置睡眠时以及唤醒后动作
编辑
3.增加测试命令
七.下载验证
一.新建工程
二.配置工程
打开pm
这时候编译会报错
提示空闲线程栈太小了,改到2048就不会提示报错了
三.移植pm驱动
此时PM框架虽然已经打开,但是还是一个空架子,没有驱动。所以要去RTT官方仓库里边拷贝一套L4系列PM驱动。驱动文件位置在rt-thread\bsp\stm32\libraries\HAL_Drivers。
需要以下三个文件
然后将drv_lptim.c 和drv_pm.c放在drivers目录下,drv_lptim.h放在drivers\include目录下,这时候编译会报错,先不理会。
四.配置cubeMX
lptim的HAL需要使用cubeMX配置生成
SYS和RCC全部保持默认就行,不然程序可能运行不了
时钟配置为80M
打开LPTIM1
打开串口1
然后点击生成,关闭cubeMX
五.修改驱动文件,干掉报错
这时候编译错误就会从40多个减少到20多个,大多数都是头文件包含的问题,在drv_pm.c文件添加头文件
#include "rt-thread\components\drivers\include\drivers\pm.h"
#include <rtthread.h>
#include <rthw.h>
#include <rtthread.h>
#include <rtdevice.h>
报错进一步减少
现在就是缺少一些PM所需函数,这些函数需要我们自己实现
在board.c文件中rt_hw_board_init函数下方添加如下代码
#include "rt-thread\components\drivers\include\drivers\pm.h"
void SystemClock_MSI_ON(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/* Initializes the CPU, AHB and APB busses clocks */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_MSI;
RCC_OscInitStruct.MSIState = RCC_MSI_ON;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
RT_ASSERT(0);
}
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_MSI;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
{
Error_Handler();
}
}
void SystemClock_MSI_OFF(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_MSI;
RCC_OscInitStruct.HSIState = RCC_MSI_OFF;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; /* No update on PLL */
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
}
void SystemClock_80M(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;
/**Initializes the CPU, AHB and APB busses clocks */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 1;
RCC_OscInitStruct.PLL.PLLN = 20;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7;
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/**Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
{
Error_Handler();
}
}
void SystemClock_24M(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;
/** Initializes the CPU, AHB and APB busses clocks */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 1;
RCC_OscInitStruct.PLL.PLLN = 12;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7;
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV4;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB busses clocks */
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
{
Error_Handler();
}
}
void SystemClock_2M(void)
{
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
/* MSI is enabled after System reset, update MSI to 2Mhz (RCC_MSIRANGE_5) */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_MSI;
RCC_OscInitStruct.MSIState = RCC_MSI_ON;
RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_5;
RCC_OscInitStruct.MSICalibrationValue = RCC_MSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
/* Initialization Error */
Error_Handler();
}
/* Select MSI as system clock source and configure the HCLK, PCLK1 and PCLK2
clocks dividers */
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_MSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
/* Initialization Error */
Error_Handler();
}
}
/**
* @brief Configures system clock after wake-up from STOP: enable HSI, PLL
* and select PLL as system clock source.
* @param None
* @retval None
*/
void SystemClock_ReConfig(uint8_t mode)
{
SystemClock_MSI_ON();
switch (mode)
{
case PM_RUN_MODE_HIGH_SPEED:
case PM_RUN_MODE_NORMAL_SPEED:
SystemClock_80M();
break;
case PM_RUN_MODE_MEDIUM_SPEED:
SystemClock_24M();
break;
case PM_RUN_MODE_LOW_SPEED:
SystemClock_2M();
break;
default:
break;
}
// SystemClock_MSI_OFF();
}
并在board.h里声明
void SystemClock_Config(void);
void SystemClock_MSI_ON(void);
void SystemClock_MSI_OFF(void);
void SystemClock_80M(void);
void SystemClock_24M(void);
void SystemClock_2M(void);
void SystemClock_ReConfig(uint8_t mode);
这时候编译就不会在出现问题,PM驱动已经完成
六.增加用户低功耗逻辑
之前步骤已经将驱动配置完了,现在需要增加一些逻辑,比如什么时候该进入睡眠,设置用什么方式唤醒,唤醒之后要做一些什么
1.设置唤醒方式
本篇文章使用PA0中断唤醒,在mian.c里添加代码
#include "board.h"
#include "stm32l431xx.h"
#include "rt-thread\components\drivers\include\drivers\pm.h"
#define PM_INT_PIN GET_PIN(A, 0) // 定义 PM 中断引脚为 PA0
void PM_int_callback(void *args)
{
rt_kprintf("PM Data Ready Interrupt Triggered!\n");
}
void PM_int_init(void)
{
// 设置 PA0 为输入模式
rt_kprintf("PM INT pin init\r\n");
rt_pin_mode(PM_INT_PIN, PIN_MODE_INPUT_PULLUP);
// 绑定中断回调函数
rt_pin_attach_irq(PM_INT_PIN, PIN_IRQ_MODE_RISING, PM_int_callback, RT_NULL);
// 使能中断
rt_pin_irq_enable(PM_INT_PIN, PIN_IRQ_ENABLE);
}
INIT_BOARD_EXPORT(PM_int_init);
下载到板子上,这时候触发PA0中断,终端打印现象,说明PA0中断可以正常触发,触发后将唤醒睡眠的单片机
2.设置睡眠时以及唤醒后动作
#define LED_1 GET_PIN(C, 1) // LED1引脚定义
void pm_notify(rt_uint8_t event, rt_uint8_t mode, void *data)
{
if (event == RT_PM_ENTER_SLEEP && mode == PM_SLEEP_MODE_DEEP) // 进入睡眠
{
rt_pin_write(LED_1, PIN_HIGH);
rt_kprintf("enter pm\n");
}
else if (event == RT_PM_EXIT_SLEEP && mode == PM_SLEEP_MODE_DEEP) // 退出休眠
{
rt_pm_dump_status(); // 打印 PM 组件的状态
rt_pm_run_enter(PM_RUN_MODE_HIGH_SPEED);
clock_information(); // 打印时钟频率
rt_pm_release(PM_SLEEP_MODE_DEEP); // 释放 DeepSleep 模式
rt_pm_request(PM_SLEEP_MODE_NONE); // 请求工作模式
rt_pin_write(LED_1, PIN_LOW);
}
}
int main(void)
{
clock_information();
rt_pm_notify_set(pm_notify, 0);
return RT_EOK;
}
这时候编译会报错
将pm.c里边的static void rt_pm_dump_status(void)的static删除就行
3.增加测试命令
在main.c里边随便找个地方放进去
int stop_mode_test(void)
{
rt_pm_request(PM_SLEEP_MODE_DEEP); // 请求 stop 模式
rt_pm_dump_status(); // 打印 PM 组件状态
rt_pm_release(PM_SLEEP_MODE_NONE); // 释放正常工作模式,释放后才能进入 stop 模式
rt_pm_dump_status(); // 打印 PM 组件状态
return 0;
}
MSH_CMD_EXPORT(stop_mode_test, stop_mode_test);
mian.c全部内容
/*
* Copyright (c) 2006-2025, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2025-03-08 RT-Thread first version
*/
#include <rtthread.h>
#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#include <rtdevice.h>
#include "board.h"
#include "stm32l431xx.h"
#include "rt-thread\components\drivers\include\drivers\pm.h"
#define PM_INT_PIN GET_PIN(A, 0) // 定义 PM 中断引脚为 PA0
void PM_int_callback(void *args)
{
rt_kprintf("PM Data Ready Interrupt Triggered!\n");
}
void PM_int_init(void)
{
// 设置 PA0 为输入模式
rt_kprintf("PM INT pin init\r\n");
rt_pin_mode(PM_INT_PIN, PIN_MODE_INPUT_PULLUP);
// 绑定中断回调函数
rt_pin_attach_irq(PM_INT_PIN, PIN_IRQ_MODE_RISING, PM_int_callback, RT_NULL);
// 使能中断
rt_pin_irq_enable(PM_INT_PIN, PIN_IRQ_ENABLE);
}
INIT_BOARD_EXPORT(PM_int_init);
#define LED_1 GET_PIN(C, 1) // LED1引脚定义
void pm_notify(rt_uint8_t event, rt_uint8_t mode, void *data)
{
if (event == RT_PM_ENTER_SLEEP && mode == PM_SLEEP_MODE_DEEP) // 进入睡眠
{
rt_pin_write(LED_1, PIN_HIGH);
rt_kprintf("enter pm\n");
}
else if (event == RT_PM_EXIT_SLEEP && mode == PM_SLEEP_MODE_DEEP) // 退出休眠
{
rt_pm_dump_status(); // 打印 PM 组件的状态
rt_pm_run_enter(PM_RUN_MODE_HIGH_SPEED);
clock_information(); // 打印时钟频率
rt_pm_release(PM_SLEEP_MODE_DEEP); // 释放 DeepSleep 模式
rt_pm_request(PM_SLEEP_MODE_NONE); // 请求工作模式
rt_pin_write(LED_1, PIN_LOW);
}
}
int stop_mode_test(void)
{
rt_pm_request(PM_SLEEP_MODE_DEEP); // 请求 stop 模式
rt_pm_dump_status(); // 打印 PM 组件状态
rt_pm_release(PM_SLEEP_MODE_NONE); // 释放正常工作模式,释放后才能进入 stop 模式
rt_pm_dump_status(); // 打印 PM 组件状态
return 0;
}
MSH_CMD_EXPORT(stop_mode_test, stop_mode_test);
int main(void)
{
clock_information();
rt_pm_notify_set(pm_notify, 0);
return RT_EOK;
}
七.下载验证
测试成功