Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Delay Macro Timing

Status
Not open for further replies.

jpanhalt

Well-Known Member
Most Helpful Member
Back on January 4th, Mike (K8LH) was kind enough to provide this utility for delays:

Code:
clock     equ     8               ;4, 8, 12, 16, or 20 MHz clock
usecs     equ     clock/4         ;cycles per microsecond multiplier
msecs     equ     clock/4*1000    ;cycles per millisecond multiplier
DelayCy macro   delay           ;12..327690 cycle range
            movlw   high((delay-12)/5)+1
            movwf   delayhi
            movlw   low((delay-12)/5)+1
            movwf   delaylo
            [B]call         uDelay-((delay-12)%5)[/B]
            endm
Subroutine:
Code:
        nop                     ;entry for (delay-12)%5 == 4
        nop                     ;entry for (delay-12)%5 == 3
        nop                     ;entry for (delay-12)%5 == 2
        nop                     ;entry for (delay-12)%5 == 1
uDelay  decf    delaylo,f       ;5 cycle loop
        skpnz 
        decfsz  delayhi,f
        goto    uDelay
        retlw   0
It worked well for serial transmission at 9600 baud. After much study, I felt was was getting enough of an understanding to test it in the MPLab simulator with this code. I used the stopwatch function to time between breakpoints set at bsf and bcf.
Code:
<snip>
 	nop		
        nop
        bsf     en                   ;"en" is an enable pin in a port 
        DelayCy (13*msecs)
        bcf     en
At 4 MHz, the timings were all 1 μsec too long, i.e., one instruction cycle. Assuming that most of my uses would include a return, such as used here, I changed the entry point in the uDelay subroutine by subtracting 13 instead of 12 (see bolded line in macro). Testing with several delays from 20 msecs to 13 μsec worked with no error. I only tested processors at 4 MHz and 8 MHz.

Now, 1 μsec is not going to make a huge difference for 10 bits at 9600 baud, but it is still appreciable. The question here is that while the delay macro is accurate in itself as originally written, the program or the method of measuring the delay (i.e., breakpoints and stopwatch) introduces an error of one instruction cycle.

Question: How accurate is the stopwatch function the way I used it?

Regards,

John

Edit: I guess bolding doesn't work in code. The line I am referencing has bolding tags on it.
 
Last edited:
Hi John,

I've found the Simulator stopwatch to be very accurate.

The K8LH DelayCy() subsystem is "cycle accurate". That is, it accounts for internal macro and subroutine "overhead" and it provides the capability to subtract cycles from the delay parameter for external "overhead" in order to produce "cycle accurate" timing.

Here's how you would generate your precise 13-ms "en" pulse with any clock by accounting for 1 instruction cycle between the bsf and the bcf instructions;

Code:
        bsf     en              ; en = 1
        DelayCy(13*msecs-1)     ; 13-ms minus 1 cycle
        bcf     en              ; en = 0
Here's another example. If you don't account for the cycles used in the loop, this routine will actually produce slightly different frequency tones when using different clock frequencies. To test the timing, place your simulator breakpoint on the movwf PORTA instruction and note the stopwatch timing between writes.

Code:
;
;  key press beep
;
;  DelayCy(1*msecs) produces     DelayCy(1*msecs-6) produces
;  497.018 Hz --  4 MHz clock    500 Hz tone -- any clock
;  498.504 Hz --  8 MHz clock
;  499.004 Hz -- 12 MHz clock
;  499.251 Hz -- 16 MHz clock
;  499.400 Hz -- 20 MHz clock
;
        bsf     Beep,5          ; do 32 msec "new press" beep     |B0
DoBeep  movf    PORTA,W         ; read port A                     |B0
        xorlw   1<<Spkr         ; toggle speaker bit              |B0
        movwf   PORTA           ; toggle speaker pin              |B0
        DelayCy(1*msecs-6)      ; delay 1 msec for 500 Hz tone    |B0
        decfsz  Beep,F          ; done?  yes, skip, else          |B0
        goto    DoBeep          ; loop (toggle Spkr pin again)    |B0
Now, 1 μsec is not going to make a huge difference for 10 bits at 9600 baud, but it is still appreciable.

I agree that 1 usec timing variation is appreciable and undesirable. However, if you simulate that 9600 baud Tx routine I posted you'll find that the Tx pin is updated at precise 104 usec intervals because external overhead (loop time) is accounted for (place your simulator breakpoint on the movwf GPIO instruction).
 
Last edited:
BTW, the DelayCy() subsystem example I posted for you in January is for 12-bit core devices like the 12F509. It's a modified version of the subsystem I use for 14-bit core devices (below) which uses one less variable.

Cheerful regards, Mike

Code:
;==================================================================
;  K8LH DelayCy() subsystem macro generates four instructions	  =
;==================================================================
        radix   dec
clock   equ     16              ; 4, 8, 12, 16, 20 (MHz), etc.
usecs   equ     clock/4         ; cycles/microsecond multiplier
msecs   equ     clock/4*1000    ; cycles/millisecond multiplier

DelayCy macro   delay           ; 11..327690 cycle range
        movlw   high((delay-11)/5)+1
        movwf   delayhi
        movlw   low ((delay-11)/5)
        call    uDelay-((delay-11)%5)
        endm
;
Code:
;******************************************************************
;  K8LH DelayCy() 16-bit uDelay (11..327690 cycle) subroutine     *
;******************************************************************
        nop                     ; entry for (delay-11)%5 == 4     |B0
        nop                     ; entry for (delay-11)%5 == 3     |B0
        nop                     ; entry for (delay-11)%5 == 2     |B0
        nop                     ; entry for (delay-11)%5 == 1     |B0
uDelay  addlw   -1              ; subtract 5 cycle loop time      |B0
        skpc                    ; borrow? no, skip, else          |B0
        decfsz  delayhi,F       ; done?  yes, skip, else          |B0
        bra     uDelay          ; do another loop                 |B0
        return                  ; return with C = Z = 0           |B0
;
 
Last edited:
Hi Mike,

Thanks for the additional information. I should have pointed out that that little macro has been a huge saver in time and writing compared to using the PICList delay calculator. I had just not realized the best way to tweak it to account for program overhead. Hence, I subtracted 13, but obviously your way is much better and makes crystal clear what is being done and why. I certainly did not mean to imply the macro was not cycle accurate and apologize if my comment came across like that.

I suspected, but wanted confirmation that the stopwatch was equally cycle accurate. Thanks for confirming that and for providing the macro for 14-bit cores.

Right now, I am focused on getting a GLCD to work with a 16F690, just for kicks. I realize it is far from optimal, but as I was driving to the "farm" today, I got to day dreaming about setting up a macro for a "virtual" 6-bit port that would combine the available bits in portA and portB for controlling the functions of the KS0108 controller that my display uses. I am just looking for more experience with macros instead of calls. I suspect timing issues will come up, so I wanted to be certain of the tool in MPLab.

Regards, John
 
... I certainly did not mean to imply the macro was not cycle accurate and apologize if my comment came across like that. ...

Hi John,

I didn't infer anything negative in your comments and I hope my replies didn't seem terse. I was simply trying to provide a couple hints on how to take advantage of the "cycle accurate" capability of the DelayCy() subsystem. BTW, I'm delighted to see you're using the simulator as it's a great tool.

Have fun. Good luck on your project.

Cheerful regards, Mike
 
Last edited:
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top