STM32单片机-PWR电源控制和WDG看门狗

一、PWR简介

  • PWR负责管理STM32内部的电源供电部分,可以实现可编程电压监测器低功耗模式的功能
  • 可编程电压监测器(PVD)可以监测VDD电源电压,当VDD下降到PVD阈值以下或上升到PVD阈值以上时,PVD会触发中断,用于执行紧急关闭任务
  • 低功耗模式包括睡眠模式(Sleep)、停机模式(Stop)和待机模式(Standby),可在系统空闲时,降低STM32的功耗,延长设备的使用时间

  下图为STM32的电源框图
  从图中需要知道每个区域的供电引脚以及供电的电路

在这里插入图片描述

二、低功耗模式

  下图为低功耗模式表
  从上到下,关闭的电路越来越多,越来越省电,越来越难唤醒
  睡眠模式:调用WFIWFE进入睡眠模式。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,文件是只读的,所以需要修改权限

#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!闪烁一次,接着进入睡眠模式,降低功耗

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

  注意按下复位按钮下载

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闹钟事件来临时,唤醒待机模式,唤醒一次后,程序从头开始,闹钟值会重新设置

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时钟(默认打开) — 键寄存器(解除写保护) — 写入预分频值和重装值键寄存器(启动独立看门狗) — 键寄存器(写重装值-喂狗)

  当程序卡死超过设定时间时,看门狗就会进行复位

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(看门狗使能计数器溢出标志位计数器有效位) — 计数器写值(喂狗)

  窗口看门狗需要设定窗口值和超时值,过早或超时喂狗都会使得看门狗复位

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);
	}
}

  • 21
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: STM32 GY-521是一种用于姿态感知的传感器模块,可以通过I2C通信与STM32单片机通信。在编写代码之前需要先进行硬件连接,将GY-521模块的VCC引脚连接到STM32的5V引脚,GND引脚连接到GND引脚,SCL引脚连接到STM32的PB6引脚,SDA引脚连接到STM32的PB7引脚。 使用STM32的HAL库,可以通过以下步骤编写代码: 1. 引入相关库文件和头文件。 2. 初始化I2C总线,使能I2C时钟,并配置GPIO引脚。 3. 设置I2C从设备地址,GY-521模块的地址为0x68。 4. 发送读取寄存器的命令,可以读取陀螺仪、加速度计和温度等数据。 5. 等待数据传输完成,可以使用HAL库提供的函数进行延时。 6. 读取传感器数据,并进行相应的处理。 7. 关闭I2C总线,释放相关资源。 下面是一个简单的例子,展示如何读取GY-521模块的加速度计数据: #include "stm32f4xx_hal.h" I2C_HandleTypeDef hi2c1; void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_I2C1_Init(void); int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_I2C1_Init(); uint8_t accelData[6]; // 存储加速度计数据的数组 while (1) { HAL_I2C_Mem_Read(&hi2c1, 0x68<<1, 0x3B, I2C_MEMADD_SIZE_8BIT, &accelData, 6, 1000); // 发送读取命令,并读取6个字节的加速度计数据 HAL_Delay(1000); // 延时1秒,可以根据实际情况调整 // 对读取的数据进行处理,例如计算加速度值等 } HAL_I2C_DeInit(&hi2c1); while (1); } void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct; RCC_ClkInitTypeDef RCC_ClkInitStruct; __HAL_RCC_PWR_CLK_ENABLE(); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = 16; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI; RCC_OscInitStruct.PLL.PLLM = 16; RCC_OscInitStruct.PLL.PLLN = 336; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4; RCC_OscInitStruct.PLL.PLLQ = 7; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } 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_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); } HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000); HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK); HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0); } ... 以上是一个简单的示例代码,可以根据实际需求进行修改和扩展。希望对你有所帮助! ### 回答2: STM32 GY-521是一种常用的传感器模块,内置了加速度计和陀螺仪。下面是一段简单的STM32 GY-521代码示例: ```c #include "stm32f4xx.h" #include "i2c.h" #define MPU6050_ADDR 0xD0 // GY-521的I2C地址 void I2C_Write(uint8_t dev_addr, uint8_t reg_addr, uint8_t data) { // 向I2C设备写入数据的函数 I2C_Start(); I2C_SendData(dev_addr); I2C_WaitAck(); I2C_SendData(reg_addr); I2C_WaitAck(); I2C_SendData(data); I2C_WaitAck(); I2C_Stop(); } void I2C_Read(uint8_t dev_addr, uint8_t reg_addr, uint8_t* data) { // 从I2C设备读取数据的函数 I2C_Start(); I2C_SendData(dev_addr); I2C_WaitAck(); I2C_SendData(reg_addr); I2C_WaitAck(); I2C_Start(); I2C_SendData(dev_addr + 1); I2C_WaitAck(); *data = I2C_ReceiveData(0); I2C_Stop(); } void MPU6050_Init() { // 初始化MPU6050传感器 I2C_Write(MPU6050_ADDR, 0x6B, 0); I2C_Write(MPU6050_ADDR, 0x1C, 0x10); } void MPU6050_ReadAccel(int16_t* ax, int16_t* ay, int16_t* az) { // 读取加速度计的数据 uint8_t buffer[6]; I2C_Read(MPU6050_ADDR, 0x3B, buffer); *ax = (buffer[0] << 8) | buffer[1]; *ay = (buffer[2] << 8) | buffer[3]; *az = (buffer[4] << 8) | buffer[5]; } void MPU6050_ReadGyro(int16_t* gx, int16_t* gy, int16_t* gz) { // 读取陀螺仪的数据 uint8_t buffer[6]; I2C_Read(MPU6050_ADDR, 0x43, buffer); *gx = (buffer[0] << 8) | buffer[1]; *gy = (buffer[2] << 8) | buffer[3]; *gz = (buffer[4] << 8) | buffer[5]; } int main() { // 初始化I2C I2C_Init(); // 初始化MPU6050 MPU6050_Init(); while(1) { // 读取并处理传感器数据 int16_t ax, ay, az; int16_t gx, gy, gz; MPU6050_ReadAccel(&ax, &ay, &az); MPU6050_ReadGyro(&gx, &gy, &gz); // 处理数据代码... } } ``` 以上代码是一个简单的MPU6050九轴传感器读取示例,通过I2C总线与STM32板子进行通信,读取加速度计和陀螺仪的数据。可以根据实际需求进行进一步的数据处理和应用开发。 ### 回答3: STM32 GY-521是一款常用的9轴陀螺仪加速度传感器模块。在STM32开发板上使用GY-521模块时,需要编写相应的代码来实现传感器数据的读取和处理。 首先,需要在STM32的开发环境中搭建好相应的开发环境,包括安装好CubeMX和相应的开发软件(如Keil、IAR等)。 接下来,可以使用CubeMX来配置STM32的引脚连接和初始化设置。将GY-521模块的SCL引脚连接到STM32的I2C SCL引脚,SDA引脚连接到STM32的I2C SDA引脚。 然后,在代码中包含必要的库文件(如stm32f4xx.h)和头文件(如i2c.h),并定义I2C总线的相关参数(如时钟频率,I2C地址等)。 在主函数中,首先对I2C总线进行初始化配置,包括I2C时钟和相关引脚的配置。 然后,可以使用I2C的Start、Send、Receive等函数(具体函数名根据所使用的库而定)来进行传感器模块的读取和处理。例如,可以使用I2C的Start函数发送一个开始信号,然后通过Send函数发送读取传感器的命令,再通过Receive函数接收传感器返回的数据。 最后,可以对接收到的数据进行相应的处理,例如解析加速度、角速度、磁场等数据,进行滤波、校正或者其他相关的算法处理。 需要注意的是,具体的代码实现可能因不同的开发环境和库而有所差异。因此在编写代码时,可以参考官方提供的例程或者其他相关的参考资料,以确保代码的正确性和稳定性。 以上是对STM32 GY-521代码的简要回答,希望能对你有所帮助。不过由于字符限制只有300字,所以无法提供更加详细的代码示例。如有需要,可以进一步咨询。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值