How To Write A Simple Bootloader For AVR in C Language
How To Write A Simple Bootloader For AVR in C Language
com/
The code for both the BLS and application section can be written as normally does and there is no
much difference. The only thing to be careful about is the size of the code binary. It should not be more
than 1KB, otherwise it wont be able to code programmed into the BLS. The project on AVR BLS coding
discusses how to program a simple code into the BLS of ATMEGA16 microcontroller with the help of
AVR studio as IDE, USBasp as the programmer and AVR-Burnomat as the burner software.
In this particular project the Boot-Loader is coded to perform UART initialization along with a
simple LED pin initialization and also an external hardware initialization of the 4 bit LCD. Hence the
application codes does not require those codes in them, still they works because before executing the
application codes the initialization functions will be executed by the Boot-Loader.
http://www.engineersgarage.com/
The BLS code has the initialization functions which should not be there in the application codes.
The initialization functions used in the Boot-Loader code for this project are given below;
Function Description
void lcd_init ( void ) Initialize the LCD in 4 bit mode
void usart_init ( void ) Initialize the usart in 9600 baud rate with transmission and reception enabled
DDRD |= 0x80; Initialization of the LED pin as output
Any application code can directly use the following function calls to access the USART, LCD
and LED without their initializing functions anywhere in their code.
Function Description
lcd_clear () Clear the LCD
lcd_string () Display a string in the LCD
usart_send_string () Send a string via usart
PORTD &= 0x7F; Turn ON the LED
PORTD |= 0x80; Turn OFF the LED
The hardware initialization before executing the application code is explained in detail in
a project on Initializing hardware from AVR BLS.
The major function of the Boot-Loader is load a code binary from storage medium or which can
be received through the external communication with other devices to the flash memory. The SPM
feature available for the code executing from the BLS helps in loading an application code binary to the
flash memory. The task of writing the BLS code with SPM has been made simple by the APIs available
in the header file <avr/boot.h>. The following are the important APIs available in the header file which
helps in the SPM.
FUNCTION DESCRIPTION PARAMETER
boot_page_erase (address) Erase the flash page that is referred by A byte address in flash
address
boot_page_fill (address, data) Fill the Boot-Loader temporary page The address is a byte
buffer for flash address with data word address. The data is a word
boot_page_write (address) Write the Boot-Loader temporary page Byte address in flash
buffer to flash page that contains address
The steps required to do the SPM on the application flash memory is explained in a project on
using SPM in AVR flash to flash programming.
In this particular project the Boot-Loader is coded in such a way that it will try to load any
application code binary which has been loaded into the built-in internal EEPROM of the AVR
microcontroller. The APIs available in the <avr/eeprom.h> is used to read the data bytes from the
EEPROM and with the help of APIs from the <avr/boot.h> the data bytes are stored into a temporary
buffer and then flashed into the application section of the flash memory.
The API provided by the <avr/eeprom.h> to read the data bytes from the built-in EEPROM of the
AVR microcontroller is;
uint8_t eeprom_read_byte (const uint8_t *p)
After performing the above three steps the Boot-Loader can then make a jump using the
statement
asm ( "jmp 0x0000" );
to the application code section and let the newly programmed application to execute.
Flash the Boot-Loader code to the BLS first any small sized application code to the EEPROM
memory using the steps explained in the previous project on LED blinking from BLS of AVR. Once the
EEPROM programming has completed and when the controller resets the Boot-Loader will start
executing. Once can observe that it is performing the initialization functions and is loading the application
from the built-in EEPROM.
lcd_clear();
lcd_1st_line();
lcd_string("Booting ATMEGA16" );
_delay_ms(2000);
lcd_clear();
lcd_1st_line();
lcd_string(" LCD [OK] " );
lcd_2nd_line();
lcd_string(" 16*2, 4 bit " );
_delay_ms(2000);
lcd_clear();
lcd_1st_line();
lcd_string(" USART [OK] " );
http://www.engineersgarage.com/
lcd_2nd_line();
lcd_string("9600bps, Tx & Rx" );
_delay_ms(2000);
lcd_clear();
lcd_1st_line();
lcd_string("loading App... " );
lcd_2nd_line();
for(i = 0; i < 16; i ++)
{
_delay_ms(350);
dis_data(0xFF);
}
lcd_clear();
lcd_1st_line();
while(1)
{
//==========================================================================//
if(j)
{
// Disable interrupts.
sreg = SREG;
cli();
eeprom_busy_wait ();
boot_page_erase (page);
boot_spm_busy_wait (); // Wait until the memory is erased.
j--;
page = page + 128;
}
//################################ BOOTING FROM EEPROM ###############################//
}
//#################### LCD #########################//
http://www.engineersgarage.com/
#define _LCD_H
#ifndef F_CPU
#define F_CPU 8000000
#endif
#include<avr/io.h>
#include<util/delay.h>
#include<inttypes.h>
#define rs PA0
#define rw PA1
#define en PA2
void lcd_init();
void dis_cmd(char);
void dis_data(char);
void lcdcmd(char);
void lcddata(char);
void lcd_clear(void);
void lcd_2nd_line(void);
void lcd_1st_line(void);
void lcd_string(const char *data);
void lcd_string(const char *data)
{
for(;*data;data++)
dis_data (*data);
}
void lcd_clear(void)
{
dis_cmd(0x01);
_delay_ms(10);
}
void lcd_2nd_line(void)
{
dis_cmd(0xC0);
_delay_ms(1);
}
void lcd_1st_line(void)
{
dis_cmd(0x80);
_delay_ms(1);
}
void lcd_init() // fuction for intialize
{
DDRA=0xFF;
dis_cmd(0x02); // to initialize LCD in 4-bit mode.
dis_cmd(0x28); //to initialize LCD in 2 lines, 5X7 dots and 4bit mode.
dis_cmd(0x0C);
dis_cmd(0x06);
dis_cmd(0x80);
dis_cmd(0x01);
_delay_ms(10);
}
void dis_cmd(char cmd_value)
{
char cmd_value1;
cmd_value1 = cmd_value & 0xF0; //mask lower nibble because PA4-PA7 pins are used.
lcdcmd(cmd_value1); // send to LCD
cmd_value1 = ((cmd_value<<4) & 0xF0); //shift 4-bit and mask
lcdcmd(cmd_value1); // send to LCD
}
void dis_data(char data_value)
{
http://www.engineersgarage.com/
char data_value1;
data_value1=data_value&0xF0;
lcddata(data_value1);
data_value1=((data_value<<4)&0xF0);
lcddata(data_value1);
}
void lcdcmd(char cmdout)
{
PORTA=cmdout;
PORTA&=~(1<<rs);
PORTA&=~(1<<rw);
PORTA|=(1<<en);
_delay_ms(1);
PORTA&=~(1<<en);
}
void lcddata(char dataout)
{
PORTA=dataout;
PORTA|=(1<<rs);
PORTA&=~(1<<rw);
PORTA|=(1<<en);
_delay_ms(1);
PORTA&=~(1<<en);
}
#endif
#ifndef _USART_H
#define _USART_H
#ifndef F_CPU
#define F_CPU 8000000
#endif
#define USART_BAUDRATE 9600
#define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)
#include<avr/io.h>
#include<util/delay.h>
void usart_init();
void usart_putch(unsigned char send);
unsigned int usart_getch();
void usart_init()
{
UCSRB |= (1 << RXEN) | (1 << TXEN);
// Turn on the transmission and reception circuitry
UCSRC |= (1 << URSEL) | (1<<USBS) | (1 << UCSZ0) | (1 << UCSZ1);
// Use 8-bit character sizes
#endif
#define rs PA0 #define rw PA1 #define en PA2 #include <avr/io.h> #include <util/delay.h> void dis_data(char
data_value); void usart_putch(unsigned char send); unsigned int usart_getch(); int main ( void ) { int i; unsigned int
c; //-------- led test -----------// DDRD |= 0x80; for ( i =0; i < 5; i ++ ) { PORTD = ~PORTD; _delay_ms ( 500 ); } //--
------ led test -----------// //----- usart + lcd test -------// while ( 1 ) { c = usart_getch(); usart_putch( ( unsigned char ) c
); dis_data( ( char ) c ); } //----- usart + lcd test -------// } void dis_data(char data_value) { char data_value1;
data_value1=data_value&0xF0; PORTA=data_value1; PORTA|=(1<<rs); PORTA&=~(1<<rw); PORTA|=(1<<en);
_delay_ms(1); PORTA&=~(1<<en); data_value1=((data_value<<4)&0xF0); PORTA=data_value1;
PORTA|=(1<<rs); PORTA&=~(1<<rw); PORTA|=(1<<en); _delay_ms(1); PORTA&=~(1<<en); } void
usart_putch(unsigned char send) { while ((UCSRA & (1 << UDRE)) == 0); // Do nothing until UDR is ready.. // for
more data to be written to it UDR = send; // Send the byte } unsigned int usart_getch() { while ((UCSRA & (1 <<
RXC)) == 0); // Do nothing until data have been received and is ready to be read from UDR return(UDR); // return
the byte }