关于AT91SAM3S4B 中看门狗分析

作者:卢老师,华清远见嵌入式学院讲师。

WDT看门狗基本原理

看门狗,又叫Watchdog Timer,是一个定时器电路,一般有一个输入,叫喂狗(kicking the dog or service the dog),一个输出到MCU的RST端。MCU正常工作的时候,每隔一段时间输出一个信号到喂狗端,给WDT清零,如果超过规定的时间不喂狗(一般在程序跑飞时),WDT定时超过预先设定值,就会给出一个复位信号到MCU,使MCU重新开始工作。看门狗的作用就是防止程序发生死循环,或者叫程序跑飞。

在SAM3S系统中,用于看门狗的递减计数器是12位,可以计数的最大周期为16s(慢速时钟,32.768Khz),其加载的值为慢速时钟的128分频。

图7-56给出WDT时钟模块寄存器控制逻辑:

144506828.jpg
图7-56 WDT时钟模块寄存器

SAM3S4B复位后,WDV的值为0xfff,允许外部复位,默认情况下,看门狗是处于运行状态,如果用户没有使能看门狗,就需要禁止看门狗,否则需要定时“喂狗”。

看门狗模式寄存器WDT_MR只能写一次,之后,复位后重新加载定时器。

在正常情况下,用户定期向WDT_CR的WDRSTT位置1,重载看门狗定时器。WDRSTT置位后,计数器从WDT_MR重新加载,并重新启动。慢速时钟128分频器也被复位及重新启动。WDT_CR是写保护寄存器,若预设值不正确,对WDT_CR的操作无效,如果发生计数器益处,且WDR_MR的WDRSTEN为1,产生“wdt_fault”,WDT_SR的WDUNF置位。

为防止软件死锁,在0和WDD之间重新加载看门狗,WDD在看门狗模式下WDT_MR中定义。

如果试图在WDV和WDD之间重启看门狗定时器,将会导致看门狗错误,即使看门狗被禁止。这将导致WDT_SR中的WDERR位被修改,wdt_fault生效。若WDD不小于WDV的值时,上述功能是无效的,看门狗定时器允许在0和WDV之间重新启动,不产生错误,芯片缺省的复位状态时WDV=WDD。

如果WDFIFN=1,WDRSTEN=0,则WDUNF(看门狗溢出)和WDERR(看门狗错误)置位,触发中断;如果WDFIFN=1,WDRSTEN=1,则触发wdt_fault,复位,WDERR,WDUNF被清零。

如果复位已经产生,读WDT_SR寄存器,状态复位,中断被清楚,此时wdt_fault无效,执行WDT_MR写操作,将重新加载计数器,是CPU复位。

在调试和空闲状态,WDT_MR中的WDIDLEHLT和WDDBGHLT置位,看门狗计数器停止运行。

表7-18控制寄存器

偏移 寄存器功能 名称 权限 复位值
0x00 控制寄存器 WDT_CR 只写 -
0x04 模式寄存器 WDT_MR 只读一次 0x3FFF_2FFF
0x08 状态寄存器 WDT_SR 只读 0x0000_0000

WDT看门狗软件设计与分析

定义看门的结构体

typedefstruct {
                     WoReg WDT_CR; /*控制寄存器 */
                     RwReg WDT_MR; /*模式寄存器 */
                     RoReg WDT_SR; /*WDT 状态寄存器 */
             } Wdt;      

获取看门狗的定时时间程序:

uint32_t wdt_get_timeout_value(uint32_t ul_us, uint32_t ul_sclk)
             {
                     uint32_t max, min;
                     //3000*1000 属于3.9 ~16000*1000之间//min = 128 * 1000000 / ul_sclk;//3000*1000  
                     min = WDT_SLCK_DIV * 1000000 / ul_sclk;
                     max = min * WDT_MAX_VALUE;//max = min * 4095;
                     if ((ul_us< min) || (ul_us> max)) {
                             return WDT_INVALID_ARGUMENT;
                     }
             return WDT_MR_WDV(ul_us / min);//ul_us/min=768=256*3;
             }      

看门狗初始化程序:

voidwdt_init(Wdt *p_wdt, uint32_t ul_mode, uint16_t us_counter,
             uint16_t us_delta)
             {
                     p_wdt->WDT_MR=ul_mode| WDT_MR_WDV(us_counter) | WDT_MR_WDD(us_delta);
                     // ul_mode|0x300|(0x300<<16);
                     printf("p_wdt->WDT_MR= %x\r",p_wdt->WDT_MR);
             }      

喂狗程序:

voidwdt_restart(Wdt *p_wdt)
             {
                     p_wdt->WDT_CR = WDT_KEY_PASSWORD | WDT_CR_WDRSTT;// 0x5a000000 |1
             }      

获取看门狗寄存器状态程序:

uint32_t wdt_get_status(Wdt *p_wdt)
             {  
                     returnp_wdt->WDT_SR;
             }      

获取看门狗定时器溢出时间程序:

uint32_t wdt_get_us_timeout_period(Wdt *p_wdt, uint32_t ul_sclk)
             {
                     return WDT_MR_WDV(p_wdt->WDT_MR) * WDT_SLCK_DIV / ul_sclk * 1000000;
             }      

看门狗中断处理函数:

voidWDT_Handler(void)
             {
                     gpio_set_pin_high(LED2_GPIO);//关闭LED2
                     gpio_set_pin_low(LED1_GPIO);//打开LED1;
                     puts("Enter watchdog interrupt.\r");
                     wdt_get_status(WDT);//获取WDT状态寄存器
                     wdt_restart(WDT);//看门狗复位
                     puts("The watchdog timer was restarted.\r");
             }      

在主程序中,初始化串口,打印相关信息,初始化看门狗3S为溢出,产生中断的时间,初始化按键BUTTON2,LED灯。

int main(void)
             {
                     Uart *p_uart=(Uart *)0x400e0600;//串口地址定义
                     uint32_t wdt_mode, timeout_value;
                     /* 系统初始化 */
                     sysclk_init();
                     //此处不能禁止看门狗,因为WDT->WDT_MR只能进行一次写操作,禁止操作意味着写进去的数值为0,此后写入的数据溢出时无效
                     pio_configure_group(PINS_UART0_PIO,PINS_UART0,PINS_UART0_FLAGS);    
                     configure_console();  //串口配置
                     SysTick_Config(sysclk_get_cpu_hz() / 1000);//系统systick,1ms中断
                     timeout_value = wdt_get_timeout_value(WDT_PERIOD * 1000,
                     BOARD_FREQ_SLCK_XTAL);//看门狗溢出时间为3S=0X300
                     if (timeout_value == WDT_INVALID_ARGUMENT) {
                             while (1) {//中断中的处理
                             }
                     }    
                     /* 配置 WDT 触发中断.*/
                     wdt_mode = WDT_MR_WDFIEN |/*使能WDT看门狗出错中断. */
                     WDT_MR_WDRPROC |/* WDT出错处理. */
                     WDT_MR_WDDBGHLT |/* WDT 停止与调试状态. */
                     WDT_MR_WDIDLEHLT;/* WDT 进入空闲状态. */
                     wdt_init(WDT, wdt_mode, timeout_value, timeout_value);//初始化看门狗
                     printf("timeout_period=%d",(int)wdt_get_us_timeout_period(WDT, BOARD_FREQ_SLCK_XTAL));
                     /*配置和使能看门狗中断. */
                     NVIC_DisableIRQ(WDT_IRQn);
                     NVIC_ClearPendingIRQ(WDT_IRQn);
                     NVIC_SetPriority(WDT_IRQn, 0);
                     NVIC_EnableIRQ(WDT_IRQn);
                     //配置LED灯,
                     gpio_configure_pin(LED1_GPIO, LED0_FLAGS);
                     gpio_configure_pin(LED2_GPIO, LED1_FLAGS);
                     gpio_set_pin_high(LED1_GPIO);
                     pmc_enable_periph_clk(ID_PIOA);
                     //按键2配置
                     gpio_configure_pin(BUTTON_2, BUTTON_INPUT);//add by luyj 2013.6.8

                           while(1)
                     {
                             /* 在指定时间启动看门狗. */
                             if (g_b_systick_event == true) {
                                     g_b_systick_event = false;
                                     if ((g_ul_ms_ticks% WDT_RESTART_PERIOD)==0) {
                                             printf("2s"); //  2s打印一次,提示该喂狗了,此时按键喂狗,
                                     }
                             }
                             if (pio_get(PIOA, PIO_TYPE_PIO_INPUT, PIO_PA0) == 0)                    
                             {
                                     mdelay(100);
                                     if (pio_get(PIOA, PIO_TYPE_PIO_INPUT, PIO_PA0) == 0)
                                     {
                                             printf("PUSH BUTTON 2!\r");
                                             wdt_restart(WDT);//喂狗,
                                             gpio_set_pin_low(LED2_GPIO); //LED2亮
                                             gpio_set_pin_high(LED1_GPIO);//LED1灭
                                     }
                             }    
                     }
             }      

将程序下载入传感板,运行程序,看门狗中断时,LED1被点亮,LED2被关闭,认为看门狗溢出,同时如果调试串口打开(115200,无校验,数据位8bit,停止位1bit),可以看到打印信息。

喂狗可以通过按键操作,按下K2,表示喂狗,当喂狗时,LED1被关闭,LED2被点亮,在3秒内及时喂狗,则不触发看门狗中断,LED1将处于关闭状态。

               


你可能感兴趣的:(看门狗,WDT,看门狗基本原理)