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: