6.1看门狗概述
看门狗(WatchDog Timer) 定时器和PWM的定时功能目的不一样。它的特点是,需要不同的接收信号(一些外置看门狗芯片)或重新设置计数器,保持计数值不为0。一旦一些时间接收不到信号,或计数值为0,看门狗将发出复位信号复位系统或产生中断。
看门狗的作用是微处理器收到干扰进入错误状态后,使系统在一定时间间隔内复位。因此看门狗是保证系统长期、可靠和稳定运行的有效措施。目前大部分的嵌入式芯片内部都集成了看门狗定时器来提高系统运行的可靠性。
4412处理器的看门狗是当系统被故障干扰时,用于处理器的复位操作,也可以作为一个通用的16位定时器来请求中断操作。看门狗定时器产生128个PCLK周期的复位信号。主要特性有如下两个。
1)通用的中断方式的16位定时器。
2)当计数器减到0(发生溢出)时,产生128个PCLK周期的复位信号。
看门狗定时器功能框图如下:
看门狗模块包括一个预比例因子放大器,一个四分频的分频器,一个16位计数器。看门狗的时钟信号源来自PCLK,为了得到宽范围的看么狗信号,PCLK先被预分频,然后再进过分频器分频。预分频比例因子和分频器的分频值,都可以由看门狗控制寄存器(WTCON)决定,预分频比例因子的范围是0~255,分频器的分频比可以是16、32、64或128。看门狗定时器时钟周期的计算如下:
t_watchdog = 1/(PCLK/(Prescaler value + 1)/Division_factor
其中,Prescaler value为预分频比例放大的值;Division_factor为分频比。
一旦看门狗定时器被允许,看门狗定时器数据寄存器(WTDAT)的值就不能被自动地装在到看门狗定时器(WTCNT)中。因此,看门狗启动前要将一个初始值写入看门狗计数器(WTCNT)中。当4412用嵌入式ICE调试时,看门狗定时器的复位功能就不被启动,看门狗定时器能从CPU内核信号判断出当前CPU是否处于调试状态。如果看门狗定时器确定当前模式是调试模式,尽管看门狗产生溢出信号,但是仍然不会产生复位信号。
6.2相关寄存器
6.2.1看门狗控制寄存器(WTCON)
WTCON寄存器的内容包括:用户是否启动看门狗定时器、4个分频比的选择、是否允许中断产生、是否允许复位操作等。
如果用户想把看门狗定时当做一般定时器使用,应该中断使能,禁止看门狗定时器复位。WTCON描述如下:
6.2.2看门狗定时器数据寄存器(WTDAT)
WTDAT用于指定超时时间,在看门狗把复位功能禁止并打开中断使能后,此时看门狗定时器就是一个普通的定时器,使用方法和普通定时器一样。当使用复位功能后,由于WTCNT的值减到0时,系统就会复位,所以WTCNT的值装不进看门狗计数寄存器(WTCNT)中。复位后初始值为0x8000。WTDAT描述如下:
6.2.3看门狗计数寄存器(WTCNT)
WTCNT包含看门狗定时器工作的时候,计数器的当前计数值。WTCNT描述如下:
6.2.4看门狗中断清除寄存器(WTCLRINT)
6.3看门狗程序设计
以为看门狗是对系统地复位或中断的操作,所以不需要外围的硬件电路。要实现看门狗的功能,只需要对看门够的寄存器组进行操作,即对看门狗的控制寄存器(WTCON)、看门狗数据寄存器(WTDAT)、看门狗计数寄存器(WTCNT)的操作。
其一般流程如下:
1)设置看门狗中断操作,包括全局中断和看门狗中断使能及看门狗中断向量的定义,如果只是进行复位操作,这一步不用设置。
2)对看门狗控制寄存器(WTCON)的设置,包括设置预分频比例因子、分频器的分频值,中断使能和复位使能等。
3)对看门狗数据寄存器(WTDAT)和看门狗计数寄存器(WTCNT)的设置。
4)启动看门狗定时器。
6.3.1普通方式
一旦看门狗定时器被允许,看门狗的数据寄存器就不能被自动装载到WTCNT中,因此需要在启动前给WTCNT写入一个初值。如果不想自动复位还必须按时喂狗。
方式一:
不使用定时器,系统自动重载计数器值。
定义
struct exynos4412_watchdog {
unsigned int CON;
unsigned int DAT;
unsigned int CNT;
unsigned int CLRINT;
};
//WATCHDOG
#define WDT_ADDR ((volatile struct exynos4412_watchdog *)0x10060000)
#define WDT (*WDT_ADDR)
初始化
void watchdog_init(int prescaler ,int clock_select)
{
WDT.CON &= (~(0xFF<<8)) & (~(0x3 << 3));
// Disable Reset, Disable Interrupt, Disable WDT Timer
WDT.CON &= (~(0x1<<0)) & (~(0x1<<2)) & (~(0x1<<5));
WDT.CON |=((prescaler & 0x0FF)<<8) | ((clock_select & 0x3) << 3);
}
启动
void wdt_start_reset()
{
// Assert Reset
WDT.CON |= (0x1<<0);
}
传值
void watch_dat(int value)
{
WDT.DAT |=value;
WDT.CNT |=value;
}
主函数
void main(void)
{
watchdog_init(0xf,0x3);
wdt_start_reset();
watch_dat(0xffff);
while(1)
{
<其他操作>
}
}
方式二:
使用定时器.
定义
struct exynos4412_watchdog {
unsigned int CON;
unsigned int DAT;
unsigned int CNT;
unsigned int CLRINT;
};
//WATCHDOG
#define WDT_ADDR ((volatile struct exynos4412_watchdog *)0x10060000)
#define WDT (*WDT_ADDR)
初始化
void watchdog_init(int prescaler ,int clock_select)
{
WDT.CON &= (~(0xFF<<8)) & (~(0x3 << 3));
// Disable Reset, Disable Interrupt, Disable WDT Timer
WDT.CON &= (~(0x1<<0)) & (~(0x1<<2)) & (~(0x1<<5));
WDT.CON |=((prescaler & 0x0FF)<<8) | ((clock_select & 0x3) << 3);
}
启动
void wdt_start_reset()
{
// Assert Reset, Enable WDT Timer
WDT.CON |= (0x1<<0);
WDT.CON |= (0x1<<5);//设置定时器
}传值
void watch_dat(int value)
{
WDT.DAT |=value;
WDT.CNT |=value;
}
主函数
void main(void)
{
watchdog_init(0xf,0x3);
wdt_start_reset();
watch_dat(0xffff);//可以不需要调用该函数
while(1)
{
WDT.CNT=0xffff;//需要喂狗
<其他操作>
}
}
注意:其他操作的时间必须大于看门狗的定时时间。
6.3.2中断方式
Watchdog.c
定义
struct exynos4412_watchdog {
unsigned int CON;
unsigned int DAT;
unsigned int CNT;
unsigned int CLRINT;
};
//WATCHDOG
#define WDT_ADDR ((volatile struct exynos4412_watchdog *)0x10060000)
#define WDT (*WDT_ADDR)
初始化
void watchdog_init(int prescaler ,int clock_select)
{
WDT.CON &= (~(0xFF<<8)) & (~(0x3 << 3));
// Disable Reset, Disable Interrupt, Disable WDT Timer
WDT.CON &= (~(0x1<<0)) & (~(0x1<<2)) & (~(0x1<<5));
WDT.CON |=((prescaler & 0x0FF)<<8) | ((clock_select & 0x3) << 3);
}
启动中断
int wdt_start_interrupt(int timeout)
{
WDT.DAT = timeout;
WDT.CNT = timeout;
// Enable Interrupt, Enable WDT Timer
WDT.CON &= ~(0x1<<0); // De-Assert Reset
WDT.CON |= (0x1<<2) | (0x1<<5);
}
中断清除函数
int wdt_clear_pending()
{
WDT.CLRINT = 0x1;
}
看门狗处理函数
int wdt_handler()
{
puts("WDT Interrupt ...\r\n");
}
Irq.c
初始化
void irq_init()
{
//GIC interrupt controller:
CPU0.ICCICR |= 0x1; //Global enable for signaling of interrupts
ICDDCR = 0x1; // GIC monitors the peripheral interrupt signals and
// forwards pending interrupts to the CPU interfaces2
CPU0.ICCPMR = 0xFF; //The priority mask level.Priority filter. threshold
// WDT ID=75
// Priority of 75 = 0x3
ICDIPR.ICDIPR14 = (ICDIPR.ICDIPR14 & ~(0xFF<<8)) | (0x3<<8);
ICDIPTR.ICDIPTR14 |= 0x01000000; // sent to CPU 0 75/4 = 18 ...3
ICDISER.ICDISER1 |= (0x2 << 11); // Enable 75 75 / 32 = 2 ... 11
}
中断处理
void do_irq(void)
{
int irq_num;
// Get ID
irq_num = (CPU0.ICCIAR & 0x1FF);
switch (irq_num) {
case 75:
puts("ID 75 Interrupted ...\r\n"); //wdt_handler();
//Clear Pend
WDT.WTCLRINT =0x1; //wdt_clear_pending();
ICDICPR.ICDICPR1 |= 0x2 << 11;
break;
default: break;
}
// End of interrupt
CPU0.ICCEOIR = (CPU0.ICCEOIR & ~(0x1FF)) | irq_num;
}
Main.c
void main(void)
{
wdt_init_interrupt(255,0x3);
wdt_start_interrupt(0xffff);
irq_init();
while(1)
{
<其他操作>
}
}
参考代码请看附件。