A Serial Bootloader for PIC18

    This page describes and documents a bootloader I have written for PIC18F series microcontrollers. While numerous bootloaders are available, this one differs in that it does not require special software running on the host computer. Instead, any communications program can be used.

    Update: new version! (November 21, 2012)

    I have made some minor changes to the bootloader to make it more robust and a bit simpler to use. It now protects itself better from user programs that don't have the correct jump address at the reset vector. As a result, the user program no longer must be programmed with the correct bootloader address and the same user program HEX file will work without the bootloader too. The rest of this web page has been updated to reflect these changes.



    A microcontroller (mcu) is a useful little computer-system-on-a-chip that can be programmed to perform an almost unlimited number of useful tasks (and probably an even greater number of useless but entertaining tasks.) An obvious question then would be 'How does the program get into the microcontroller?' Usually a special programmer is used to 'burn' a program into the microcontroller's memory. For PIC microcontrollers, a PICkit 3 or an ICD 3 is a typical programmer (which can also function as a debugger, but that is a whole different story.)

    However, modern microcontrollers have an interesting ability to write to their own memory and this feature opens up an entirely different programming method. A special program running on the microcontroller could communicate with an external device and receive data containing the desired program that it could then write to itself. An astute reader might ask how the microcontroller knows whether it should run the program that performs the device's task or run the programming function. This is exactly the third critical aspect of the special program running on the microcontroller. This program is known as a BOOTLOADER and is the subject of this page.



Bootloader Requirements

    As we have just seen, a bootloader requires three aspects in order to function. It must be able to communicate with the outside world, it must be able to write to memory and it must be able to assume control of the microcontroller.

    A typical bootloader assumes control of the mcu when the chip first powers up and comes out of reset, or boots. The bootloader then checks for a predefined signal or condition that indicates whether the bootloader should continue to run or relinquish control to the user program instead. The signal could be a particular I/O pin pulled high or low when the controller comes out of reset, or a particular instruction received through a communications link.

    A new program to be burned into an mcu must come from somewhere. The source could be at the other end of a communications link such as an RS-232 port, a USB connection, ethernet, wireless or some other such. Most bootloaders of this type use custom software to communicate with and control the bootloader. A bootloader can also be designed to recognize new firmware present on a removable memory device such as an SD card.

    All the above would not amount to a hill of beans if the microcontroller were unable to write to its own memory. Indeed, bootloaders were not possible on older PIC chips and are not possible with many of the current, smaller devices. The same is true for other microcontroller families too. Fortunately, modern chips have become complex enough to have the ability to write to their own flash memory, of which there is an ample amount easily sufficient to hold both the user's program and the bootloader.



Firmware Description

    Bootloader programs are nothing new. There are numerous different ones written for PIC microcontrollers. Microchip Technologies Inc has published a few application notes (such as A.N.851, A.N.1302 and A.N.1310) as have many other programmers. The smallest one appears to be Tiny PIC bootloader by Claudiu Chiculita. (The bottom of his page includes a somewhat dated list of other available bootloaders.)

    So why have I gone and re-invented the wheel? Well, mainly, none of the other bootloaders worked exactly the way I wanted. I tried Microchip's AN1310:High-Speed Bootloader for PIC16 and PIC18 Devices with poor results, studied others and decided I would take a crack at it myself. I especially wanted two particular characteristics: the bootloader must not require custom software running on a PC and the bootloader must not in any way affect the operation of the user program.

    The desire to avoid custom software, in my opinion, provides a few benefits. I may use PC's running Windoze for development, but there are those who don't. There could even be, shockingly, Macintosh users who might like to burn new firmware to a PIC microcontroller. Imagine that! With enough jumping through hoops, they could perhaps get that custom bootloader control software working but why make things so complicated? By removing the need for custom software, any RS-232 serial device with basic communications software can be used to update firmware.

    My reasons for wanting to avoid affecting user program execution stems from a particular application I was developing. Many of the available bootloaders live at the low end of the PIC's memory map and require the user program be relocated higher up. A side effect of this is that the (high and low priority) interrupts of the PIC18 must be vectored with an extra GOTO or BRA statement. Because of the high interrupt load my application was placing on the processor, I didn't really want to give up even those 2 or 3 instruction cycles. As a result, I wrote my bootloader into the top of program space. The user program can work with the standard interrupt vectors exactly the same as if the bootloader weren't there at all. The only change that the user program needs to work in harmony with my bootloader is that two GOTO statements must be placed at memory location 0x0000. The bootloader will change the first one when the user program is written to flash memory.

    Fortunately, the (high priority, if priorities are used) interrupt vector is at location 0x0008 so there is room for a second GOTO statement. So the start of the user program becomes:

      ORG     0x0000                   ;reset vector
      GOTO    UserProgramStart
      GOTO    UserProgramStart
      ORG     0x0008                   ;(high priority) interrupt vector
      GOTO    HighPriorityInterruptHandler
      ORG     0x0018                   ;low priority interrupt vector
      GOTO    LowPriortiyInterruptHandler

    There are two nice side effects of this requirement. The user program does not need to know anything about where the bootloader is and the exact same user program will work without any changes on a PIC chip with no bootloader. (This differs from the previous version that required 'GOTO BootLoaderStart' as the first statement at the reset vector. Wouldn't work if the location of BootLoaderStart was unknown or wrong, or there was nothing there.)

    If interrupt priorities are not used, the low priority vector can be ignored and the interrupt service routine can start at location 0x0008 without any GOTO statement. If interrupts are not used at all, both interrupt vectors can be ignored and the user program can start right after the GOTO BootLoaderStart statement without any GOTO statement. Just be aware that an error in the processor configuration code that results in the enabling of any interrupt will produce some nasty bugs if there is no code to trap unexpected interrupts.

    The bootloader is quite simple to use once it has been burned to flash. Use your choice of programmer to accomplish this. The HEX file for a particular device contains all the necessary information including the configuration registers. HEX files for various PIC18F devices are available near the bottom of the page.



Usage and Limitations

    The bootloader is very easy to use once everything is put together. With a straight forward hardware setup, the firmware programmed into the PIC chip, a serial link of some sort to a host computer and standard communications software, everything is ready for bootloading. Of course, you will also need an application to actually program into the chip.

    The hardware setup is quite simple. The PIC chip's UART/USART peripheral is used for the basic serial communication. Another I/O pin is used to output the hardware flow control signal. This line, called CTS, is driven low by the PIC chip when it is ready to receive more characters and is set high when the PIC chip is busy or it's receive buffer is almost full. (A complete hardware flow control interface would require a second I/O pin used as an input to receive the RTS signal from the host computer. However, it is a reasonable assumption that the host machine will never be overwhelmed by data coming from the PIC and therefore this signal line can be safely omitted. This bootloader does NOT monitor an RTS input signal.) An LED connected to another output pin is useful for monitoring status but isn't strictly necessary. The particular pins used depend on the particular PIC18F chip. Since the CTS pin and the LED pins are just ordinary output pins, they are easily move to other pins if necessary.

    PIC18F Device


    Transmit Pin

    Receive Pin

    CTS Pin

    Status LED



















    Table 1: Interface Pins for Various PIC18F Microcontrollers

    Figure 1: Basic Connection Diagram for a PIC18F14K22 Microcontroller (Decoupling caps not shown!)

    The serial port pins of the PIC microcontroller operate at TTL voltage levels, 5 volts for many of the devices, 3.3 volts or lower for some. However, the serial port on the back of a computer (assuming your modern computer even has a serial port) uses RS-232 voltage levels which could be +/- 15 volts. This voltage discrepancy requires an RS-232 level shifter to bridge the gap. A better solution in today's USB world is to use a direct serial-TTL to USB converter, such as these cables from FTDI, available in both 5 and 3.3 volt versions and various cable ends with drivers for major operating systems.

    With the bootloader firmware programmed into the PIC chip and the mcu serially linked to the host computer, the system is ready. Start your communications program program of choice on the host computer (For PC's running Windoze, I use RealTerm. Hyperterminal works also. I've had hardware handshaking problems with TeraTerm that I've never been able to solve.) Set the communications software to use 38400 baud, 8 data bits, no parity, 1 stop bit and hardware (RTS/CTS) handshaking. The bootloader has not been written with the ability to auto-detect the baud rate. Local echo should be off, no terminal emulation is required and default CR-LF (carriage return, line feed) settings should be fine.

    To enter the bootloader, hold the BootLoad button and reset the PIC. If the LED is connected, it will turn on. Once the BootLoad button is released, the bootloader will output an information screen with a list of commands. The LED should flicker to show serial activity.

    Figure 2: BootLoader Splash Screen and Commands

    The BL> prompt indicates that the bootloader is waiting for a command. Note the two gibberish characters at the top of the screen image. These are a side effect of toggling the TX output pin to determine if the BootLoad button is pressed when the PIC comes out of reset. This is an unavoidable side effect of the method chosen to signal the bootloader to take control.

    The available commands should be quite straight forward and easy to understand. The commands are single letters and can be in upper or lower case.


    Program a HEX file to memory. Any valid locations contained in the HEX file will be programmed. Requires confirmation.

    Write only the EEPROM locations contained in a HEX file to memory. Any FLASH locations will be ignored. (They'll appear as Range Errors.)


    Erase all of EEPROM. Requires confirmation.


    Read all of EEPROM and display on screen.


    Help - display list of commands.


    Reset the PIC chip using a software RESET command.

    Because large scale erasing is involved, the P and E commands both request user confirmation. A capital Y is the only response that proceeds with the command. Any other character cancels it.

    The Program and Write commands request a standard HEX file containing the program and/or data to be writen to FLASH or EEPROM. MPLAB generates a suitable HEX file when building a project. This HEX file is just a text file and can easily be sent by the comm software. As each line is sent, the bootloader will reply with a two letter result code followed by an echo of the line. Lines that are more than 255 charcters long will generate an error. The last line of a properly formed HEX file indicates the end of a file. CTRL-C can be used to terminate HEX file mode early. Note that a Program Flash command will already have erased flash memory before a CTRL-C can cancel the HEX file mode.

    Figure 3: Programming Example with Result Codes

    A sample programming session is shown in figure 3 and shows the different result codes. The first 3 lines were accepted and acted upon. The 4th line gave a No Good error, indicated by the NG. The error was generated in this case because the checksum didn't match. Malformed lines, illegal block types and lines that are too long will also generate NG errors. The 5th line generated a Sequence Error, SQ, because the requested write address is out of order or sequence. This is explained in more detail below. The 6th line reported a Range Error, RE, because the requested write address is in the memory range occupied by the bootloader. The bootloader protects itself by preventing any attempts to over-write it. The last two lines generated Range Errors because they were requesting configuration memory locations. This version of the bootloader specifically will not allow changes to the mcu configuration or ID locations. The very last line of the HEX file, which always contains the text ":00000001FF", does not actually get echoed. Instead, Finished! is displayed.

    The bootloader requires that the data in the HEX file be in increasing memory order. This is because flash memory must be written in blocks and is not writeable on a byte by byte basis. During the flash programming process, an index is initialized to location 0 and advances up through memory as the HEX file is received. Any lines in the HEX file that specify an address previous to this running index will generate a sequence error. MPLAB appears to generate HEX files with nice ascending addresses. (EEPROM is byte addressable so the this requirement for sequential locations while writing to EEPROM is not quite as strict.)

    I specifically wrote the bootloader to not allow changes to the mcu configuration registers. The main reason for this was to prevent configuration changes that could cause the bootloader to no longer function. The bootloader HEX files provided below include a listing of the CONFIG settings used. Generally, these are set to use the internal oscillator at its highest frequency, no watchdog timer and no code or other memory protection.



PIC18F Hex Files

    Here are HEX files pre-built for various different PIC18F chips.
    Each HEX file includes a ReadMe that lists the pre-set CONFIG settings plus other notes of interest.

    An easy way to get the HEX file programmed into the PIC chip is to use MPLAB and your programmer of choice. Start MPLAB and ensure no project is open. From the File menu, select Import.... Browse to where ever you saved the HEX file downloaded from below and open the HEX file. Visit the Configure --> Configuration Bits... menu to confirm that the settings are correct. Connect your programmer to the computer and select it from the Programmer --> Select Programmer menu. Finally, click the program icon, or select Programmer --> Program to burn the HEX image to the PIC chip. If all has gone well, the chip is now ready for use with the bootloader installed.

    PIC18F Device

    (Right click and Save Link As...)





    ASM Template




    ASM Template




    ASM Template

    Other devices can probably be added to the list. If you need something, post a request in the forum. Depending on the device characteristics, porting could be as simple as a re-assembly. Note that devices with more than 64k of flash or more than 256 bytes of EEPROM would require more extensive source code changes to take full advantage of the increased memory.


    Send any comments, concerns, questions or anything else to
    vegipete at this domain dot net.


Last Update: November 21, 2012

Copyright Strong Edge Dynamics, April 2012.