一,独立看门狗
二,独立看门狗的时钟源
独立看门狗拥有自己的时钟源,不依赖PLL时钟输出的分频信号,能够独立运行,这样子的好处就是PLL假如受到干扰,
导致运行异常,独立的看门狗还能正常地进行工作,如果没有正常的喂狗动作,就复位CPU。
三、程序设计
1. 添加复位检测代码,有助于观察当前工作的可靠性
/* Check if the system has resumed from IWDG reset,检查当前复位是否有独立看门狗导致 */
if (RCC_GetFlagStatus(RCC_FLAG_IWDGRST) != RESET)
{
/* IWDGRST flag set */
printf("iwdt reset cpu\r\n");
/* Clear reset flags */
RCC_ClearFlag();
}
else
{
/* IWDGRST flag is not set */
printf("normal reset cpu\r\n");
}
2.
/* Enable write access to IWDG_PR and IWDG_RLR registers,独立看门狗是受到保护的,现在进行解锁动作 */
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
/* IWDG counter clock: LSI/256 ,设置看门狗的时钟 = 32KHz / 256 =125Hz */
IWDG_SetPrescaler(IWDG_Prescaler_256);
/* 设置看门狗的超时时间,也就是设置它的计数值
当前看门狗的时钟为125Hz,然后设置超时时间为1秒,那么重载值为125
当前看门狗的时钟为125Hz,然后设置超时时间为2秒,那么重载值为250
*/
IWDG_SetReload(125);
/* Reload IWDG counter,重载独立看门狗的计数值,说白了就是喂狗 */
IWDG_ReloadCounter();
/* Enable IWDG (the LSI oscillator will be enabled by hardware),使能独立看门狗 */
IWDG_Enable();
3. 喂狗技巧
1.在裸机代码实现喂狗,放在定时器里面,因为定时器与看门狗是使用不同的时钟源,允许这么做!
2. 如果有实时的操作系统的加持,可以在任务里面添加喂狗动作,如果操作系统崩溃了,能够检测到软件的错误,触发CPU的复位。
在定时器中断服务函数当中,添加喂狗动作!
#include "stm32f4xx.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_usart.h"
#include "stdio.h"
static GPIO_InitTypeDef GPIO_InitStructure;
static USART_InitTypeDef USART_InitStructure;
static NVIC_InitTypeDef NVIC_InitStructure;
void delay_us(uint32_t nus)
{
uint32_t temp;
SysTick->LOAD =SystemCoreClock/8/1000000*nus; //时间加载
SysTick->VAL =0x00; //清空计数器
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //使能滴答定时器开始倒数
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
void delay_ms(uint16_t nms)
{
uint32_t temp;
SysTick->LOAD=SystemCoreClock/8/1000*nms; //时间加载(SysTick->LOAD为24bit)
SysTick->VAL =0x00; //清空计数器
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //能滴答定时器开始倒数
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
void LED_Init(void)
{
//使能GPIOE,GPIOF时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE | RCC_AHB1Periph_GPIOF, ENABLE);
//GPIOF9,F10初始化设置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //LED0和LED1对应IO口
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //普通输出模式,
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽输出,驱动LED需要电流驱动
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOF, &GPIO_InitStructure); //初始化GPIOF,把配置的数据写入寄存器
//GPIOE13,PE14初始化设置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14; //LED2和LED3对应IO口
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //普通输出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOE, &GPIO_InitStructure); //初始化GPIOE,把配置的数据写入寄存器
GPIO_SetBits(GPIOF,GPIO_Pin_9 | GPIO_Pin_10); //GPIOF9,PF10设置高,灯灭
GPIO_SetBits(GPIOE,GPIO_Pin_13 | GPIO_Pin_14);
}
void USART1_Init(uint32_t baud)
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); //使能USART1时钟
//串口1对应引脚复用映射
GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //GPIOA9复用为USART1
GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); //GPIOA10复用为USART1
//USART1端口配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9与GPIOA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA9,PA10
//USART1 初始化设置
USART_InitStructure.USART_BaudRate = baud; //波特率设置
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字长为8位数据格式
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); //初始化串口1
USART_Cmd(USART1, ENABLE); //使能串口1
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //开启相关中断
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //串口1中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3; //抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
}
//重定义fputc
int fputc(int ch,FILE *f)
{
USART_SendData(USART1,ch);
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
return ch;
}
void usart1_send_bytes(uint8_t *pbuf,uint32_t len)
{
while(len--)
{
USART_SendData(USART1,*pbuf++);
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
}
}
void usart1_send_str(char *pbuf)
{
while(pbuf && *pbuf)
{
USART_SendData(USART1,*pbuf++);
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
}
}
int main(void)
{
LED_Init();
//系统定时器初始化,时钟源来自HCLK,且进行8分频,
//系统定时器时钟频率=168MHz/8=21MHz
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
//设置中断优先级分组2
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//串口1,波特率115200bps,开启接收中断
USART1_Init(115200);
/* Check if the system has resumed from IWDG reset,检查当前复位是否有独立看门狗导致 */
if (RCC_GetFlagStatus(RCC_FLAG_IWDGRST) != RESET)
{
/* IWDGRST flag set */
printf("iwdt reset cpu\r\n");
/* Clear reset flags */
RCC_ClearFlag();
}
else
{
/* IWDGRST flag is not set */
printf("normal reset cpu\r\n");
}
/* Enable write access to IWDG_PR and IWDG_RLR registers,独立看门狗是受到保护的,现在进行解锁动作 */
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
/* IWDG counter clock: LSI/256 ,设置看门狗的时钟 = 32KHz / 256 =125Hz */
IWDG_SetPrescaler(IWDG_Prescaler_256);
/* 设置看门狗的超时时间,也就是设置它的计数值
当前看门狗的时钟为125Hz,然后设置超时时间为1秒,那么重载值为125
当前看门狗的时钟为125Hz,然后设置超时时间为2秒,那么重载值为250
*/
IWDG_SetReload(125);
/* Reload IWDG counter,重载独立看门狗的计数值,说白了就是喂狗 */
IWDG_ReloadCounter();
/* Enable IWDG (the LSI oscillator will be enabled by hardware),使能独立看门狗 */
IWDG_Enable();
while(1)
{
//重载计数值,就是喂狗,就是不让计数值变为0
IWDG_ReloadCounter();
}
}
void USART1_IRQHandler(void) //串口1中断服务程序
{
uint8_t d;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断
{
//接收数据
d = USART_ReceiveData(USART1);
//发送数据
usart1_send_bytes(&d,1);
}
}