| Name: | Mr. John W Nall |
| Web Site: | |
Code:
delay.htm
Precision delay routine for 18F452 at 40 MHz. Entry points for delays of 1 second, 100 milliseconds, 10 milliseconds and 1 millisecond. Voluminous comments. Easily adapted to other clock speeds.
;
; General delay routine, for delaying 1 millisecond, 10 milliseconds,
; 100 milliseconds, or 1 second. Written specifically for the 18F452 chip
; running at 40Mhz.
;
; As written, the routine is set up to be assembled as relocable object code,
; and linked with other object routines. To use as absolute code, just extract
; the pertinent code and paste it into your routine.
; Use: call d_1_ms (or any of the other entry points) from your routine
;
; Any change in clock speed will require changes to the constants used.
;
; We are using Timer0 for the delay, loading a constant value into the counter which
; the timer increments once each instruction cycle. Therefore, the only things that
; need to be changed are the constants. Since the delay routine offers a choice of
; 1ms, 10ms, 100ms or 1 second delays, there are four constants generated. Each constant
; represents 65535 minus the number of instructions cycles to be performed until the
; specific time value is reached. Since the timer increments a 16-bit counter and sets
; a bit when the counter overflows, then we merely have to start the value of the counter
; at 65535 less the number of instruction cycles.
;
; EXAMPLE: With a 40MHZ clock (10 MHz crystal using the PLL option of the 18F452), the
; instructions are executed with a 10Mhz clock (40MHZ/4) since the clock speed is divided
; down by a factor of 4. Therefore, 10 million instructions are executed every second.
; One instruction is executed in 100 nanoseconds.
; 6 -6 -9
; (1 second)/(10 * 10 ) = .1 * 10 = 100 * 10
;
; Incrementing a 16-bit counter every 100 nanoseconds, it would not take very long before it
; overflows. Luckily, Timer0 has the capability to prescale, using factors of 2, 4, 8, 16,
; 32, 64, 128 or 256. If we use the 256 prescale, then the counter is only incremented
; once for each 256 instruction cycles, which makes things a bit more doable. Specifically,
; the 16-bit counter would not overflow for 1.664 seconds, if we started at zero.
;
; If, on the other hand, we used the 128 prescale factor, the timer would overflow in 832
; milliseconds. So we would have to make another run to get up to 1 second (although it
; would work for the others). So we use 256 to make sure we can get everything up to and
; including 1 second during just one run of timer.
;
; Now, since we are dividing our actual clock speed (10 MIPS) by 256, the effective
; clock speed (for the purpose of incrementing the counter) will be 39.0625 KHz.
; 6
; (10 * 10 )/256 = 39062.5 instructions per second
;
; This means, of course, that rather than being incremented every 100 nanoseconds,
; the counter is incremented every 25.6 microseconds
; 3 -3 -6
; (1 second)/(39.0625 * 10 ) = 0.0256 * 10 = 25.6 * 10
;
; Since 39062.5 instructions execute in one second, then what we need to do is to set our
; one second counter to 65535 - 39063 (rounding up) = 26472. If it starts there, and we
; then activate the timer, it will do an additional 39063 increments and set the overflow
; bit.
;
; This makes it easy to set the constants for 100 ms, 10 ms and 1 ms, since we just have
; to divide by some power of ten.
;
; 100 ms = .1 seconds, 10 ms = .01 seconds, and 1 ms = .001 seconds.
;
; To get the figure for 100 ms, we have 65535 - 3906 = 61629.
;
; Likewise, for 1 ms, the constant is 65496
; 10 ms, 65144
; 1 second, 26472
;
; All of which means that we have finally gotten to the routine.
#include <p18f452.inc>
global d_1_ms, d_10_ms, d_100_ms, d_1_sec
radix dec
;
; Generate the constants which will be loaded into TMR0L and TMR0H
; These will have to be changed if a different clock speed is used.
; Just use the technique as outlined above to calculate new constants.
; Also note the "radix dec" statement above. These are decimal numbers. :)
d_1ms_h equ 65496/256 ; generate the high order byte
d_1ms_l equ 65496 % 256 ; and the low order byte (modulus 256)
d_10ms_h equ 65144/256
d_10ms_l equ 65144 % 256
d_100ms_h equ 61629/256
d_100ms_l equ 61629 % 256
d_1sec_h equ 26472/256
d_1sec_l equ 26472 % 256
d_code code
d_1_ms movlw d_1ms_h ; if called to delay 1 millisecond
movwf TMR0H
movlw d_1ms_l
bra delay
d_10_ms movlw d_10ms_h ; if called to delay 10 milliseconds
movwf TMR0H
movlw d_10ms_l
bra delay
d_100_ms movlw d_100ms_h ; if called to delay 100 milliseconds
movwf TMR0H
movlw d_100ms_l
bra delay
d_1_sec movlw d_1sec_h ; if called to delay 1 second
movwf TMR0H
movlw d_1sec_l
delay movwf TMR0L ; saved a few instructions :-)
;
; Set the necessary bits for the timer
;
clrf T0CON ; start with a virgin
;
; 16-bit timer, use internal instruction clock, use prescaler, 1:256
;
movlw B'00000111'
iorwf T0CON,1
bcf INTCON,TMR0IF ; clear the overflow bit
tlp1 btfsc INTCON,TMR0IF
bra tlp1 ; make sure overflow bit is clear
bsf T0CON,TMR0ON ; enable the timer
;
; Now, just hang out, looking cool, and wait for the bit to be set
;
tlp2 btfss INTCON,TMR0IF
bra tlp2
bcf T0CON,TMR0ON ; disable timer
return
end
Interested: