Como escrever um bootloader simples para AVR em linguagem C – (Parte 35/46)

How to write a simple bootloader for AVR in C language – (Part 35/46)

BootLoader is code that runs when a microcontroller is powered on or restarted. Basically, it defines an environment for running application code. It is the Boot-Loader that configures the hardware and loads the application code from any storage medium or received through external communication and lets the application run. Thus, a Boot-Loader must perform the following basic function: Initialize the controller peripherals, Initialize the devices on the board, Allow the user to select from available applications to load, Load the selected application, Let the application code run. Apart from the above-mentioned functions, some Boot-Loaders can perform many other functions and there are Boot-Loaders which do not perform all these functions like providing option to select required application etc. Boot-Loader codes in microcontrollers are actually very small and simple compared to Boot-Loaders in advanced devices like PC. In most microcontrollers, the functionality of a Boot-Loader is limited to only setting the initial clock and other microcontroller settings, loading an application binary from the serial port, etc. On an AVR microcontroller writing a Boot-Loader code is comparatively easy as any code written in the BLS section of the flash memory can have complete access to the microcontroller hardware. The flash memory of the AVR microcontroller is divided into two sections; an application flash section and a Boot-Loader (BLS) section.

Any code run from the BLS can use self-programming mode (SPM). Using the SPM feature, a code from the BLS section can read or write the entire flash memory, including the BLS section from where the code is running. This feature can be used to load any application binary code into flash memory and let it run. Required initialization of controller peripherals like USART, port bits etc. and initialization of external devices like LCD etc. can be done from the BLS itself before loading the application code. In this project, discuss how to write a simpler Boot-Loader code for the AVR microcontroller that can initialize the peripherals like USART, LED port and initialize a 4-bit LCD connected to the controller and then load any application that has been updated on the built-in in the EEPROM of the microcontroller. The microcontroller used in this project is the ATMEGA16, and the programmer used is the USBASP. AVR Studio 4 is used as the IDE and the recording software used is AVR-BURNO-MAT.

AVR flash memory is divided into two sections: the Applications section and the Boot-Loader (BLS) section. In the case of ATMEGA16 it has 16 KB of flash memory of which 15 KB is application section and the remaining 1 KB is BLS. The memory architecture of ATMEGA16 is shown in the following figure;

Arquitetura de memória Flash do ATMEGA16

Fig. 2: ATMEGA16 Flash memory architecture

The code for the BLS and the application section can be written normally and there is not much difference. The only thing to be careful about is the size of the binary code. It must not be more than 1 KB, otherwise it will not be possible to code programmed in BLS. The project on AVR BLS coding discusses how to program a simple code in the BLS of ATMEGA16 microcontroller with the help of AVR studio as IDE, USBasp as programmer and AVR-Burnomat as recording software.

In this particular project, the Boot-Loader is coded to perform UART initialization along with a simple LEAD pin initialization and also an external hardware initialization from the 4-bit LCD . Therefore the application codes do not require these codes, they still work because before executing the application codes the initialization functions will be executed by the Boot-Loader.

The BLS code has initialization functions that should not be present in application codes. The initialization functions used in the Boot-Loader code for this project are given below;

Function

Description

empty lcd_init (empty)

Initialize the LCD in 4-bit mode

empty usert_init (void)

Initialize the user at baud rate 9600 with transmit and receive enabled

DDRD = 0x80;

LED pin initialization as output

Fig.3: Boot functions used AVR Boot-Loader code

Any application code can directly use the following function calls to access USART, LCD and LED without initializing their functions anywhere in the code.

Function

Description

lcd_clear

Clean the LCD

lcd_string

Display a string on the LCD

usert_send_string

Send a string via usert

PORTD &= 0x7F;

Turn on the LED

PORTD = 0x80;

Turn OFF the LED

Fig. 4: Function calls used by application code to access peripherals on the AVR

Hardware initialization before executing application code is explained in detail in a project in AVR BLS Hardware Initialization.

The main function of the Boot-Loader is to load code in binary format from a storage medium or that can be received through external communication with other devices to flash memory. The SPM resource available for code running in BLS helps load binary application code into flash memory. The task of writing BLS code with SPM has been simplified by the APIs available in the header file . Following are the important APIs available in the header file that help in SPM.

FUNCTION

DESCRIPTION

PARAMETER

boot_page_erase (address)

Delete the flash page referred to by the address

A byte address in flash

boot_page_fill (address, data)

Fill Boot-Loader temporary page buffer for flash address with data word

The address is a byte address. Data is a word

boot_page_write (address)

Write the Boot-Loader temporary page buffer to the flash page containing the address

Flash byte address

Figure 5: Important APIs in AVR

The steps required to do SPM on application flash memory are 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 attempt to load any application binary code that has been loaded into the AVR microcontroller's built-in internal EEPROM. The APIs available on are used to read the data bytes from the EEPROM and with the help of the the data bytes are stored in a temporary buffer and then transferred to the application section of flash memory.

The API provided by to read the data bytes from the AVR microcontroller's integrated EEPROM;

uint8_t eeprom_read_byte (const uint8_t *p)

FUNCTION

DESCRIPTION

PARAMETER

uint8_t eeprom_read_byte (const uint8_t *p)

The function returns a byte of data that is stored in the EEPROM address referred to by the pointer p

The pointer refers to the EEPROM address from which the data byte needs to be read

Figure 6: API to read data bytes from the AVR microcontroller's integrated EEPROM

With the help of the APIs discussed above It is one can use the SPM feature of the AVR microcontroller to write a Boot-Loader code that can load an application that has been programmed into the AVR microcontroller's built-in EEPROM. To do this, you must follow the steps mentioned below, which are explained in a project on using SPM in AVR EEPROM for flash programming.

{C}{C}{C}{C}{C}{C}{C}{C}{C}{C}{C}{C}{C}{C} · {C}{C}{C}{C}{C}{C}{C}{C}{C}{C}{C}{C}{C}{C} Step: 1 Delete the flash page that is about to write

{C}{C}{C}{C}{C}{C}{C}{C}{C}{C}{C}{C}{C}{C} · {C}{C}{C}{C}{C}{C}{C}{C}{C}{C}{C}{C}{C}{C} Step: 2 Store the binary code that is read from EEPROM into a temporary buffer before writing to a flash page

{C}{C}{C}{C}{C}{C}{C}{C}{C}{C}{C}{C}{C}{C} · {C}{C}{C}{C}{C}{C}{C}{C}{C}{C}{C}{C}{C}{C} Step: 3 Program the temporary buffer filled on the already deleted flash page

After performing the above three steps, the Boot-Loader can jump using the instruction

asm(“jmp 0x0000”);

to the application code section and let the newly programmed application run.

Flash the Boot-Loader code to BLS first, any small size application code to EEPROM memory using the steps explained in the previous project on AVR BLS Blinking LED . Once EEPROM programming is complete and the controller is reset, the Boot-Loader will begin running. It can be seen that it is performing initialization functions and loading the application from the integrated EEPROM.

Gravando código BLS no BLS do circuito AVR na placa de ensaio

Fig. 7: Writing BLS code to the BLS of the AVR circuit on the breadboard

Como escrever um bootloader simples para AVR em linguagem C

Display LCD inicializado pelo código BLS na configuração do AVR na placa de ensaio

Fig. 9: LCD display initialized by BLS code in AVR configuration on breadboard

Como escrever um bootloader simples para AVR em linguagem C

Fig. 10: USART initialized with baud rate 9600 by BLS of AVR configuration on breadboard

Como escrever um bootloader simples para AVR em linguagem C

Project source code

###


 #define F_CPU 8000000
#include #include #include #include #include #include #include #include #include "usart.h" #include "lcd.h" int main (void) { uint16_ti; uint8_tA(512); uint8_t registry; page uint32_t = 0; uint16_t w; unsigned character *buf = A; volatile int j = 4; //========================================================= == =================================== lcd_init; //LCD initialization lcd_clear; lcd_1st_line ; lcd_string("ENGINEERS"); lcd_2nd_line ; lcd_string("GARAGE"); _delay_ms(3000); lcd_clear; lcd_1st_line ; lcd_string("Initializing ATMEGA16"); _delay_ms(2000); lcd_clear; lcd_1st_line ; lcd_string("LCD(OK)"); lcd_2nd_line ; lcd_string("16*2.4 bits"); _delay_ms(2000); usert_init; //USART initialization lcd_clear; lcd_1st_line ; lcd_string("USART(OK)"); lcd_2nd_line ; lcd_string("9600bps, Tx and Rx"); _delay_ms(2000); lcd_clear; lcd_1st_line ; lcd_string("loading application... "); lcd_2nd_line ; for(i = 0; i < 16; i++) { _delay_ms(350); data_dis(0xFF); } lcd_clear; lcd_1st_line ; //========================================================= == =================================== //############################## Booting from EEPROM ############# # #################// for (i = 0; i < 512; i++) A (i) = eeprom_read_byte ((const uint8_t *) i); while(1) { //========================================================= == ========================// if(j) { //Disables interrupts. sreg = SREG; cli; eeprom_busy_wait ; boot_page_erase (page); boot_spm_busy_wait; //Wait until the memory is erased. for (i=0; i { //Set up the little-endian word. w = *buf++; w += (*buf++) << 8; boot_page_fill(page + i, w); } boot_page_write (page); // Store buffer in flash page. boot_spm_busy_wait; //Wait until the memory is written. boot_rww_enable; SREG = sreg; } other { asm("jmp 0x0000"); } j--; page = page + 128; //============================================= == ===========================================// } //############################## Booting from EEPROM ############# # #################// } //#################### LCD ########################//

#define _LCD_H #ifndef F_CPU #define F_CPU 8000000 #endif #include #include #include #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); empty lcd_clear(empty); empty lcd_2nd_line(empty); empty lcd_1st_line(empty); 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 // function to initialize { DDRA=0xFF; dis_cmd(0x02); // to initialize the LCD in 4-bit mode. dis_cmd(0x28); //to initialize the LCD in 2 lines, 5X7 points and 4-bit 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_valor1 = cmd_valor & 0xF0; //masks the bottom nibble because pins PA4-PA7 are used. lcdcmd(cmd_valor1); // send to LCD cmd_value1 = ((cmd_value<<4) & 0xF0); //shift 4 bits and mask lcdcmd(cmd_value1); //send to LCD } void dis_data(char data_value) { 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; DOOR&=~(1<

###

Project source code

###


 #ifndef _USART_H
#define _USART_H #ifndef F_CPU #define F_CPU 8000000 #end if #define USART_BAUDRATE 9600 #define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1) #include #include void usert_init ; void usert_putch(unsigned character send); unsigned int user_getch; void user_init { UCSRB = (1 << RXEN) (1 << TXEN); //Activates the transmit and receive circuit UCSRC = (1 << URSEL) (1< //Use 8-bit character sizes UBRRL = BAUD_PRESCALE; // Loads the lower 8 bits of the baud rate value. //in the low byte of the UBRR register UBRRH = (BAUD_PRESCALE >> 8); // Loads the upper 8 bits of the baud rate value. //in the high byte of the UBRR register } void usert_putch(unsigned character send) { while ((UCSRA & (1 << UDRE)) == 0); // Do nothing until the UDR is ready. // for more data to be written to it UDR = send; // Send the byte } unsigned int usat_getch { while ((UCSRA & (1 << RXC)) == 0); // Do nothing until the data has been received and is ready to be read into the UDR return(UDR); //returns the byte } #end if

#define rs PA0 #define rw PA1 #define en PA2 #include #include void dis_data(char data_value); void usert_putch(unsigned character send); unsigned int user_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 -----------// //----- user + lcd test -------// while ( 1 ) { c = usert_getch ; usert_putch((unsigned char)c); dis_data((char)c); } //----- use + lcd test -------// } void dis_data(char data_value) { char data_value1; data_value1=data_value&0xF0; PORT=data_value1; PORT =(1<


###

Circuit diagrams

Circuit diagram of how to write a simple bootloader for AVR in C language

Project Components

  • ATmega16
  • LCD
  • LED
  • Resistor

Project video

Related Content

Back to blog

Leave a comment

Please note, comments need to be approved before they are published.