STM32-电源管理(实现低功耗)

电源管理

STM32 HAL库对电源管理提供了完善的函数和命令。

工作模式(高功耗->低功耗):运行、睡眠、停止、待机。

若备份域电源正常供电,备份域内的RTC都可以正常运行,备份域内的寄存器的数据会被保存,不受功耗模式影响。

上电复位(POR)和掉电复位(PDR)

当检测到VDD的电压低于阈值VPOR及VPDR时,无需外部电路辅助,STM32芯片会自动保持在复位状态,防止因电压不足强行工作而带来严重的后果。

在刚开始电压低于VPOR时(约1.92V),STM32保持在上电复位状态(POR,Power On Reset)。当VDD电压持续上升至大于VPOR时,芯片开始正常运行。

而在芯片开始正常运行的时候,当检测到VDD电压下降至低于VPDR阈值(约1.88V),会进入掉电复位状态(PDR,Power Down Reset)。

配置PVD监控功能

PVD可监控VDD的电压,当它低于阈值时可产生PVD中断以让系统进行紧急处理,这个阈值可以直接使用库函数PWR_PVDLevelConfig配置成某一个的阈值等级。

WFI和WFE命令

进入各种低功耗模式时都需要调用WFI或WFE命令,实质上都是内核指令,在库文件 core_cm3.h 或 cmsis_armcc.h 中把这些指令封装成了函数。

/* 等待中断。是一种暂停执行指令,暂停至任意中断产生后被唤醒 */
#define __WFI                             __wfi

/* 等待事件。是一种暂停执行指令,暂停至任意事件产生后被唤醒 */
#define __WFE                             __wfe

这两个指令,调用后都能进入低功耗模式,需要使用__WFI();和__WFE();来调用(因为__wfi和__wfe是编译器内置的函数,函数内部调用了相对应的汇编指令)。

具体可查《cortex-CM3/CM4权威指南》。

进入停止模式

直接调用WFI和WFE指令可以进入睡眠模式,而进入停止模式这还需要在调用指令前设置一些寄存器位,STM32 HAL库把这部分的操作封装到HAL_PWR_EnterSTOPMode()。

/**
  * @brief 进入停止模式
  * @note  在停止模式下所有I/O都会保持在停止前的状态.
  * @note  当使用中断或唤醒事件退出停止模式时,HSI RC振荡器被选择为系统时钟。
  * @note  当稳压器在低功率模式下工作时,从停止模式唤醒时会产生额外的启动延迟。
  *        通过在停止模式中保持内部稳压器打开,虽然启动时间减少,但消耗更高。
  * @param Regulator: 在停止模式下指定稳压器状态。
  *            @arg PWR_MAINREGULATOR_ON: 稳压器正常运行
  *            @arg PWR_LOWPOWERREGULATOR_ON: 稳压器低功耗运行
  * @param STOPEntry: 指定是否使用WFI或WFE指令进入停止模式。
  *            @arg PWR_STOPENTRY_WFI: 使用WFI指令进入停止模式
  *            @arg PWR_STOPENTRY_WFE: 使用WFE指令进入停止模式
  * @retval None
  */
void HAL_PWR_EnterSTOPMode(uint32_t Regulator, uint8_t STOPEntry)
{
    /* 检查参数 */
    assert_param(IS_PWR_REGULATOR(Regulator));
    assert_param(IS_PWR_STOP_ENTRY(STOPEntry));

    /* 清除PWR寄存器中的PDDS位以指定当CPU进入深度睡眠时进入停止模式 */
    CLEAR_BIT(PWR->CR,  PWR_CR_PDDS);

    /* 根据稳压参数值,通过在PWR寄存器中设置LPDS位来选择稳压模式 */
    MODIFY_REG(PWR->CR, PWR_CR_LPDS, Regulator);

    /* 设置内核系统控制寄存器的SLEEPDEEP位 */
    SET_BIT(SCB->SCR, ((uint32_t)SCB_SCR_SLEEPDEEP_Msk));

    /* 选择停止模式进入 */
    if (STOPEntry == PWR_STOPENTRY_WFI)
    {
        /* 请求等待中断 */
        __WFI();
    }
    else
    {
        /* 请求等待事件 */
        __SEV();
        PWR_OverloadWfe(); /* 本地重新定义WFE */
        PWR_OverloadWfe(); /* 本地重新定义WFE */
    }

    /* 以下的程序是当重新唤醒时才执行的,清除内核系统控制寄存器的SLEEPDEEP位 */
    CLEAR_BIT(SCB->SCR, ((uint32_t)SCB_SCR_SLEEPDEEP_Msk));
}

进入停止模式后,STM32的所有I/O都保持在停止前的状态,而当它被唤醒时,STM32使用HSI作为系统时钟(8MHz)运行,由于系统是在会影响很多外设的工作状态,所以一般我们在唤醒后会重新开始HSE,把系统时钟设置成原来的状态。

进入待机模式

STM32 HAL库把这部分的操作封装到HAL_PWR_EnterSTANDBYMode()。

/**
  * @brief 进入待机模式
  * @note  待机模式下,除以下情况外,所有I/O引脚均为高阻抗::
  *          - 复位引脚(仍然有效)
  *          - TAMPER pin if configured for tamper or calibration out.
  *          - WKUP pin (PA0) (如果使能WKUP唤醒功能).
  * @retval None
  */
void HAL_PWR_EnterSTANDBYMode(void)
{
    /* 选择待机模式 */
    SET_BIT(PWR->CR, PWR_CR_PDDS);

    /* 设置内核系统控制寄存器的SLEEPDEEP位*/
    SET_BIT(SCB->SCR, ((uint32_t)SCB_SCR_SLEEPDEEP_Msk));

    /* 存储操作完毕时才能进入待机模式,使用以下语句确保存储操作执行完毕 */
#if defined ( __CC_ARM)
    __force_stores();
#endif
    /* 请求等待中断 */
    __WFI();
}

待机模式也可以使用WFE指令进入的,如果有需要可以自行修改。

在进入待机模式后,除了被使能用于唤醒的I/O,其余I/O都进入高阻态,而从待机模式唤醒后,想防御复位STM32芯片,程序重新从头开始执行。

实验环节1:PWR_PVD监控

实验操作

使用外部可调电源,调节成5V输出,连接到开发板5V和GND排针给板子进行供电;

复位开发板,电压正常时LED为绿色;

向下调节可调电源的电压,大约降到4V时,LED为红色。(程序中控制PVD监控电压约为2.8V,当5V降到4V时,连接STM32的VDD电源会降于2.8V,产生PVD事件,在中断中控制亮红灯)。

注意:其他电源线都拔掉(包括下载器、USB线)。不能远高于5V而导致烧坏开发板。

PVD配置

void PVD_Config(void)
{
	PWR_PVDTypeDef sConfigPVD;
	
	/*使能 PWR 时钟 */
	__HAL_RCC_PWR_CLK_ENABLE();
	
	/* 配置 PVD 中断 */
	HAL_NVIC_SetPriority(PVD_IRQn, 0 ,0);
	HAL_NVIC_EnableIRQ(PVD_IRQn);  

	/* 配置PVD级别6 (PVD检测电压的阈值为2.8V,VDD电压低于2.8V时产生PVD中断,
	   具体数据可查询数据手册获知) 具体级别根据自己的实际应用要求配置*/
	sConfigPVD.PVDLevel = PWR_PVDLEVEL_6;
	sConfigPVD.Mode 	= PWR_PVD_MODE_IT_RISING_FALLING;
	HAL_PWR_ConfigPVD(&sConfigPVD);
	
	/* 使能PVD输出 */
	HAL_PWR_EnablePVD();
}

测试环节

void PVD_IRQHandler(void)
{
	HAL_PWR_PVD_IRQHandler();
}

void HAL_PWR_PVDCallback(void)
{
	LED红灯
}

void test(void)
{
	初始化
	LED绿灯

	// 配置PVD,当电压过低时,会进入中断服务函数,亮红灯
	PVD_Config();
	
	while(1)
	{}
}

实验环节2:PWR睡眠模式

实验操作

LED:绿灯正常运行,红灯睡眠状态,蓝灯刚被唤醒。

KEY:key1和key2配置成IO中断模式。

运行一段时间后自动进入睡眠时间,通过按键(key1或key2)唤醒。

睡眠状态下,DAP下载器无法给STM32下载程序,可唤醒后再下载或按复位键使芯片处于复位状态下下载后松开复位键。

测试环节

int main(void)
{
	初始化

    while (1)
    {
        LED绿灯
        HAL_Delay(2000);

        LED红灯
        HAL_SuspendTick();	//暂停滴答时钟,防止通过滴答时钟中断唤醒
        //进入睡眠模式
        HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);
		
        //等待中断唤醒  K1或K2按键中断
		
        LED蓝灯
        HAL_ResumeTick();	//被唤醒后,恢复滴答时钟
        HAL_Delay(2000);
		
        //继续执行while循环
    }
}

实验环节3:PWR待机模式

实验操作

LED:绿灯表示本次复位是上电或引脚复位,红灯待机状态,蓝灯刚被唤醒。

KEY:key2配置成输入模式。

长按KEY2按键会进入待机模式,待机模式下KEY1按键可唤醒,唤醒后系统会复位。可通过检测PWR_CSR:WUF标志确定复位来源。

待机模式下,DAP下载器无法给STM32下载程序,可唤醒后再下载。

注意:由于WKUP引脚(PA0)必须使用上升沿才能唤醒待机状态的系统,所以硬件设计PA0引脚连接到KEY1,且按下KEY1时会在PA0引脚产生上升沿,从而可实现唤醒的功能。

测试环节

/**
  * @brief  用于检测按键是否被长时间按下
  * @param  无
  * @retval 1 :按键被长时间按下  0 :按键没有被长时间按下
  */
static uint8_t KEY2_LongPress(void)
{
    uint8_t downCnt = 0;		//记录按下的次数
    uint8_t upCnt = 0;			//记录松开的次数
	
    while (1)					//死循环,由return结束
    {	
        HAL_Delay(20);			//延迟一段时间再检测

        if (HAL_GPIO_ReadPin(KEY2_GPIO_PORT, KEY2_PIN) == SET)	//检测到按下按键
        {
            downCnt++;			//记录按下次数
            upCnt = 0;			//清除按键释放记录

            if (downCnt >= 100)	//按下时间足够
            {
                return 1; 		//检测到按键被时间长按下
            }
        }
        else
        {
            upCnt++; 			//记录释放次数

            if (upCnt > 5)		//连续检测到释放超过5次
            {
                return 0;		//按下时间太短,不是按键长按操作
            }
        }
    }
}

int main(void)
{
	初始化

    /* 使能电源管理单元的时钟,必须要使能时钟才能进入待机模式 */
	__HAL_RCC_PWR_CLK_ENABLE();

    //检测复位来源
    if (__HAL_PWR_GET_FLAG(PWR_FLAG_WU) == SET)
    {	// 复位前为待机模式
        __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
        LED蓝灯
    }
    else
    {
		// 复位前为正常运行
        LED绿灯
    }

    while (1)
    {
        // K2 按键长按进入待机模式
        if (KEY2_LongPress())
        {
            LED红灯
            HAL_Delay(1000);

            /*清除WU状态位*/
            __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);

            /* 使能WKUP引脚的唤醒功能,使能PA0*/
            HAL_PWR_EnableWakeUpPin(0x00000100U);

            //暂停滴答时钟,防止通过滴答时钟中断唤醒
            HAL_SuspendTick();
			
            /* 进入待机模式 */
            HAL_PWR_EnterSTANDBYMode();
        }
    }
}

实验现象

开机正常运行绿灯。长按KEY2按键,显红灯,过1s后进入待机模式LED灭。按下KEY1按键退出待机模式自动复位,显蓝灯。按下复位键,重新运行绿灯。

实验环节4:PWR停止模式

实验操作

LED:绿灯正常运行,红灯停止状态,蓝灯刚被唤醒。

KEY:key1和key2配置成IO中断模式。

运行一段时间后自动进入停止时间,通过按键(key1或key2)唤醒。

待机模式下,DAP下载器无法给STM32下载程序,可唤醒后再下载。

注意:由于WKUP引脚(PA0)必须使用上升沿才能唤醒待机状态的系统,所以硬件设计PA0引脚连接到KEY1,且按下KEY1时会在PA0引脚产生上升沿,从而可实现唤醒的功能。

测试环节

/**
  * @brief  从停止模式唤醒后配置系统时钟:启用HSE、PLL并选择PLL作为系统时钟源。

  * @param  无
  * @retval 无
  */
static void SYSCLKConfig_STOP(void)
{
	RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
	RCC_OscInitTypeDef RCC_OscInitStruct = {0};
	uint32_t pFLatency = 0;

	/* 启用电源控制时钟 */
	__HAL_RCC_PWR_CLK_ENABLE();

	/* 根据内部RCC寄存器获取振荡器配置 */
	HAL_RCC_GetOscConfig(&RCC_OscInitStruct);

	/* 从停止模式唤醒后重新配置系统时钟: 启用HSE和PLL */
	RCC_OscInitStruct.OscillatorType  = RCC_OSCILLATORTYPE_HSE;
	RCC_OscInitStruct.HSEState        = RCC_HSE_ON;
	RCC_OscInitStruct.PLL.PLLState    = RCC_PLL_ON;
	if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
	{
		while(1) { ; }
	}

	/* 根据内部RCC寄存器获取时钟配置 */
	HAL_RCC_GetClockConfig(&RCC_ClkInitStruct, &pFLatency);

	/* 选择 PLL 作为系统时钟源, 并配置 HCLK、PCLK1 和 PCLK2时钟分频系数 */
	RCC_ClkInitStruct.ClockType     = RCC_CLOCKTYPE_SYSCLK;
	RCC_ClkInitStruct.SYSCLKSource  = RCC_SYSCLKSOURCE_PLLCLK;
	if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, pFLatency) != HAL_OK)
	{
		while(1) { ; }
	}
}

int main(void) 
{
	uint32_t 	SYSCLK_Frequency = 0; 
	uint32_t 	HCLK_Frequency = 0;
	uint32_t 	PCLK1_Frequency = 0;
	uint32_t 	PCLK2_Frequency = 0;
	uint32_t 	SYSCLK_Source = 0;
	
	初始化

	while(1)
	{	
		LED绿灯
		HAL_Delay(2000);		

		// 进入停止模式,亮红灯,按KEY1或KEY2按键可唤醒
		LED_RED;
		HAL_SuspendTick();			//暂停滴答时钟,防止通过滴答时钟中断唤醒
		/* 进入停止模式,设置电压调节器为低功耗模式,等待中断唤醒 */
		HAL_PWR_EnterSTOPMode(PWR_MAINREGULATOR_ON, PWR_STOPENTRY_WFI);
		
		// 等待中断唤醒  K1或K2按键中断	
		
		// 被唤醒,亮蓝灯指示
		LED蓝灯
		SystemCoreClockUpdate();	//根据时钟寄存器的值更新SystemCoreClock变量
		//获取唤醒后的时钟状态	
		SYSCLK_Frequency = HAL_RCC_GetSysClockFreq(); 
		HCLK_Frequency   = HAL_RCC_GetHCLKFreq();
		PCLK1_Frequency  = HAL_RCC_GetPCLK1Freq();
		PCLK2_Frequency  = HAL_RCC_GetPCLK2Freq();
		SYSCLK_Source    = __HAL_RCC_GET_SYSCLK_SOURCE();
		 
		/* 从停止模式唤醒后配置系统时钟:启用HSE、PLL*/
		/* 选择PLL作为系统时钟源(HSE和PLL在停止模式下被禁用)*/
		SYSCLKConfig_STOP();
		
		HAL_ResumeTick();			//被唤醒后,恢复滴答时钟
		//获取重新配置后的时钟状态
		SYSCLK_Frequency = HAL_RCC_GetSysClockFreq(); 
		HCLK_Frequency   = HAL_RCC_GetHCLKFreq();
		PCLK1_Frequency  = HAL_RCC_GetPCLK1Freq();
		PCLK2_Frequency  = HAL_RCC_GetPCLK2Freq();
		SYSCLK_Source    = __HAL_RCC_GET_SYSCLK_SOURCE();
		
		HAL_Delay(2000);	

		//继续执行while循环
	}
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

家用洗地机哪个牌子质量最好?家用洗地机推荐

洗地机也就是集吸尘器,拖地,洗地,功能于一体的家电,无论干湿垃圾都能清理的干干净净,而且还不用弯腰,有的只用换个头,就从拖地变成了吸尘器和除螨仪简直就是清洁家里卫生的打扫神器啦!那么面对市…

Spring-Spring 之底层架构核心概念解析

BeanDefinition BeanDefinition表示Bean定义,BeanDefinition中存在很多属性用来描述一个Bean的特点。比如: class,表示Bean类型scope,表示Bean作用域,单例或原型等lazyInit:表示Bean是否是懒加载initMeth…

chatgpt接口调用

在线接口文档: https://app.apifox.com/invite?tokensymrLP7sojF6N31kZqnpZ 接口地址 https://chat.xutongbao.top/api/light/chat/createChatCompletion 请求方式 POST 请求参数 token String, 必须 prompt Array, 必须 例子一: 包含上下文 [ { "…

行政处罚类型有哪些?哪里能够查到一家企业的行政处罚信息?

在查询企业信息的时候,处理企业基础的工商信息之外,我们还会注意到到的就是企业的处罚信息。毕竟处罚可以直观反应出一个企业的违法违规行为,帮助我们直接了解企业。 那么,企业的行政处罚包括哪些内容呢? 根据《中华…

【MATLAB源码-第66期】基于麻雀搜索算法(SSA)的栅格路径规划,输出做短路径图和适应度曲线。

操作环境: MATLAB 2022a 1、算法描述 麻雀搜索算法(Sparrow Search Algorithm, SSA)是一种新颖的元启发式优化算法,它受到麻雀社会行为的启发。这种算法通过模拟麻雀的食物搜索行为和逃避天敌的策略来解决优化问题。SSA通过模拟…

Macroscope安全漏洞检测工具简介

学习目标: 本介绍旨在帮助感兴趣者尽快了解 Macroscope,这是一款用于安全测试自动化和漏洞管理的企业工具。 全覆盖应用程序安全测试: 如下图所示,如果使用多种互补工具(SAST/DAST/SCA 等)来检测应用程序…

网络取证-Tomcat-简单

题干: 我们的 SOC 团队在公司内部网的一台 Web 服务器上检测到可疑活动。为了更深入地了解情况,团队捕获了网络流量进行分析。此 pcap 文件可能包含一系列恶意活动,这些活动已导致 Apache Tomcat Web 服务器遭到破坏。我们需要进一步调查这一…

解决uniapp的video标签和transition属性使用时出现错位的问题

template:三个视频都每个占满屏幕,点击按钮滚动最外层bgBox元素, style: 想要加上动画过渡效果: 这是显示第一个视频: 点按钮向上滑动滚动到第二个视频时: 视频错位了 ,因为视频消失又出现的时候…

这两天公司面了一个字节来的要求月薪23K,明显感觉他背了很多面试题...

最近有朋友去字节面试,面试前后进行了20天左右,包含4轮电话面试、1轮笔试、1轮主管视频面试、1轮hr视频面试。 据他所说,80%的人都会栽在第一轮面试,要不是他面试前做足准备,估计都坚持不完后面几轮面试。 其实&…

泡泡玛特首度跨界超跑品牌兰博基尼汽车,以潮流基因探索时空边界

近期,泡泡玛特携手兰博基尼汽车,于上海国际赛车场进行了一场玩味十足的赛道体验。25位兰博基尼车主,及多位汽车领域知名媒体人、kol到场参与。兰博基尼跑车巡游、专业车手驾驶的兰博基尼涂装赛车试乘、MEGA SPACE MOLLY 1000%/400%兰博基尼汽…

【Amazon】AWS实战 | 快速发布安全传输的静态页面

文章目录 一、实验架构图二、实验涉及的AWS服务三、实验操作步骤1. 创建S3存储桶,存放网站网页2. 使用ACM建立域名证书3. 设置Cloudfront,连接S3存储桶✴️4. 设置Route53,解析域名服务5. 通过CLI工具上传网页更新内容【可选】 四、实验总结 …

CentOS、linux安装squid搭建正向代理,window11配置正向代理

1.CentOS安装配置squid 1.1.安装 yum install -y squid1.2.修改配置文件 在配置文件添加以下2行代码 acl localnet src 0.0.0.0/0.0.0.0 # add by lishuoboy http_access allow all # add by lishuoboy1.3.启动squid systemctl restart squid2.win11…

node复制当前目录下的文件夹到另一层目录(包含多层文件夹嵌套)

前段时间在跟进node项目时有个node项目的需求,然后上线流程是把前端build后的文件夹放到后端仓库的静态资源目录下,再把后端代码发布上线。这样做的好处是在前端页面调用接口时,可以直接 /xxx来调用(浏览器会自动把域名补全&#…

基于仿真的飞机ICD工具测试

机载电子系统是飞机完成飞行任务的核心保障之一。从1949年新中国建立至今70余年的发展过程中,随着我国在航空航天领域的投资逐年增多,机载电子系统大致经历了四个发展过程阶段,按照出现的先后顺序进行排序,分别为: 1、…

Springboot使用EasyExcel导入导出Excel文件

1&#xff0c;准备Excel文件和数据库表结果 2&#xff0c;导入代码 1&#xff0c;引入依赖 <!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel --><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifac…

掌握微信批量添加好友技巧,让你的社交更高效

微信作为当今的热门通讯工具&#xff0c;在企业营销中扮演着越来越重要的角色。然而&#xff0c;微信并没有提供自动批量添加好友的功能&#xff0c;给运营者带来了不小的挑战。一个个手动添加不仅耗时&#xff0c;而且频繁操作还容易导致账号被封。本文将介绍几种手动批量添加…

PHP foreach 循环跳过本次循环

$a [[id>1],[id>2],[id>3],[id>4],[id>5],[id>6],[id>7],[id>18],];foreach($a as $v){if($v[id] 5){continue;}$b[] $v[id];}return show_data(,$b); 结果&#xff1a;

Linux软件安装包管理器yum

Linux软件安装 Linux软件安装的本质 ​ 对于安装软件最基本的理解就是把可执行程序拷贝到指定路径下&#xff0c;我们知道直接输入指令就可以实现想要的功能&#xff0c;这些指令本质上都是放在指定路径下的可执行文件&#xff0c;如果我们把写好的程序编译后的可执行文件放到…

dubbo没有找到生产者

1、没有找到生产者 com.alibaba.dubbo.rpc.RpcException: No provider available from registry 127.0.0.1:2181 for service .... , please check status of providers(disabled, not registered or in blacklist)2、 查看是不是 对应的providers 没有 注册上去 找到 zk 对应…

Java 为什么不推荐在 while 循环中使用 sleep() 我悟了

文章目录 前言原因是否正确方案是否合理定时轮询场景事件机制等待和唤醒 个人简介 前言 最近逛 CSDN 看到一篇文章&#xff0c;文章大意是说为什么在循环中不推荐使用 sleep 操作&#xff0c;原因在于线程挂起和唤醒会有很大的性能消耗&#xff0c;并推荐使用 Timer 及 Schedu…