AVR blink program
This article details the compilation and write of a "blink" program to an at90s2313 microcontroller. Similar to the pic_blink_program article, its purpose was merely to find and test a suitable toolchain.
A comment on PIC vs AVR
To be capable at programming microcontrollers, you must be able to program multiple types. By all means have a preference, but it is better to be flexible in the type of hardware you are able to work with. This means learning both PIC and AVR at a minimum.
Hardware
The hardware used was:
- an at90s2313 (see pdf)
- a development board: Olimex AVR-P20 (see html)
- a programmer: USBasp clone, sold by Mengjin Su as a kit (see html)
- a usb cable and a 10-pin parallel cable
- a power supply
The USBasp programmer was originally built by Thomas Fischl (see html).
An entry in /etc/udev/rules.d/50-usb.rules
was required for Linux to recognize the clone.
For instructions on how to do this, see building_a_pickit2.
Code
On this board, the LED was connected to the PORTB register, pin 7. It was a pull-down LED, if that makes sense, so setting the pin low caused current to flow through the LED. The blink program is listed below.
#define __AVR_AT90S2313__ 1
#define F_CPU 10000000L
#define valbit(reg, bit) ((reg) & (1 << (bit)))
#define setbit(reg, bit) ((reg) |= (1 << (bit)))
#define clrbit(reg, bit) ((reg) &= ~(1 << (bit)))
#define STATUS_LED_ON clrbit(PORTB, PB7)
#define STATUS_LED_OFF setbit(PORTB, PB7)
#define SUCCESS 0
#define FAILURE 1
#include "avr/io.h"
#include "util/delay.h"
int
setup(void)
{
DDRB = 0b10000000; // set PORTB pin 7 as output
STATUS_LED_ON; // led off
return SUCCESS;
}
int
main(void)
{
setup();
while (1) {
STATUS_LED_ON;
_delay_ms(1000);
STATUS_LED_OFF;
_delay_ms(1000);
}
return SUCCESS;
}
The delay function was very easy to use, and being able to simply define a F_CPU
value to correctly time delays was an elegant solution.
The only aspect that is harder than pic MCUs is that pins cannot be addressed directly.
Instead, you must use bit shift operations.
In the above code, this is done using the STATUS_LED(X)
macro for clarity.
To test the status of a bit for a conditional, something like the following may be used
#define STATUS_LED_BIT (PINB & (1 << PB7))
if (STATUS_LED_BIT) {
// do something
} else {
// do something else
}
The button was also implemented as an interrupt. Interrupts are not as portable, as PIC and AVR require different code, still more portable is better.
ISR(INT0_vect)
{
TOGGLE_LED0;
_delay_ms(100); // debounce
}
int
setup(void)
{
...
INIT_BUT0;
// interrupts
setbit(SREG,SREG_I); // enable global intrrupts
setbit(GIMSK,INT0); // enable INT0
// interrupt on falling edge (button push)
setbit(MCUCR,ISC01);
setbit(MCUCR,ISC00);
...
}
Contact buttons use a simple metal spring. After release the spring oscillates and makes contact a few more times, which the MCU picks up as additional button presses. After a lot of time looking into removing this, a simple time delay was implemented (see above). It's not a great solution but it's easy and works most of the time. The most sophisticated you might get is adding a very small capaitor along with the delay, but it is best to avoid contact buttons altogether, and use silicon pads or switches instead.
Makefile
The toolchain used to program AVRs is easier than PIC under Linux, primarily because of good open-source tools and support. Code can be compiled with avr-gcc, avr-objcopy and uploaded with avrdude. The makefile used is listed below.
CC=avr-gcc
MCU=at90s2313
CFLAGS=-mmcu=$(MCU) -Os
all: clean build
clean:
rm -f *.hex *.o
blink.o: blink.c
$(CC) $(CFLAGS) -c blink.c -o blink.o
build: blink.o
$(CC) $(CFLAGS) -g blink.o -o blink-main.o
avr-objcopy -O ihex blink-main.o blink.hex
install:
avrdude -c usbasp -p 2313 -P /dev/AVRasp -U flash:w:blink.hex:i
Conclusion
This was a fairly straightforward process, primarily due to good available tools and support. The next article will involve SPI communication using the DB9 serial port.