1. What?
This is a hardware/firmware which connects to a PC via a serial port (or usb-to-serial-converter) and enables it to flash/program/read/verify/debug microcontrollers of the AVR family by http://www.atmel.com
2. Purpose
There already are some programmers around, but I wanted to have additional features:
-
Provide several interfaces to the target hardware with adjustable voltage levels: SPI, UART, TWI.
-
Use any of these interfaces to connect to the target hardware while it is up and running in order to read/write variables (debug helper).
-
Modular software to make using different hardware implementations easy. Porting to another hardware should not touch a large number of files.
3. Origin
Lots of things (protocol for AVR109 and AVR910) have been taken
from Klaus Leidinger’s avr910\_2313\_v37e.asm
.
4. Hardware
4.1. Connectors
I do not use standard connectors for several reasons.
4.1.1. SPI connector to target
In contrast to the usual connector, this one contains a pin which connects
to the /SS
pin of the target to help debugging it.
4.1.2. Serial connector (PC, target)
The serial connector features a VCC output and a VCC input. Examples uses:
-
a serial-to-usb converter will provide +5V at its VCC output pin
-
the
avrdev
hardware can be powered by the serial-to-usb converter which is used to connect it to the PC -
the
avrdev
hardware can be used to power the target hardware via the serial link which is used for debugging
Voltage levels on TXD
and RXD
are not UART-like, but 1.8V..5V.
4.2. Controller
The programming hardware itself uses an AVR, so you need to be able to program that one in the first step…
4.3. Target interface: voltage levels
The voltage level of the output pins on the SPI and UART interface is adjustable via software ([lAVRDebug]). Note that levels on both interfaces can be different.
The variable voltage output is made using PWM output, an opamp and transistors. At 5.0V supply voltage, the PWM outputs are able to provide nearly 5V, but the opamp may not. I used a LM358, which gives 3.8V. However, this is sufficient for an AVR, which recognizes high level at 0.6\*VCC and above. At VCC\_max=5.5V it would be sufficient to provide 0.6\*5.5V=3.3V.
Use a rail-to-rail type or the jumper to get higher voltages.
5. Supported devices
This programmer should be able to handle the devices which can be handled using other AVR910-type programmers, too…but I currently have limited the device-data-tables to devices which I have actually tested:
-
ATMega8
-
ATMega16
-
ATMega48 (not thoroughly tested, but I was able to flash/verify the device)
-
ATMega644
-
ATMega128
-
ATMega8535
-
AT90S4433A
-
AT90S8515A
-
AT90S8535
Adding another device should be easy (just add a line with three values from the datasheet, compile).
6. Usage
The application AVRDebug ([lAVRDebug]) can be used to adjust voltage levels and debug the target device.
In order to flash/verify program memory, eeprom and fuses, I have only used this programmer with avrdude on the PC, but there are lots of others talking the same protocol.
Some devices (for example 8535) need to leave reset state and resync
after memory erase. Therefore use avrdude in two steps: first erase
using -D
, program in a second step using -e
to inhibit auto erase.
Sadly, avrdude changed its behaviour after version 5.2, so using it is not that easy and straightforward as it used to be.
6.1. avrdude <= 5.2
These versions are nice, because they allow you to have unified scripts
(usually part of the make system) in all of your AVR projects
to read/write everything of your AVR; you only need to supply the string ID
of the device (for example m64
for the ATMega64). Aside from this ID
the rest of the avrdude command line is just the same, no matter which
device is connected.
read/write eeprom, erase/write/verify flash on any device: -c butterfly
read/write fuses, read calibration byte on any device: -c avr910
6.2. avrdude 5.3.1
This version did not work for me, but I didn’t care about finding out why…
6.3. avrdude >= 5.4
These versions show a strange behaviour with -c butterfly
, I’ll go into
details here and first describe what happened up to version 5.2:
-
avrdude knows which device is connected by the string ID (option
-p
) and looks up the correspondingavr910_devcode
in its config file. Let’s assume we want to program an ATMega16; itsavr910_devcode
is0x74
. -
avrdude asks the programmer about which device codes it does support. The programmer will respond with a list of codes, for example (
0x68
,0x74
,0x43
). If the needed code (0x74
in this case) is not included in the list, avrdude would stop here. -
avrdude tells the programmer "hey, there is a
0x74
connected, adjust yourself to it".
Starting from 5.4, the behaviour changed. In step 3, avrdude does not send
the avr910_devcode
for the connected device, but some other code (the
first one from the list of supported codes). As the programmer needs this
code to correctly adjust itself to the device, it will fail now.
Starting from firmware 0.7, this programmer has a workaround: it simply ignores the code sent by avrdude, identifies the device by its signature bytes and adjusts itself to it. This does work in any case.
However, there is another problem: avrdude does not use block mode with byte mode devices since 5.4. This is no problem for other programmers, as they implement byte transfer AND block transfer — this one does not do so: byte transfer is slow (and even more if you’re using a usb-to-serial-converter); this one does only provide block transfer and is smart enough to use the correct programming algorithm.
6.3.1. Workaround using avrdude.conf
You can workaround this problem by editing avrdude.conf
. For example, I
added paged
, page_size
, and num_pages
to the 8535:
memory "flash"
paged = yes;
size = 8192;
page_size = 128;
num_pages = 64;
min_write_delay = 9000;
Just use page_size
= 128 and num_pages
= size
/ page_size
.
6.3.2. Workaround by recompiling avrdude
Edit avr.c
: remove && mem->page_size != 0
in avr_read
and avr_write
,
the resulting lines are
if (pgm->paged_load != NULL) {
and
if (pgm->paged_write != NULL) {
Compile, install.
7. avrdebug
The software to adjust voltage levels and debug the target device can be found here:
7.1. Debugging the target using avrdebug
avrdebug talks to avrdev using a block oriented serial protocol. avrdev talks to the target using a simple SPI protocol or (when using the UART) a simple serial protocol; both of them are explained in the documentation of avrdebug. Furthermore there are code examples which show how to implement both protocols for the AVR.
8. TWI/I2C interface
avrdev is able to act as a TWI/I2C interface even while debugging a device. However, I didn’t publish an application to use this feature yet.
9. Details
9.1. Debugging the target
Both the firmware on the adapter and the software on the host PC are not designed to debug via SPI, UART and SELF at the same time. This means you can only debug one target, although three can be connected at the same time. I thought that this will rarely be neccessary.
9.2. Optimization for speed in debug mode
Some things have been tweaked especially for usb to serial converters, but offer an enhancement with standard serial ports, too (see below). One can read a range of addresses in one command and it is possible to send a set of commands which are not answered before the whole block of commands has been received. Using such a usb to serial converter, exchanging a little number of big packets is faster than a high number of small packets (half duplex mode).
Stateless communication is not used because the hardware is not able to receive/send on its (hardware) uart and doing a software uart or spi at the same time.
For example, my prototype has a buffer of 128 bytes to receive commands from the host (PC). I tested writing 512 bytes on the target, which takes 512 single commands. Baudrate is only 19200 baud. There is no real communication to a target (uDebugMode==DEBUG\_SELF). The host PC runs linux 2.6.8. I measured the complete runtime of the program, which includes some additional communication not included in the calculation below (syncing to adapter, reading status information).
commands: 512\*6 Bytes (plus 25\*6 Bytes when using command blocks)
replies: 512\*2 Bytes
4096 Byte -> 2.13 seconds at 19200 baud
4246 Byte -> 2.21 seconds at 19200 baud
using a usb-serial converter:
time with command blocks : 2.8 s
time without command blocks : 8.6 s
using standard serial port:
time with command blocks : 2.6 s
time without command blocks : 4.0 s