ARM 看门狗定时器
一、什么是看门狗、有什么用
(1) 看门狗定时器和普通的定时器并无本质区别。定时器可以设定一个时间,在这个时间完成之前定时器不断计时,时间到的时候,定时器会复位 CPU(重启系统)。
(2 )系统正常工作的时候当然不希望被重启,但是系统受到干扰、极端环境等可能会产生异常工作或者不工作,这种状态可能会造成不良影响(至少是不工作),此时解决方案就是重启系统。
(3) 普通设备重启不是问题,但是有些设备人工重启存在困难。这时候我们希望系统能够自己检验自己是否已经跑飞,并且在意识到自己跑飞的时候,可以很快的(几个ms或者更短)自我重启。这个功能就要靠看门狗定时器来实现。
(4) 典型应用的情景是:我们在应用程序中打开看门狗设备,初始化好给它一个时间,然后应用程序使用一个线程来喂狗,这个线程的执行时间安全短于看门狗的复位时间。当系统(或者应用程序)异常后,喂狗线程自然就不工作了,然后到时候看门狗就会复位。
(5) 补充:实战中有时候为了绝对的可靠,我们并不会用 SoC 中自带的看门狗,而是使用专门的外置的看门狗芯片来实现看门狗。
二、S5PV210 看门狗定时器的结构框图
看门狗定时器使用的时钟域及其频率。
(1) PCLK_PSYS 经过两级分频后生成 WDT(watchdog timer)的时钟周期,然后把要定的时间写到 WTDAT 寄存器中,刷到 WTCNT 寄存器中去减 1,减到 0 时(定时时间到)产生复位信号或中断信号。
(2) 典型应用中是配置为产生复位信号,我们应该在 WTCNT 寄存器减到 0 之前给 WTDAT 寄存器中重新写值以喂狗。
(3) 注意,看门狗定时器的计数值无法自动装载。在第一次使能看门狗之前,需要手动装载看门狗定时器的计数值。
三、看门狗定时器的主要寄存器
1、WTCON
2、WTDAT
3、WTCNT
4、WTCLRINT
四、看门狗定时器的编程实践
1、产生中断信号
文件名:wdt.c
#include "stdio.h"
#include "init.h"
#define WTCON (0xE2700000)
#define WTDAT (0xE2700004)
#define WTCNT (0xE2700008)
#define WTCLRINT (0xE270000C)
#define rWTCON (*(volatile unsigned int *)WTCON)
#define rWTDAT (*(volatile unsigned int *)WTDAT)
#define rWTCNT (*(volatile unsigned int *)WTCNT)
#define rWTCLRINT (*(volatile unsigned int *)WTCLRINT)
unsigned int pow(unsigned int base, unsigned int exponent)
{
if (base == 2)
{
return (0x1 << exponent);
}
else
{
unsigned int sum = 1, temp = 0;
for (temp = 0; temp < exponent; temp++)
{
sum *= base;
}
return sum;
}
}
/*************************************************************************/
#define BIT_LOCATION_WTCON_PRESCALER_VALUE (0xff << 8)
#define BIT_WTCON_PRESCALER (pow(2,8))
#define WTCON_FUNC_WDT_TIMER_ENABLE (0x1 << 5)
#define WTCON_FUNC_WDT_TIMER_DISABLE (0x0 << 5)
#define BIT_LOCATION_WTCON_CLOCK_SELECT (0b11 << 3)
#define WTCON_FUNC_CLOCK_SELECT_16 (0b00 << 3)
#define WTCON_FUNC_CLOCK_SELECT_32 (0b01 << 3)
#define WTCON_FUNC_CLOCK_SELECT_64 (0b10 << 3)
#define WTCON_FUNC_CLOCK_SELECT_128 (0b11 << 3)
#define WTCON_FUNC_INTERRUPT_GENERATION_ENABLE (0x1 << 2)
#define WTCON_FUNC_INTERRUPT_GENERATION_DISABLE (0x0 << 2)
#define BIT_LOCATION_WTCON_RESET (0b1 << 0)
#define WTCON_FUNC_RESET_ENABLE (0x1 << 0)
#define WTCON_FUNC_RESET_DISABLE (0x0 << 0)
//初始化 WDT 使之可以产生中断
void wdt_init_interrupt(void)
{
//第一步,设置好预分频器和分频器,得到时钟周期是 128 us
rWTCON &= ~(BIT_LOCATION_WTCON_PRESCALER_VALUE);
//t_watchdog = 1/( PCLK / (Prescaler value + 1) / Division_factor )
rWTCON |= (65 * BIT_WTCON_PRESCALER); // 1MHZ
rWTCON &= ~(BIT_LOCATION_WTCON_CLOCK_SELECT);
rWTCON |= (WTCON_FUNC_CLOCK_SELECT_128); // 1/128 MHZ, T = 128us
//第二步,设置中断和复位信号的使能或禁止
rWTCON |= (WTCON_FUNC_INTERRUPT_GENERATION_ENABLE); // enable wdt interrupt
rWTCON &= ~(BIT_LOCATION_WTCON_RESET);
rWTCON |= (WTCON_FUNC_RESET_DISABLE); //disable wdt reset
//第三步,设置定时时间
//WDT 定时计数个数,最终定时时间为这里的值 x 时钟周期
rWTDAT = 10000; //定时 1.28s
rWTCNT = 10000; //定时 1.28s
//第四步,先把所有寄存器都设置好之后,再去打开看门狗
rWTCON |= (WTCON_FUNC_WDT_TIMER_ENABLE); //enable wdt
}
//wdt 的中断处理程序
void isr_wdt(void)
{
static int i = 0;
//看门狗定时器时间到了,应该做的有意义的事情
printf("wdt interrupt, i = %d...\r\n", i++);
//清除中断
intc_clearVectaddr();
rWTCLRINT = 1;
}
现象截图:可以看到,wdt 中断的时间确实是 1.28s左右发生一次。
2、产生复位信号
#include "stdio.h"
#include "init.h"
#define WTCON (0xE2700000)
#define WTDAT (0xE2700004)
#define WTCNT (0xE2700008)
#define WTCLRINT (0xE270000C)
#define rWTCON (*(volatile unsigned int *)WTCON)
#define rWTDAT (*(volatile unsigned int *)WTDAT)
#define rWTCNT (*(volatile unsigned int *)WTCNT)
#define rWTCLRINT (*(volatile unsigned int *)WTCLRINT)
unsigned int pow(unsigned int base, unsigned int exponent)
{
if (base == 2)
{
return (0x1 << exponent);
}
else
{
unsigned int sum = 1, temp = 0;
for (temp = 0; temp < exponent; temp++)
{
sum *= base;
}
return sum;
}
}
/*************************************************************************/
#define BIT_LOCATION_WTCON_PRESCALER_VALUE (0xff << 8)
#define BIT_WTCON_PRESCALER (pow(2,8))
#define WTCON_FUNC_WDT_TIMER_ENABLE (0x1 << 5)
#define WTCON_FUNC_WDT_TIMER_DISABLE (0x0 << 5)
#define BIT_LOCATION_WTCON_CLOCK_SELECT (0b11 << 3)
#define WTCON_FUNC_CLOCK_SELECT_16 (0b00 << 3)
#define WTCON_FUNC_CLOCK_SELECT_32 (0b01 << 3)
#define WTCON_FUNC_CLOCK_SELECT_64 (0b10 << 3)
#define WTCON_FUNC_CLOCK_SELECT_128 (0b11 << 3)
#define BIT_LOCATION_WTCON_INTERRUPT (0b1 << 2)
#define WTCON_FUNC_INTERRUPT_GENERATION_ENABLE (0x1 << 2)
#define WTCON_FUNC_INTERRUPT_GENERATION_DISABLE (0x0 << 2)
#define BIT_LOCATION_WTCON_RESET (0b1 << 0)
#define WTCON_FUNC_RESET_ENABLE (0x1 << 0)
#define WTCON_FUNC_RESET_DISABLE (0x0 << 0)
//初始化 WDT 使之可以产生中断
void wdt_init_interrupt(void)
{
//第一步,设置好预分频器和分频器,得到时钟周期是 128 us
rWTCON &= ~(BIT_LOCATION_WTCON_PRESCALER_VALUE);
//t_watchdog = 1/( PCLK / (Prescaler value + 1) / Division_factor )
rWTCON |= (65 * BIT_WTCON_PRESCALER); // 1MHZ
rWTCON &= ~(BIT_LOCATION_WTCON_CLOCK_SELECT);
rWTCON |= (WTCON_FUNC_CLOCK_SELECT_128); // 1/128 MHZ, T = 128us
//第二步,设置中断和复位信号的使能或禁止
rWTCON &= ~(BIT_LOCATION_WTCON_INTERRUPT);
rWTCON |= (WTCON_FUNC_INTERRUPT_GENERATION_DISABLE); // disable wdt interrupt
rWTCON &= ~(BIT_LOCATION_WTCON_RESET);
rWTCON |= (WTCON_FUNC_RESET_ENABLE); //enable wdt reset
//第三步,设置定时时间
//WDT 定时计数个数,最终定时时间为这里的值 x 时钟周期
rWTDAT = 10000; //定时 1.28s
rWTCNT = 10000; //定时 1.28s
//第四步,先把所有寄存器都设置好之后,再去打开看门狗
rWTCON |= (WTCON_FUNC_WDT_TIMER_ENABLE); //enable wdt
}
//wdt 的中断处理程序
void isr_wdt(void)
{
static int i = 0;
//看门狗定时器时间到了,应该做的有意义的事情
printf("wdt interrupt, i = %d...\r\n", i++);
//清除中断
intc_clearVectaddr();
rWTCLRINT = 1;
}
现象截图:可以看到,定时1s之后,芯片就复位了,不会再打印信息。
源自朱友鹏老师.