AVR - How to program an AVR chip in Linux
I don't have the time for a full explanation, but I can give you cookbook-style the commands I use on my Linux box to program AVRs:
Preparations
- On Ubuntu, make sure several required packages are installed:
sudo apt-get install avr-libc avrdude binutils-avr gcc-avr srecord
optionally throw ingdb-avr simulavr
for debug and simulation. - I started to create a directory in which all my ATtiny projects find a home:
mkdir ~/attiny: cd ~/attiny
- For each project I create a dedicated subfolder (and I don't mind long names):
mkdir waveShare4digit8segmentDisplay; cd waveShare4digit8segmentDisplay
Create source
- Edit the source file with your favorite text editor:
vi project.cpp
Settings
The commands below heavily rely on environment variables, to keep maintenance easy.
- The base name of the files used/created:
src=project
- Common compiler flags:
cflags="-g -DF_CPU=${avrFreq} -Wall -Os - Werror -Wextra"
The variables below may need to be changed depending on the exact programmer you use. Refer to the man
pages for details.
baud=19200
The baudrate your programmer communicates at with the PC:programmerDev=/dev/ttyUSB003
The device name where your programmer is located. Checkdmesg
output for details.programmerType=avrisp
This may be different for your exact programmer.
The variables below depend on the exact controller you want to program:
avrType=attiny2313
Checkavrdude -c $programmerType
for supported devices.avrFreq=1000000
Check the controller's datasheet for default clock.
Compile
- First step is to create an object file:
avr-gcc ${cflags) -mmcu=${avrType) -Wa,-ahlmns=${src).lst -c -o ${src).o ${src).cpp
- Second step is to create an ELF file:
avr-gcc ${cflags) -mmcu=${avrType) -o ${src).elf ${src).o
- Third step is to create an Intel Hex file, this is the file that is actually sent to the programmer:
avr-objcopy -j .text -j .data -O ihex ${src).elf ${src).flash.hex
Programming
- Final step is to program the device:
avrdude -p${avrType} -c${programmerType} -P${programmerDev} -b${baud} -v -U flash:w:${src}.flash.hex
Makefile
As an alternative to remembering the commands, I cooked up a makefile to my personal liking, you can save it under the name Makefile
(mind the capital M
). It works as follows:
make makefile
Edit the makefile;make edit
Edit the source file;make flash
Program the device's flash memory;make help
List other commands.
Here is the makefile:
baud=19200
src=project
avrType=attiny2313
avrFreq=4000000 # 4MHz for accurate baudrate timing
programmerDev=/dev/ttyUSB003
programmerType=arduino
cflags=-g -DF_CPU=$(avrFreq) -Wall -Os -Werror -Wextra
memoryTypes=calibration eeprom efuse flash fuse hfuse lfuse lock signature application apptable boot prodsig usersig
.PHONY: backup clean disassemble dumpelf edit eeprom elf flash fuses help hex makefile object program
help:
@echo 'backup Read all known memory types from controller and write it into a file. Available memory types: $(memoryTypes)'
@echo 'clean Delete automatically created files.'
@echo 'disassemble Compile source code, then disassemble object file to mnemonics.'
@echo 'dumpelf Dump the contents of the .elf file. Useful for information purposes only.'
@echo 'edit Edit the .cpp source file.'
@echo 'eeprom Extract EEPROM data from .elf file and program the device with it.'
@echo 'elf Create $(src).elf'
@echo 'flash Program $(src).hex to controller flash memory.'
@echo 'fuses Extract FUSES data from .elf file and program the device with it.'
@echo 'help Show this text.'
@echo 'hex Create all hex files for flash, eeprom and fuses.'
@echo 'object Create $(src).o'
@echo 'program Do all programming to controller.'
edit:
vi $(src).cpp
makefile:
vi Makefile
#all: object elf hex
clean:
rm $(src).elf $(src).eeprom.hex $(src).fuses.hex $(src).lfuse.hex $(src).hfuse.hex $(src).efuse.hex $(src).flash.hex $(src).o
date
object:
avr-gcc $(cflags) -mmcu=$(avrType) -Wa,-ahlmns=$(src).lst -c -o $(src).o $(src).cpp
elf: object
avr-gcc $(cflags) -mmcu=$(avrType) -o $(src).elf $(src).o
chmod a-x $(src).elf 2>&1
hex: elf
avr-objcopy -j .text -j .data -O ihex $(src).elf $(src).flash.hex
avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" --change-section-lma .eeprom=0 -O ihex $(src).elf $(src).eeprom.hex
avr-objcopy -j .fuse -O ihex $(src).elf $(src).fuses.hex --change-section-lma .fuse=0
srec_cat $(src).fuses.hex -Intel -crop 0x00 0x01 -offset 0x00 -O $(src).lfuse.hex -Intel
srec_cat $(src).fuses.hex -Intel -crop 0x01 0x02 -offset -0x01 -O $(src).hfuse.hex -Intel
srec_cat $(src).fuses.hex -Intel -crop 0x02 0x03 -offset -0x02 -O $(src).efuse.hex -Intel
disassemble: elf
avr-objdump -s -j .fuse $(src).elf
avr-objdump -C -d $(src).elf 2>&1
eeprom: hex
#avrdude -p$(avrType) -c$(programmerType) -P$(programmerDev) -b$(baud) -v -U eeprom:w:$(src).eeprom.hex
date
fuses: hex
avrdude -p$(avrType) -c$(programmerType) -P$(programmerDev) -b$(baud) -v -U lfuse:w:$(src).lfuse.hex
#avrdude -p$(avrType) -c$(programmerType) -P$(programmerDev) -b$(baud) -v -U hfuse:w:$(src).hfuse.hex
#avrdude -p$(avrType) -c$(programmerType) -P$(programmerDev) -b$(baud) -v -U efuse:w:$(src).efuse.hex
date
dumpelf: elf
avr-objdump -s -h $(src).elf
program: flash eeprom fuses
flash: hex
avrdude -p$(avrType) -c$(programmerType) -P$(programmerDev) -b$(baud) -v -U flash:w:$(src).flash.hex
date
backup:
@for memory in $(memoryTypes); do \
avrdude -p $(avrType) -c$(programmerType) -P$(programmerDev) -b$(baud) -v -U $$memory:r:./$(avrType).$$memory.hex:i; \
done
It may be seem necessary to run avrdude
as root
, if that happens it justifies a question in its own. It can be solved with udev
but requires a bit specific information from how the programmer is recognized by the operating system.
Hello World
Let me throw in an 'Hello World' that makes a controller pin 2 (PB3) (eg. ATtiny13, ATtiny45, ATtiny85) toggle at 1Hz. Attach an LED and series resistor to the pin and the LED should start to blink.
- make edit
i
#include <avr/io.h>
#include <util/delay.h>
int main(void)
{
DDRB = 0x08;
while (1) {
PORTB = 0x00; _delay_ms(500);
PORTB = 0x08; _delay_ms(500);
}
}
<ESC>:wq
- make flash
Done.
You can use the AVR GNU tools as standalone packages in linux. These include avr-gcc, avr-binutils, and avr-libc. This is what is referred to as the toolchain.
Once you have built a hex file and you are wishing to flash it onto your chip, you can use avrdude.
All of these are freely and readily available on Linux and not too difficult to configure to work together.
LadyAda has a solid step by step tutorial on the whole process.