首页 > 代码库 > 【转】AVR BOOT

【转】AVR BOOT

技术分享
  1 #include <string.h>  2 #include <iom128v.h>  3 #include "main.h"  4 #include <macros.h>  5 //#include "assembly.h"  6   7 static unsigned long timer_1ms = 0;  8 static unsigned char sys_state = 0;  9 static unsigned char uart0_rx_buf[UART0_BUF_SIZE]; 10 static unsigned long flash_addr = 0; 11 void sys_set_state(unsigned char state) 12 { 13  sys_state = state; 14 } 15 unsigned long get_sys_time_ms(void) 16 { 17  return timer_1ms; 18 } 19 extern int _textmode; 20   21 int putchar(char c) 22     { 23     //if (_textmode && c == ‘\n‘) 24     //    putchar(‘\r‘); 25     /* Wait for empty transmit buffer */ 26     while ( !(UCSR0A & (1<<UDRE0)) ) 27         ;                      28     /* Putting data into buffer , sends the data */ 29     UDR0 = c;   30     return c; 31 } 32 int getchar(void) 33 { 34  unsigned char dat = 0; 35  unsigned char status = 0; 36  WDR(); 37  if(((UCSR0A & 0x80)==0)){ 38   return -1; 39  } 40  status = UCSR0A; 41  dat = UDR0; 42  if(status & 0x1C){ 43   return -2; 44  } 45   46  return dat;  47 } 48 void sendStr(char *str) 49 { 50  int len = 0; 51  unsigned char i = 0; 52  len = strlen(str); 53  for(i = 0; i < len; i++) 54   putchar(str[i]); 55  while ( !(UCSR0A & (1<<UDRE0)) ); 56 } 57 void port_init(void) 58 { 59 // MCUCSR |= 0x80; 60 // MCUCSR |= 0x80; 61   PORTA = 0x07;    //PA3~PA7 IS LOW,PA2~PA0 IS HIGH 62   DDRA  = 0xFF; 63   PORTB = 0xFb;    //MOSI(PB2) is low ***** 64   DDRB  = 0xF7;    //PB3 IS INPUT 65   PORTC = 0xFF;     //m103 output only 66   DDRC  = 0xFF; 67   PORTD = 0xFF; 68   DDRD  = 0x00;    //PD0~7 IS INPUT 69   PORTE = 0xFF; 70   DDRE  = 0xFE; 71   PORTF = 0xFF; 72   DDRF  = 0x00; 73   PORTG = 0x1F; 74   DDRG  = 0x00; 75 } 76 void uart0_init(void) 77 { 78   UCSR0B = 0x00;   //disable while setting baud rate 79   UCSR0A = 0x00; 80   UCSR0C = 0x06; 81  //6MHz  82   UBRR0L = 0x26;   //set baud rate lo 83   //14.7456MHz 9600bps 84 //  UBRR0L = 0x5F;   //set baud rate lo 85   //14.7456MHz 115200bps 86 //  UBRR0L = 0x07;   //set baud rate lo 87   UBRR0H = 0x00;   //set baud rate hi 88 //  UCSR0B = 0xD8;  //0xB8 89 //  UCSR0B = 0xB8;  //0xD8 90   //UCSR0B = 0x98;  //0xD8,0xB8 91   UCSR0B = 0x18;  //0xD8,0xB8,disable rcv interrupt 92 } 93 // OSC:6MHz, prescale:1024 94 void timer0_init(void) 95 { 96   TCCR0 = 0x00; //stop 97   ASSR  = 0x00; //set async mode 98   TCNT0 = 0xf9; //set count 99   OCR0  = 0xD7;100   TCCR0 = 0x07; //start timer101 }102 #pragma interrupt_handler timer0_ovf_isr:17103 void timer0_ovf_isr(void)104 {105   TCNT0 = 0xf9; //reload counter value106   timer_1ms++;107 }108 void watchdog_off(void)109 {110 // Write logical one to WDCE and WDE111  WDTCR = (1<<WDCE) | (1<<WDE);112 // Turn off WDT113  WDTCR = 0x00;114 }115 void watchdog_init(unsigned char time)116 {117   WDR();     //this prevents a timout on enabling118 // Write logical one to WDCE and WDE119   WDTCR = (1<<WDCE) | (1<<WDE);120   time&=0x07;   // prescale: n cycles  time=0~7,WDCE=0121   time|=0x08;   // prescale: n cycles  time=0~7,WDE=1122  WDTCR=time;   //WATCHDOG ENABLED - dont forget to issue WDRs123 }124 #if 0125 void delay_1ms(void)126 {127     unsigned int i;128     for(i=1;i<(unsigned int)(XTAL*143-20);i++)129     {130      WDR();131     }132 }133 void delay_ms(unsigned int n)134 {135     unsigned int i=0;136     while(i<n)137     {138          delay_1ms();139          i++;140     }141 }142 #endif143 void init_devices(void)144 {145   CLI(); //disable all interrupts146   XDIV  = 0x00;   //xtal divider147   XMCRA = 0x00;   //external memory148   port_init();149   uart0_init();150  timer0_init();151  152   MCUCR = 0x00;153   EICRA = 0x00;   //extended ext ints154   sbi(TIMSK,TOIE0);155 //  sbi(TIMSK,TOIE1); //允许定时器1溢出中断156 //  sbi(TIMSK,TOIE2); //允许定时器2溢出中断157  watchdog_init(7);158   //SEI();     //re-enable interrupts159  //all peripherals are now initialised160 }161 #if 0162 void EEPROMwrite(unsigned int location, unsigned char byte)163 //unsigned int eepromwrite(unsigned int location, unsigned char byte)164 {165  unsigned char oldSREG;166  oldSREG = SREG;167  SREG &= ~0x80;   // disable interrupt168 // Wait for completion of previous write169  while(EECR & (1<<EEWE)) ;170 // Set up address and data registers171  EEAR = location;172  EEDR = byte;173 // Write logical one to EEMWE174  EECR |= (1<<EEMWE);175 // Start eeprom write by setting EEWE176  EECR |= (1<<EEWE);177  178  SREG = oldSREG;179 // return 0;                           // return Success.  ?180 }                   181 unsigned char EEPROMread(unsigned int location)182 //unsigned char eepromread(unsigned int location)183 {184  unsigned char oldSREG;185  oldSREG = SREG;186  SREG &= ~0x80;   // disable interrupt187 // Wait for completion of previous write188  while(EECR & (1<<EEWE)) ;189 // Set up address register190  EEAR = location;191 // Start eeprom read by writing EERE192  EECR |= (1<<EERE);193  194  SREG = oldSREG;195 // Return data from data register196  return EEDR;197 }198 #endif199 #if 1200 void wait_page_rw_ok(void)201 {202  //while(SPMCSR & 0x40){203  {204   while(SPMCSR & 0x01);205   //SPMCSR = 0x11;206   asm("spm\n");207  }208 }209 // code:0x03:erase, code:0x05:write a page210 void boot_page_ew(unsigned long addr,unsigned char code)211 {212  wait_page_rw_ok();213  asm("mov r30,r16\n"214      "mov r31,r17\n"215      "out 0x3b,r18\n"216   ); // put addr into Z register and RAMPZ‘s BIT0217  SPMCSR = code; // set operate code218  asm("spm\n"); // operate flash page219  wait_page_rw_ok();220 }221 // fill one word into a page222 void boot_page_fill(unsigned int pg_addr,unsigned int data)223 {224  pg_addr = pg_addr;225  data =http://www.mamicode.com/ data;226  wait_page_rw_ok();227  asm("mov r30,r16\n"228      "mov r31,r17\n" // Z register is page addr229      "mov r0,r18\n"230      "mov r1,r19\n" // R0R1 operate word 231      );232  SPMCSR = 0x01;233  asm("spm\n");  // operate flash page234 }235 void write_one_page(unsigned long f_addr)236 {237  int i = 0;238  for(i = 0; i < FLASH_PG_SIZE;i+=2){239   boot_page_fill(i,uart0_rx_buf[i]|(uart0_rx_buf[i+1]<<8));240  }241  boot_page_ew(flash_addr,0x03);242  boot_page_ew(flash_addr,0x05);243 }244 int check_one_page_ok(unsigned long f_addr)245 {246  unsigned char dat = 0;247  int i = 0;248  #if 0249  asm("mov r30,r16\n"250      "mov r31,r17\n"251      "out 0x3b,r18\n"252   ); // put addr into Z register and RAMPZ‘s BIT0253  for(i = 0; i < FLASH_PG_SIZE; i++){254   asm("elpm r0,Z+");255   asm("mov %dat, r0");256   //if(dat != uart0_rx_buf[i])257   // return 0;258  }259  #endif260  return 1;261 }262 #else263 void WriteFlash(void)264   {265     unsigned int i;266     unsigned int TempInt;267     for (i=0;i<FLASH_PG_SIZE;i+=2)268       {269        TempInt=uart0_rx_buf[i]+(uart0_rx_buf[i+1]<<8);270        fill_temp_buffer(TempInt,i);    //call asm routine.271       } 272     write_page(flash_addr,0x03);       //擦除页273     write_page(flash_addr,0x05);       //写页数据274    275     enableRWW();276   }277 #endif278 #if 0279 #pragma interrupt_handler uart0_rxc_isr:19  //iv_USART0_RX280 void uart0_rxc_isr( void )281 {282  #if 0283   unsigned char data;284  unsigned char index;285  data =http://www.mamicode.com/ UDR0; 286  #endif287 }288 #endif289 void sys_choice_process(void)290 {291  unsigned long time_enter = 0;292  int dat = 0;293  time_enter = get_sys_time_ms();294  //sendStr("press ‘d‘ to download fw:");295  while(1){296   dat = getchar();297   //if(‘d‘ == dat){298   {299    putchar(XMODEM_NAK);300    sys_set_state(SYS_STATE_READ_DAT);301    return;302   }303   #if 0304   if(get_sys_time_ms() - time_enter >= SYS_WAIT_TIME){305    sys_set_state(SYS_STATE_ERROR);306    return;307   }308   #endif309  }310  311  return;312 }313 #if 0314 void sys_ack_process(void)315 {316  // Nothing, put ack to read data process317  return;318 }319 #endif320 unsigned char read_start = 0;321 void sys_read_data_process(void)322 {323  int dat = 0;324  int count = 0;325  unsigned int i = 0,ee_addr = 0,err_count = 0,cancel_flag = 0;326  unsigned char checksum = 0;327  //unsigned long flash_addr = 0;328  unsigned char packet_sn = 0,block = 0;329  unsigned long timer = 0;330  read_start = 0;331  while(1){332   #if 1333   if(0 == read_start){334    timer++;335    if((timer > 600000) ){336     sys_set_state(SYS_STATE_END);337     return;338    }339   }340   #endif341   dat = getchar();342   if(dat >= 0){343    344    if(0 == count){345     if(XMODEM_EOT == dat){346      read_start = 1;347      if(1 == block){348       write_one_page(flash_addr);349      }350      putchar(XMODEM_ACK);351      sys_set_state(SYS_STATE_END);352      return;353     }else if(XMODEM_SOH == dat){  // start flag354      cancel_flag = 0;355     }else if(XMODEM_CAN == dat){  // cancel flag356      cancel_flag = 1;357     }358     359    }else if(1 == count){360     if(1 == cancel_flag){361      if(XMODEM_CAN == dat){362       cancel_flag = 2;363      }else{364       putchar(1);365       goto error;366      }367     }368     //  Note:when packet number reach to 255, it reset to zero,not 1369     if((dat - packet_sn == 1)|| ((0x0 == dat) &&(0xff == packet_sn))){  // sn ok370      packet_sn = dat;371     }else{     // sn error372      putchar(2);373      goto error;374     }375    }else if(2 == count){376     if(2 == cancel_flag){377      if(XMODEM_CAN == dat){378       sys_set_state(SYS_STATE_CANCEL);379       return;380      }else{381       putchar(3);382       goto error;383      }384     }385     if((dat + packet_sn) == 0xff){   // ok386     }else{     // error387      putchar(4);388      goto error;389     }390    }391    read_start = 1;392    count++;393    if(count > 3 && count < (128 + 4)){  // get data packets394     checksum += dat;395     uart0_rx_buf[i++] = dat;396    }397    if(count >= 132){398     if(dat == checksum){  // checksum ok399      #if 0400      for(i = 0; i < FLASH_PG_SIZE; i++){401       EEPROMwrite(ee_addr++,uart0_rx_buf[i]);402      }403      #else404      block++;405      if(2 == block){406       write_one_page(flash_addr);407       //WriteFlash();408       //if(!check_one_page_ok(flash_addr)){409       // putchar(‘5‘);410       // goto error;411       //}412       flash_addr += FLASH_PG_SIZE;413       i = 0;414       block = 0;415      }416      #endif417      putchar(XMODEM_ACK);418      err_count = 0;419     420     }else{    // retry421      putchar(XMODEM_NAK);422      err_count++;423      packet_sn--;424     }425     426     427     count = 0;428     checksum = 0;429     if(err_count > SYS_RETRY_MAX){430      putchar(6);431      goto error;432     }433    }434   }435  }436  return;437 error:438  sys_set_state(SYS_STATE_ERROR);439  return;440 }441 void sys_run_app(void)442 {443  //while(1);  // TODO: just for debug444  MCUCR = 0x01; 445  MCUCR = 0x00;  //将中断向量移至flash的起始位置446  RAMPZ = 0x00;447  asm("jmp 0x00000\n");448 }449 void sys_end_process(void)450 {451  sendStr("OK.\r\n");452  sys_run_app(); 453  454  return;455 }456 void sys_cancel_process(void)457 {458  sendStr("CANCEL.\r\n");459  sys_run_app();460  461  return;462 }463 void sys_error_process(void)464 {465  sendStr("ERROR.\r\n");466  sys_run_app();467  468  return;469 }470 void sys_run(void)471 {472  473  switch(sys_state){474   case SYS_STATE_CHOICE:475    sys_choice_process();476    break;477   //case SYS_STATE_ACK:478    //sys_ack_process();479    break;480   case SYS_STATE_READ_DAT:481    sys_read_data_process();482    break;483   case SYS_STATE_END:484    sys_end_process();485    break;486   case SYS_STATE_CANCEL:487    sys_cancel_process();488    break;489   case SYS_STATE_ERROR:490   default:491    sys_error_process();492    break;493  }494 }495 #if 0496 void test_flash(void)497 {498  int i = 0;499  for(i = 0; i < FLASH_PG_SIZE; i++){500   uart0_rx_buf[i] = 0x11;501  }502  write_one_page(flash_addr);503  flash_addr += FLASH_PG_SIZE;504  for(i = 0; i < FLASH_PG_SIZE; i++){505   uart0_rx_buf[i] = 0x22;506  }507  write_one_page(flash_addr);508  flash_addr += FLASH_PG_SIZE; 509  for(i = 0; i < FLASH_PG_SIZE; i++){510    uart0_rx_buf[i] = 0x33;511  }512  write_one_page(flash_addr);513  flash_addr += FLASH_PG_SIZE;514  while(1);515 }516 #endif517 void main(void)518 {519  static unsigned long time_last = 0;520  unsigned long time_now = 0;521  522  init_devices();523  sys_set_state(SYS_STATE_CHOICE);524  //sendStr("started.\r\n");525  //EEPROMwrite(0,‘M‘);526  //EEPROMwrite(1,‘W‘);527  //delay_ms(3000);528  529  while(1){530   #if 0531   time_now = get_sys_time_ms();532   if(time_now - time_last > 2000){533    sendStr("alive...\r\n");534    time_last = time_now;535   }536   #endif537   sys_run();538   //test_flash();539  }540 }
View Code

  main.c

 

技术分享
 1  #ifndef _MAIN_H 2 #define _MAIN_H 3 #define XMODEM_NUL  (0x00) 4 #define XMODEM_SOH  (0x01)  // Xmodem start header(128 data packets) 5 #define XMODEM_STX  (0x02)  // 1K xmoder start header(1024 data packets) 6 #define XMODEM_EOT  (0x04) 7 #define XMODEM_ACK  (0x06) 8 #define XMODEM_NAK  (0x15)  // 表示接收方想按照累加和的校验方式接收 9 #define XMODEM_CAN  (0x18)10 #define XMODEM_EOF  (0x1A)11 #define XMODEM_CRC_CK  ‘C‘  // 表示接收方想按照CRC的校验方式接收12 #define SYS_STATE_CHOICE (1)13 #define SYS_STATE_READ_DAT (2)14 //#define SYS_STATE_ACK  (3)15 #define SYS_STATE_END  (4)16 #define SYS_STATE_ERROR (5)17 #define SYS_STATE_CANCEL (6)18 #define SYS_WAIT_TIME  (5000)  // 5 seconds19 #define SYS_RETRY_MAX  (3)20 #define UART0_BUF_SIZE  (150*2)21 #define XTAL   (6)  //晶振频率(MHz) 6MHz22 #define FLASH_PG_SIZE  (128*2)23 #define OSC_VAR   (6000000)24 #define cbi(reg, bit)   (reg &= ~BIT(bit))25 #define sbi(reg, bit)   (reg |= BIT(bit))26  27 #endif
View Code

main.h

 

 

 折腾了差不多3天,终于调试成功了,不过是个初期的东西,以后还需要加强可靠性和易用性及安全性的考虑,好歹现在可以直接通过bootloader升级程序了,先整理下整个过程吧,方便自己以后查阅,也方便有需要的人,因为开发的过程中借鉴了好多别人的东西。 

开发的缘由:公司的定制化软件比较多,用户拿到产品后又会有新的需求,其实很多就是纯软件的修改,现在就是工程师到现成开盖,拿下载器重新烧程序,会比较麻烦,因为产品的对外接口都有串口,而且Atmega 本身支持bootloader功能,于是想到了用串口升级程序这个法子J。

开发环境及工具:硬件平台:用的是公司Atmega128平台下的板子,AVR的一个并口下载器,软件:编译器:ICCAVR 7.22,串口调试助手,serial port monitor(用来监控串口数据的,好东东)及windows的超级终端,下载软件:PonyProg2000(这个软件用来读EEPROM和Flash比较方便,不过用来烧写Atmega2561的话就不能用它了,有问题),hexbin.exe工具(网上找的现成的),用来将intel的hex文件转换成纯16进制文件(单片机Flash中存储的格式)。

要实现的功能:如果要升级程序,则通过超级终端的串口(Xmodem协议),将固件烧写到flash的0地址中,然后跳转到flash的0地址中,否则等待一段时间则自动跳转到flash中的0地址中。bootloader的大小:1024字节以内。

一些注意事项:

1.         熔丝位的烧写:BOOTRST要进行编程,这样单片机在复位后自动跳转到bootloader区执行bootloader的代码,然后要根据自己bootloader的大小设置boot区的的熔丝位:具体设置如下图,这里我选择的是1024大小(注意1代表为编程,0代表已编程):

  

技术分享

 

 

2.         设置引导区锁定位:为了保护bootloader不被应用程序修改或者擦除,所以要对其进行保护,Atmega提供了熔丝位的保护方式,具体设置如下图(我设置的为BLB0为11,BLB1为00):

技术分享

 

 

3.         Flash页的设定:因flash的擦除和写入是按照页来操作的,看手册上说是1页有128个字节,但实际调试时候发现需要一次写入256个字节才有效的,如果按照128来写入,会将第二个128的内容覆盖掉第一个128字节的内容,那就按照实际为准了。

4.         Xmodem协议的注意事项:具体的xmodem不在本文叙述了,只说一下要注意的地方,校验和是可以选择的,我使用的是checksum(就是单纯的累加),也可以选择16为的CRC,这个是根据单片机第一次返回的响应字节来确定的,另外当包的序列号超过255时会重新从0开始而不是从1开始,首次传输是从1开始编号的,这个要注意一下。

5.         文件格式文件:和网上好多人一样,遇到同样的文件,在bootloader将应用程序烧写到flash中后,发现没有执行应用程序,开始我也以为是跳转不成成功的问题,上网查了半天都没找到答案,都是问问题的L。没办法,靠自己了,一致纳闷,烧写到Flash中的程序和原始文件内容一模一样怎么就不能执行啊,后来偶然用烧写软件打开要烧写的固件,发现内容和我用16进制工具打开的并不一样,Oh my god,豁然想到了问题的关键,原来用ICC生成的hex文件是intel hex形式,Intel HEX文件是由一行行符合Intel HEX文件格式的文本所构成的ASCII文本文件。在Intel HEX文件中,每一行包含一个HEX记录。这些记录由对应机器语言码和/或常量数据的十六进制编码数字组成。Intel HEX文件通常用于传输将被存于ROM或者EPROM中的程序和数据。大多数EPROM编程器或模拟器使用Intel HEX文件。而实际存储到Flash中的数据是要从这个HEX文件中提取出来,然后在通过xmodem发送到单片机,不要直接发送ICC生成的HEX文件,转换的话可以自己写一个小工具或者上网搜类似功能的工具,为了省事,我找了一个叫hex2bin的工具做的转换。

6.         在ICC工程中要设置,我的设置如下:

技术分享

 

 

7.         关于C中嵌套汇编的参数和返回值的用法:下面引用ICC的帮助文件“

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. ”

剩下的没什么了,下面附上程序(刚调试完,还没整理,很乱)

【转】AVR BOOT