Delay Loops
- Often we need to slow our programs down so that the user can see the results
- Delay loops are used to burn clock cycles.
ldi r16, val
loop:
dec r16
brne loop
- The biggest number we can put in val is 255.
- Our clock frequency is 16MHz, so the period is T = 1/f = 62.5 x 10-9 seconds per cycle.
- We can determine the length of time required for the loop as follows:
# of times # of clocks Total clocks
ldi r16, val 1 1 1
loop:
dec r16 val 1 val
brne loop val 2 2*val-1
- The -1 on 2*val-1 is because the BRNE instruction only requires on clock cycle the last time it runs.
- With val=255, this works out to 1+255+509 -> 47.8 x 10-6 seconds.
- This doesn't provide much of a delay... let's consider a nested loop:
# of times # of clocks Total clocks
ldi r17, val1 1 1 1
loop2:
ldi r16, val2 val1 1 val1
loop1:
dec r16 val2*val1 1 val1*val2
brne loop1 val2*val1 2 2*val1*val2
dec r17 val1 1 val1
brne loop2 val1 2 2*val1
- To simplify things, we're ignoring assuming that the BRNE execution is 2 cycles all the time.
- With val1=val2=255, this works out to 1+255+65025+130050+255+510 -> 0.0123 seconds.
- If you need a timing loop to provide a delay that is longer than 0.0123 seconds requires at least three nested loops.
The Stack
- Is a memory structure located in SRAM
- Typically we make the starting address for the stack at the last available SRAM location (0x85F or RAMEND).
- ATmon actually uses the last 32 memory locations for it's stack, so we will initialize our stack to RAMEND-0x20.
- There is a special pointer to the stack (called SP)
- The stack pointer points to the "top" of the stack.
- The SP is 16 bits wide since it contains an address.
- Hence, it consists of two registers (SPH and SPL).
- We initialize it like this:
ldi r16, HIGH(RAMEND-0x20)
out SPH, r16
ldi r16, LOW(RAMEND-0x20)
out SPL, r16
- The stack may be used as a temporary storage area for data.
- The PUSH instruction puts data onto the stack.
- PUSH R0 does the following:
- (SP) ← R0 (puts the value of R0 in the memory location that SP is pointing to)
- SP ← SP - 1 (moves the stack pointer down one address)
- Each PUSH should have a corresponding POP operation.
- POP R0 does the following:
- SP ← SP + 1 (moves the stack pointer up one address)
- R0 ← (SP) (puts the value at the top of the stack into R0)
- Recall that the stack is a Last In/First Out structure.
- It can be used to save/restore the values in the registers before/after a call to a subroutine. E.g.,
push r0
push r1
push r2
...
push r31
; call some subroutine
pop r31
...
pop r2
pop r1
pop r0
Stack Example
.nolist
.include "m32def.inc"
.list
.cseg
.org 0
rjmp start
.org 0x2a
start:
ldi r16, HIGH(RAMEND-0x20) ; Initialize stack pointer
out SPH, r16
ldi r16, LOW(RAMEND-0x20)
out SPL, r16
ldi r20, 0x25 ; Initialize registers r20-r22
ldi r21, 0x50
ldi r22, 0x75
push r20 ; Save r20-r22 register values
push r21 ; by placing r20-r22 onto the stack
push r22
; blah blah blah
pop r22 ; Restore r20-r22 registers by
pop r21 ; by popping their values off the stack
pop r20
forever:
rjmp forever ; This loop is tight