Most of the delay code available on the Internet for PIC is based on real time delay. The problem with this approach is that it's hardcoded for certain clock frequency only. When we use the code for different frequency clock, we have to change the code, sometimes drastically.
Another approach is just to make a routine to waste number of instruction cycles, regardless of the clock frequency. The caller then later calculate how many cycles it needs to waste in order to get the wanted delay. It's more maintainable this way than the former. All PIC MCUs have each instruction executed in 4 clock frequency, or F_cy = F_clock/4, hence T_cy = 1/F_cy = 4/F_clock.
Here is the example of the code for PIC16F877A to waste 10 T_cy cycles. If F_clock is 16 MHz, F_Cy = 4MHz and T_Cy = 0.25 uSec. So 10*T_cy = 2.5 uSec delay.
I don't see any reason it won't be compiled and working on other pic16-based MCUs. I compiled the code with gpasm and header file from sdcc.
title "Delay 10 Tcy"
include <p16f_common.inc>
list n=0
radix dec
global _delay10tcy
extern _d1
; -----------------------------------------------------------------------
; Variables declaration
;DLY_VAR UDATA_SHR 0x190
;WREG res 1
WREG equ _d1
code_delay10tcy code
_delay10tcy:
; polynomial for 10tcy delay is f(x) = 10 + 10 * (x-1)
; caller takes 2 TCy, return takes 2 Tcy, so we need 6 more Tcy here
banksel WREG ; 2 Tcy
decf WREG,f ; (x-1), 1 TCy. TCy so far = 4 + 2 + 1 = 7
movfw WREG ; 1, TCy so far = 8
bz @delay10_end ; 2 Tcy if x=0, otherwise 1, TCy so far = 9 (if x>0)
nop ; 1 TCy, TCy so far = 10
@delay10_loop: ; (x-1) * 10
goto $+1 ; 2 TCy, TCy so far = 2
goto $+1 ; 2 TCy, TCy so far = 4
goto $+1 ; 2 TCy, TCy so far = 6
nop ; TCy so far 7
decfsz WREG, f ; TCy so far 8 (if x>0), else 9
goto @delay10_loop ; TCy so far 10
nop
@delay10_end:
return ; 2
end
Save the code to a file name delay10tcy.S.
To compile it on Linux:
gpasm -c -M -m --mpasm-compatible -e ON -DSDCC -Dpic16f877a -p16f877a -I/usr/local/share/sdcc/include/pic14 delay10tcy.S
The variable "_d1" above has been defined elsewhere as 8-bit user data (so it's shareable among other delayxtcl routines), but if we want to make it local just declare is such as:
DELAY_VAR udata_shr 0x190
d1 res 1
and remove underscore in all references to d1.
Another approach is just to make a routine to waste number of instruction cycles, regardless of the clock frequency. The caller then later calculate how many cycles it needs to waste in order to get the wanted delay. It's more maintainable this way than the former. All PIC MCUs have each instruction executed in 4 clock frequency, or F_cy = F_clock/4, hence T_cy = 1/F_cy = 4/F_clock.
Here is the example of the code for PIC16F877A to waste 10 T_cy cycles. If F_clock is 16 MHz, F_Cy = 4MHz and T_Cy = 0.25 uSec. So 10*T_cy = 2.5 uSec delay.
I don't see any reason it won't be compiled and working on other pic16-based MCUs. I compiled the code with gpasm and header file from sdcc.
title "Delay 10 Tcy"
include <p16f_common.inc>
list n=0
radix dec
global _delay10tcy
extern _d1
; -----------------------------------------------------------------------
; Variables declaration
;DLY_VAR UDATA_SHR 0x190
;WREG res 1
WREG equ _d1
code_delay10tcy code
_delay10tcy:
; polynomial for 10tcy delay is f(x) = 10 + 10 * (x-1)
; caller takes 2 TCy, return takes 2 Tcy, so we need 6 more Tcy here
banksel WREG ; 2 Tcy
decf WREG,f ; (x-1), 1 TCy. TCy so far = 4 + 2 + 1 = 7
movfw WREG ; 1, TCy so far = 8
bz @delay10_end ; 2 Tcy if x=0, otherwise 1, TCy so far = 9 (if x>0)
nop ; 1 TCy, TCy so far = 10
@delay10_loop: ; (x-1) * 10
goto $+1 ; 2 TCy, TCy so far = 2
goto $+1 ; 2 TCy, TCy so far = 4
goto $+1 ; 2 TCy, TCy so far = 6
nop ; TCy so far 7
decfsz WREG, f ; TCy so far 8 (if x>0), else 9
goto @delay10_loop ; TCy so far 10
nop
@delay10_end:
return ; 2
end
Save the code to a file name delay10tcy.S.
To compile it on Linux:
gpasm -c -M -m --mpasm-compatible -e ON -DSDCC -Dpic16f877a -p16f877a -I/usr/local/share/sdcc/include/pic14 delay10tcy.S
The variable "_d1" above has been defined elsewhere as 8-bit user data (so it's shareable among other delayxtcl routines), but if we want to make it local just declare is such as:
DELAY_VAR udata_shr 0x190
d1 res 1
and remove underscore in all references to d1.