AVR Bootloader and Programmer

For the AVR microcontrollers, control of program loading to the FLASH memory, and manipulating of the fuse and lock bits for configuring certain features of the microcontroller, can be simply done through a PC parallel port programmer (see the cdk4avr tools howto and references). Many of the AVRs provide a small section of FLASH memory with capabilities that allow it to modify its own FLASH memory. This is sometimes referred to as the bootblock. It normally resides in the top of FLASH memory and can be set, through fuse bits, to different sizes, typically 256 bytes to 4K bytes. The microcontroller can also be configured through a fuse bit so that after a reset, a jump directly to the bootloader section can be forced, rather than the usual jump to address 0.

For microcontrollers that have a UART, programming can be done through a PC serial port using the bootloader. This makes programming even simpler, particularly for PCs and laptops that do not have a parallel port. For those without a serial port, a commercially available USB to serial adaptor can be used.

Some tools developed for AVR programming are presented here. Similar tools are available in both open source and commercial versions, including bootloaders, PC programming control applications and hardware programmers. The tools described here were constructed to add some useful features.

AVR Bootloader

Atmel's application note AVR109 describes a bootloader that uses the UART to communicate FLASH programming instructions. They also provide some sample code in C that allows the programming of FLASH, EEPROM, and lock bits. Note that the AVR109 bootloader does not support the programming of fuses. The communication protocol used is compatible with their AVRPROG programming software. After loading this into the bootloader section, the device can be configured so that on reset the program counter starts at the bootloader, and programmer software can program the FLASH memory.

The ATMEL example bootloader program proposes the use of a port pin on the microcontroller to signal whether to execute the bootloader program or to jump directly to the application. This allows a user to set a jumper on the microcontroller card that pulls the pin low when programming of the FLASH is needed.

The adaptation of this code described here, keeps it almost unchanged in the interests of compatibility. By disabling the port pin test and excluding the EEPROM programming section and the AVRPROG compatibility section, the code will fit neatly into a 1K block of the bootloader FLASH section on ATMega48/ATMega88/ATMega168 AVRs*. Disabling the port pin test means that on reset the microcontroller always ends up in the bootloader program (if the appropriate fuse bit has been set), and must be explicitly sent out again with a bootloader exit command. For the moment this is acceptable as the device will normally operate while connected to a PC serial port. For standalone operation a pin will need to be made available to implement the port pin test, or the appropriate fuse bit programmed to start the device on reset at location 0.

Although the AVR109 code is freely available, Atmel has not released it under a clear open source licence. As such the modified code will not be reproduced here. Later I may write my own bootloader or find another suitable source, however it turns out to be difficult to produce more compact C code than that provided in Atmel's sample. There are assembler versions of the bootloader available (e.g. that by Herbert Dingfelder) that take up less space, however assembler code is less readable and potentially less general than a higher level lenguage. The changes made to the code are quite reasonable and simple (most are corrections to perceived minor deficiencies):

  1. Modify the block read and block write functions to add a compiler directive REMOVE_FLASH_BYTE_SUPPORT around the FLASH programming section (be careful of if-else clauses).
  2. Modify the block read and block write functions to add a compiler directive REMOVE_EEPROM_BYTE_SUPPORT around the EEPROM programming section (be extra careful of if-else clauses).
  3. Move the 'E' command outside of REMOVE_AVRPROG_SUPPORT so that we can get out of the bootloader into the application.
  4. Add a compiler directive REMOVE_PROGPIN_TEST around the the port pin check section. This must also be added at the end of the "for" loop where the jump to the application is made.
  5. The preprocessor.sh script can be modified to generate an all-encompassing defines.h file covering all microcontroller types, rather than using the suggested cut and paste system. This makes use of the availability of the MMCU variable in avr-gcc to identify the microcontroller type.
To allow the application to execute a jump into the bootloader I used an instruction to enable the Watchdog Timer on a very short timeout to force the MCU to be reset. There seemed to be a problem either with the compiler or the AVR that a long jump was not possible. This has the advantage that it does not require the exact address of the bootloader to be set in the code, but of course prevents the Watchdog Timer from being used for for other purposes. I then added code to disable the Watchdog Timer at the start of the bootloader. This turned out to be problematical as the ATMega88 and related microcontrollers use a two step process to prevent accidental changes to the Watchdog Timer control registers. Therefore the code below is specific to these MCUs. A small amount of work will be needed to adapt it to other devices. The wdt.h header file provided with avr-libc does not support this at the time of writing.

For the bootloader, clear all reset flags in the MCUSR, and then perform a write to WDTCSR to set the WDCE and WDE bits. This enables changes to occur in a short timeframe. Then immediately afterwards wipe the entire register to disable the timer.

    asm("cli");
    asm("wdr");
    MCUSR = 0;
    WDTCSR |= (1 << WDCE) | (1 << WDE);
    WDTCSR = 0;

For the application
, clear all reset flags in the MCUSR, and then perform a write to WDTCSR to set the WDCE and WDE bits to enable changes. Then immediately afterwards perform a write to WDTCSR to set the WDE bit to enable the timer for system resets only, with the timeout period set at its smallest value (about 15ms).

    MCUSR = 0;
    WDTCSR = (1 << WDCE) | (1 << WDE);
    WDTCSR = (1 << WDE);

Programmer GUI Application

A GUI programmer for Linux was written in QT4 and C++ (see the documentation) . This provides basic hex file load and verify, and some fuse/lock bit programming for selected devices (do not fiddle with these settings until you have carefully read and understood the datasheets). The programmer opens a serial port (which may need to be changed to match the target system) and synchronizes with the device using no parity, 1 stop bit and 8 bit data. A character 0xDD is sent and the character received back from the device is checked for either a 0xDD or a "?". The latter indicates that the bootloader may have been found. The 0xDD character relates to the packet protocol used in the Acquisition project. If this is received, then a packet is sent to instruct the device to jump to the bootloader. This can be removed if the application protocol is not needed, but that is not really necessary as the bootloader will just reject anything unknown. If the bootloader is not present the device will probably just reset itself. If neither character is received, the baud rate is cyclically changed through a set of standard rates for a couple of cycles.



When the program has synchronized with the device, it gathers a bunch of information and presents the above window. An Intel hex file can be opened and will be immediately read and loaded to the application area of the bootloader (the bootloader is never overwritten by this: it can only be changed through the parallel or SPI programming method).

The default baud rate to start with is 38400 baud. If this does not match that of the device, then the program will cycle through standard baud rates between 2400 and 57600 baud until it finds the correct one. This process can be sped up by changing the default baud rate to match that of the bootloader.

The program allows for turning off block mode, and also allows verification without programming and vice-versa. Autoaddress currently does nothing so it is disabled. There is a chip erase feature provided: normally the E button is invisible until the checkbox has been selected to minimize accidental erasures. An erase may be necessary if the lock bits have been set incorrectly. This requires an external programmer. A bootloader will not reset the lock bits.

The GUI program relies on inserting delays into the serial communications to adapt the speed of the PC to the bootloader or serial programmer. Also when waiting for a response from the programmer, the program will timeout if this takes too long. If problems are experienced while programming or verifying, it may be that these delays need to be increased. This is particularly the case if the clock speed used with the MCU is low. The GUI program was tested with 8MHz MCU clock frequencies and 38400 baud communications.

Additional sections have been added to enable fuse and lock bits to be read and changed for a limited number of devices. The program uses the device signature bytes to identify the device uniquely, and the appropriate window is invoked. Note that there may be bugs in this code so use it at your own risk. It has only been partly tested.



Installation

To install this, unpack into a directory which by default will be serial-programmer-pc. The serial device used (/dev/ttyUSB0 in Linux) is specified in the main program and refers to a USB port for use with a USB to serial converter. Obtain QextSerialPort and unpack the tarball into the directory qextserialport, making sure that all the source directories are under the same top directory (otherwise the .pro files will need to be changed). Make sure that the QT4 tools are installed and also g++. These should be readily available for most distros. I have tested this on Ubuntu.

Go into the directory qextserialport and execute:

$ qmake-qt4
$ make clean
$ make

This will build the libraries. Now go into the
serial-programmer-pc directory and execute:

$ qmake-qt4
$ make clean
$ make

This will build the application. Copy the binary to a suitable place and invoke with:

$sudo avrserialprog

AVR Programmer

An AT90S2313 (possibly obsolete by now but replaceable by the ATTiny2313) was used to build a small serial programmer (described here). This was designed to communicate serially with a PC programming application using the AVRPROG protocol as used in the bootloader, and to program the target AVR device using the SPI interface.

A feature of the circuit is that when programming of a target device has completed, an output port pin is configured to switch the serial communications from PC to programmer order to PC to target. This allows the programmer to remain connected after programming while the target device program runs normally. To restore to programming mode the programmer must be physically reset. This approach reduces a lot of fiddling with plugs during application development. There are two SPI connectors in the circuit, a 10 pin header compatible with the ET-AVR series of boards sold through Futurlec, and a 6-pin polarised header. The AT90S2313 program supports target programming of FLASH, EEPROM, lock and fuse bits. The programmer source code is available but is missing a number of files that were taken from the AVR109 code, notably: defines.h, flash.h, parts.txt, preprocessor.sh, preprocessor.xls, serial.c, serial.h. These are not reproduced here for copyright reasons. Also provided is a gEDA schematic of the circuit shown below.


* Note: the version of avr-gcc available at the time of writing produces code that is slightly larger than that produced by the cdk4avr version of February 2006 even for the best optimization level. This is enough to push the resulting bootloader binary just beyond the 1K limit (enough to require the disabling of a number of desirable features).

Contact: My email address can be constructed from the username "ksarkies" and the ISP DNS address internode.on.net in the usual way.


First created 13 May 2007

Last Modified 6 April 2010