【笔记】STM32L4系列使用RT-Thread Studio电源管理组件(PM框架)实现低功耗

news2025/3/9 7:00:48

硬件平台: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;
}

 七.下载验证

测试成功

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2312011.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

类和对象:

1. 类的定义&#xff1a; 1. 类定义格式&#xff1a; 对于我们的类的话&#xff0c;我们是把类看成一个整体&#xff0c;我们的函数里面没有找到我们的成员变量&#xff0c;我们就在我们的类里面找。 我们看我们的第二点&#xff1a; 我们的类里面&#xff0c;我们通常会对…

【十三】Golang 通道

&#x1f4a2;欢迎来到张胤尘的开源技术站 &#x1f4a5;开源如江河&#xff0c;汇聚众志成。代码似星辰&#xff0c;照亮行征程。开源精神长&#xff0c;传承永不忘。携手共前行&#xff0c;未来更辉煌&#x1f4a5; 文章目录 通道通道声明初始化缓冲机制无缓冲通道代码示例 带…

软考中级_【软件设计师】知识点之【面向对象】

简介&#xff1a; 软件设计师考试中&#xff0c;面向对象模块为核心考点&#xff0c;涵盖类与对象、继承、封装、多态等基础概念&#xff0c;重点考查UML建模&#xff08;类图/时序图/用例图&#xff09;、设计模式&#xff08;如工厂、单例模式&#xff09;及SOLID设计原则。要…

分布式锁—7.Curator的分布式锁一

大纲 1.Curator的可重入锁的源码 2.Curator的非可重入锁的源码 3.Curator的可重入读写锁的源码 4.Curator的MultiLock源码 5.Curator的Semaphore源码 1.Curator的可重入锁的源码 (1)InterProcessMutex获取分布式锁 (2)InterProcessMutex的初始化 (3)InterProcessMutex.…

《UE5_C++多人TPS完整教程》学习笔记35 ——《P36 武器类(Weapon Class)》

本文为B站系列教学视频 《UE5_C多人TPS完整教程》 —— 《P36 武器类&#xff08;Weapon Class&#xff09;》 的学习笔记&#xff0c;该系列教学视频为计算机工程师、程序员、游戏开发者、作家&#xff08;Engineer, Programmer, Game Developer, Author&#xff09; Stephen …

[密码学实战]Java实现国密TLSv1.3单向认证

一、代码运行结果 1.1 运行环境 1.2 运行结果 1.3 项目架构 二、TLS 协议基础与国密背景 2.1 TLS 协议的核心作用 TLS(Transport Layer Security) 是保障网络通信安全的加密协议,位于 TCP/IP 协议栈的应用层和传输层之间,提供: • 数据机密性:通过对称加密算法(如 AE…

最小栈 _ _

一&#xff1a;题目 二&#xff1a;思路 解释&#xff1a;一个栈名为st&#xff0c;其用来正常的出入栈&#xff0c;一个栈名为minst&#xff0c;其的栈顶元素一定是最小的元素 入栈&#xff1a;第一个元素&#xff0c;两个栈一起入&#xff0c;后面再入栈&#xff0c;只有入栈…

HTTPS加密原理详解

目录 HTTPS是什么 加密是什么 HTTPS的工作流程 1.使用对称加密 2.引入非对称加密 3.引入证书机制 客户端验证证书真伪的过程 签名的加密流程 整体工作流程 总结 HTTPS是什么 HTTPS协议也是一个应用程协议&#xff0c;是在HTTP的基础上加入了一个加密层&#xff0c;由…

黑金风格人像静物户外旅拍Lr调色教程,手机滤镜PS+Lightroom预设下载!

调色教程 针对人像、静物以及户外旅拍照片&#xff0c;运用 Lightroom 软件进行风格化调色工作。旨在通过软件中的多种工具&#xff0c;如基本参数调整、HSL&#xff08;色相、饱和度、明亮度&#xff09;调整、曲线工具等改变照片原本的色彩、明度、对比度等属性&#xff0c;将…

安装pyqt6出现的问题

安装PyQt6报错&#xff1a; PermissionError: [WinError 32] 另一个程序正在使用此文件&#xff0c;进程无法访问。: C:\\Users\\xyj19\\AppData\\Local\\Temp\\tmp3xfmekh7 [end of output] note: This error originates from a subprocess, and is likely not a pr…

java调用c++

VScode 配置java 并且使用JNA调用c 动态库 安装 Java 开发环境 ​ 安装 JDK官网直接下载就好&#xff0c;推荐镜像下载 通过网盘分享的文件&#xff1a;jdk-8u144-windows-x64.exe​ 链接: https://pan.baidu.com/s/1Ov9bJkPNnOgcliBL-PSTFQ?pwdpg43 提取码: pg43 ​ 直接安…

gitlab+jenkins+harbor+k8s安装操作流程之Jenkins

准备环境 一台centos7系统 4C/8G/100G 如果是jenkins2.5以上版本需要centos8以上版本 JDK1.8编译安装(最新版本jdk需要18以上) MAVEN编译安装 GIT编译安装 JDK1.8步骤 tar -zxvf 解压 vim /etc/profile export JAVA_HOME/data/jdk1.8.0_111 export JRE_HOME$JAVA…

【机械视觉】C#+VisionPro联合编程———【三、加载CogToolBlock工具详解,以及实例】

【机械视觉】C#VisionPro联合编程———【三、加载CogToolBlock工具详解&#xff0c;以及实例】 在VisionPro中&#xff0c;CogToolBlock 是一种容器工具&#xff0c;可以将多个视觉工具&#xff08;如CogBlob、CogPMAlign等&#xff09;组合成一个可复用的流程。通过C#与Visi…

启动wsl里的Ubuntu24报错:当前计算机配置不支持 WSL2,HCS_E_HYPERV_NOT_INSTALLED

问题&#xff1a;启动wsl里的Ubuntu24报错 报错信息&#xff1a; 当前计算机配置不支持 WSL2。 请启用“虚拟机平台”可选组件&#xff0c;并确保在 BIOS 中启用虚拟化。 通过运行以下命令启用“虚拟机平台”: wsl.exe --install --no-distribution 有关信息&#xff0c;请访…

信息安全与网络安全的区别_信息安全与网络安全之差异探析

在当今数字化时代&#xff0c;信息安全与网络安全成为了人们关注的热点话题。尽管这两个概念经常被提及&#xff0c;但它们之间存在着明显的区别。本文旨在探讨信息安全与网络安全的定义、范畴及应对策略&#xff0c;以帮助读者更好地理解和应对相关挑战。 一、定义与范畴的差…

充电桩快速搭建springcloud(微服务)+前后端分离(vue),客户端实现微信小程序+ios+app使用uniapp(一处编写,处处编译)

充电桩管理系统是专为中小型充电桩运营商、企业和个人开发者设计的一套高效、灵活的管理平台。系统基于Spring Cloud微服务架构开发&#xff0c;采用模块化设计&#xff0c;支持单机部署与集群部署&#xff0c;能够根据业务需求动态扩展。系统前端使用uniapp框架&#xff0c;可…

设计AI芯片架构的入门 研究生入行数字芯片设计、验证的项目 opentitan

前言 这几年芯片设计行业在国内像坐过山车。时而高亢&#xff0c;时而低潮。最近又因为AI的热潮开始high起来。到底芯片行业的规律是如何&#xff1f; 我谈谈自己观点&#xff1a;芯片设计是“劳动密集型”行业。 “EDA和工具高度标准化和代工厂的工艺标准化之后&#xff0c;芯…

串口助手的C#编写以及有人串口服务器USR-DR301的使用

本文介绍C#编写串口程序的要点,串口服务器USR-DR301(RS232转TCP)的使用、以及调试过程中碰到的两个问题: 1). 调用串口报“连到系统上的设备没有发挥作用”. 2). “所有文本框都变成了透明”的异常处理 代码见:https://download.csdn.net/download/qq_34047402/9046713…

Android中AIDL和HIDL的区别

在Android中&#xff0c;AIDL&#xff08;Android Interface Definition Language&#xff09; 和 HIDL&#xff08;HAL Interface Definition Language&#xff09; 是两种用于定义跨进程通信接口的语言。AIDL 是 Android 系统最早支持的 IPC&#xff08;进程间通信&#xff0…

sqlserver删除表记录语句,及删除表时清零ID的SQL语句

sqlserver中&#xff0c;删除表中所有记录的语句如下 Delete from tableName 例&#xff0c;删除表logs的所有记录 sqlserver&#xff0c;删除表中所有数据&#xff0c;标识列ID归零&#xff0c;保留表结构的语句 truncate table tableName 例&#xff0c;删除表logs的所…