AVR bootloader development notes

Publisher:JoyfulHarmonyLatest update time:2016-10-19 Source: eefocusKeywords:avr  bootloader Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere
After nearly 3 days of hard work, I finally succeeded in debugging. However, it is still an early stage and reliability, usability and security need to be strengthened in the future. At least now I can upgrade the program directly through the bootloader. I will sort out the whole process first, which will be convenient for my future reference and for those in need, because I borrowed a lot of things from others during the development process.

Reason for development: The company has a lot of customized software, and users will have new requirements after receiving the product. In fact, many of them are pure software modifications. Now engineers have to open the cover and use the downloader to re-burn the program, which is more troublesome. Because the product's external interface has a serial port, and Atmega itself supports the bootloader function, so we thought of using the serial port to upgrade the programJ.

Development environment and tools: Hardware platform: The board used is the company's Atmega128 platform, a parallel port downloader for AVR, software: Compiler: ICCAVR 7.22, serial port debugging assistant, serial port monitor (used to monitor serial port data, a good thing) and Windows HyperTerminal, download software: PonyProg2000 (this software is convenient for reading EEPROM and Flash, but it cannot be used to burn Atmega2561, there are problems), hexbin.exe tool (ready-made one found on the Internet), used to convert Intel's hex file into a pure hexadecimal file (the format stored in the microcontroller Flash).

Function to be realized: If you want to upgrade the program, burn the firmware to address 0 of the flash through the serial port of the hyperterminal (Xmodem protocol), and then jump to address 0 of the flash, otherwise wait for a period of time and automatically jump to address 0 in the flash. Size of bootloader: within 1024 bytes.

Some notes:

1. Burning of fuses: BOOTRST needs to be programmed so that the MCU automatically jumps to the bootloader area to execute the bootloader code after reset. Then, the fuses of the boot area need to be set according to the size of your bootloader: The specific settings are as shown in the figure below. Here I choose 1024 (note that 1 represents programming and 0 represents programmed):

  

AVR bootloader development notes (original) - Cang Hai Yi Xiao - Life is about tossing

 

 

2. Set the boot lock bit: In order to protect the bootloader from being modified or erased by the application, it must be protected. Atmega provides a fuse bit protection method. The specific settings are as follows (I set BLB0 to 11 and BLB1 to 00):

AVR bootloader development notes (original) - Cang Hai Yi Xiao - Life is about tossing

 

 

3. Flash page settings: Flash erase and write are done on a page basis. The manual says that 1 page has 128 bytes, but during actual debugging, it is found that 256 bytes need to be written at a time to be effective. If 128 bytes are written, the second 128 bytes will overwrite the first 128 bytes. In this case, the actual situation will prevail.

4. Notes on the Xmodem protocol: The specific xmodem is not described in this article. I will only talk about the points that need attention. The checksum is optional. I use checksum (which is a simple accumulation). You can also choose 16-bit CRC. This is determined based on the first response byte returned by the microcontroller. In addition, when the sequence number of the packet exceeds 255, it will restart from 0 instead of 1. The first transmission starts numbering from 1. Please pay attention to this.

5. File format file: Like many people on the Internet, I encountered the same file. After the bootloader burned the application into the flash, it was found that the application was not executed. At first, I thought it was a problem of unsuccessful jump. I searched the Internet for a long time but couldn't find the answer. There were many people asking questions. There was no way, I had to rely on myself. I was puzzled, why couldn't the program burned into the Flash be executed when the content of the original file was exactly the same? Later, I accidentally opened the firmware to be burned with the burning software and found that the content was different from what I opened with the hexadecimal tool. Oh my god, I suddenly thought of the key to the problem. It turned out that the hex file generated by ICC was in the Intel hex format. The Intel HEX file is an ASCII text file composed of lines of text that conform to the Intel HEX file format. In the Intel HEX file, each line contains a HEX record. These records consist of hexadecimal coded numbers corresponding to machine language codes and/or constant data. Intel HEX files are usually used to transfer programs and data to be stored in ROM or EPROM. Most EPROM programmers or simulators use Intel HEX files. The data actually stored in the Flash needs to be extracted from this HEX file and then sent to the microcontroller through xmodem. Do not directly send the HEX file generated by ICC. For conversion, you can write a small tool yourself or search for tools with similar functions on the Internet. To save trouble, I found a tool called hex2bin to do the conversion.

6. In the ICC project, you need to set it up. My settings are as follows:

AVR bootloader development notes (original) - Cang Hai Yi Xiao - Life is about tossing

 

 

7. Regarding the usage of parameters and return values ​​of nested assembly in C: The following is quoted from the ICC help file "

In the absence of a function prototype, integer arguments smaller than ints (for example, char) must be promoted to int type. If the function prototype is available, the C standard leaves the decision to the compiler implementation. ICCV7 for AVR does not promote the argument types if the function prototype is available.

If registers are used to pass byte arguments, it will use both registers but the higher register is undefined. For example, if the first argument is a byte, both R16/R17 will be used with R17 being undefined. Byte arguments passed on the software stack also take up 2 bytes. We may change the behavior and pack byte arguments tightly in some future release.

 

The first argument is passed in registers R16/R17 if it is an integer and R16/R17/R18/R19 if it is a long or floating point. The second argument is passed in R18/R19 if available. All other remaining arguments are passed on the software stack. If R16/R17 is used to pass the first argument and the second argument is a long or float, the lower half of the second argument is passed in R18/R19 and the upper half is passed on the software stack.

 

Integer values are returned in R16/R17 and longs and floats are returned in R16/R17/R18/R19. Byte values are returned in R16 with R17 undefined. ”

There is nothing left. The program is attached below (just debugged, not organized yet, very messy)

main.h


#ifndef _MAIN_H
#define _MAIN_H

#define XMODEM_NUL (0x00)
#define XMODEM_SOH (0x01) // Xmodem start header(128 data packets)
#define XMODEM_STX (0x02) // 1K xmoder start header(1024 data packets)
#define XMODEM_EOT (0x04)
#define XMODEM_ACK (0x06)
#define XMODEM_NAK (0x15) // Indicates that the receiver wants to receive according to the cumulative check method
#define XMODEM_CAN (0x18)
#define XMODEM_EOF (0x1A)
#define XMODEM_CRC_CK 'C' // Indicates that the receiver wants to receive according to the CRC check method

#define SYS_STATE_CHOICE (1)
#define SYS_STATE_READ_DAT (2)
//#define SYS_STATE_ACK  (3)
#define SYS_STATE_END  (4)
#define SYS_STATE_ERROR (5)
#define SYS_STATE_CANCEL (6)

#define SYS_WAIT_TIME  (5000)  // 5 seconds
#define SYS_RETRY_MAX  (3)

#define UART0_BUF_SIZE  (150*2)
#define XTAL   (6)  //晶振频率(MHz) 6MHz
#define FLASH_PG_SIZE  (128*2)
#define OSC_VAR   (6000000)
#define cbi(reg, bit)   (reg &= ~BIT(bit))
#define sbi(reg, bit)   (reg |= BIT(bit))

 

#endif


main.c



#include
#include
#include "main.h"
#include
//#include "assembly.h"


static unsigned long timer_1ms = 0;
static unsigned char sys_state = 0;
static unsigned char uart0_rx_buf[UART0_BUF_SIZE];
static unsigned long flash_addr = 0;

void sys_set_state(unsigned char state)
{
 sys_state = state;
}
unsigned long get_sys_time_ms(void)
{
 return timer_1ms;
}

extern int _textmode;
 
int putchar(char c)
    {
    //if (_textmode && c == '\n')
    //    putchar('\r');
    /* Wait for empty transmit buffer */
    while ( !(UCSR0A & (1<         ;                    
    /* Putting data into buffer , sends the data */
    UDR0 = c; 
    return c;
}
int getchar(void)
{
 unsigned char dat = 0;
 unsigned char status = 0;

 WDR();
 if(((UCSR0A & 0x80)==0)){
  return -1;
 }
 status = UCSR0A;
 dat = UDR0;
 if(status & 0x1C){
  return -2;
 }
 
 return dat; 
}
void sendStr(char *str)
{
 int len = 0;
 unsigned char i = 0;

 len = strlen(str);
 for(i = 0; i < len; i++)
  putchar(str[i]);
 while ( !(UCSR0A & (1< }
void port_init(void)
{
// MCUCSR |= 0x80;
// MCUCSR |= 0x80;
  PORTA = 0x07;    //PA3~PA7 IS LOW,PA2~PA0 IS HIGH
  DDRA  = 0xFF;
  PORTB = 0xFb;    //MOSI(PB2) is low *****
  DDRB  = 0xF7;    //PB3 IS INPUT
  PORTC = 0xFF;     //m103 output only
  DDRC  = 0xFF;
  PORTD = 0xFF;
  DDRD  = 0x00;    //PD0~7 IS INPUT
  PORTE = 0xFF;
  DDRE  = 0xFE;
  PORTF = 0xFF;
  DDRF  = 0x00;
  PORTG = 0x1F;
  DDRG  = 0x00;
}
void uart0_init(void)
{
  UCSR0B = 0x00;   //disable while setting baud rate
  UCSR0A = 0x00;
  UCSR0C = 0x06;

 //6MHz 
  UBRR0L = 0x26;   //set baud rate lo
  //14.7456MHz 9600bps
//  UBRR0L = 0x5F;   //set baud rate lo
  //14.7456MHz 115200bps
//  UBRR0L = 0x07;   //set baud rate lo

  UBRR0H = 0x00;   //set baud rate hi
//  UCSR0B = 0xD8;  //0xB8
//  UCSR0B = 0xB8;  //0xD8
  //UCSR0B = 0x98;  //0xD8,0xB8
  UCSR0B = 0x18;  //0xD8,0xB8,disable rcv interrupt
}
// OSC:6MHz, prescale:1024
void timer0_init(void)
{
  TCCR0 = 0x00; //stop
  ASSR  = 0x00; //set async mode
  TCNT0 = 0xf9; //set count
  OCR0  = 0xD7;
  TCCR0 = 0x07; //start timer
}

#pragma interrupt_handler timer0_ovf_isr:17
void timer0_ovf_isr(void)
{
  TCNT0 = 0xf9; //reload counter value
  timer_1ms++;
}
void watchdog_off(void)
{
// Write logical one to WDCE and WDE
 WDTCR = (1< // Turn off WDT
 WDTCR = 0x00;
}
void watchdog_init(unsigned char time)
{
  WDR();     //this prevents a timout on enabling
// Write logical one to WDCE and WDE
  WDTCR = (1<   time&=0x07;   // prescale: n cycles  time=0~7,WDCE=0
  time|=0x08;   // prescale: n cycles  time=0~7,WDE=1
 WDTCR=time;   //WATCHDOG ENABLED - dont forget to issue WDRs
}
#if 0
void delay_1ms(void)
{
    unsigned int i;
    for(i=1;i<(unsigned int)(XTAL*143-20);i++)
    {
     WDR();
    }
}

void delay_ms(unsigned int n)
{
    unsigned int i=0;
    while(i     {
         delay_1ms();
         i++;
    }
}
#endif
void init_devices(void)
{
  CLI(); //disable all interrupts
  XDIV = 0x00; //xtal divider
  XMCRA = 0x00; //external memory
  port_init();
  uart0_init();
 timer0_init();
 
  MCUCR = 0x00;
  EICRA = 0x00; //extended ext ints
  sbi(TIMSK,TOIE0);
// sbi(TIMSK,TOIE1); //Enable timer 1 overflow interrupt
// sbi(TIMSK,TOIE2); //Enable timer 2 overflow interrupt

 watchdog_init(7);
  //SEI();     //re-enable interrupts
 //all peripherals are now initialised
}
#if 0
void EEPROMwrite(unsigned int location, unsigned char byte)
//unsigned int eepromwrite(unsigned int location, unsigned char byte)
{
 unsigned char oldSREG;
 oldSREG = SREG;
 SREG &= ~0x80;   // disable interrupt

// Wait for completion of previous write
 while(EECR & (1< // Set up address and data registers
 EEAR = location;
 EEDR = byte;
// Write logical one to EEMWE
 EECR |= (1< // Start eeprom write by setting EEWE
 EECR |= (1<  
 SREG = oldSREG;
// return 0;                           // return Success.  ?
}                  

unsigned char EEPROMread(unsigned int location)
//unsigned char eepromread(unsigned int location)
{
 unsigned char oldSREG;
 oldSREG = SREG;
 SREG &= ~0x80;   // disable interrupt
// Wait for completion of previous write
 while(EECR & (1< // Set up address register
 EEAR = location;
// Start eeprom read by writing EERE
 EECR |= (1<  
 SREG = oldSREG;
// Return data from data register
 return EEDR;
}
#endif
#if 1
void wait_page_rw_ok(void)
{
 //while(SPMCSR & 0x40){
 {
  while(SPMCSR & 0x01);
  //SPMCSR = 0x11;
  asm("spm\n");
 }
}

// code:0x03:erase, code:0x05:write a page
void boot_page_ew(unsigned long addr,unsigned char code)
{
 wait_page_rw_ok();
 asm("mov r30,r16\n"
     "mov r31,r17\n"
     "out 0x3b,r18\n"
  ); // put addr into Z register and RAMPZ's BIT0
 SPMCSR = code; // set operate code
 asm("spm\n"); // operate flash page
 wait_page_rw_ok();
}
// fill one word into a page
void boot_page_fill(unsigned int pg_addr,unsigned int data)
{
 pg_addr = pg_addr;
 data = data;
 wait_page_rw_ok();
 asm("mov r30,r16\n"
     "mov r31,r17\n" // Z register is page addr
     "mov r0,r18\n"
     "mov r1,r19\n" // R0R1 operate word 
     );
 SPMCSR = 0x01;
 asm("spm\n");  // operate flash page
}

void write_one_page(unsigned long f_addr)
{
 int i = 0;

 for(i = 0; i < FLASH_PG_SIZE;i+=2){
  boot_page_fill(i,uart0_rx_buf[i]|(uart0_rx_buf[i+1]<<8));
 }
 boot_page_ew(flash_addr,0x03);
 boot_page_ew(flash_addr,0x05);
}
int check_one_page_ok(unsigned long f_addr)
{
 unsigned char dat = 0;
 int i = 0;
 #if 0
 asm("mov r30,r16\n"
     "mov r31,r17\n"
     "out 0x3b,r18\n"
  ); // put addr into Z register and RAMPZ's BIT0

 for(i = 0; i < FLASH_PG_SIZE; i++){
  asm("elpm r0,Z+");
  asm("mov %dat, r0");
  //if(dat != uart0_rx_buf[i])
  // return 0;
 }
 #endif
 return 1;
}
#else
void WriteFlash(void)
  {
    unsigned int i;
    unsigned int TempInt;
    for (i=0;i       {
       TempInt=uart0_rx_buf[i]+(uart0_rx_buf[i+1]<<8);
       fill_temp_buffer(TempInt,i);    //call asm routine.
      }
    write_page(flash_addr,0x03);       //擦除页
    write_page(flash_addr,0x05);       //写页数据
  
    enableRWW();
  }
#endif
#if 0
#pragma interrupt_handler uart0_rxc_isr:19  //iv_USART0_RX
void uart0_rxc_isr( void )
{
 #if 0
  unsigned char data;
 unsigned char index;

 data = UDR0;
 #endif
}
#endif
void sys_choice_process(void)
{
 unsigned long time_enter = 0;
 int dat = 0;

 time_enter = get_sys_time_ms();

 //sendStr("press 'd' to download fw:");

 while(1){
  dat = getchar();
  //if('d' == dat){
  {
   putchar(XMODEM_NAK);
   sys_set_state(SYS_STATE_READ_DAT);
   return;
  }
  #if 0
  if(get_sys_time_ms() - time_enter >= SYS_WAIT_TIME){
   sys_set_state(SYS_STATE_ERROR);
   return;
  }
  #endif
 }
 
 return;
}
#if 0
void sys_ack_process(void)
{
 // Nothing, put ack to read data process
 return;
}
#endif

unsigned char read_start = 0;

void sys_read_data_process(void)
{
 int dat = 0;
 int count = 0;
 unsigned int i = 0,ee_addr = 0,err_count = 0,cancel_flag = 0;
 unsigned char checksum = 0;
 //unsigned long flash_addr = 0;
 unsigned char packet_sn = 0,block = 0;
 unsigned long timer = 0;

 read_start = 0;
 while(1){
  #if 1
  if(0 == read_start){
   timer++;
   if((timer > 600000) ){
    sys_set_state(SYS_STATE_END);
    return;
   }
  }
  #endif
  dat = getchar();

  if(dat >= 0){
   
   if(0 == count){
    if(XMODEM_EOT == dat){
     read_start = 1;
     if(1 == block){
      write_one_page(flash_addr);
     }
     putchar(XMODEM_ACK);
     sys_set_state(SYS_STATE_END);
     return;
    }else if(XMODEM_SOH == dat){  // start flag
     cancel_flag = 0;
    }else if(XMODEM_CAN == dat){  // cancel flag
     cancel_flag = 1;
    }
    
   }else if(1 == count){
    if(1 == cancel_flag){
     if(XMODEM_CAN == dat){
      cancel_flag = 2;
     }else{
      putchar('1');
      goto error;
     }
    }
    //  Note:when packet number reach to 255, it reset to zero,not 1
    if((dat - packet_sn == 1)|| ((0x0 == dat) &&(0xff == packet_sn))){  // sn ok
     packet_sn = dat;
    }else{     // sn error
     putchar('2');
     goto error;
    }
   }else if(2 == count){
    if(2 == cancel_flag){
     if(XMODEM_CAN == dat){
      sys_set_state(SYS_STATE_CANCEL);
      return;
     }else{
      putchar('3');
      goto error;
     }
    }
    if((dat + packet_sn) == 0xff){   // ok
    }else{     // error
     putchar('4');
     goto error;
    }
   }
   read_start = 1;
   count++;
   if(count > 3 && count < (128 + 4)){  // get data packets
    checksum += dat;
    uart0_rx_buf[i++] = dat;
   }
   if(count >= 132){
    if(dat == checksum){  // checksum ok
     #if 0
     for(i = 0; i < FLASH_PG_SIZE; i++){
      EEPROMwrite(ee_addr++,uart0_rx_buf[i]);
     }
     #else
     block++;
     if(2 == block){
      write_one_page(flash_addr);
      //WriteFlash();
      //if(!check_one_page_ok(flash_addr)){
      // putchar('5');
      // goto error;
      //}
      flash_addr += FLASH_PG_SIZE;
      i = 0;
      block = 0;
     }
     #endif
     putchar(XMODEM_ACK);
     err_count = 0;
    
    }else{    // retry
     putchar(XMODEM_NAK);
     err_count++;
     packet_sn--;
    }
    
    
    count = 0;
    checksum = 0;
    if(err_count > SYS_RETRY_MAX){
     putchar('6');
     goto error;
    }
   }
  }
 }
 return;
error:
 sys_set_state(SYS_STATE_ERROR);
 return;
}
void sys_run_app(void)
{
 //while(1); // TODO: just for debug
 MCUCR = 0x01;
 MCUCR = 0x00; //Move the interrupt vector to the beginning of flash
 RAMPZ = 0x00;
 asm("jmp 0x00000\n");

}
void sys_end_process(void)
{
 sendStr("OK.\r\n");
 sys_run_app(); 
 
 return;
}
void sys_cancel_process(void)
{
 sendStr("CANCEL.\r\n");
 sys_run_app();
 
 return;
}
void sys_error_process(void)
{
 sendStr("ERROR.\r\n");
 sys_run_app();
 
 return;
}
void sys_run(void)
{
 
 switch(sys_state){
  case SYS_STATE_CHOICE:
   sys_choice_process();
   break;
  //case SYS_STATE_ACK:
   //sys_ack_process();
   break;
  case SYS_STATE_READ_DAT:
   sys_read_data_process();
   break;
  case SYS_STATE_END:
   sys_end_process();
   break;
  case SYS_STATE_CANCEL:
   sys_cancel_process();
   break;
  case SYS_STATE_ERROR:
  default:
   sys_error_process();
   break;
 }
}
#if 0
void test_flash(void)
{
 int i = 0;

 for(i = 0; i < FLASH_PG_SIZE; i++){
  uart0_rx_buf[i] = 0x11;
 }
 write_one_page(flash_addr);
 flash_addr += FLASH_PG_SIZE;
 for(i = 0; i < FLASH_PG_SIZE; i++){
  uart0_rx_buf[i] = 0x22;
 }
 write_one_page(flash_addr);
 flash_addr += FLASH_PG_SIZE; 
 for(i = 0; i < FLASH_PG_SIZE; i++){
   uart0_rx_buf[i] = 0x33;
 }
 write_one_page(flash_addr);
 flash_addr += FLASH_PG_SIZE;
 while(1);
}
#endif
void main(void)
{
 static unsigned long time_last = 0;
 unsigned long time_now = 0;
 
 init_devices();
 sys_set_state(SYS_STATE_CHOICE);
 //sendStr("started.\r\n");

 //EEPROMwrite(0,'M');
 //EEPROMwrite(1,'W');
 //delay_ms(3000);
 
 while(1){
  #if 0
  time_now = get_sys_time_ms();

  if(time_now - time_last > 2000){
   sendStr("alive...\r\n");
   time_last = time_now;
  }
  #endif
  sys_run();
  //test_flash();
 }
}

 

 
Keywords:avr  bootloader Reference address:AVR bootloader development notes

Previous article:Method of judging pulse width by AVR microcontroller
Next article:Share the design of PWM pulse width modulator based on tc1

Latest Microcontroller Articles
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号