The Watchdog Timer
We are now going to look at an internal timer, called a Watchdog Timer
So what is a Watchdog Timer?
Suppose you have written a program that is continuously running on a PIC. Now, you want to make sure that this program is always running, and that no matter what happens it will never stop. The first thing you would have, of course, is a loop back at the end of the program that brings us back to the start of the program. But consider this case. Let us say that the PIC is monitoring an input. When this input goes high, it jumps to another part of the program and waits for another pin to go high. If the second pin doesn’t go high, the PIC will just sit there and wait. It will only exit if the second pin goes high. Let us consider another example. Suppose you have written a program. You have compiled it successfully, and you have even simulated it over and over again using a simulator such as MPLAB. Everything seems to work fine. You program the PIC and place it into a circuit. However after a long period of time, the program gets stuck somewhere and the PIC gets caught in a loop. What’s needed in both cases is some kind of reset if the program gets stuck. This is the purpose of a watchdog circuit.
A watchdog circuit is nothing new. Many microprocessors and microcontrollers have them. But how does it work? Well, inside the PIC there is a resistor/capacitor network. This provides a unique clock, which is independent of any external clock that you provide in your circuit. Now, when the Watchdog Timer (abbreviated to WDT) is enabled, a counter starts at 00 and increments by 1 until it reaches FF. When it goes from FF to 00 (which is FF + 1) then the PIC will be reset, irrespective of what it is doing. The only way we can stop the WDT from resetting the PIC is to periodically reset the WDT back to 00 throughout our program. Now you can see that if our program does get stuck for some reason, then the WDT will not be set. The WDT will then reset the PIC, causing our program to restart from the beginning.
In order to use the WDT, we need to know three things. First, how long have we got before we need to reset the WDT, secondly how do we clear it. Finally, we have to tell the PIC programming software to enable the WDT inside the PIC. Let’s look at these separately.
WDT Times
The PIC data sheet specifies that the WDT has a period from start to finish of 18mS. This is dependant several factors, such as the supply voltage, temperature of the PIC etc. The reason for the approximation is because the WDT clock is supplied by an internal RC network. The time for an RC network to charge depends on the supply voltage. It also depends on the component values, which will change slightly depending on their temperature. So, for the sake of simplicity, just take it that the WDT will reset every 18mS. We can, however, make this longer. Inside the PIC is a thing called a Prescaler. We can program this prescaler to divide the RC clock. The
more we divide the RC clock by, the longer it takes for the WDT to reset.
The prescaler is located in the OPTION register at address 81h, bits 0 to 2 inclusive. Below is a table showing the bit assignments with the division rates and the time for the WDT to time out:
Bit 2,1,0 |
Rate |
WDT Time |
0,0,0 |
1:1 |
18mS |
0,0,1 |
1:2 |
36mS |
0,1,0 |
1:4 |
72mS |
0,1,1 |
1:8 |
144mS |
1,0,0 |
1:16 |
288mS |
1,0,1 |
1:32 |
576mS |
1,1,0 |
1:64 |
1.1Seconds |
1,1,1 |
1:128 |
2.3Seconds |
Remember these times are irrespective of your external clock frequency. Think of these times as real time, rather than clock times. To help make this clear, let us suppose we want the WDT to reset our PIC after about half a second as a failsafe. The nearest we have is 576mS, or 0.576 seconds. All we do is send b’101’ to our OPTION register, as follows:
movlw b’101’ ;This is 0x05 in Hex
movwf 81h ;This is the Option Register
Simple, really. Now, there is a catch. By default the prescaler is assigned to the other internal timer. This means that we have to change the prescaler over to the WDT. First, we have to reset the other counter to 0 first. We then have to change to Bank 1 to assign the prescaler to the WDT and to set up the time, and then come back to Bank 0. The code is below, where xx is the prescaler time:
bcf STATUS,0 ;make sure we are in bank 0
clrf 01h ;address of the other timer – TMR0
bsf STATUS,0 ;switch to bank 1
clrwdt ;reset the WDT and prescaler
movlw b’1xxx’ ;Select the new prescaler value and assign
movwf OPTION ;it to WDT
bcf STATUS,0 ;come back to bank 0
The CLRWDT command above is how we clear the WDT before it resets the PIC. So, all we need to do is calculate where in our program the WDT will time out, and then enter the CLRWDT command just before this point to ensure the PIC doesn’t reset. If your program is long, bear in mind that you may need more than one CLRWDT. For example, if we use the default time of 18mS, then we need to make sure that the program will see CLRWDT every 18mS.
So now we come to the point where we need to work out how long our code takes in real time. The principle is very simple, but could cause you to pull your hair out!
Instruction Timing
As you are probably already aware, the PIC takes the external clock timing and divides it by 4. This internal time is called an instruction cycle. Now if we have, say, a 4MHz xtal connected to the PIC, internally the PIC will run at 1MHz. In timing terms, this is 1/(4MHz/4) = 1uS. Now, some instructions take just one instruction cycle to complete, i.e. 1uS using a 4MHz crystal, while others take two cycles – 2uS – to complete. The data sheet tells us how many cycles each instruction takes. The easiest way to remember this is quite simple. Assume ALL instructions take 1 cycle. But, if an instruction causes the program to go somewhere else, then it will take 2 cycles. Let me give you a couple of examples. The movwf command takes only one cycle, because it is only moving data from one place to another. The goto command takes 2 cycles, because it is causing the Program Counter (PC) to go elsewhere in the program. The RETURN command takes 2 cycles, because it is causing the PC to go back in the program. We think you can see the pattern here. However, there are four commands which can take 1 or 2 cycles. These are DECFSZ, INCFSZ, BTFSC and BTFSS. These commands have one thing in common. They will skip the next instruction is a certain condition is met. If that condition is not met, then the next instruction will be carried out. For example, the DECFSZ
command will decrement the value stored in
the F register by 1. If the result is
not 0, then the next instruction will be
executed. This instruction therefore
takes 1 cycle. If the result is 0,
then the next instruction will be skipped,
and the one following that will be executed.
In this instance the instruction takes 2
cycles. The reason is that the
instruction alters the value of the PC.
It needs one cycle to carry out the
function, and it will need another to alter
the PC by an extra one.
To clarify this, let us look at a sample
code, and work out how many instruction
cycles it takes.
movlw 02
movwf COUNT
loop decfsz COUNT
goto loop
end
Our first instruction simply moves the value 02 into w. This does not cause the program to off course, therefore it is only 1 cycle. The next instruction is similar, in as much that it moves the contents of the w register into COUNT. Again, this will be 1 cycle. Now, the next instruction will first decrement COUNT by 1. This is 1 cycle. It will then do a test to see if COUNT is equal to 0. At this stage it doesn’t, and so we move onto the next instruction. The next instruction is a goto statement, and so is 2 cycles long. We come back to our decfsz instruction, which decrements COUNT by 1 again. This is another instruction cycle. It does a test to see if COUNT is equal to 0. This time it does, and so the next instruction is skipped. To skip the next instruction requires another cycle. We reach the end of the program. So in total, with the value 02 placed into COUNT, this program will take a total of 7 cycles. If we were using a 4MHz crystal for our clock, then the program will take:
1/(4MHz/4) = 1uS per cycle, therefore 7 cycles takes 7 x 1uS = 7uS.
So you can see that it can get a little confusing when you have instructions like DECFSZ.
Programmer Software
Inside the PIC there are things called ‘Fuses’. These are not the same as the fuses you would find in a mains plug, but electronic switches which are ‘blown’ by the programmer. Now, one of these fuses has to be ‘blown’ in order for the WDT to operate. There are two ways of doing this. One way is to write a couple of lines at the beginning of your program to tell the PIC programming software to enable or disable certain fuses. The other way is to tell the PIC programming software manually which fuses to enable. We will look at getting your program to instruct the programming software in a later tutorial, when we look at including other files and macros. To tell the programming software manually, varies from program to program. The documentation that came with the programmer should tell you how to do this. As We are using the PICALLW software, which is linked on my main page, We will explain how to do change fuses within this program. The fuses are configured by pressing the F3 key, or clicking on the ‘Config’ button. Then you can select the fuse you want enabled, in this case the WDT, by clicking on the box next to it.
Sample Program
Let us write a program, where we will turn on the WDT, and let the PIC perform a function. We will first of all periodically clear the WDT, to show that the program works, and then remove the CLRWDT
command to show that the PIC will indeed
reset.
The program We have chosen is the one used in tutorial 9 where we cause a row of LEDs to light up one at a time from left to right, then right to left. The circuit is shown below, and with the RC values shown will give us a clock frequency of 8KHz. This clock speed will allow us to actually see the LEDs moving one by one. We chose this program because it is slow enough for us to play with the WDT, and you can easily see when the PIC is reset. We have removed the original comments, and We have replaced them with a description of the WDT lines, a running total of the time from the start (assuming a 8KHz clock), and the number of clock cycles at each line.
TIME equ 9FH ; Variable for the delay loop.
PORTB equ 06H ; Port B address.
TRISB equ 86H ; Port B Tristate address.
PORTA equ 05H ; Port A address.
TRISA equ 85H ; Port A Tristate address.
STATUS equ 03H ; Page select register.
COUNT1 equ 0CH ; Loop register.
COUNT2 equ
0DH
; Loop register.
bsf STATUS,5 ; 1 cycle, 0.5mS
movlw 00H ; 1 cycle, 1.0mS
movwf TRISB ; 1 cycle, 1.5mS
movlw 00H ; 1 cycle, 2.0mS
movwf TRISA ; 1 cycle, 2.5mS
bcf STATUS,5 ; 1 cycle, 3.0mS
movlw 00H ; 1 cycle, 3.5mS
movwf PORTA ; 1 cycle, 4.0mS
; Start of main program
RUN
movlw 01H ; 1 cycle, 4.5mS
movwf PORTB ; 1 cycle, 5.0mS
call DELAY ; 2 cycles, 486mS
call DELAY ; 2 cycles, 967mS
; Move the bit on Port B left, then pause.
rlf PORTB,1 ; 1 cycle, 967.5mS
call DELAY ; 2 cycles, 1.45S
call DELAY ; 2 cycles, 1.93S
rlf PORTB,1 ; 1 cycle, 1.93S
call DELAY ; 2 cycles, 2.41S
call DELAY ; 2 cycles, 2.89S
rlf PORTB,1 ; 1 cycle, 2.89S
call DELAY ; 2 cycles, 3.37S
call DELAY ; 2 cycles, 3.85S
rlf PORTB,1 ; 1 cycle, 3.85S
call DELAY ; 2 cycles, 4.34S
call DELAY ; 2 cycles, 4.82S
rlf PORTB,1 ; 1 cycle, 4.82S
call DELAY ; 2 cycles, 5.30S
call DELAY ; 2 cycles, 5.78S
rlf PORTB,1 ; 1 cycle, 5.78S
call DELAY ; 2 cycles, 6.26S
call DELAY ; 2 cycles, 6.74S
rlf PORTB,1 ; 1 cycle, 6.74S
call DELAY ; 2 cycles, 7.22S
call DELAY ; 2 cycles, 7.70S
rlf PORTB,1 ; 1 cycle, 7.70S
; Now move onto Port A, and move the bit left.
rlf PORTA,1 ; 1 cycle, 7.70S
call DELAY ; 2 cycles, 8.19S
call DELAY ; 2 cycles, 8.67S
rlf PORTA,1 ; 1 cycle, 8.67S
call DELAY ; 2 cycles, 9.15S
call DELAY ; 2 cycles, 9.63S
rlf PORTA,1 ; 1 cycle, 9.63S
call DELAY ; 2 cycles, 10.11S
call DELAY ; 2 cycles, 10.59S
rlf PORTA,1 ; 1 cycle, 10.59S
call DELAY ; 2 cycles, 11.07S
call DELAY
; 2 cycles, 11.55S
; Move the bit back on Port A
rrf PORTA,1 ; 1 cycle, 11.55S
call DELAY ; 2 cycles, 12.04S
call DELAY ; 2 cycles, 12.52S
rrf PORTA,1 ; 1 cycle, 12.52S
call DELAY ; 2 cycles, 12.99S
call DELAY ; 2 cycles, 13.48S
rrf PORTA,1 ; 1 cycle, 13.48S
call DELAY ; 2 cycles, 13.96S
call DELAY ; 2 cycles, 14.44S
rrf PORTA,1 ; 1 cycle, 14.44S
; Now move the bit back on Port B
rrf PORTB,1 ; 1 cycle, 14.44S
call DELAY ; 2 cycles, 14.92S
call DELAY ; 2 cycles, 15.40S
rrf PORTB,1 ; 1 cycle, 15.40S
call DELAY ; 2 cycles, 15.89S
call DELAY ; 2 cycles, 16.37S
rrf PORTB,1 ; 1 cycle, 16.37S
call DELAY ; 2 cycles, 16.84S
call DELAY ; 2 cycles, 17.33S
rrf PORTB,1 ; 1 cycle, 17.33S
call DELAY ; 2 cycles, 17.81S
call DELAY ; 2 cycles, 18.29S
rrf PORTB,1 ; 1 cycle, 18.29S
call DELAY ; 2 cycles, 18.77S
call DELAY ; 2 cycles, 19.25S
rrf PORTB,1 ; 1 cycle, 19.25S
call DELAY ; 2 cycles, 19.73S
call DELAY ; 2 cycles, 20.22S
rrf PORTB,1 ; 1 cycle, 20.22S
call DELAY ; 2 cycles, 20.70S
call DELAY ; 2 cycles, 21.18S
goto RUN ; 2 cycles, 21.18S
; Subroutine to give a delay between bit
movements.
;Total of 957 cycles, 480mS
DELAY
movlw TIME ; 1 cycle
movwf COUNT1 ; 1 cycle
LOOP1 ;
decfsz COUNT1 ; 9F x 1 cycle + 1 cycle = 160 cycles
goto LOOP1 ; 9E x 2 cycles = 316 cycles
movwf COUNT1 ; 1 cycle
LOOP2 ;
decfsz COUNT1 ; 9F x 1 cycle + 1 cycle = 256 cycles
goto LOOP2 ; 9E x 2 cycles = 316 cycles
return ; 2 cycles
END ;
With an 8KHz clock, it takes just under 1 second for the next LED illuminates, and it takes a total of about 21 seconds to run from one end to the other and back again i.e. to go through the routine once only. The delay routine takes 480mS, and we are calling it twice before moving the bit on the ports. Now, we need to periodically reset the WDT. The largest time we can set the WDT is 2.3 seconds, and the next one down form this is 1.1 seconds. We have two options here. We could make a call to a subroutine to clear the WDT after the two delays have finished, or we could incorporate the CLRWDT within the delay itself. We have decided, for no real reason at all, to incorporate the CLRWDT within the delay loop.
TIME equ 9FH ; Variable for the delay loop.
PORTB equ 06H ; Port B address.
TRISB equ 86H ; Port B Tristate address.
PORTA equ 05H ; Port A address.
TRISA equ 85H ; Port A Tristate address.
STATUS equ 03H ; Page select register.
COUNT1 equ 0CH ; Loop register.
COUNT2 equ 0DH ; Loop register.
OPT equ 81h ; Option Register to control the WDT
;*************Set up the ports, WDT and prescaler****************** clrf 01h ;Clear TMR0
bsf STATUS,5 ;Switch to bank 1
clrwdt ;reset the WDT and prescaler
movlw b’1101’ ;Select the new prescaler value and assign
movwf OPT ;it to WDT
movlw 00H ; Now set up the ports
movwf TRISB ;
movlw 00H ;
movwf TRISA ;
bcf STATUS,5 ;Come back to bank 0
movlw 00H ;
movwf PORTA ;
;*************Start of main
program***************************** RUN
movlw 01H
;
movwf PORTB ;
call DELAY ;
call DELAY ;
; *************Move the bit on Port B left, then pause.**************
rlf PORTB,1 ;
call DELAY ;
call DELAY ;
rlf PORTB,1 ;
call DELAY ;
call DELAY ;
rlf PORTB,1 ;
call DELAY ;
call DELAY ;
rlf PORTB,1 ;
call DELAY ;
call DELAY ;
rlf PORTB,1 ;
call DELAY ;
call DELAY ;
rlf PORTB,1 ;
call DELAY ;
call DELAY ;
rlf PORTB,1 ;
call DELAY ;
call DELAY ;
rlf
PORTB,1
;
; *************Now move onto Port A, and move the bit left.***********
rlf PORTA,1 ;
call DELAY ;
call DELAY ;
rlf PORTA,1 ;
call DELAY ;
call DELAY ;
rlf PORTA,1 ;
call DELAY ;
call DELAY ;
rlf PORTA,1 ;
call DELAY ;
call DELAY ;
;************** Move the bit back on Port A************************
rrf PORTA,1 ;
call DELAY ;
call DELAY ;
rrf PORTA,1 ;
call DELAY ;
call DELAY ;
rrf PORTA,1 ;
call DELAY ;
call DELAY ;
rrf PORTA,1 ;
;****************** Now move the bit back on Port B******************
rrf PORTB,1 ;
call DELAY ;
call DELAY ;
rrf PORTB,1 ;
call DELAY ;
call DELAY ;
rrf PORTB,1 ;
call DELAY ;
call DELAY ;
rrf PORTB,1 ;
call DELAY ;
call DELAY ;
rrf PORTB,1 ;
call DELAY ;
call DELAY ;
rrf PORTB,1 ;
call DELAY ;
call DELAY ;
rrf PORTB,1 ;
call DELAY ;
call DELAY ;
goto RUN ; ; ******************Subroutine to give a delay between bit movements.******
DELAY
movlw TIME ;
movwf COUNT1 ;
LOOP1
;
decfsz COUNT1 ; goto LOOP1 ;
movwf COUNT1 ;
LOOP2 ;
decfsz COUNT1 ;
goto LOOP2
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; This part resets the WDT ;;
;;Comment out or remove this command to see the WDT ;;
;; in action. It should reset the PIC ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
clrwdt ;This simply resets the WDT.
;***************Return from our original DELAY routine***************
return
;
END ;
If you comment out, or remove the CLRWDT command, you will find that the PIC will not go past lighting the second LED. This is because the WDT is resetting the PIC. With the CLRWDT
in place, the program works as it should.
|