看门狗定时器用来当系统由于噪声或者系统错误而malfunction时将系统恢复过来。它可以用作一个普通的16位内部定时器,来产生中断请求。看门狗定时器产生128个PCLK周期的复位信号。
看门狗定时器使用PCLK作为时钟源,首先经过一个8位预分频器,然后再分频一次,再分频系数可为16, 32, 64或128,这样就产生看门狗定时器时钟,频率为:
f = PCLK / (1 + prescaler value) / division_factor
一旦看门狗定时器开启,定时器数据的值不会自动改变,所以需要在看门狗定时器启动前写入初始值。
当CPU处于调试模式时,看门狗不会输出复位信号。一旦DBGACK信号asserted。
Watchdog timer control (WTCON) register
用来开启/关闭看门狗、选择时钟源,开启/关闭中断,开启/关闭定时器输出。
Register | Address | R/W | Desc | Reset value |
WTCON | 0x53000000 | R/W | Watchdog timer control register | 0x8021 |
WTCON | bit | desc | inital state |
Prescaler value | [15:8] | Prescaler value 0-255 | 0x80 |
Reserved | [7:6] | must be 00 | 00 |
watchdog timer | [5] | enable or disable bit of watchdog timer 0 = disable 1 = enable | 1 |
clock select | [4:3] | determine the clock division factor 00: 16 01:32 10:64 11:128 | 00 |
interrupt generation | [2] | enable or disable bit of the interrupt 0 = disable 1 = enable | 0 |
reserved | [1] | must be 00 | 0 |
reset enable/disable | [0] | enable or disable bit of watchdog timer output for reset signal 1: assert reset signal, at watchdog time-out 0: disable the reset function of the watchdog timer | 1 |
Watchdog timer data (WTDAT) register
确定超时的长度,第一次启动看门狗不会自动加载到WTCNT,只有在超时时才会自动加载到WTCNT。
Register | Address | R/W | Desc | Reset value |
WTDAT | 0x53000004 | R/W | Watchdog timer data register | 0x8000 |
[15:0] count reload value
Watchdog timer data (WTCNT) register
首次启动时,不会从WTCNT加载,所以需要手动设置,WTCNT可以在到达0之前随时手动设置,这叫喂狗。到达0后,根据WTCON的配置,可产生中断,也可产生reset,不reset的话,它会自动从WTDAT拿数据,重新开始一次定时。
Register | Address | R/W | Desc | Reset value |
WTCNT | 0x53000008 | R/W | Watchdog timer COUNT register | 0x8000 |
[15:0] the current count value of the watchdog timer
要求:开机时,3个灯灭,按任何一个键,开启看门狗,3个等轮流亮,5s内需要再次按键喂狗,不喂狗超时发生reset。
init.c的本实战相关的代码如下,有个变量pollmod表示是否有按键按下,按下之后就变成了1,main函数一开始关闭所有灯,然后进入循环,如果pollmod不为0,则跑马亮灯。中断里面,如果pollmod为0,表示第一次按下键,初始化看门狗,并将pollmod置为1,再次按键后,只执行喂狗的操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | static int count = 0; void start_watchdog() { #define PCLK_HZ (67*1000*1000) #define WATCHDOG_TIMER 5 #define prescaler 255 #define division_factor 128 count = PCLK_HZ / prescaler / division_factor * WATCHDOG_TIMER; WTCNT = count; // 15:8 prescaler value // 5: watchdog timer enable // 4:3 clock division factor, 11:128 // 0 reset enable / disable WTCON = (255 << 8) | (1 << 5) | (3 << 3) | 1; } void feed_watchdog() { WTCNT = count; } static int pollmod = 0; void main() { register int i = 0; register int led = 4; GPFDAT = -1; while (1) { i++; if (i == 300000) { if (pollmod) GPFDAT = ~(1 << led); led++; if (led == 7) led = 4; i = 0; } } } void irq_handler() { unsigned long off = intcont->INTOFFSET; if (pollmod == 0) { pollmod = 1; start_watchdog(); } else feed_watchdog(); if (off == 5) { EINTPND = 1 << 11; } intcont->SRCPND = 1 << off; intcont->INTPND = 1 << off; } |
因为要在main和中断处理函数中共享,使用全局变量pollmod,由于其初始值为0,编译器缺省将其放入.bss section,链接时.bss section的大小为0,所以编出的bin是没有pollmod的内容的。而编出的固件小于4K nand页大小,没有数据的内容在nand flash中默认擦除为0xff,这其实看烧录程序是怎么做的。所以这里通过-fno-zero-initialized-in-bss选项将初始化为0的变量放入.data section,这样板子起来后,pollmod为正确的值。
1 | $(CC) -c -o init.o init.c -O0 -g -fno-zero-initialized-in-bss |
当然也在可以代码里面加一个pollmod = 0。
参考
https://os.mbed.com/cookbook/WatchDog-Timer