Wednesday, May 02, 2012

WRITING A BOOTLOADER

I’ve been working on a simple bootloader for PIC18F devices called pbldr. In this article I’ll explain what a bootloader is, and how pbldr currently works. Note that is code is experimental, and is intended to show how a bootloader works.

What is a Bootloader?

The simplest bootloader just runs a program: it could be as simple as a single jump instruction that jumps to the program. In embedded systems, bootloaders usually provide a method of flashing new code to the device and initialize the hardware before running the main program. One example of this is the Arduino bootloader, which loads code from the Arduino IDE to the ATmega microcontroller over asynchronous serial (UART). Bootloaders can include other features, such as code decryption and power-on tests of the device.

Bootloaders allow code to be flashed to a microcontroller without specific programming hardware. This allows end-users to upgrade firmware without needing special hardware.  It can also simplify firmware updates for installed devices that are difficult to physically connect to. For example, an automotive controller might use Controller Area Network to load new code.

How does it work?

A bootloader runs immediately after the device is powered on. It first checks if the user is trying to load new code. If so, it receives the code and loads it into program memory at a specific memory location. Otherwise, it jumps to the start of the user’s program, which has already been loaded at that specific memory location.

Lets look at pbldr, which is currently a (very) minimal example of a bootloader. Full source is available from github.

UART

The UART1 port is used to load programs. There are a few functions that deal with UART:

/********************
 UART 1 Functions
********************/
 
// initializes UART1 at specified baud rate
void UART1Init(long baud){
    RCSTA1bits.SPEN = 1; // enable port
    TRISCbits.TRISC7 = 1; // make rx pin an input
    RCSTA1bits.CREN = 1; // enable receive
    TRISCbits.TRISC6 = 0; // make tx pin an output
    TXSTA1bits.TXEN = 1; // enable transmit
    TXSTA1bits.SYNC = 0; // use async serial
    TXSTA1bits.BRGH = 1; // high speed mode
    BAUDCON1bits.BRG16 = 1; // use 16 bit baud rate generator
    SPBRG1 = (FCY/baud/4)-1; // set baud rate generator
    return;
}
 
// writes a byte to UART1
void UART1TxByte(char byte)
{
    while (!TXSTA1bits.TRMT); // wait until buffer is empty
    TXREG1 = byte;            // write the byte
    return;
}
 
// reads a byte from UART 1
char UART1RxByte(unsigned int timeout)
{
    while (!PIR1bits.RC1IF && timeout > 0) // wait for data to be available
        timeout--;
    return RCREG1; // return data byte
 
}
// writes a string from ROM to UART1
void UART1TxROMString(const rom char *str)
{
    int i = 0;
 
    while(str[i] != 0){
        UART1TxByte(str[i]);
        i++;
    }
    return;
}
 
// writes a string from RAM to UART1
void UART1TxString(char *str)
{
    int i = 0;
 
    while(str[i] != 0){
        UART1TxByte(str[i]);
        i++;
    }
    return;
}

Read more: ERIC EVENCHICK
QR: Inline image 1

Posted via email from Jasper-Net