;) This forum is the property of Proton software developers

Main Menu

Creating nano second accurate delays

Started by See_Mos, Apr 09, 2022, 03:33 PM

Previous topic - Next topic


A quite a few years ago I would have struggled with this problem but at least I would have known where to start however when we moved home around five and a half years ago I gave away all of my Microchip assembler programming books and forty five plus years of printed electronics magazines.

What I need to do is create some very short, accurate, delays and change the state of a couple of I/O pins or an entire port.  I will also need some longer delays accurate to 50nS, for example 25.6uS.

The code is purely sequential, apart from the loop back to start, so no need for the overheads involved with using interrupt timers.  I remember the basic (?) concept of loading a variable with a value and DECFSZ but that is as far as it goes. Working out the values to load is going to give me a headache!!

I am using a 25K22 running at 64MHz and typical values are 5.65uS, 4.7uS, 1.65uS

Any pointers will be much appreciated, meanwhile I will be doing more research.


It might sound stupid but if the code is linear and the delay is known then why not expressed as multiple of Nop's where each Nop at 64MHz is 62.5ns?



Use the DelayCs command. Which is short for Delay Cycles.

This delays by the amount of time an instruction clock cycle takes. For a simple example... A device running at 4MHz has an instruction clock cycle of 1us (4:1 ratio). So DelayCs 1 on that device will delay for 1us exactly. To create a particular delay, work out how long an instruction clock cycle is for the speed the device is operating at, and multiply the delay required by the instruction clock cycle value, and this will give you the amount of clock cycles required, and place that constant value after the DelayCs command. But remember, the 8-bit devices have a frequency to instruction clock ratio of 4:1, and the PIC24 and dsPIC devices have a ratio of 2:1. For example:

AmountOfCycles = (delay us * (oscillator frequency in MHz / 4))

That is the very reason I added the DelayCs command, because I needed delays below microseconds. It has come in very handy since I added it, particularly on the PIC24 and dsPIC devices operating at high frequencies, so a program can delay in nano seconds.

It produces inline code, so it is extremely accurate. For example, DelayCs 1 will create a single Nop mnemonic, or a ClrWdt mnemonic if watchdog is enabled by the Declare, and other delays will create multiple Nop and Bra mnemonics, until it will use too many and waste code memory space, then it creates a precise loop for the amount of instruction clock cycles, and sometimes a few Nop or Bra mnemonics added to create a particular cycle value that the loop itself cannot accomplish. That's why it only accepts a constant value as a parameter, and the value can be 0 to 1000. Too many cycles would create too many loops and waste inline code memory space, but by the time a value of 1000 is required, a DelayUs command can be used instead. :-)

An example is a DelayCs 20 command on an 18F device, will produce the assembler code:

movlw 6
bra $ + 2
decfsz WREG,F
bra $ - 2

The code is based upon how many instruction cycles a particular mnemonic requires, and the compiler calculates the value to place at the top of the loop for a particular amount of instruction clock cycles required in the parameter. :-)

But a DelayCs 6 will produce the assembler code:

bra $ + 2
bra $ + 2
bra $ + 2

Because each Bra mnemonic takes 2 instruction clock cycles, and the "$ + 2" will branch to the next mnemonic on an 18F device.

And a DelayCs 5 will produce the assembler code:

bra $ + 2
bra $ + 2

Because each Bra mnemonic takes 2 instruction clock cycles, and a Nop mnemonic takes 1 instruction clock cycle. :-)

That is also the reason why the DelayCs command is not supported with the old 12-bit core and standard 14-bit core devices, because they sometimes need code memory page bank switching, which will disturb the timing of the loop. But 18F and enhanced 14-bit core devices have relative branch mnemonics that ignore page banks.