USB Bootloader

The USB-Bootloader is a project used in the fluorescence tomography hardware which is designed at the Institute of Medical Engineering. It consists of a PIC18F2550 (Microchip) containing a small USB stack. The device readily communicates with the host PC over an USB 2.0 connection. The user's firmware can be downloaded and written into the PIC flash over USB, i.e. there is no need for an external programmer any longer. The user's firmware can also use the bootloader's USB functionality if required and does not have to implement it again which saves both development time and program memory.

Hardware

Currently, the bootloader is to be programmed into a Microchip PIC18F2550. The PIC has to be supplied from a crystal running at 20MHz. The frequency will drive the internal PLL-module generating another auxiliary clock with 96MHz. Then the real CPU clock is generated by dividing the auxiliary clock by 2. This means that the processor will run at a frequency of 48MHz. As every instruction needs four cycles to complete, the code is executed at 12MHz. This is fast enough for the USB 2.0 protocol.

The device has to be powered with 5V. It is possible to use either directly the USB as power supply or to use an external supply.

When the device is powered, it will read the input RE3 (pin 1). If this input is zero, it will enter the bootloader-mode in which it is possible to re-program the device. If the pin is high, the program will jump to the user's firmware instead. A good practice is to use a pull-up resistor at pin 1 to +5V and a jumper or push-button to GND. If the jumper is in place (or the button is pressed, respectively), the device can be re-programmed. Otherwise the device will simply execute the user's firmware.

The bootloader itself is written into the first block of the PIC's flash and is write protected, i.e. there is no chance of overwritting the bootloader.

Software

As the bootloader occupies the first block in the PIC program memory (0x0000-0x07FF), the user's firmware has to start at some point afterwards. The bootloader also relocates the low and high priority interrupt vectors. The exact addresses of the start and interrupt vectors can be found in the file bootloader.inc.

Reusing the bootloader's USB functionality

The user's firmware can reuse the bootloader's USB functionality for its own purposes. The communication with the USB module is done by the two variables USB_CONTROL (data memory 0x00-0x05) and USB_HANDLER_FUNCTION (data memory 0x06-0x08).

The first task is to set the USB_HANDLER_FUNCTION to a function which will handle USB vendor requests (the standard requests are handled by the bootloader itself). Then the function InitializeUSB has to be called to set up the USB module. The USB communication is not yet established at this point. To do so, call the function HandleUSB repeatedly until the status of the USB module changes to USB_STATUS_CONFIGURED which will be indicated in the USB_CONTROL data structure. At this point the USB module may be used for communication. In the user's program the function HandleUSB has to be called regularly, to handle pending USB requests. In pseudo-code a very simple program might look as follows:

Set start of USB buffer in USB_CONTROL
Call InitializeUSB
DO
Call HandleUSB
UNTIL (USB_CONTROL[1] AND USB_STATUS_MASK) == USB_STATUS_CONFIGURED
[...user program...]
Call HandleUSB regularly to keep up the communication with the host
 

Custom USB descriptor

All the information about the USB capabilities (the USB version, the maximum package size, the number of endpoints...) is found in USB descriptors. According to the USB standard, there is at least a device descriptor, and a configuration desriptor. Additionally there might be endpoint descriptors and string descriptors.

The bootloader has internally a set of descriptors which it needs in order to set up the communication with the PC. However, most often it is desireable that the firmware can replace these default descriptors with its own. To achieve this the firmware has to provide a function that fills a memory buffer with data from its own descriptors.

The current strategy is the following: Whenever the USB module needs parts of a USB descriptor, it will branch to a certain address stated in the file bootloader.inc which is located in the memory area of the user's program. If the user wants to reuse the bootloader's USB descriptor a

 goto GetUSBDescriptor

has to be placed at that specific memory address. If the device needs its own USB descriptor, it places a jump to its own function which has to fill the memory buffer with the own USB descriptor.

A warning at this place: If you do not want to provide your own descriptor but still want to have USB functionality, you can use the bootloader's descriptors. But in that case you have to put a

 goto GetUSBDescriptor

in your firmware at the correct location (to be found in bootloader.inc) or your program will most likely crash.

Copying a new firmware to the device

The file bootloader.cpp is the main source file for the USB-Bootloader. The default Makefile will create an executable named bootloader. Note: The control software on the host needs to have access to the USB module. Chances are high that you have to be a privileged user (e.g. root) to communicate with the device.

The bootloader is controlled by the command line. The most important arguments are "-h" to get the help message, "-e [start:end]" to erase the program memory from [start] to [end] (start and end addresses are hex values!) and "-w [file.hex]" to write the given hex file into the microchip.

Example usage: Erase the memory from 0x0800 to 0x0A00

 ./bootloader.exe -e 0800:0A00

Write the program 'project_template.hex' into the USB device

 ./bootloader.exe -w project_template.hex

Verify the correctness of the programming operation

 ./bootloader.exe -c project_template.hex

All the commands can be chained and are executed from left to right. Thus, to first erase the flash and to write a new firmware the single command

 ./bootloader.exe -e 0800:0A00 -w project_template.hex

can be used.

Student projects using the USB-Bootloader

In this section some guidelines for student projects using the USB-Bootloader are established. The reason is to have similar program structures which should easy the maintenance of the applications.

USB descriptors

It is highly recommended that all student projects have their own USB descriptor. This makes the project independent of future changes to the bootloader (e.g. a future version could specify a lower current limit or a different packet size for endpoint 0). All student projects have to use the following pairing for idVendor and idProduct:

idVendor: 0x1C40
idProduct: 0x07EC

Because all devices have the same idVendor/idProduct pairing, it is necessary to introduce additional means for descriminating the different devices. This is done by a special vendor request called GET VERSION which is mandatory to implement for all projects.

Mandatory commands

Two commands are mandatory for every device of a student project using the USB-Bootloader. The first one is GET VERSION (bRequest=0x00) and the second one is RESET (bRequest=0xFA).

GET VERSION

GET VERSION is used for identifying a device. The devices have to return a block of 8 bytes upon receiving a GET VERSION transfer command. The command in detail is:

GET VERSION (0x00)
Input:
None
Output: 8 bytes
byte 0-3: Reserved. These bytes have to be set to 0.
byte 4-5: Project id. This is a unique number for the project. You'll
get this number from your advisor. The id is sent LSB first.
byte 6-7: The version of the device' firmware. The version has the
form 'major.minor', where both major and minor have one
byte. minor is sent first, followed by major.

A possible scenario for initialization is the following: On the host, the software will check all attached devices if they have an idVendor/idProduct-pairing equal to 0x1C40/0x07EC. If so, the a GET VERSION request is sent to the device. If the project id and the version match, the device is found.

RESET

The RESET command is also mandatory for all USB-Bootloader devices. When receiving this command, the device should bring itself and all attached units to a safe state and reset itself afterwards.

RESET (0xFA)
Input:
None
Output:
None. The transfer will not even be acknowledged because the device
will reset itself as fast as possible.
 

List of student project IDs

The following table contains all project IDs that have been assigned. It is supposed to be complete, so all IDs that are not listed here are free to use.

Project ID (hex) Contact person Description
0x0000-0xEFFF Reserved
0xF000 M. Freiberger Used for test purposes.
0xF001 L. Pirpamer Streukeulenmessgerät
0xF002 M. Forobosko Mausrotator
0xF003 Ch. Kapeller Oberflächenscanner (David)
0xF004 R. Zettl Galvanometerscanner
0xF005 M. Glitzner High-speed DDS
0xF006 M. Schöllauf Verschiebetisch (Fluoreszenztomograph)
0xF007 M. Schöllauf Phasenschieber (Fluoreszenztomograph)
0xF008 M. Schöllau Lasermodul (Fluoreszenztomograph)
0xF009 M. Schöllauf Shutter (Fluoreszenztomograph)

Literature

This is a collection of internet resources regarding the USB protocol. They give a fairly good overview although there might be some wrong or lacking details of the USB standard.

Download

The files for the USB-Bootloader can be found in this archive. The sources compile with g++ (tested up to version 4.5) and gpasm.

To top