stm32 窗口看门狗[操作寄存器+库函数]
2017-02-06 来源:eefocus
stm32有两个看门狗,独立看门狗和窗口看门狗,其实两者的功能是类似的,只是喂狗的限制时间不同。
独立看门狗是限制喂狗时间在0-x内,x由你的相关寄存器决定。喂狗的时间不能过晚。
窗口看门狗,所以称之为窗口就是因为其喂狗时间是一个有上下限的范围内,你可以通过设定相关寄存器,设定其上限时间和下限时间。喂狗的时间不能过早也不能过晚。
窗口看门狗的上窗口就是配置寄存器WWDG->CFR里设定的W[6:0],下窗口是固定的0x40;当窗口看门狗的计数器在上窗口值之外,或是低于下窗口值都会产生复位。
上窗口的值可以只有设定,7位二进制数最大只可以设定为127,最小又必须大于下窗口的0x40,所以其取值范围为40~127;
窗口看门狗的核心是一个7位的递减计数器,只有当计数器的值在窗口范围内在可以执行喂狗操作,即更新此计数器的值,否则将导致复位操作。 在配置寄存器WWDG->CFR中可以为此计数器设定时钟分频系数,确定这个计数器可以定时的时间范围,从而确定窗口的时间范围。
窗口看门狗的时钟来自于PCLK1,在时钟配置中,其频率为外部时钟经倍频器后的二分频时钟,即为36MHz,根据手册可以知道其定时时间计算方法:
上窗口时间 T_min = 4096 * (2^WDGTB)*(WWDG_CR[6:0] - WWDG_CFR[6:0])/36 (us)
下窗口时间 T_max = 4096 * (2^WDGTB)*(WWDG_CR[6:0] - 0x40)/36 (us)
36Mhz下相关窗口看门狗的喂狗时间范围:
WDGTB(计数器分频值) | 最早喂狗时间/us | 最晚喂狗时间/ms |
---|---|---|
0 | 113 | 7.28 |
1 | 227 | 14.56 |
2 | 455 | 29.12 |
3 | 910 | 58.25 |
本例直接寄存器实现4种测试模式,测试窗口看门狗的复位原理。
MODE_1 0 // 在30ms时喂狗,在窗口范围内喂狗,LED灯闪烁
MODE_2 0 // 在10ms时喂狗,在窗口范围外导致复位 ,LED灯常亮
MODE_3 0 // 在100ms时喂狗,在窗口范围外喂狗导致复位
MODE_4 1 // 主函数不执行喂狗,开启提前唤醒中断,在WWDG中断函数中喂狗
库函数实现在提前唤醒中断中,喂狗操作,PA4口LED正常闪烁。当外部中断发生(按下PA0按键),长时间不喂狗,引发窗口看门狗复位。
直接操作寄存器
相关寄存器如下:
控制寄存器WWDG->CR: 低8位有效。第8位为WDGA,看门狗激活位。低7位[6:0]为看门狗计数器的计数值。
配置寄存器WWDG->CFR:
低十位有效。
第10位为EWI:提醒唤起中断。 此位置1,当计数器值到达0x40时,将产生中断。
第8,9位WDGTB:为计数器分频系数设置为:
00: CK计时器时钟(PCLK1除以4096)除以1 01: CK计时器时钟(PCLK1除以4096)除以2
10: CK计时器时钟(PCLK1除以4096)除以4 11: CK计时器时钟(PCLK1除以4096)除以8
低7位[6:0]:为窗口值。
代码如下: (system.h 和 stm32f10x_it.h 等相关代码参照 stm32 直接操作寄存器开发环境配置)
User/main.c
#include#include 'system.h' #include 'wdg.h' #define LED1 PAout(4) //选择测试模式,一次置1一个测试 #define MODE_1 0 // 在30ms时喂狗,在窗口范围内喂狗 #define MODE_2 0 // 在10ms时喂狗,在窗口范围外导致复位 #define MODE_3 0 // 在100ms时喂狗,在窗口范围外喂狗导致复位 #define MODE_4 1 // 主函数不执行喂狗,开启提前唤醒中断,在WWDG中断函数中喂狗 void Gpio_Init(void); int main(void) { Rcc_Init(9); //系统时钟设置 Wwdg_Init(127,100,3); //设定为只能在24.57ms~58.25ms内喂狗,时间计算方法参见wdg.c Wwdg_Init(); #if MODE_4 Nvic_Init(0,0,WWDG_IRQChannel,0); //设置抢占优先级为0,响应优先级为0,中断分组为0 #endif Gpio_Init(); while(1){ LED1 = !LED1; #if MODE_1 delay(30000); //延时30ms后喂狗,LED闪烁 Wwdg_Feed(); //喂狗 #elif MODE_2 delay(10000); //延时10ms,引发窗口看门狗复位,LED不闪烁 Wwdg_Feed(); //喂狗 #elif MODE_3 delay(100000); //延时100ms,引发窗口看门狗复位,LED不闪烁 Wwdg_Feed(); //喂狗 #elif MODE_4 delay(30000); //延时30ms后在中断中喂狗,LED闪烁 #endif } } void Gpio_Init(void) { RCC->APB2ENR|=1<<2; //使能PORTA时钟 GPIOA->CRL&=0x0000FFFF; // PA0~3设置为浮空输入,PA4~7设置为推挽输出 GPIOA->CRL|=0x33334444; }
User/stm32f10x_it.c
#include 'stm32f10x_it.h' extern void Wwdg_Feed(void); void WWDG_IRQHandler(void) { //必须判断 计数值小于 窗口值之后才可以喂狗,不加此判断直接喂狗会导致复位,原因不详 if((WWDG->CR&0x7F) < (WWDG -> CFR&0x7F)){ Wwdg_Feed(); } WWDG->SR = 0x00; //清除中断标志位 }
Library/src/wdg.c 和 Library/inc/wdg.h 参见 stm32 独立看门狗[操作寄存器+库函数]
在中断函数 stm32f10x_it.c WWDG_IRQChannel()中 必须判断 计数值小于 窗口值之后才可以喂狗,不加此判断直接喂狗会导致复位,原因不详。
此中断是在计数器计数到0x40时触发,必然会小于窗口设定值 ,除非是此中断不止在此条件下触发,启动WWDG计数器可能也会导致中断。猜测一下而已 。。。
库函数操作
main.c
#include 'stm32f10x.h' #include 'stdio.h' #define PRINTF_ON 1 void RCC_Configuration(void); void GPIO_Configuration(void); void NVIC_Configuration(void); void USART_Configuration(void); void WWDG_Configuration(void); void EXTI_Configuration(void); int main(void) { RCC_Configuration(); GPIO_Configuration(); NVIC_Configuration(); USART_Configuration(); EXTI_Configuration(); WWDG_Configuration(); while(1){ if(RCC_GetFlagStatus(RCC_FLAG_WWDGRST) != RESET) { printf('\r\n The Stm32 has been reset by WWDG .\r\n'); RCC_ClearFlag(); } } } void EXTI_Configuration(void) { EXTI_InitTypeDef EXTI_InitStructure; EXTI_InitStructure.EXTI_Line = EXTI_Line0; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0); } void GPIO_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_Init(GPIOA , &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOA , &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA , &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA , &GPIO_InitStructure); } void WWDG_Configuration(void) { WWDG_SetPrescaler(WWDG_Prescaler_8); WWDG_Enable(0x7F); WWDG_ClearFlag(); WWDG_EnableIT(); } void RCC_Configuration(void) { /* 定义枚举类型变量 HSEStartUpStatus */ ErrorStatus HSEStartUpStatus; /* 复位系统时钟设置*/ RCC_DeInit(); /* 开启HSE*/ RCC_HSEConfig(RCC_HSE_ON); /* 等待HSE起振并稳定*/ HSEStartUpStatus = RCC_WaitForHSEStartUp(); /* 判断HSE起是否振成功,是则进入if()内部 */ if(HSEStartUpStatus == SUCCESS) { /* 选择HCLK(AHB)时钟源为SYSCLK 1分频 */ RCC_HCLKConfig(RCC_SYSCLK_Div1); /* 选择PCLK2时钟源为 HCLK(AHB) 1分频 */ RCC_PCLK2Config(RCC_HCLK_Div1); /* 选择PCLK1时钟源为 HCLK(AHB) 2分频 */ RCC_PCLK1Config(RCC_HCLK_Div2); /* 设置FLASH延时周期数为2 */ FLASH_SetLatency(FLASH_Latency_2); /* 使能FLASH预取缓存 */ FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); /* 选择锁相环(PLL)时钟源为HSE 1分频,倍频数为9,则PLL输出频率为 8MHz * 9 = 72MHz */ RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); /* 使能PLL */ RCC_PLLCmd(ENABLE); /* 等待PLL输出稳定 */ while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); /* 选择SYSCLK时钟源为PLL */ RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); /* 等待PLL成为SYSCLK时钟源 */ while(RCC_GetSYSCLKSource() != 0x08); } /* 打开APB2总线上的GPIOA时钟*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR|RCC_APB1Periph_BKP|RCC_APB1Periph_WWDG, ENABLE); } void USART_Configuration(void) { USART_InitTypeDef USART_InitStructure; USART_ClockInitTypeDef USART_ClockInitStructure; USART_ClockInitStructure.USART_Clock = USART_Clock_Disable; USART_ClockInitStructure.USART_CPOL = USART_CPOL_Low; USART_ClockInitStructure.USART_CPHA = USART_CPHA_2Edge; USART_ClockInitStructure.USART_LastBit = USART_LastBit_Disable; USART_ClockInit(USART1 , &USART_ClockInitStructure); USART_InitStructure.USART_BaudRate = 9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx; USART_Init(USART1,&USART_InitStructure); USART_Cmd(USART1,ENABLE); } void NVIC_Configuration(void) { NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); NVIC_InitStructure.NVIC_IRQChannel = WWDG_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } #if PRINTF_ON int fputc(int ch,FILE *f) { USART_SendData(USART1,(u8) ch); while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET); return ch; } #endif
stm32f10x_it.c
#include 'stm32f10x_it.h' #include 'stdio.h' extern vu32 Display; void WWDG_IRQHandler(void) { WWDG_SetCounter(0x7F); GPIO_WriteBit(GPIOA,GPIO_Pin_4,(BitAction)(1-GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_4))); WWDG_ClearFlag(); }