Assembly Delays in the AVR

There are many reasons why we must create delays in assembler, and before you use them you must think to yourself “is a delay the best way to do what it is I need to do?” Sometimes the answer is ‘Yes’, but sometimes we just can’t get around them. A classic example is when initialising an LCD screen. When initialising (and using) an LCD, there are times we simply must wait before interacting further with it.

Essentially I have two different (but similar) approaches to this and I am going to give you three ways these can be applied. All involve a seperate file that is included at compile time by using the .include directive.

Before we begin

First we need to understand how a delay works and what we need to do to make it (relatively) accurate. We need to understand that every instruction we have within a delay routine takes a certain about of time to execute. Fortunately on the AVR all instructions generally take one clock cycle, with a few exceptions (as we’ll see) that take two. Therefore the answer is to determine how many clock cycles we need to kill. On the ATMega128 running at 16MHz, we need to take out 16,000,000 cycles to delay one second. All my delay routines work around the same piece of code, and so I know that my delay loop will take 5 cycles to complete (at 16MHz this is true for timings between 4ms and just over 1 second). Therefore to delay one second I need to kill off 16,000,000 / 5 cycles.

That’s 3.2 million cycles! 3,200,000 decimal in hex is 0x30D400. That’s a three byte number and hence I use 3 registers: d0, d1 and d2.

How did I come to 5 clock cyles per loop? Well the basic delay loop consists of 4 instructions:

subi delay0, 1
sbci delay1, 0
sbci delay2, 0
brcc PC-3

Create a routine for every delay type

This is by far the method I use the most, in fact, always. It is memory hungry in that it takes a lot of code space, but my preferred chip is the ATMega128, so I have the space to play with. I’ll provide two alternatives later, but for now I simply define every delay and process them individually. For this example, let’s say I need three major delay periods: 5ms, 20ms and 100ms.

Delay_5ms:                                                        

ldi     R18,byte3(ClockMHz * 1000 * 5 / 5)
ldi     R17,high(ClockMHz * 1000 * 5 / 5)
ldi     R16,low(ClockMHz * 1000 * 5 / 5)

subi    R16,1
sbci    R17,0
sbci    R18,0
brcc    pc-3

ret

;------------------------------------------
Delay_20ms:                                                        

ldi     R18,byte3(ClockMHz * 1000 * 20 / 5)
ldi     R17,high(ClockMHz * 1000 * 20 / 5)
ldi     R16,low(ClockMHz * 1000 * 20 / 5)

subi    R16,1
sbci    R17,0
sbci    R18,0
brcc    pc-3

ret

;-------------------------------------------
Delay_100ms:                                                    

ldi     R18,byte3(ClockMHz * 1000 * 100 / 5)
ldi     R17,high(ClockMHz * 1000 * 100 / 5)
ldi     R16,low(ClockMHz * 1000 * 100 / 5)

subi    R16,1
sbci    R17,0
sbci    R18,0
brcc    pc-3

ret

unfinished business – I’ll be back

Leave a comment