Programming Technique

Here are some tips for reading, writing and debugging programs using the AVR instruction set.


The AVR has 32 general purpose registers. This is enough that many Cybords need no additional memory. We adopt the convention of using three registers, that we call a, b and c, for temporary values.

	.def a =r17	; Temporary register
	.def b =r18	; Temporary register
	.def c =r19	; Temporary register

Registers r0 through r15 can't be specified in immediate-mode instructions. We use them last. Register r16 is used by the Bynase library so we avoid it. Hence we start our temporary register allocation at r17.

Our practice is to name all the registers we will use in a program in a similar way. Collecting these register assignment statements near the front of the program makes allocating available registers easier.


Most AVR instructions take a predictable amount of time, typically one or two clock cycles, which is one or two microseconds when the clock is running at 1 MHz.

This sequence will produce a short (one cycle) pulse on pin pb0.

	sbi portb,pb0
	cbi portb,pb0

If we want a longer pulse, say three cycles, we can add no-operation instructions to the sequence.

	sbi portb,pb0
	cbi portb,pb0

For longer delays we prefer to use a loop. This sequence repeats the decrement and branch 30 times, for a delay of about 90 cycles.

	ldi a,30
	dec a
	brne tic

When counting out the cycles for this loop we count one for the dec, and two for the brne. The branch takes two cycles, one to figure out what to do, and another to do it.

Decrement loops nest nicely, relying on the fact that a register decremented to zero is ready to count down 256 more times to the next zero. This loop will delay almost a million clock cycles (1 sec at 1 MHz) by nesting three loops inside each other.

	ldi a,5
	dec c
	brne tic
	dec b
	brne tic
	dec a
	brne tic

The above loop is a bit careless by not initializing b and c to zero before starting. This could cause a small amount of timing noise that won't mater in most applications.

You can use GoogleCalculator to compute the actual period for this loop by typing this in your search bar:

  5 * 256 * 256 * 3  / (1 MHz)

Working through the numbers from the other direction, say you wanted to delay 1.45 sec while running with an 8MHz clock. Use Google to compute the number of cycles (11.6 million), and the initial value of a (a = 59) this way:

  1.45 sec * 8 MHz = 11 600 000
  1.45 sec * 8 MHz / (256 * 256 * 3) = 59.000651

A Cybord application must call byop at regular intervals, normally about once every 60 microseconds. Clocking at 1MHz that is once every 20 instructions, at 8MHz, every 480 instructions. That makes a typical 8MHz Cybord main program look like this

	(do something)
	rcall byop
	ldi a,160
	dec a
	brne mai1
	rjmp main

The 160 comes from 480/3. Bynase is tolerant of loose timing so we need not consider the cycles consumed by the do-something or the rcall-byop. We can even vary the dominance of the Bynase signal over a factor of two or so each way by varying the rate that we call byop. Faster makes for a stronger signal when mixing multiple Bynace sources. See BynaseProtocol.


Last edited February 2, 2008
Return to WelcomeVisitors