# Is this a correct way to have a 1 second delay?

If I have a PIC24 with a 4 MHz frequency. Is it accurate to have a 1 second delay by having 4 million clock cycles?

I made a 1 second delay like this on my PIC24FJ128GA010:

    MOV     #244, W3
DL: REPEAT  #16383
NOP
DEC     W3, W3
BRA     NZ, DL
REPEAT  #1566
NOP
RETURN


Clearly if you calculated the cycles right you will get 3999995 cycles plus 3 for RETURN and 2 for CALL = 4000000 cycles. Can this be exact ? On a LED blink example with a comparison with a real clock, I can see (almost) a very very minor difference between them.

What factors can affect this accuracy ?

P.S: I used many delay techniques, the __delay_ms function in the XC16 C compiler, the timer interrupt in both assembly and C, and the RTCC module for accurate timing. I'm asking this just for practice and learning.

• The internal oscillator isn't particularly accurate. If you want watch-like accuracy you need a crystal with some sort of temperature compensation. Sep 18, 2014 at 9:41
• I can see (Almost) a very very minor difference between them. Optical tricks. But mainly what pjc50 said. You did go through your 4 million cycles, but if your 4mHz clock is actually 4.02 at X temperature... Sep 18, 2014 at 10:02

Repeat loops are only really any good for "rough" timing and for short periods. There are many factors that can effect just how long your loop takes:

1. Clock frequency accuracy. 4 million clock cycles is only exactly 1 second if you have precisely a 4,000,000.00000000000000000000000000 Hz clock.
2. Interrupts. If an interrupt occurs during your loop the loop will take longer.
3. Unaccounted for extras. If you're working in C and your delay is in a function you have to take into account such things as the function preamble / postamble, time taken for the call instruction, etc.

Basically instruction cycle / clock cycle counting is a right pain in the rectum and not something you want to do very often.

The PIC, and most other microcontrollers, have better mechanisms.

1. Internal timers. These are good for short periods and are far simpler than counting cycles. Set the timer up with an interrupt, sit back, and relax. For longer periods though they do tend to involve triggering the interrupt multiple times and counting up your own variable. One common way is to have a constantly running 1ms timer that is always counting up a millisecond counter. That is then used to time your delays. Not always 100% accurate, but very simple to manage. Timers of course are still dependant on the accuracy of your clock source.
2. Real-time clocks. For longer periods of a second or longer, where you don't need fine grained accuracy but do you need coarse grained accuracy (i.e., accuracy to the second, not the µS, but that accuracy stable over long periods), you can't beat a real time clock module - either internal to the chip or external in a separate chip. The lower frequency clock sources (32768Hz) used in RTC modules tends to make them more accurate (50ppm in 4MHz is 200Hz; 50ppm in 32768Hz is 1.6384Hz).

Usually delay loops are used in unsophisticated programs where a short or rough delay is required. Of course you can make them cycle-perfect by adding a few NOPs or whatever. If the micro will do nothing else other than count a clock and output a square wave, you can count exactly 500,000 cycles with a 4MHz clock (inclusive of the looping and toggling instructions), toggle the output pin and repeat, and the output will be a 1Hz square wave as accurate as the clock (give or take nanosecond jitter) and with 50% duty cycle.

If you want accurate 1-second times, you can also use a timer/counter peripheral, clocked by the system clock, which allows the micro to do something else other than counting. Many PICs also have a secondary oscillator input which can work with (say) a 32,768Hz watch crystal, allowing an inaccurate clock such as the internal RC to be used for the system clock. If the counter/timer is programmed to toggle the output pin directly, the micro can be used for other purposes than just counting, and need only return to set up the next compare value before the next comparison would come due. Again the output will be about as accurate as the input clock frequency to the counter. Some have a real time counter peripheral that does the counting for you.

If you don't care too much about a bit of jitter (but frequency can still be bang-on) you can trigger periodic interrupts and count those. For example, if you had interrupts every 2ms you could simply count (in the interrupt service routine) to 250, then toggle the output, and reset the counter. Since the ISR can take varying amounts of time on some micros to begin (depending on what it was doing when it was interrupted) and for other reasons there can be a bit of jitter in this approach (some instruction cycles).

I lied a bit when I implied that the micro could do nothing else if it was counting cycles- it's possible (but rather tedious) to craft assembler code that is isochronous - it takes exactly the same number of cycles to execute no matter which branch is taken- so that useful things can be accomplished within a delay. It's not really a nice thing to have to do.

Any of these methods may be "correct" for a given purpose.

Microcontroller has an oscillator inside only suitable for its operation and normal time measurement. You have to use external oscillator for accurate measurement.

An oscillator needs careful designing and have a lot to consider(e.g. temperature compensation), so if you don't mind using IC, I recommend using RTC(Real Time Clock) IC or similar.

delay1s movlw d'120'
movwf 10h
movlw d'98'
movwf 11h
movlw d'10'
movwf 12h
decfsz 12h
goto $-1 decfsz 11h goto$-5
decfsz 10h
goto \$-9
return

• I added 2 spaces at the end of each lines to stop lines wrapping. || It would be useful to add some comments to your code and also to explain why this is a good solution. Sep 12, 2020 at 9:11