一,独立看门狗

 

独立看门狗HAL_IWDG_Refresh不好用 独立看门狗程序_嵌入式

二,独立看门狗的时钟源

独立看门狗HAL_IWDG_Refresh不好用 独立看门狗程序_看门狗_02

独立看门狗拥有自己的时钟源,不依赖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.在裸机代码实现喂狗,放在定时器里面,因为定时器与看门狗是使用不同的时钟源,允许这么做!

独立看门狗HAL_IWDG_Refresh不好用 独立看门狗程序_操作系统_03

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

}