# How to produce time delays in PIC18 ASM programming?

I'm a newbie with microcontrollers programming. I have a high level programming understanding but I'm getting involved in low lever CPU/Microcontrollers development.

I want to do this simple exercise of getting an array of LED's on and off for 1 second each.

I tried this assembly (thinking about two leds):

    List    P=18F4550
include <P18F4550.inc>

CONFIG FOSC = INTOSC_EC ;INTOSC_EC          ; Internal oscillator
CONFIG PWRT= ON             ; Power-up Timer Enable bit
CONFIG BOR=OFF              ; Brown-out Reset disabled in hardware and software
CONFIG WDT=OFF              ; WDT disabled
CONFIG MCLRE=ON             ; MCLR pin enabled
CONFIG PBADEN=OFF           ; PORTB<4:0> pins are configured as digital I/O
CONFIG LVP=OFF              ; Single-Supply ICSP disabled
CONFIG DEBUG = OFF                  ; Background debugger disabled
CONFIG XINST = OFF          ; Extended Instruction disabled
;******************************Variables***********************************
count equ 0x00

;**********************************************************************************

org     0x0000
movlw   0x62
movwf   OSCCON     ;Working at 4 MhZ
clrf    TRISD      ;D port as output
;leds OFF so far

LGHTLOOP
bcf PORTD,0
call DELAY

bsf PORTD,0
CALL DELAY

bcf PORTD,1
call DELAY

bsf PORTD,1
CALL DELAY

bra  LIGHTLOOP

DELAY
movlw   .1000
movwf   count

LOOP2
DECFSZ   count,F

bra  LOOP2

nop

return

end


I tried simulate it at Proteus software, having this configuration:

I can actually see leds getting ON and OFF but nothing like a second, no matter how I change movlw .255 at DELAY subroutine. I tried with different higher values and got same wierd behaviour.

What did I get wrong?

Counting the instructions cycles at subroutines with movlw .255 should be more than 1 second I think. But still simulation shows it faster. Adding two more leds makes it seem even faster.

• Yeah! I just reck it on. I tried with 255 but I'm not realling getting a second ... – diegoaguilar Jun 8 '14 at 18:28
• Yeah, sorry.. I translated the code, I'm editing it now. – diegoaguilar Jun 8 '14 at 18:33
• As I know if I want to put decimals, compiler will know it's a base 10 number prefixing a dot – diegoaguilar Jun 8 '14 at 18:40
• I don't know how many cycles the DECFSZ instruction takes, but if you're counting 255 ticks on a 4MHz clock your delay will be 63.75µs. Even if it took five cycles for the instruction you'd still never see a blink. – Samuel Jun 8 '14 at 18:46
• Well that's why I have a nop at last. That should be inducing the retard. – diegoaguilar Jun 8 '14 at 18:52

The problem is that the decfsz instruction implicitly works on a one-byte (8-bit) register. The largest value that such a register can hold is hex FF (decimal 255). When you attempt to initialize count to decimal 1000 (hex 3E8), you're really just setting that register to the low-order 8 bits of that value, or E8 (decimal 232).

To get around this limitation of the decfsz instruction, the usual technique is to create a "nest" of two loops, each using a separate variable. This would allow you to execute the inner loop body as many as 65535 times (hex 0xFFFF).

    ; control variable for inner loop
i equ 0

; control variable for outer loop
j equ 1

; initialize j with the MSBs of the loop count
movlw .1000 >> 8
movwf j
; initialize i with the LSBs of the loop count
movlw .1000 & 0xFF
movwf i

loop:

; body of loop here

nop

; end of inner loop
decfsz i
bra loop

; end of outer loop
decfsz j
bra loop


Note that the inner loop runs the number of times i is initialized to on the first iteration of the outer loop. Every time after that, it runs 256 times.

• Thanks, I checked it and it works nice. The problem is the time. I want delays to be 1 second – diegoaguilar Jun 8 '14 at 20:51
• OK, with a 4 MHz CPU clock, you need to execute instructions that add up to 1,000,000 machine cycles. If the loop body is just a single nop, then each iteration will require 4 machine cycles, which means you'll need to execute 250,000 iterations. This is too many for the double-nested loop, so you can either put more instructions into the loop body, or you can add a third level of looping. – Dave Tweed Jun 8 '14 at 21:16
• Or use a more appropriate delay mechanism, such as the timer module acting as a counter, with the input scaled from the oscillator. – David Jun 8 '14 at 21:18
• @David can you offer an example? – diegoaguilar Jun 8 '14 at 21:24
• @DaveTweed can you explain a bit more? – diegoaguilar Jun 8 '14 at 21:24

This is what you need:

http://www.piclist.com/cgi-bin/delay.exe

probably the most useful site ever created. (After this one!)

It should be noted that whilst this will solve your problem, it will also teach you absolutely nothing about how to go about programming your own, although if you read the code it generates you can work it out.

The other answer should be read properly if you want a real understanding. This is just to get stuff working.

• Can you offer an example about how to use the generated code in own code? – diegoaguilar Jun 16 '14 at 14:14
• Type in what you need, in terms of seconds or clock cycles, input your clock freq, give it a unique name (my1SecDelay), then hit the generate button. The site you are taken to provides the code for you, all you need to do is copy and paste it, but MAKE SURE you declare the variables at the top of your code, in ASM its something like: delay1 equ 0x28h. then you are done, just call it. – Tim Mottram Jun 16 '14 at 14:19
• To use it you just use "call my1SecDelay" or what ever you have named it. – Tim Mottram Jun 16 '14 at 14:22
• Sorry, edit: If you have declared the var's it needs, at the top you your code, then you don't need the start of it up to the point when it says ;4999 (or however many) cycles. I.E throw away the "cblock d1 d2 d3 endc" Part – Tim Mottram Jun 16 '14 at 14:24