【轉】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 timer
101 }
102 #pragma interrupt_handler timer0_ovf_isr:17
103 void timer0_ovf_isr(void)
104 {
105   TCNT0 = 0xf9; //reload counter value
106   timer_1ms++;
107 }
108 void watchdog_off(void)
109 {
110 // Write logical one to WDCE and WDE
111  WDTCR = (1<<WDCE) | (1<<WDE);
112 // Turn off WDT
113  WDTCR = 0x00;
114 }
115 void watchdog_init(unsigned char time)
116 {
117   WDR();     //this prevents a timout on enabling
118 // Write logical one to WDCE and WDE
119   WDTCR = (1<<WDCE) | (1<<WDE);
120   time&=0x07;   // prescale: n cycles  time=0~7,WDCE=0
121   time|=0x08;   // prescale: n cycles  time=0~7,WDE=1
122  WDTCR=time;   //WATCHDOG ENABLED - dont forget to issue WDRs
123 }
124 #if 0
125 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 #endif
143 void init_devices(void)
144 {
145   CLI(); //disable all interrupts
146   XDIV  = 0x00;   //xtal divider
147   XMCRA = 0x00;   //external memory
148   port_init();
149   uart0_init();
150  timer0_init();
151  
152   MCUCR = 0x00;
153   EICRA = 0x00;   //extended ext ints
154   sbi(TIMSK,TOIE0);
155 //  sbi(TIMSK,TOIE1); //容許定時器1溢出中斷
156 //  sbi(TIMSK,TOIE2); //容許定時器2溢出中斷
157  watchdog_init(7);
158   //SEI();     //re-enable interrupts
159  //all peripherals are now initialised
160 }
161 #if 0
162 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 interrupt
168 // Wait for completion of previous write
169  while(EECR & (1<<EEWE)) ;
170 // Set up address and data registers
171  EEAR = location;
172  EEDR = byte;
173 // Write logical one to EEMWE
174  EECR |= (1<<EEMWE);
175 // Start eeprom write by setting EEWE
176  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 interrupt
187 // Wait for completion of previous write
188  while(EECR & (1<<EEWE)) ;
189 // Set up address register
190  EEAR = location;
191 // Start eeprom read by writing EERE
192  EECR |= (1<<EERE);
193  
194  SREG = oldSREG;
195 // Return data from data register
196  return EEDR;
197 }
198 #endif
199 #if 1
200 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 page
210 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 BIT0
217  SPMCSR = code; // set operate code
218  asm("spm\n"); // operate flash page
219  wait_page_rw_ok();
220 }
221 // fill one word into a page
222 void boot_page_fill(unsigned int pg_addr,unsigned int data)
223 {
224  pg_addr = pg_addr;
225  data = data;
226  wait_page_rw_ok();
227  asm("mov r30,r16\n"
228      "mov r31,r17\n" // Z register is page addr
229      "mov r0,r18\n"
230      "mov r1,r19\n" // R0R1 operate word 
231      );
232  SPMCSR = 0x01;
233  asm("spm\n");  // operate flash page
234 }
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 0
249  asm("mov r30,r16\n"
250      "mov r31,r17\n"
251      "out 0x3b,r18\n"
252   ); // put addr into Z register and RAMPZ's BIT0
253  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  #endif
260  return 1;
261 }
262 #else
263 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 #endif
278 #if 0
279 #pragma interrupt_handler uart0_rxc_isr:19  //iv_USART0_RX
280 void uart0_rxc_isr( void )
281 {
282  #if 0
283   unsigned char data;
284  unsigned char index;
285  data = UDR0; 
286  #endif
287 }
288 #endif
289 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 0
304   if(get_sys_time_ms() - time_enter >= SYS_WAIT_TIME){
305    sys_set_state(SYS_STATE_ERROR);
306    return;
307   }
308   #endif
309  }
310  
311  return;
312 }
313 #if 0
314 void sys_ack_process(void)
315 {
316  // Nothing, put ack to read data process
317  return;
318 }
319 #endif
320 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 1
333   if(0 == read_start){
334    timer++;
335    if((timer > 600000) ){
336     sys_set_state(SYS_STATE_END);
337     return;
338    }
339   }
340   #endif
341   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 flag
354      cancel_flag = 0;
355     }else if(XMODEM_CAN == dat){  // cancel flag
356      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 1
369     if((dat - packet_sn == 1)|| ((0x0 == dat) &&(0xff == packet_sn))){  // sn ok
370      packet_sn = dat;
371     }else{     // sn error
372      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){   // ok
386     }else{     // error
387      putchar('4');
388      goto error;
389     }
390    }
391    read_start = 1;
392    count++;
393    if(count > 3 && count < (128 + 4)){  // get data packets
394     checksum += dat;
395     uart0_rx_buf[i++] = dat;
396    }
397    if(count >= 132){
398     if(dat == checksum){  // checksum ok
399      #if 0
400      for(i = 0; i < FLASH_PG_SIZE; i++){
401       EEPROMwrite(ee_addr++,uart0_rx_buf[i]);
402      }
403      #else
404      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      #endif
417      putchar(XMODEM_ACK);
418      err_count = 0;
419     
420     }else{    // retry
421      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 debug
444  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 0
496 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 #endif
517 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 0
531   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   #endif
537   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 seconds
19 #define SYS_RETRY_MAX  (3)
20 #define UART0_BUF_SIZE  (150*2)
21 #define XTAL   (6)  //晶振頻率(MHz) 6MHz
22 #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.hwindows

 

 

 折騰了差很少3天,終於調試成功了,不過是個初期的東西,之後還須要增強可靠性和易用性及安全性的考慮,好歹如今能夠直接經過bootloader升級程序了,先整理下整個過程吧,方便本身之後查閱,也方便有須要的人,由於開發的過程當中借鑑了好多別人的東西。 

開發的原因:公司的定製化軟件比較多,用戶拿到產品後又會有新的需求,其實不少就是純軟件的修改,如今就是工程師到現成開蓋,拿下載器從新燒程序,會比較麻煩,由於產品的對外接口都有串口,並且Atmega 自己支持bootloader功能,因而想到了用串口升級程序這個法子J。安全

開發環境及工具:硬件平臺:用的是公司Atmega128平臺下的板子,AVR的一個並口下載器,軟件:編譯器:ICCAVR 7.22,串口調試助手,serial port monitor(用來監控串口數據的,好東東)及windows的超級終端,下載軟件:PonyProg2000(這個軟件用來讀EEPROM和Flash比較方便,不過用來燒寫Atmega2561的話就不能用它了,有問題),hexbin.exe工具(網上找的現成的),用來將intel的hex文件轉換成純16進制文件(單片機Flash中存儲的格式)。app

要實現的功能:若是要升級程序,則經過超級終端的串口(Xmodem協議),將固件燒寫到flash的0地址中,而後跳轉到flash的0地址中,不然等待一段時間則自動跳轉到flash中的0地址中。bootloader的大小:1024字節之內。async

一些注意事項:ide

1.         熔絲位的燒寫:BOOTRST要進行編程,這樣單片機在復位後自動跳轉到bootloader區執行bootloader的代碼,而後要根據本身bootloader的大小設置boot區的的熔絲位:具體設置以下圖,這裏我選擇的是1024大小(注意1表明爲編程,0表明已編程):工具

  

avr bootloader 開發筆記(原創) - 蒼海一笑 - 生命在於折騰

 

 

2.         設置引導區鎖定位:爲了保護bootloader不被應用程序修改或者擦除,因此要對其進行保護,Atmega提供了熔絲位的保護方式,具體設置以下圖(我設置的爲BLB0爲11,BLB1爲00):this

avr bootloader 開發筆記(原創) - 蒼海一笑 - 生命在於折騰

 

 

3.         Flash頁的設定:因flash的擦除和寫入是按照頁來操做的,看手冊上說是1頁有128個字節,但實際調試時候發現須要一次寫入256個字節纔有效的,若是按照128來寫入,會將第二個128的內容覆蓋掉第一個128字節的內容,那就按照實際爲準了。編碼

4.         Xmodem協議的注意事項:具體的xmodem不在本文敘述了,只說一下要注意的地方,校驗和是能夠選擇的,我使用的是checksum(就是單純的累加),也能夠選擇16爲的CRC,這個是根據單片機第一次返回的響應字節來肯定的,另外當包的序列號超過255時會從新從0開始而不是從1開始,首次傳輸是從1開始編號的,這個要注意一下。spa

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工程中要設置,個人設置以下:

avr bootloader 開發筆記(原創) - 蒼海一笑 - 生命在於折騰

 

 

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

剩下的沒什麼了,下面附上程序(剛調試完,還沒整理,很亂)

相關文章
相關標籤/搜索