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. I 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 I am using the PICALLW software, which is linked on my main
page, I 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 I 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. I 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. I have
removed the original comments, and I 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. I 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.