Cornell University
Electrical Engineering 476
Program Organization for AVR microcontrollers
Introduction
There are many ways to organize realtime programs. By organization,
I mean the scheme you use to coordinate the interaction of concurrent program
sections (e.g. interrupt service routines) , as well as the usual hierarchical
structure of each individual program section. The way you organize a program
will depend on may things including:
- The length and expected lifetime of the code. Will you or some other poor
soul have to modify the code in a year?
- The use to which the code will be put. You design a toy differently than
you design a high-power gamma ray source.
- The speed with which the code must respond to outside events, or must generate
events itself. Fast responses may dictate a very simple code structure.
- The number of concurrent tasks being handled by the code. More tasks, or
more complicated tasks, may require more formal methods, such as a realtime
operating system (RTOS) to ensure correct execution..
This document will show some of the ways or organizing your code
for the AVR platform and try to address some of the tradeoffs of the various
approaches.
Typical programs for desktop systems are not realtime. You write
a chunk of code which executes according to the logic you construct and outside
events (e.g. mouse input) are under your control. Typical programs for microcontrollers
have to consider outside events, as well as timing, and typically operate with
at least one, and often several, interrupt sevice routines (ISRs) able to jerk
control away from the main code at any time. Many of the most difficult bugs
to fix in a design arise from the unintended interaction between programs which
are interrupting each others execution.
Approaches to Organization
Here is a list of some organization techniques with their advantages
and disadvantages. A simple example is used to illustrate three styles. The
example: (1) blinks LED 0 at 2 Hz unless button 0 is pressed. (2) blinks LED
1 at 1 Hz. (3) detects the button press and switches LED 0 to 8 Hz.
- Don't use any interrupts. Poll for events as fast as possible and use software
timing loops.
- Code written in this style will be generally
unacceptable in this class.
- Advantages: No problems with concurrent tasks interfering with each
other. For fast I/O, nothing is faster than a tight code loop.
- Disadvantages: Often too slow, impossible to modify, nonreproducable
response times.
- Even with the obvious problems of this approach it is occasionally desirable.
The Mars Rover used a polling loop for all events from some 200 input
devices because there was a severe power limit and only one device at
a time could be used.
- Write all time-critical functions into ISRs and use the main program for
some slow stuff, such a button pushes. This is called "forground/background"
programming and is widely used for relatively simple controllers. However
it often contradicts one of the basic tenets for maintaining your sanity in
this course: Keep your ISRs short.
- Advantages:Good response time for a broad range of events, minimum interrupt
latency. There is no code overhead, either in time or flash memory space.
- Disadvantages:Long ISRs have many ways to fail. For instance inadvertant
shared variables and other interrupts missed.
- ASM example code using forground/background
programming. The state variables in this example use cpu registers.
- Put only the most time-critical functions into ISRs and use a time-based
scheduler for other functions. This is a variant of forground/background programming
in which the background receives timing information from the forground.
- Advantages: Shorter ISRs and more structured code.
- Disadvantages: Somewhat less reproducable event handling time. Not fast
enough from some events, such as audio or video synthesis.
- ASM example code using a simple time-based
scheduler. Intertask communication and state variables in this example
use RAM.
- C example code of the same program written
in CodeVisionAVR C Compiler.
The same program is written again in Codevision,
but with embedded assembler code for speed.
- A modification of this example shows what
happens when you have two ISRs defined and one of them uses too much cpu
time. Timer 1 is used here only to toggle an LED, but the program toggles
the LED at progressively shorter times until the (higher priority) timer
1 ISR never lets the timer 0 interrupt occur. At this point all the task
execution stops, but the timer 1 ISR keeps running. Except for one dim
LED, it looks like the cpu stopped.
- This style makes it easy to run a state-machine at a given interval.
A C code example shows how to debounce a single
pushbutton.
- Use an cooperative RTOS. The term 'cooperative' means that tasks run until
they are done and it is the job of the programmer to make sure that all time-critical
functions can happen on time.
- Advantages: Interaction between tasks is handled by the RTOS, which
means that debugging is much easier. More program state is maintained
by the RTOS, so coding is often easier. Task swithching is simple (and
fast) compared to preemptive RTOS.
- Disadvantages: Often too slow, sometimes too big (in code size) or too
expensive. Compared to a preemptive RTOS (see below) tasks have to know
a lot about each other.
- Ben Greenblatt wrote a simple
RTOS in assembler for the AVR processors. It handles scheduling and
message-passing and is non-preemptive. There is an ASM and C version.
- ASM example code using his RTOS. Intertask
communication and state variables in this example use RTOS facilities.
- C example code using his RTOS. Further optimizations
and examples were written by Nick Liu.
- A program with two tasks shows that it takes
around 300 cycles to switch between tasks. Optimization efforts continue.
This example hase one task which executes only when a message is received,
and another which executes on the clock.
- Use a preemptive RTOS. A preemptive RTOS has the ability to yank contol
away from a task when another task needs to be run.
- Advantages: Interaction between tasks is handled by the RTOS, which
means that debugging is much easier. More program state is maintained
by the RTOS, so coding is often easier. Compared to a cooperative RTOS,
tasks appear to each have their own machine. Timing of complex events
is more predictable.
- Disadvantages: Often too slow, sometimes too big (in code size) or too
expensive. Compared to a cooperative RTOS task switching is slow.
- There is a separate page devoted to
examples.
You can use any interrupt-driven style you wish in this course, but for the
first few lab exercises, I suggest a time-scheduled programming organization.
As the projects demand more speed, you may find that a combination of time-scheduled
(for the slow stuff) and foreground/background techniques (for the fast stuff)
works. See, for example, the next section.
An Example with two ISRs
Another example shows how to handle slow events (button presses) and relatively
fast events (audio synthesis). The example program will play a musical scale
when any button is pressed.
The example is coded in the three different styles shown above.
- Forground/background
- The background task tests for button presses and starts the timer 0
interrupt when button 0 is pushed.
- Timer 0 ISR counts to 1/4 second to control the rate at which notes
are played,
then sets the audio signal period for timer 1.
- Timer 1 ISR toggles an output line to produce a squarewave.
- ASM Code.
- Time-scheduled
- Background task 1 tests for button presses every 60 mSec and sends a
message to task 2 to start the scale.
- Background task 2 runs every 1/4 sec. When it receives a message that
the button is pushed, it sets the audio signal period for timer 1. Task
2 must maintain a state variable indicating which note is being played.
- Timer 0 ISR maintains a 1 mSec time base.
- Timer 1 ISR toggles an output line to produce a squarewave and is written
in foreground/background style.
- C code and ASM Code.
- C code with embedded ASM.
- A modified version of this example adds
non-blocking RS232 control to play/stop the scale. A new background task
3 runs every 50 mSec. It checks to see if there is a vaild character in
the UART data register and, if so, reads it and sets a ready-flag. A slightly
modified task 1 interprets the character as a start/stop command. The
Serial Communication page has an example code which is non-blocking because
all serial receives are done in an ISR.
- Cooperative RTOS
- Task 1 tests for button presses every 30 mSec and sends a message to
task 2 to start the scale.
- Task 2 runs when it receives a button message from task 1, then every
1/4 second until the scale is complete. Task 2 sets the audio signal period
for timer 1. Task 2 must maintain a state variable indicating which note
is being played.
- Timer 0 is used by the RTOS.
- Timer 1 ISR toggles an output line to produce a squarewave.
- ASM Code.
Copyright Cornell University Feb 2005