Porting Peter Fleury’s STK500V2 bootloader to a custom avr board.

mega128 isp
mega128 accelerometer board with stk200 compatible isp programmer and usb to serial port adapter (rs232 to ttl circuit not shown).

I had problems porting the bootloader to my board twice, once with the atmega128 and now with the atmega168p while it worked already on the atmega128.

And I need a bootloader, for boards hidden from the user in a bag or in a box. I can’t use the SPI there. Plus the classic SPI connector takes a huge surface on the board compared to the chip size. I prefer to program a bootloader once with SPI and then upload the firmware through a serial port.

In this article I will describe the steps to port the bootloader created by Peter Fleury to a custom board. It is a compact bootloader with many stk500v2 functions. It has a lot of configuration options selected by macro definitions.


1/ Get the source code:

Many versions can be found. You can take my copy at github.com/mandraga/SombreroBms in the firmwarebms/stk500v2bootloader/ section (or you can find different bootloaders at github.com/arduino/Arduino).


2/ Change the makefile:

Change the µcontroller name to what you are using:

# MCU name
MCU = atmega128;

Change the clock to your clock:

#         F_CPU =  1000000
#         F_CPU =  1843200
#         F_CPU =  2000000
#         F_CPU =  3686400
#         F_CPU =  4000000
#         F_CPU =  7372800
#         F_CPU =  8000000
#         F_CPU = 11059200
#         F_CPU = 14745600
#         F_CPU = 16000000
#         F_CPU = 18432000
#         F_CPU = 20000000
F_CPU = 7372800

You can notice that most clocks are not simple numbers like 8000000hz. It is because avrdude uses 115200 bauds for the serial communication and in order to transmit data at this baudrate without any error you need a clock like 1843200hz or multiple of it. With this kind of frequency, the serial clock will be at 0,0% difference to the ideal baudrate. But if you use a 16Mhz clock you will have a 2,5% error. And 2,5% is enough to mess with the communication on a serial line (big chances to upload crap). In this case I picked a 7372800hz crystal.

Change the bootloader area start address:

# Bootloader
# Please adjust if using a different AVR
# 0x0e00*2=0x1C00 for ATmega8  512 words Boot Size
# Atmega168PA 0x1E00 512W but avrgcc takes bytes so   3C00
# Atmega128   0xF000 4096W but avrgcc takes bytes so 1E000


On this table from the atmega128 datasheet you can find the flash sections selected with the BOOTSZ1 and BOOTSZ0 fuses. For my atmega128 board I chose the 4096 words (8KBytes) bootloader size. And as you can see here, the bootloader start section begins at $F000. But this address is in words and gcc needs it in bytes. Therefore you must shift it left once to get the address given to gcc. $F000 becomes $1E000.

I made a mistake here and it took me several days to find it (that’s why I post this article). I used $F000 as the bootloader start address in gcc but my bootloader was somehow executing anyway while being in the middle of the 128kBytes of flash. And I could test the stk500v2 protocol, read the fuses… But when it came to use the SPM instruction to erase/program the flash, it failed. I had this message:

avrdude: verifying ...
avrdude: verification error, first mismatch at byte 0x0000
0x0c != 0xff
avrdude: verification error; content mismatch

First I though that the lock fuses were wrong, but they were 0xFF (SPM can read/write anything when executing in the boot section). Then I though that the power went down during programming, but no. And then I though that the avr-libc boot.h headers of the atmega168pa were not compatible. So I wrote the function in assembler. And what lost me is that when downloading the flash memory with the isp programmer, it missed the end of the flash and stopped at the end of the boot code. The downloaded flash content looked like the bootloader was at the end of the flash. Therefore I assumed that my boot address was the right one. This is where I began to lose time double checking everything.

By checking everything I finally changed the gcc code address to 0x1E000. And at this point SPM did erase the memory and write the page content to the flash.

3/ Changing stk500boot.c:

Chose the uart: some atmegas like the atmega128 have 2 uarts. By default it uses UART0 and if you define “#define USE_USART1”, you select the second uart.

The bootloader can use a led to indicate that it is working and an input button to indicate that you want to program the chip. The ports for it are specified here:

* Pin "PROG_PIN" on port "PROG_PORT" has to be pulled low
* (active low) to start the bootloader
* uncomment #define REMOVE_PROG_PIN_PULLUP if using an external pullup
#define PROG_DDR   DDRD
#define PROG_IN    PIND
#define PROG_PIN   PIND2

* Active-low LED on pin "PROGLED_PIN" on port "PROGLED_PORT"
* indicates that bootloader is active
#define PROGLED_PIN  7

I do not use the prog pin and everyone can change this in his own way. Change to your led port and pin here. And depending if it is a source or sink led port, write PROGLED_PORT = (1<<PROGLED_PIN); or PROGLED_PORT &= ~(1<<PROGLED_PIN); at the start of the main.

The flash address:

* Calculate the address where the bootloader starts from FLASHEND and BOOTSIZE
* (adjust BOOTSIZE below and BOOTLOADER_ADDRESS in Makefile if you want to change the size of the bootloader)
//#define BOOTSIZE 512
#define BOOTSIZE 4096
#define APP_END  (FLASHEND -(2*BOOTSIZE) + 1)

Here the value is defined in words, but APP_END is the same value as in the makefile, int Bytes.

4/ In circuit programming

Once you have changed all the parameters and compiled the bootloader, pick your spi programmer and do two things (assuming the clock fuses are already setup):

  •  Set the fuses: BOOTSZ1/0 to the flash size you chose and BOOTRST enabled by setting it to 0 in order to boot to the bootloader memory instead of $0000 on reset.
  • Program the bootloader with the SPI programmer.

At this point your bootloader should be running on the board. If you call avrdude like

avrdude -p atmega128 -P /dev/ttyUSB0 -c stk500v2 -U flash:w:main.hex

it should program the flash.


5/ What if avrdude does not work?

If at this point, avrdude fails you will ask yourself how to check the protocol? And by running “avrdude –help” you see that adding -v -v -v to the command line gives a lot of information. But not enough to debug the protocol.

I had a problem with an stk500v2 command, SPI_MULTI did not pass. A good solution to find a protocol problem is to download avrdude’s source code and in stk500v2.c change this:

#if 0
#define DEBUG(...) fprintf(stderr, __VA_ARGS__)
#define DEBUG(...)

Change #if 0 to #if 1 and recompile (make) reinstall (sudo make install). Now your avrdude will print all the data sent and received to and from the serial port. You will be able compare the data to what is specified in the protocol here: http://www.atmel.com/images/doc2591.pdf

And if avrdude fails somewhere you know on which command it is. In my case I forgot to enable SPI_MULTI by defining #define REMOVE_CMD_SPI_MULTI. You must comment this define otherwise avrdude will fail when sending the SPI_MULTI command. And if you use the default avrdude you will have no way to tell it was this command who failed.

The STK500v2 command and parameter codes can be found in the include file “command.h”.

About binary code size.

On the atmega168pa I need a bootloader size of 512Words, but I have this output with the same code as the atmega128. My binary is 1068Bytes long. Therefore it does not fit the 512W/1024Bytes.

Creating Symbol Table: stk500boot.sym
avr-nm -n stk500boot.elf > stk500boot.sym

Size after:
   text	   data	    bss	    dec	    hex	filename
   1068	      4	      0	   1072	    430	stk500boot.elf

-------- end --------

To go down to 512Workds, I uncommented “#define REMOVE_CMD_SPI_MULTI”. It removes part of the protocol, but it works with avrdude 6.3.

Creating Symbol Table: stk500boot.sym
avr-nm -n stk500boot.elf > stk500boot.sym

Size after:
   text	   data	    bss	    dec	    hex	filename
    962	      4	      0	    966	    3c6	stk500boot.elf

-------- end --------

In this case it was just because I needed all the memory, the BMS serial protocol takes a lot of program space (like 14700B/16KB). On the next version I will use an atmega32C1 and the bootloader size will not be a problem.


Configuring a bootloader for your avr may be difficult but it could be the best choice to program your board. With the condensed info here it should be easier to adapt it.


Battery voltage values over serial port over usb.

Capture d'écran de 2016-03-07 21:52:41

Just added a display of each battery voltage. It will be more usefull than I though for the development of the balancing algorithm. If it is possible to balance before charging it could be best solution. Because it removes completelly the capcitive effect. However if a battery is too low the passive draining of the batteries must not go to this level.

By balancing before charging, you wait for the balancing to finish when maybe you need to charge the car quickly. But if the pack is not too umbalanced or too low, it may be a reliable solution.

Also added a CRC to the transmited frames in order to be more confident in the transfered data. I do ont trust that much a serial port. On the linux side, it has his own thread and a stop char but a CRC does no harm.


BMS simulation

Capture d'écran de 2016-03-04 14:49:15

How to make a BMS simulator to test the AVR program? I wanted to keep the original AVR sources but test them as a linux app. I decided to separate the hardware and pure software sides of the BMS program. I modified the source code such that everytime the hardware is used, it is through a C function with no hardware includes. Therefore replacing the hardware C function calls to software C++ simulations and calling the state machine in a loop makes a simulator. I needed to add a graphic view of the battery packs (made with the SDL2 graphic calls) and voilà.

The BMS has a USB serial port to be able to update firmware and to communicate the battery information. This could be simulated with the tty simulator interface included in Linux. But I stayed with my first idea of using a named pipe. Because when created with mkfifo, you chose the name of the pipe, no need to pass it to the second app. I just needed two named fifos in /tmp/, one for each direction. I implemented them in a child class of the serial communication class. In that way the “BMS manager” app can dialog with the simulator or with the real hardware with the same functions.

Simulation speeds up the development process. Because it is easier to recompile and run from the PC than having to turn on and off switches on the battery pack test setup. I had a ton of bugs and still have to clean shit. I prefer bugs in the simulation than fearing of a balancing error killing batteries on the real hardware.

As you can see on the picture, the simulator allows to plug/unplug the charger, run/stop the motor, and change temperature. It is easy to check the diverse safety cases.

The GUI is coded with FLTK. From the internet pages about FLTK it looked like it only suports OpenGL 1.5 but in fact I ported the 2D drawing library using OpenGL 2.0 from Scoreview and it worked fine. So FLTK has this common point witht he SDL: it gives an OpenGL context directly.

And finally I burnt my 2 AD7280A, when installing the board on the test battery pack. I had reverse currents going up the AD7280A  and down to the µController (confused a via silkscreen circle with the 1 pin dot and mounted the ships with a -90° angle). I changed the design anyway, the fact that it is powered by the battery itself makes subtle changes from a standard design for a car.


Lithium batteries voltages.

I am working on a golf cart modification for lithium batteries for the go-lithium company in Romania. Two things are annoying with the lithium batteries:

  • if you go out of the specifications (temperature/current/tension) you destroy them.
  • measuring the sate of charge cannot be based on voltage unless you have a precise model of each battery and a temperature input.

In this article I will discuss the battery voltage behaviour on LiFePO4 batteries. And the consequences on the development process.

Encountered phenomenon:

When you charge, the electric tension is higher than at rest tension (say 39V against 37V in my example) and when you stop charging, it will slowly go down to a rest tension (37V) during approximately half an hour. But when you start using the battery pack, it will go down to the lower value (say 33,2V at 200Amps). The drop in tension is proportional to the current used, the more current used, lower the drop. But when you stop using current it will NOT return to the rest value found one hour after the charge. During 20 seconds after use, it will go back to another rest tension (say 36V) with a logarithmic curve shape, this is the “relax” time. And more over, all the measured thresholds will also change with temperature. (you can find a graph explaining this behaviour in Texas Instrument’s SLUA450 pdf document ti SLUA450)

So if you want to use the tension for balancing or you want to use the voltage measurement to know the State Of Charge, it will not be easy to calculate nor reliable. Unless a test bench is used to make a model. But even with a test bench, how to be sure that it will behave the same model after many years. And what if the chemical formula changes at the factory?

Knowing the SOC (State Of Charge) can be done with current measurement. Using a shunt or a hall effect current sensor and counting down micro Amp/Hours from a full charge value. The SOC problem can be solved with current measurement and Coulomb counting.

However for simple passive balancing you have to use the voltage measurement during the charge. But when stopping the charger for a wile to do passive balancing you will face the fancy capacitive behaviour of lithium batteries. And the reference tensions taken before stopping the charger will mean nothing after 2 minutes of balancing. Voltage will drop faster at the beginning of passive balancing and maybe go below the lowest battery.

This behaviour pushed me to create a simulator for the micro-controller program. And I believe that the simulator spares me a lot of error and trial. In fact it speeds up error and trial. I can test charge and discharge at high currents and test current protection without raping the pack. I can test the balancing algorithm at hight speed by skipping frames on the simulator.

Actually I am in the process of simulating the behaviour of the LiFePO batteries. It implies a state machine, and Vbat functions depending on time since state transitions (states like: charging, charger_stoped, running, relax…). I believe that otherwise, the design of advanced algorithms would be a nightmare.