I’ve got a couple of projects on the go that use the PIC18F2550 (or PIC18F2553) microcontrollers.
Normally I program PICs in-circuit with a PICkit 3 programmer while debugging my code. While this works great, I also need a way for people to easily field-upgrade my PIC firmware, without the need for a PIC programmer.
So I decided to modify my projects to be compatible with an existing USB bootloader.
The bootloader will allow new firmware to be downloaded over the PIC’s in-built USB interface. This requires two special pieces of software: a boot loader application (running on a PC) that takes a compiled PIC user program (in “.hex” format) produced by the C18 compiler, and bootloader firmware (running on the PIC) that accepts commands and data from the bootloader application, and stores the downloaded user program in the PIC’s program memory.
The main downsides of using a boot loader are:
- the bootloader firmware permanently occupies space in the PIC’s program memory. So it reduces the maximum potential length of your user program.
- because the bootloader firmware sits in low memory, the reset and interrupt vectors need to be remapped to appropriate locations in the user program, above the bootloader firmware.
- the configuration bits will need to be set in the PIC to suit the bootloader application. Your user code will need to use these configuration bits, as there cannot be one set of configuration bits for the bootloader and a different set for the user code.
Choosing a bootloader
It seems there are two good USB bootloader options available from Microchip. The first is known as a “CDC bootloader”. This has the advantage of the bootloader firmware only occupying 0x0800 bytes of the PIC’s program memory, but the downside is that (I think) special USB drivers need to be loaded on the PC. The second option is a “HID bootloader”. This doesn’t need any special USB drivers loaded on the PC, but the bootloader firmware occupies the lower 0x1000 bytes of the PIC’s memory.
I’ve decided to go with the HID bootloader method, to avoid the need for users to install special USB drivers on their PC.
Obtaining the bootloader software
Unfortunately Microchip don’t make the required files available as a simple stand-alone download. Instead you have to download and install the whole “Microchip Libraries for Applications” (MLA) 278MB catastrophe.
Downloading
You can download various versions of MLA from here on Microchip’s website. You will find Windows, Linux and Mac OS X versions of MLA available for both the current IDE environment (MPLAB X), or the one I am using (MPLAB 8).
Because I’m using MPLAB IDE v8.92 (I’ve not converted my user programs to work with MPLAB X), I had to go to the “Legacy” tab of the MLA website to get the right version of MLA. I chose the latest available release (v2013-06-15) and downloaded the Windows version of it.
Installation
Installation of MLA is fairly straight forward. I accepted all the defaults during setup (including the proposed destination folder of C:microchip_solutions_v2013-06-15). On the “Select Components” screen, I accepted the pre-selected defaults, which probably installs a bunch of stuff that we don’t really need. I got an error message towards the end of the somewhat-lengthy installation process that said “Jave Run Time Environment Version is invalid or not found. Download JRE?”. I answered NO, as I know that my MPLAB 8 IDE environment is functioning just fine. The install process seemed to complete OK.
The bootloader application
Once MLA has been installed, navigate to “C:microchip_solutions_v2013-06-15USBDevice – BootloadersHIDSoftware Windows Executable”. In this directory you will find the file “HIDBootloader.exe”. This is the bootloader application that you will run on the PC. When run it reports its name as being “USB Bootloader v2.9j”. You can’t just move it elsewhere in your file system, as it seems to rely on one or more DLLs in the MLA directory structure. So instead, create a link to it in the directory you like to work in (such as the directory containing the project files for your PIC project files).
The bootloader firmware
Navigate to “C:microchip_solutions_v2013-06-15USBDevice – BootloadersHID”. Here you will find a bunch of “.hex” files. The file “USB Device – HID – HID Bootloader – C18 – PIC18F4550.hex” is the bootloader firmware that you need to download to your PIC18F2550. Yes – this file works with both a PIC18F4550 and a PIC18F2550. You will need to use the PICkit programmer to get this firmware into the PIC chip.
The firmware requires that your board uses a 20MHz crystal, and the bootloader switch is connected to pin RB4.
To program the bootloader firmware into the PIC, do the following:
- Start the MPLAB IDE
- Use Configure -> Select Device, to select PIC18F2550 as the target device
- Use Programmer -> Select Programmer, to select the PICkit 3 (or other programmer that you might be using)
- Use File -> Import, to read the bootload firmware “.hex” file into MPLAB
- Use Programmer -> Program, to program and verify the PIC
Testing the bootloader functionality
With the bootloader firmware loaded in the PIC, and the PIC’s pin RB4 held low, reset the PIC. This will run the bootloader code inside the PIC.
Now start the bootloader application on your PC. The screen should appear as follows:
This shows us that both pieces of bootloader software (the application on the PC, and the firmware on the PIC) are talking to each other.
You’re now *almost* ready to download user programs (in “.hex” format) to the PIC via the bootloader. The missing step is that you need to modify your PIC program’s source code, and compile it using a modified linker script, so it is compatible with a PIC chip that has the bootloader resident in lower memory. These steps are explained below.
Under the Program -> Settings there are 3 checkboxes that you can set or clear. One of these says “EEPROM”. If this setting is ticked, EEPROM values that are set in your user code can be downloaded (via the bootloader) to the PIC. This is a nice touch. However, the trap here is that even if this box is not ticked, the bootloader firmware will still erase all EEPROM memory locations. There isn’t any option in the bootloader application that allows you to retain the existing EEPROM contents when downloading your user code to the PIC’s program memory. Just be aware of this, if your user code starts misbehaving because it expected the EEPROM contents to be preserved.
Linker script
You will need to add a linker script to your project in MPLAB. A bunch of sample linker scripts are provided by MLA in the directory “C:microchip_solutions_v2013-06-15USBDevice – BootloadersHIDFirmware – PIC18 Non-JLinker files for applications”.
I don’t recall all the steps I took in experimenting with linker scripts, but here’s the one I’ve been using successfully recently with the PIC18F2550:
LIBPATH . FILES c018i.o FILES clib.lib FILES p18F2550.lib CODEPAGE NAME=bootloader START=0x0 END=0xFFF PROTECTED CODEPAGE NAME=vectors START=0x1000 END=0x1029 PROTECTED CODEPAGE NAME=page START=0x102A END=0x7FFF CODEPAGE NAME=idlocs START=0x200000 END=0x200007 PROTECTED CODEPAGE NAME=config START=0x300000 END=0x30000D PROTECTED CODEPAGE NAME=devid START=0x3FFFFE END=0x3FFFFF PROTECTED CODEPAGE NAME=eedata START=0xF00000 END=0xF000FF PROTECTED ACCESSBANK NAME=accessram START=0x0 END=0x5F DATABANK NAME=gpr0 START=0x60 END=0xFF DATABANK NAME=gpr1 START=0x100 END=0x1FF DATABANK NAME=gpr2 START=0x200 END=0x2FF DATABANK NAME=gpr3 START=0x300 END=0x3FF DATABANK NAME=usb4 START=0x400 END=0x4FF PROTECTED DATABANK NAME=usb5 START=0x500 END=0x5FF PROTECTED DATABANK NAME=usb6 START=0x600 END=0x6FF PROTECTED DATABANK NAME=usb7 START=0x700 END=0x7FF PROTECTED ACCESSBANK NAME=accesssfr START=0xF60 END=0xFFF PROTECTED SECTION NAME=CONFIG ROM=config STACK SIZE=0x100 RAM=gpr3 SECTION NAME=USB_VARS RAM=usb4
Remapping reset and interrupt vectors
Each time the PIC is reset, the bootloader code looks at input pin RB4.
If the pin in high, the bootloader code doesn’t execute, and instead the bootloader jumps to the start address of your user program at 0x1000. When high and low priority interrupts occur, the bootloader codes re-directs them to your user code at addresses 0x1008 and 0x1018 respectively.
So our user code needs to be written to take the above considerations into account. The “main.c” files in various projects in MLA will give you guidance as to how to do this.
But the basic principles are as follows:
- your code should have jump vectors at 0x0000 (to 0x1000), 0x0008 (to 0x1008) and 0x0018 (to 0x1018) so that if programmed without the bootloader, the code will still operate normally. If the PIC is programmed via the bootloader, the bootloader will simply discard the attempts to write to addresses with the address space occupied by the bootloader
- your code’s startup address should be 0x1000
- your code should assume high priority interrupts will arrive at 0x1008 and low priority interrupts will arrive at 0x1018
Here’s an outline of the relevant parts of my main.c file that addresses the above structural points:
#define REMAPPED_RESET_VECTOR_ADDRESS 0x1000 #define REMAPPED_HIGH_INTERRUPT_VECTOR_ADDRESS 0x1008 #define REMAPPED_LOW_INTERRUPT_VECTOR_ADDRESS 0x1018 #define APP_VERSION_ADDRESS 0x1016 //Fixed location, so the App FW image version can be read by the bootloader. #define APP_SIGNATURE_ADDRESS 0x1006 //Signature location that must be kept at blaknk value (0xFFFF) in this project (has special purpose for bootloader). //-------------------------------------------------------------------------- //Application firmware image version values, as reported to the bootloader //firmware. These are useful so the bootloader can potentially know if the //user is trying to program an older firmware image onto a device that //has already been programmed with a with a newer firmware image. //Format is APP_FIRMWARE_VERSION_MAJOR.APP_FIRMWARE_VERSION_MINOR. //The valid minor version is from 00 to 99. Example: //if APP_FIRMWARE_VERSION_MAJOR == 1, APP_FIRMWARE_VERSION_MINOR == 1, //then the version is "1.01" #define APP_FIRMWARE_VERSION_MAJOR 1 //valid values 0-255 #define APP_FIRMWARE_VERSION_MINOR 0 //valid values 0-99 //-------------------------------------------------------------------------- #pragma romdata AppVersionAndSignatureSection = APP_VERSION_ADDRESS ROM unsigned char AppVersion[2] = {APP_FIRMWARE_VERSION_MINOR, APP_FIRMWARE_VERSION_MAJOR}; #pragma romdata AppSignatureSection = APP_SIGNATURE_ADDRESS ROM unsigned short int SignaturePlaceholder = 0xFFFF; #pragma code HIGH_INTERRUPT_VECTOR = 0x08 void High_ISR (void) { _asm goto REMAPPED_HIGH_INTERRUPT_VECTOR_ADDRESS _endasm } #pragma code LOW_INTERRUPT_VECTOR = 0x18 void Low_ISR (void) { _asm goto REMAPPED_LOW_INTERRUPT_VECTOR_ADDRESS _endasm } extern void _startup (void); // See c018i.c in your C18 compiler dir #pragma code REMAPPED_RESET_VECTOR = REMAPPED_RESET_VECTOR_ADDRESS void _reset (void) { _asm goto _startup _endasm } #pragma code REMAPPED_HIGH_INTERRUPT_VECTOR = REMAPPED_HIGH_INTERRUPT_VECTOR_ADDRESS void Remapped_High_ISR (void) { _asm goto YourHighPriorityISRCode _endasm } #pragma code REMAPPED_LOW_INTERRUPT_VECTOR = REMAPPED_LOW_INTERRUPT_VECTOR_ADDRESS void Remapped_Low_ISR (void) { _asm goto YourLowPriorityISRCode _endasm } #pragma code //These are your actual interrupt handling routines. #pragma interrupt YourHighPriorityISRCode void YourHighPriorityISRCode() { //Check which interrupt flag caused the interrupt. //Service the interrupt //Clear the interrupt flag //Etc. } //This return will be a "retfie fast", since this is in a #pragma interrupt section #pragma interruptlow YourLowPriorityISRCode void YourLowPriorityISRCode() { //Check which interrupt flag caused the interrupt. //Service the interrupt //Clear the interrupt flag //Etc. } //This return will be a "retfie", since this is in a #pragma interruptlow section
Other things worth knowing
At startup, the bootloader firmware looks at address 0x1006 (the “APP_SIGNATURE_ADDRESS”) and expects to find the value 0x600D (the “APP_SIGNATURE_VALUE”) here. If a different value is found, the firmware assumes there is no valid application loaded in the PIC (apart from the bootloader itself), and will therefore enter the bootloader even if the input switch was not pressed. For this reason, it is essential that your application loads address 0x1006 with the value 0x600D. Unfortunately some of Microchip’s own sample code does not do this (see the code immediately above!) and is therefore not HID bootloader compatible.
The bootloader application gives you an option to download PIC configuration settings via the bootloader. This is a very dangerous practice, and generally you shouldn’t select this option unless you really know what you are doing. One of the limitations of using a bootloader is that your user code should use the same configuration settings as the bootloader. If you do choose to download new configuration setting via the bootloader, those new settings will also be used by the bootloader istelf (when it is next restarted). If they are incompatible with the settings needed for the bootloader to function properly, you will have “bricked” your PIC and will need to re-program the bootloader into it using a PICkit programmer.
The source code for the bootloader firmware is available. You can modify it if needed (for example, to use an input other than RB4 as the input switch). However, if you compile it with the regular (free) C18 compiler, it won’t fit in the required 0x1000 bytes. So you’ll need to allocate extra space (perhaps a total of 0x1100 bytes) and make all necessary adjustments to the linker script and user source code files to accommodate the changed address boundaries.
Some people prefer to use v2.6 of the bootloader application. There are obvious user-interface differences between v2.6 and v2.9j, but I don’t have a strong preference for one or the other. Consider trying them both out. One really nice thing about v2.6 is that you can download and run this as a stand-alone program (ie without downloading and installing the whole “Microchip Libraries for Applications” environment). I found a copy on RADO’s webpage (Look for the link to “USB HID bootloader and application template package.zip”).
There are many other helpful articles out there in relation to USB bootloaders for the PIC18F4550. Some links are provided below.
Customising the HID bootloader firmware
The HID bootloader firmware can be customised if necessary, to suit the hardware design that you are using your PIC in.
For example, the pre-compiled “.hex” firmware file makes the following (non-changeable) assumptions your hardware, if you are using a PIC18F2550 or PIC18F4550:
- The bootloader switch is connected to pin RB4
- The status LED is connected to pin D0 (which doesn’t exist on a PIC18F2550)
- The PIC has a 20 MHz crystal connected to it
If your hardware can’t support the 3 criteria above, you will need to modify the HID bootloader project files, and re-compile the project.
Obtaining the HID bootloader firmware project files
Microchip have made the HID bootloader firmware source code available as part of the MLA distribution in the directory “C:\microchip_solutions_v2013-06-15\USB\Device – Bootloaders\HID”.
For the PIC18F2550 and PIC18F4550, the relevant MPLAB 8 Project File is “HID Bootloader PIC18F4550 Family.mcp”, which you can find in the subdirectory “Firmware – PIC18 Non-J”.
Compiling the HID bootloader firmware
Once you’ve opened the Project, you need to make sure you have the correct PIC selected in MPLAB. So go to “Configure -> Select Device” and make sure you have the PIC18F4550 selected in the “Device” drop-down box. Use this setting even if you are compiling for the PIC18F2550, as the project will generate errors during the build phase if you select PIC18F2550 here. The bootloader (even when configured for a PIC18F4550) will compile and work just fine in the PIC18F2550. You will get an error message that says “Target Device ID (00001240) does not match expected Device ID (00001200)”, but you can proceed to program the PIC18F2550 and it will work just fine.
MPLAB 8 with C18 can re-compile the HID bootloader firmware. Unfortunately the “standard” edition of the compiler is crippled (optimisations are disabled) and the resulting complied code won’t fit in the first 0x1000 bytes of the PIC’s program memory. You could fix this by changing various vectors etc in the firmware project (so the compiled firmware occupies the first 0x1100 bytes of program memory). However, if you are operating the C18 compiler in the time-limited “evaluation mode” (also known as “extended mode”) that automatically applies when you first install the compiler, the compiled code will fit in the first 0x1000 bytes of program memory.
It is easy to verify whether the compiler is in “standard” or “evaluation” mode. If it is in evaluation mode, you will get the following messages in the MPLAB “output” window during the build phase:
MPLAB C18 3.47 (evaluation) Copyright 2000-2011 Microchip Technology Inc. Days remaining until evaluation becomes feature limited: 2
It is also easy to verify whether your compiled code crosses the 0x1000 boundary. To check this, in MPLAB go to “View -> Program Memory” and scroll down until you can see the code at 0x1000. With the un-modified HID bootloader firmware, after compilation you should see the final byte of the bootloader code at 0x0FB0. There will then be some NOPs up to 0x0FFF, and then a “GOTO 0x1C” instruction at 0x1000 (this GOTO instruction will ultimately be over-written by the user code that you download to the PIC using the bootloader firmware).
Changing the I/O pin assignments
The following notes should be helpful, if you want to customise the HID bootloader firmware, to make changes to suit the I/O assignment that your project uses.
- The Project selects the I/O pins to use for the bootloader switch and status LED based on the “hardware platform” that you are using. It determines the hardware platform based on the device type you have selected in MPLAB. The code for determining the hardware platform is at lines 71-80 of the file “usbcfg.h. Assuming you have selected a PIC18F2550 or PIC18F4550 in MPLAB, the Project defines “PIC18F4550_PICDEM_FS_USB” (see lines 72 and 73) and this is used elsewhere in the Project to select the I/O pins to be used.
- The Project will require Pins RA1 and RA2 for USB-related functionality, if the symbols “USE_SELF_POWER_SENSE_IO” and/or “USE_USB_BUS_SENSE_IO” are defined. Fortunately these are commented out by default (see lines 52 and 53 of usbcfg.h), so these pins are not normally needed by the bootloader firmware.
- To change the I/O pins used for the bootloader switch and/or the status LED, edit lines 68 to 80 of “io_cfg.h”.
- If you want to disable the output LED, you can do this by commenting out “#define ENABLE_USB_LED_BLINK_STATUS” at line 48 of “usbcfg.h”. This define statement is enabled by default. If you disable it, the size of the compiled firmware will be reduced a little, as the LED output code is not compiled into the firmware.
The original version of lines 68 to 80 of “io_cfg.h” are shown below:
/** L E D ***********************************************************/ #define mInitAllLEDs() LATD &= 0xFE; TRISD &= 0xFE; #define mLED_1 LATDbits.LATD0 #define mLED_1_On() mLED_1 = 1; #define mLED_1_Off() mLED_1 = 0; #define mLED_1_Toggle() mLED_1 = !mLED_1; /** S W I T C H *****************************************************/ #define mInitAllSwitches() {mInitSwitch2();} #define mInitSwitch2() {ADCON1 = 0x0F;} #define sw2 PORTBbits.RB4 #define mDeInitSwitch2() {ADCON1 = 0x07;} /********************************************************************/
Changing the configuration bits
If you want to change the configuration bits that are used by the bootloader firmware, you will need to edit the block of code at lines 168 to 219 of “main.c”.
The original version of lines 168 to 219 of “main.c” are shown below:
/** C O N F I G U R A T I O N ************************************************/ // Note: For a complete list of the available config pragmas and their values, // see the compiler documentation, and/or click "Help --> Topics..." and then // select "PIC18 Config Settings" in the Language Tools section. #if defined(PIC18F4550_PICDEM_FS_USB) // Configuration bits for PICDEM FS USB Demo Board #if defined(__18F4550) || defined(__18F4553) #pragma config PLLDIV = 5 // (20 MHz crystal on PICDEM FS USB board) #pragma config CPUDIV = OSC1_PLL2 #pragma config USBDIV = 2 // Clock source from 96MHz PLL/2 #pragma config FOSC = HSPLL_HS #pragma config FCMEN = OFF #pragma config IESO = OFF #pragma config PWRT = OFF #pragma config BOR = ON #pragma config BORV = 3 #pragma config VREGEN = ON //USB Voltage Regulator #pragma config WDT = OFF #pragma config WDTPS = 32768 #pragma config MCLRE = ON #pragma config LPT1OSC = OFF #pragma config PBADEN = OFF //NOTE: modifying this value here won't have an effect //on the application. See the top of the main() function. //By default the RB4 I/O pin is used to detect if the //firmware should enter the bootloader or the main application //firmware after a reset. In order to do this, it needs to //configure RB4 as a digital input, thereby changing it from //the reset value according to this configuration bit. // #pragma config CCP2MX = ON #pragma config STVREN = ON #pragma config LVP = OFF // #pragma config ICPRT = OFF // Dedicated In-Circuit Debug/Programming #pragma config XINST = OFF // Extended Instruction Set #pragma config CP0 = OFF #pragma config CP1 = OFF // #pragma config CP2 = OFF // #pragma config CP3 = OFF #pragma config CPB = OFF // #pragma config CPD = OFF #pragma config WRT0 = OFF #pragma config WRT1 = OFF // #pragma config WRT2 = OFF // #pragma config WRT3 = OFF #pragma config WRTB = OFF // Boot Block Write Protection #pragma config WRTC = OFF // #pragma config WRTD = OFF #pragma config EBTR0 = OFF #pragma config EBTR1 = OFF // #pragma config EBTR2 = OFF // #pragma config EBTR3 = OFF #pragma config EBTRB = OFF #endif //18F4550 and 18F4553
Care should be taken in making any changes to these configuration bits, as you will need to consider the impact of the changes on both the bootloader firmware itself and the user code that will be downloaded and executed later. But if (for example) you need to make changes to the PLL settings to accommodate a crystal other than 20 MHz, this is the place to do it.
Downloads
Below are 4 versions of the HID bootloader firmware, to suit a PIC18F2550 or PIC18F4550 with a 20 MHz crystal.
The first file was compiled using Microchip’s standard configuration. The other 3 versions were compiled using modified PIC source code and the process described above.
File | Description |
---|---|
hid-bootloader-pic18f4550-family-sw-rb4-led-rd0.hex | Switch=RB4, LED=RD0 |
hid-bootloader-pic18f4550-family-sw-rb4-led-disabled.hex | Switch=RB4, LED=Disabled |
hid-bootloader-pic18f4550-family-sw-rc2-led-rc0.hex | Switch=RC2, LED=RC0 |
hid-bootloader-pic18f4550-family-sw-rc2-led-disabled.hex | Switch=RC2, LED=Disabled |
Useful links
Microchip’s Microchip Libraries for Applications
RADO’s webpage regarding Microchip’s USB HID bootloader
Digital DIY’s webpage on PIC18 USB Bootloaders
Hade’s webpage on USB bootloading (covers both CDC and HID bootloaders)
Brian Schmalz’s UBW32 webpage (which includes bootloader downloads)