What a Microcontroller Bootloader Is and How It Works?
Generally, when you want to program a microcontroller, you need a programmer for that particular microcontroller. Apart from being expensive, it might have other disadvantages, like long programming times or too many interconnections between the micro and the programmer itself; some programmers are not capable of programming in circuit. Sometimes the micro is not available while mounted in the end product, as it might be inside a metal or plastic housing, with only some standard communications interfaces being available on a connector.
Many of these problems are addressed by a bootloader. For a hobby, sometimes it seems quite expensive to spend tens of euros (and sometimes hundreds of euros) for such a device. In university laboratories, it might not be practical to get a programmer for each working bench, and the best solution is to provide the students with microcontroller samples which have already been programmed once and which have the Bootloader in the program memory. In the automotive industry, many Electronic Control Units are encapsulated in housings leaving very few pins accessible, but among these pins there generally is a CAN interface available, making CAN based Bootloaders very popular in this environment.
So, the Bootloader has to be programmed in the program memory of the microcontroller just once, using a conventional programmer. After this, the microcontroller can be programmed without a programmer. Once in the microcontroller, the bootloader is such programmed that each time after reset it starts running like any conventional program. What it does however is different from a regular program. First of all, depending on what type of bootloader it is, it starts “listening” for incoming bytes via a specific interface. For instance, a UART bootloader will listen to the UART buffer of the micro, checking for incoming bytes. If the bytes start arriving, the bootloader will grab them and write them in the program memory in the sequence it receives them and at predefined locations. Once all bytes have been received, the bootloader executes a jump at the start of the memory zone it has received and then the “normal” program starts running. This is how a normal program would be programmed in the 8 Kbyte memory of a micro:
But in case you would want to use a bootloader, you would first need to program it instead of the regular program. So, as a first step, the bootloader gets programmed in the program memory. A good location for the bootloader is the end of the memory space, but a little part of it will always have to be at the first address location, as the bootloader will have to start running when the micro comes out of reset:
Once the bootloader is in place, and the micro comes out of reset, the bootloader will start listening to one of the micro interfaces (like UART, SPI, I2C, CAN etc) for incoming bytes. Sometimes, “listening” might not be the proper way to describe this, as the bootloader might need to generate some clock pulses on the SPI clock line, or it would have to initiate the I2C protocol. If data does come through in the format expected by the bootloader, it starts writing the incoming bytes in the program memory. However, due to the fact that a few locations at the reset vector are occupied by the bootloader code itself, the data of the incoming program that would normally be written there will have to be relocated at a known address (EFFFh in the given example):
Once the whole incoming program has been written to the flash program memory, the bootloader will execute a jump at the known address of EFFFh, and the program that was sent to the micro will start running. In case no data will came through the intended interface, the bootloader will wait for a while, and then execute the jump to the known address anyway, based on the supposition that the user just wants to run a program that was already burned in the program memory at a previous stage. So the general steps in a working bootloader are:
Step 1 – right after reset, the bootloader takes over and executes a jump to the memory area where its own code exists. The code existing there starts to listen the chosen external interface for incoming data.
Step 2 – the incoming data is written in the program memory as it would be written by a programmer, except for the first few bytes, which would normally be burned at the reset vector.
Step 3 – once the entire incoming data has been written, the bootloader executes a jump at the first instructions of the regular program.
The above example is only one of the ways in which a bootloader may function. There are also other dedicated ways, in which the micro, for instance, might have a dedicated area in the program memory for a bootloader. The PIC18F52 (datasheet) is only such an example where a bootloader will be written at the beginning of the program memory:
Of course, in such a case, not only the reset vector of the regular program will have to be mapped, but all the interrupt vectors. This is easily done on a micro which only has two interrupt vectors (one for high priority, another for low priority interrupts), but it might be not so handy on other micros, where the interrupt vectors for each interrupt source have different addresses (like on the good old 8051 architecture).
Now let’s say a word about the type of bootloaders.
The most common one used to be the UART bootloader. This meant that after each reset the micro would start to listen to the UART interface for the incoming data. They are still widely used today as it is a common interface for many microcontrollers. This type of bootloader basically removes the need for a programmer. You only need a PC software (cheap and easy to make/find) and a serial cable, provided the UART interface is used by the micro anyway. Gradually, after the appearance of USB interfaces on micros, the USB bootloader came into existence. It performs the same functions and is pretty handy to use.
Another more special type of bootloader is the SPI/I2C bootloader. Basically, the way it works is similar to any bootloader, but instead of listening to UART/USB it tries to read data from an attached EEPROM memory with an I2C or SPI interface. This bootloader is designed for allowing easy upgrade of software in the field. You could attach the SPI memory in a socket, and a firmware change might mean as little as changing an IC in a socket. Of course in order to do this, you need to have physical access to the EEPROM in question.
The CAN bootloader is extensively used in the automotive industry. Most of the devices to be found in a car have a CAN port nowadays, which is accessible at two pins on a connector. Due to this fact, bootloaders that work with the CAN interface of the micro have been developed, and therefore you may upgrade the firmware of such a device without even opening its housing or without even taking it out of the circuit, due to the networking prone nature of CAN. Thus, it allows in field upgrades of software for cars and even small corrections to cover bugs on the cars that have been sold like that. So next time you take your car to an “authorized” garage think twice about it when you see the technician plugging a “tester” into your car. He might actually be rewriting the software fixing bugs you never even knew existed…