STM32单片机-PWR电源控制和WDG看门狗
- 一、PWR简介
- 二、低功耗模式
- 三、修改主频&睡眠模式&停机模式&待机模式
-
- [3.1 修改主频](#3.1 修改主频)
- [3.2 睡眠模式](#3.2 睡眠模式)
- [3.3 停机模式](#3.3 停机模式)
- [3.4 待机模式](#3.4 待机模式)
- 四、WDG简介
-
- [4.1 独立看门狗原理](#4.1 独立看门狗原理)
- [4.2 窗口看门狗原理](#4.2 窗口看门狗原理)
- [4.3 IWDG和WWDG对比](#4.3 IWDG和WWDG对比)
- 五、独立看门狗&窗口看门狗
-
- [5.1 独立看门狗](#5.1 独立看门狗)
- [5.2 窗口看门狗](#5.2 窗口看门狗)
一、PWR简介
- PWR负责管理STM32内部的电源供电部分,可以实现可编程电压监测器 和低功耗模式的功能
- 可编程电压监测器(PVD)可以监测VDD电源电压 ,当VDD下降到PVD阈值以下 或上升到PVD阈值以上 时,PVD会触发中断,用于执行紧急关闭任务
- 低功耗模式包括睡眠模式 (Sleep)、停机模式 (Stop)和待机模式 (Standby),可在系统空闲时,降低STM32的功耗,延长设备的使用时间
下图为STM32的电源框图
从图中需要知道每个区域的供电引脚以及供电的电路
二、低功耗模式
下图为低功耗模式表
从上到下 ,关闭的电路越来越多,越来越省电,越来越难唤醒
睡眠模式 :调用WFI 和WFE 进入睡眠模式。WFI:任何外设发生中断 时,芯片都会立刻醒来。WFE:事件唤醒 ,不需要进入中断。只关闭CPU时钟,对他电路无任何操作
关闭电路通常有关闭时钟和关闭电源两个做法,关闭时钟 :所有运算和涉及时序的操作都会暂停,寄存器和存储器的数据可以维持 ,不会消失。关闭电源 :电路直接断电,电路操作和数据会直接丢失
停机模式 和待机模式 :首先SLEEPDEEP= 1 ,之后PDDS=0-停机模式 ,PDDS=1-待机模式 ,LPDS=0-电压调节器开启 ,LPDS=1-电压调节器进入低功耗 ,最终调用WFI 或者WFE 进入低功耗 模式。任一外部中断(不需要时钟)或外部事件 唤醒停机模式 。WKUP上升沿 、RTC闹钟 等唤醒待机模式 。停机和待机同时关闭CPU和外设时钟 、内外部高速时钟 。停机模式不关闭电源 ,所以CPU和外设寄存器数据维持原状 ,待机模式全部关闭
下图为模式选择配置
执行WFI和WFE指令后,STM32进入低功耗模式
三、修改主频&睡眠模式&停机模式&待机模式
3.1 修改主频
在system.stm32f1ox.c文件中修改系统主频,默认72Mhz,文件是只读的,所以需要修改权限
c
#if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
/* #define SYSCLK_FREQ_HSE HSE_VALUE */
#define SYSCLK_FREQ_24MHz 24000000
#else
/* #define SYSCLK_FREQ_HSE HSE_VALUE */
/* #define SYSCLK_FREQ_24MHz 24000000 */
/* #define SYSCLK_FREQ_36MHz 36000000 */
/* #define SYSCLK_FREQ_48MHz 48000000 */
/* #define SYSCLK_FREQ_56MHz 56000000 */
#define SYSCLK_FREQ_72MHz 72000000
#endif
3.2 睡眠模式
对于中断触发的代码,加入低功耗模式,不进入中断的时候,可以节省资源,有中断进来再进入中断函数
利用串口收发函数模拟,调用__WFI();函数 ,程序进入睡眠模式,Running!不再闪烁,当STM接收到数据触发中断时,Runnging!闪烁一次,接着进入睡眠模式,降低功耗
c
uint8_t RxData;
int main(void)
{
OLED_Init();
Serial_Init();
OLED_ShowString(1,1,"RxData:");
while(1)
{
if(Serial_GetRxFlag() == 1)
{
RxData = Serial_GetRxData();
OLED_ShowHexNum(1,8,RxData,2);
}
OLED_ShowString(2,1,"Running!");
Delay_ms(100);
OLED_ShowString(2,1," ");
Delay_ms(100);
__WFI();//开启睡眠模式(中断唤醒)
}
}
3.3 停机模式
停机模式使用外部中断唤醒 ,利用对射红外传感器模拟
当外部中断不触发时,CountSensor_Get()会一直被扫描,浪费资源,可以使STM32进入低功耗模式,节省资源
进入停机模式,需要使用PWR外设,所以需要开启APB1PWR时钟 ,然后调用PWR_EnterSTOPMode()函数 ,开启停机模式,外部中断发生 时,芯片唤醒
复位后第一次Running!闪烁很快,后面的Running!闪烁很慢,是由于第一次在是72Mhz主频,后面进入停止模式,默认时钟是8MHz
注意按下复位按钮下载
c
int main(void)
{
OLED_Init();
CountSensor_Init();
OLED_ShowString(1,1,"Count:");
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);//开启PWR时钟
while(1)
{
OLED_ShowNum(1,7,CountSensor_Get(),5);
OLED_ShowString(2,1,"Running:");
Delay_ms(100);
OLED_ShowString(2,1," ");
Delay_ms(100);
PWR_EnterSTOPMode(PWR_Regulator_ON,PWR_STOPEntry_WFI);//开启停止模式
SystemInit();//恢复主频
}
}
3.4 待机模式
待机模式唤醒需要特定的信号,使用RTC唤醒待机模式
首先开启PWR时钟 ,然后调用PWR_EnterSTANDBYMode()开启待机模式 。当RTC闹钟事件来临时,唤醒待机模式,唤醒一次后,程序从头开始,闹钟值会重新设置
c
int main(void)
{
MyRTC_Init();
OLED_Init();
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);//开启PWR时钟
OLED_ShowString(1,1,"CNT:");
OLED_ShowString(2,1,"ALR:");
OLED_ShowString(3,1,"ALRF:");
uint32_t Alarm = RTC_GetCounter()+10;
RTC_SetAlarm(Alarm);//设定闹钟值
OLED_ShowNum(2,6,Alarm,10);
while(1)
{
OLED_ShowNum(1,6,RTC_GetCounter(),10);
OLED_ShowNum(3,6,RTC_GetFlagStatus(RTC_FLAG_ALR),1);
OLED_ShowString(4,1,"Running!");
Delay_ms(100);
OLED_ShowString(4,1," ");
Delay_ms(100);
PWR_EnterSTANDBYMode();//开启待机模式
}
}
四、WDG简介
- 看门狗可以监控程序的运行状态 ,当程序因为设计漏洞 、硬件故障 、电磁干扰 等原因,出现卡死或跑飞现象 时,看门狗能及时复位程序 ,避免程序陷入长时间的罢工状态,保证系统的可靠性和安全性
- 看门狗本质上是一个定时器 ,当指定时间范围内 ,程序没有执行喂狗 (重置计数器 )操作时,看门狗硬件电路 就会自动产生复位信号
- STM32内置两个看门狗
- 独立看门狗 (IWDG):独立工作,对时间精度要求较低
- 窗口看门狗 (WWDG):要求看门狗在精确计时窗口起作用
4.1 独立看门狗原理
下图为独立看门狗框图
与定时器类似,看门狗使用的是自减运行 ,自减到0后,定时器产生更新事件或者中断,看门狗是直接产生复位 ,定时器是产生事件后自动重装值 ,看门狗需要在自减到0之前手动重装 ,不然就会进行复位,手动重装就是喂狗
看门狗输入时钟是低速时钟LSI40KHz ,之后时钟进入8位预分频器进行预分频,最大256分频,预分频寄存器可以配置分频系数 ,之后每来一个时钟,12位递减计数器自减一个数 ,最大值是4095,自减到0后产生IWDG复位 ,在重装载数值写一个值,在键寄存器里写一个特定数据,控制电路进行喂狗,这时重装值就会复制到当前的计数器中,计数器就会回到重装值,重新自减运行了
下面给出IWDG键寄存器
- 键寄存器本质上是控制寄存器,用于控制硬件电路的工作
- 在可能存在干扰的情况下,一般通过在整个键寄存器写入特定值 来代替 控制寄存器写入一位的功能,以降低硬件电路收到干扰的概率
- 超时时间 :TIWDG = TLSI x PR预分频系数 x (RL(重装值)+1)
- 其中:TLSI = 1/FLSI = 1/40K = 0.025ms
下图为PR寄存器和分频系数的对应关系以及RL和时间关系
4.2 窗口看门狗原理
下图为窗口看门狗框图
PCLK1时钟源36MHz 进入预分频器WDGTB,然后到6位递减计数器 (T6是溢出标志位,溢出产生复位信号)CNT ,窗口看门狗没有重装寄存器,喂狗 只需要在计数器里写入数据 即可
复位信号输出部分,WDGA是窗口看门狗激活位 ,给1 启动窗口看门狗。T6=0时表示计数器溢出 ,产生复位 信号,计算一个最早界限的计数值 写入到W6-W0中,固定不变,执行写入CR操作时,即喂狗时的CNT计数值 > 窗口值 ,比较结果为1 ,也可以申请复位
喂狗太晚 ,6位计数器减到0后,复位;喂狗太早,计数器的值超过窗口值,复位
当计数器减到0x40(1000 0000)时,可以产生早期唤醒中断(EWI),下一时刻才复位
- 超时时间 (喂狗的最晚时间 ):TWWDG = TPCLK1 x 4096 x WDGTB预分频系数 x (T[5:0] + 1)
- 窗口时间 (喂狗的最早时间 ):TWIN = TPCLK1 x 4096 x WDGTB预分频系数 x (T[5:0] - W[5:0])
- TPCLK1 = 1 / FPCLK1(36MHz)
下图为最小/最大超时值与分配系数关系
WDGTB = 0,1,2,3对应1,2,4,8分频
4.3 IWDG和WWDG对比
下图为IWDG和WWDG对比图
五、独立看门狗&窗口看门狗
5.1 独立看门狗
步骤:打开LSI时钟(默认打开 ) --- 键寄存器 (解除写保护 ) --- 写入预分频值和重装值 --- 键寄存器 (启动独立看门狗 ) --- 键寄存器 (写重装值-喂狗)
当程序卡死超过设定时间时,看门狗就会进行复位
c
int main(void)
{
OLED_Init();
Key_Init();
OLED_ShowString(1,1,"IWDG TEST");
if(RCC_GetFlagStatus(RCC_FLAG_IWDGRST) == SET)//本次复位由IWDG产生
{
OLED_ShowString(2,1,"IWDGRST");
Delay_ms(500);
OLED_ShowString(2,1," ");
Delay_ms(500);
RCC_ClearFlag();//清除标志位
}
else//普通复位
{
OLED_ShowString(3,1,"RST");
Delay_ms(500);
OLED_ShowString(3,1," ");
Delay_ms(500);
}
//时钟自动配置
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);//接触写保护
//1000ms超时时间
IWDG_SetPrescaler(IWDG_Prescaler_16);//配置预分频值
IWDG_SetReload(2499);//配置重装值/喂狗
IWDG_ReloadCounter();//先喂狗,CNT初始值就是2499
//启动看门狗
IWDG_Enable();
while(1)
{
Key_GetNum();//按键一直按下,程序卡死,看门狗复位
IWDG_ReloadCounter();//喂狗
//Delay_ms(950);//看门狗不复位
//Delay_ms(1010);//程序卡死超过1000ms,看门狗会一直复位
}
}
5.2 窗口看门狗
步骤:打开PCLK1 时钟(APB1时钟 ) --- 配置预分频和窗口寄存器值 --- 写入控制寄存器CR (看门狗使能 、计数器溢出标志位 和计数器有效位 ) --- 计数器写值 (喂狗)
窗口看门狗需要设定窗口值和超时值,过早或超时喂狗都会使得看门狗复位
c
int main(void)
{
OLED_Init();
Key_Init();
OLED_ShowString(1,1,"WWDG TEST");
if(RCC_GetFlagStatus(RCC_FLAG_WWDGRST) == SET)//本次复位由WWDG产生
{
OLED_ShowString(2,1,"WWDGRST");
Delay_ms(500);
OLED_ShowString(2,1," ");
Delay_ms(500);
RCC_ClearFlag();//清除标志位
}
else//普通复位
{
OLED_ShowString(3,1,"RST");
Delay_ms(500);
OLED_ShowString(3,1," ");
Delay_ms(500);
}
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG,ENABLE);//开启PCLK1时钟
//超时50ms 窗口30ms
WWDG_SetPrescaler(WWDG_Prescaler_8);//分频系数
WWDG_SetWindowValue(0x40 | 21);//窗口值是W5-W0,W6为1
WWDG_Enable(0x40 | 54);//计数器值是T5-T0,T6为1
while(1)
{
//Key_GetNum();
//Delay_ms(32);//过早喂狗:避免第一次喂狗和第二次间隔小于窗口30ms
Delay_ms(55);//超时喂狗
WWDG_SetCounter(0x40 | 54);
}
}