3

Edit: PIC 16F684

Okay, I have a simple 3 LED binary clock that counts from 0-7, and want to add a delay of approx 1 second between each light turning on.

I've worked out that each light needs to be in a sort of loop, and I have to use a count to measure ticks, and rollover etc.

I think the clock is 4MHz, here's a screenshot of the manual: https://i.stack.imgur.com/3A3sJ.png

Here's the relevant extracts from my code:

COUNT1 EQU 20h      ; Delay counter #1
COUNT2 EQU 21h      ; Delay counter #2

...

LOOP0
        MOVLW   TRIS_D0_D1      ; Move value defined in Constants to TRISA, to switch on LED 0.
        TRIS    PORTA           ;
        CLRF    PORTA           ; Clear all outputs.
        MOVLW   D0              ; Set the accumulator to the value of D0.
        MOVWF   PORTA           ; Move the accumulator to PORTA, to switch on LED 0.

    ; Using COUNTs to add a delay
        decfsz    COUNT1,1       ; Decrease COUNT1 by 1, and skip the next line if the result is 0.
        goto      LOOP0          ; If COUNT1 is 0, carry on. If not, go to LOOP0.   
        decfsz    COUNT2,1       ; Decrease COUNT2 by 1, and skip the next line if the result is 0.
        goto      LOOP0          ; If COUNT1 is 0, carry on. If not, go to LOOP0.

However, I'm fairly sure I'm screwing up on the timing, could someone give me a hand?

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
Jake Lee
  • 7,549
  • 8
  • 45
  • 86
  • The first section defined the counters, didn't it? And those 2 are (hopefully) to be used to create the delay. Also, I added the PIC model to the post. Sorry. – Jake Lee Apr 29 '12 at 19:11
  • 1
    I don't know much about PIC Assembly, but to me it looks like your're counting to 20h(32 decimal) and 21h(33 decimal). But you have to count up to 20 Million cycles. IIRC PIC executes instruction every 4 cycles. If you count your instructions that would mean you need to count till 20Million/4cycles/7instructions. But in general using such loops for delays is not very reliable. The PIC you're using provides a total of 6 timers. Its far better to use them for the job. – Nico Erfurth Apr 29 '12 at 22:02
  • Is there an adversion to looking at the data sheet [here](http://ww1.microchip.com/downloads/en/DeviceDoc/41202F-print.pdf) ? – user957902 Apr 30 '12 at 15:03
  • User957902, that's where the imgur link is from. I wasn't sure which value was the correct one. – Jake Lee May 01 '12 at 17:25

2 Answers2

4

Assumption: The code at LOOP0 is code you want to execute once per delay rather than as many times as possible during the delay. I also assume that you're setting COUNT1 and COUNT2 to something - the code you posted declares two "variables" but doesn't assign them.

The code you have at the moment will repeatedly execute the code at LOOP0 COUNT1 + COUNT2 times. This is because each loop is separate. This gives you a maximum delay of 510 cycles. As other commenters have said, PIC16s execute roughly one instruction per cycle, so you need to delay 1,000,000 cycles to wait one second at 4MHz.

If we consider a situation where we want to wait 196392 cycles, we essentially need to implement a 16 bit counter. We do this by decrementing one counter in a loop. Each time that loop exits, we decrement another counter. When both counters are zero the loop returns. Here's an example:

COUNT1 EQU 20h
COUNT2 EQU 21h

LOOP0
    ;do some stuff here
        ...

    ;delay loop starts here:
    ;assume COUNT1=0 and COUNT2=0
Delay_0
    decfsz COUNT1
    goto Delay_0
    decfsz COUNT2   ;COUNT1 = 0 so 0xff cycles have passed
    goto Delay_0
    goto LOOP0 ;both COUNT1 and COUNT2 = 0 - 196392 cycles have now passed

Branch instructions cost 1 cycle if they don't skip, and 2 if they do. goto always takes 2 cycles, meaning the actual time taken to do one full count is 767 cycles (255 * 3 + 2). We can calculate the time taken for both as ((255 * 3 + 2) + 3) * 255 + 2.

There's an excellent explanation of delay routines over at Dos4Ever. This looks at how delay routines work and how to calculate the counter values and cost of a delay routine.

Finally, if you just want cut-and-paste delay routines, the Delay routine generator on PIClist is pretty much perfect.

Mathew Hall
  • 994
  • 9
  • 18
  • Good answer +1! Maybe only a few things. If we enter to the delay routine with `COUNT2` as 0 then 256 (not 255) short loops should be performed! And I miss `CLRWDT` intruction. :) – GJ. May 01 '12 at 08:25
  • Cheers, I used this to figure it out, very helpful. – Jake Lee May 01 '12 at 17:26
1

The code above with the two loops under assumption of both counters initially set to zero takes 197119 cycles (instead of 196392) right?

I'm referring to the code:

  • Delay_0 decfsz COUNT1
  • goto Delay_0
  • decfsz COUNT2
  • goto Delay_0

The reason is that the inner loop associated with count1 will loop for 255 times, which means {255 times 3 instruction cycles} plus the final decfsz takes an extra 2 cycles. So for the very FIRST time that this inner loop cycles through, the associated delay (d1F) will be d1F = 255*3 + 2 = 767 cycles. This all happens before we even get to the decfsz for count2. Next up, the rest of the activity occurs when we reach decfsz count2; which begins with decfsz count2, followed by goto Delay_0, where the 'goto' will invoke another inner loop delay (equal to d1F). So this triple combination consisting of decfsz count2, goto Delay_0, and d1F will be associated with a count2 index value of 255. We then keep getting more triple combos...with index 254, then index 253...all the way down to count2 index of 1. So this means we get 255 triple combinations. And lastly, we then terminate with the final decfsz count2 (with index 0). The final decfsz count2 'instruction' takes 2 cycles instead of 1. So the second portion of the delay is then (d1F+3)*255 + 2. The '3' (instruction cycles) is due to decfsz plus goto instructions during normal looping.

So when we put the first and second portions of the delay together, we get:

  • d2F = d1F + (d1F+3)x255 + 2 = 767 + (767+3)x255 + 2 = 197119

Now if we have multiple loops, then we can use equations:

  • d(n)_F = d(n-1)_F + {d(n-1)_F + 3}x255 + 2 = 256xd(n-1)_F + 767

and

  • d(n)_C = d(n-1)_C + {d(n-1)_F + 3}x{count_n - 1} + 2

where the 'F' in d(n)_F or d(n-1)_F denotes a condition where all counters are initialized with ZERO value. And 'C' in d(n)_C denotes a condition where the counter for the nth loop is initialized with whatever value we had initially chosen. And 'n' is associated with the nth loop. And 'n-1' is associated with the (n-1)th loop.

So if we have 2 loops, then d(1)_F is the delay due to loop #1 with 'Full' number of cycles (ie.. counter1 is initially ZERO or 256); and d(2)_F is the delay due to loop #1 AND loop #2 for the case when both counter1 and counter2 are initially equal to ZERO or 256.

  • d(1)_C is the delay due to loop #1 for the case where count1 is initialized with whatever value we had initially chosen.
  • d(2)_C is the delay due to loop #1 AND loop #2 for the case where count2 is initialized with whatever value we had initially chosen.

Note that count_n is the INITIAL counter value for the nth loop. Also, if a particular counter is initially initialized with a ZERO value, then it is often convenient to treat that value as being '256'. This is for an EIGHT bit counter of course. For example, if count1 = 0, it is convenient to treat it as being count1 = 256 (instead of 0).

  • We can also define: d(0)_F = 0, and d(0)_C = 0.

So for a 3 loop system with count1 = 1, count2 = 4, and count3 = 2,

  • d(1)_F = 256xd(0)_F + 767 = 256x0 + 767 = 767

  • d(1)_C = 0 + {0 + 3}x{1 - 1} + 2 = 2

  • d(2)_F = 256xd(1)_F + 767 = 256x767 + 767 = 197119

  • d(2)_C = d(1)_C + {d(1)_F + 3}x{4 - 1} + 2 = 2 + {767+3}x3 + 2 = 2314

  • d(3)_F = 256xd(2)_F + 767 = 256x197119 + 767 = 50463231

  • d(3)_C = d(2)_C + {d(2)_F + 3}x{2 - 1} + 2 = 199438

The 3 loop system is like:

  • Delay_0 decfsz count1
  • goto Delay_0
  • decfsz count2
  • goto Delay_0
  • decfsz count3
  • goto Delay_0

Kenny Leong