Sunday, July 7, 2013

Create Delay using Number of Cycles in PIC MCU

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.