求助avr单片机bootloader

2019-03-24 20:13发布

网上的通用xmega的bootloader移植出来的,但是发送XModem并不能完成升级。求指教!
  1. #include "usart.h"
  2. #include "bootcfg.h"
  3. #include "sp_driver.h"
  4. #include "XMODEM.h"
  5. #include "LED.h"
  6. #include "timer.h"
  7. #include "clksys_driver.h"


  8. #define Test_The_Uart  1 //如果这个标志为1那么程序只发送字符,用于测试串口 正常设置为0
  9. /*
  10. Xmega Bootloaber 移植AVRUBD
  11. 上位机,AVRUDB
  12. 系统时钟:内部RC32MHZ,波特率38400 (32M 时钟时,波特率误差较小)
  13. */
  14. //使用UARTD0  定义下载串口
  15. #define USART      USARTD0
  16. #define USART_PORT PORTD
  17. #define USART_TX   PIN3_bm
  18. #define USART_RX   PIN2_bm

  19. // SPM_PAGESIZE/PROG_START 已经在avr/io.h里面定义了

  20. #define BUFSIZE  SPM_PAGESIZE
  21. //用户程序起始地
  22. #define PROG_START  PROGMEM_START

  23. //串口打印字符
  24. #define borad_massage "Xmega Bootload Programer Is Runing..."
  25. #define MCU_massage   "Replant by chiplab7.com(lisn3188@163.com)"

  26. //----------------------------------------------------------------------------
  27. //接收缓冲区
  28. unsigned char buf[BUFSIZE];
  29. unsigned char ReadBuffer[BUFSIZE];
  30. unsigned int bufptr, pagptr;
  31. unsigned char ch, cl;
  32. //当前Flash地址指针
  33. unsigned long int FlashAddr;
  34. //----------------------------------------------------------------------------
  35. void putstr(const char *str);
  36. void repay_sysclk();
  37. void Timer_initial(void);
  38. void Timer_Stop(void);
  39. //----------------------------------------------------------------------------
  40. //更新一个Flash页
  41. void write_one_page(unsigned char *buf)
  42. {
  43.     //数据填入Flash缓冲页
  44.         /* Load the flashbuffer with the test buffer. */
  45.         SP_LoadFlashPage(buf);
  46.                 /* Perform page erase. *///将缓冲页数据写入一个Flash页
  47.         SP_EraseWriteApplicationPage(FlashAddr);
  48.         /* Wait for NVM to finish. */
  49.         SP_WaitForSPM();//等待页编程完成                     
  50. }
  51. //----------------------------------------------------------------------------
  52. //跳转到用户程序
  53. void quit()
  54. {
  55.   //SP_LockSPM();                            //locks the SPM instruction 如果这个开启,那么一但执行,bootloader只能等待下一个复位才能对flash写
  56.   putstr(msg7);                             //提示进入Application
  57.   repay_sysclk();                            //把系统时钟返回为内部2M RC (XMEGA 复位后系统运行在内部2M RC )
  58.   (*((void(*)(void))PROG_START))();            //跳转,这样比'jmp 0'节省空间
  59. }
  60. //----------------------------------------------------------------------------
  61. //串口初始化38400,8,N,1,@32M CPU时钟
  62. void USART_initial(void)
  63. {
  64.         /* (TXD) as output. */
  65.         USART_PORT.DIRSET = USART_TX;
  66.         /* (RXD) as input. */
  67.         USART_PORT.DIRCLR = USART_RX;
  68.         /* USART, 8 Data bits, No Parity, 1 Stop bit. */
  69.         USART_Format_Set(&USART, USART_CHSIZE_8BIT_gc, USART_PMODE_DISABLED_gc, 0);
  70.         /* Set Baudrate to 38400 bps:
  71.          * Use the default I/O clock fequency that is 32 MHz.
  72.          * Do not use the baudrate scale factor
  73.          * Baudrate select = (1/(16*(((I/O clock frequency)/Baudrate)-1)
  74.          *                 = 25
  75.          */
  76.         USART_Baudrate_Set(&USART, 25 , 0);  //38400
  77.         /* Enable both RX and TX. */
  78.         USART_Rx_Enable(&USART);
  79.         USART_Tx_Enable(&USART);
  80. }
  81. //----------------------------------------------------------------------------
  82. //写入数据到串口
  83. void WriteCom(unsigned char data)
  84. {
  85.                  do{
  86.                 /* Wait until it is possible to put data into TX data register.
  87.                  * NOTE: If TXDataRegister never becomes empty this will be a DEADLOCK. */
  88.                 }while(!USART_IsTXDataRegisterEmpty(&USART));
  89.                 USART_PutChar(&USART, data);
  90. }
  91. //----------------------------------------------------------------------------
  92. //发送字符串并添加回车
  93. void putstr(const char *str)
  94. {
  95.   while(*str)
  96.     WriteCom(*str++);

  97.   WriteCom(0x0D);
  98.   WriteCom(0x0A);  //发送回车
  99. }
  100. //----------------------------------------------------------------------------
  101. //等待串口数据
  102. unsigned char WaitCom(void)
  103. {
  104.   do{
  105.   /* Wait until data received */
  106.   }while(!USART_IsRXComplete(&USART));
  107.   return(USART_GetChar(&USART));
  108. }
  109. //----------------------------------------------------------------------------
  110. //计算CRC校验:1021
  111. void crc16(uint8_t *ptr, int count)
  112. {
  113.         int crc = 0;
  114.         char i;
  115.         
  116.         while (--count >= 0)
  117.         {
  118.                 crc = crc ^ (int) *ptr++ << 8;
  119.                 i = 8;
  120.                 do
  121.                 {
  122.                         if (crc & 0x8000)
  123.                         crc = crc << 1 ^ 0x1021;
  124.                         else
  125.                         crc = crc << 1;
  126.                 } while(--i);
  127.         }
  128.         //return (crc);
  129.         ch = crc / 256;
  130.         cl = crc % 256;       //已定义全局变量
  131. }
  132. //设置系统时钟 为内部RC 32M
  133. //----------------------------------------------------------------------------
  134. void sysclk_initial()
  135. {
  136.                 /*  Enable internal 32 MHz ring oscillator and wait until it's
  137.                  *  stable. Divide clock by two with the prescaler C and set the
  138.                  *  32 MHz ring oscillator as the main clock source.
  139.                  */
  140.                 CLKSYS_Enable( OSC_RC32MEN_bm );
  141.                 CLKSYS_Prescalers_Config( CLK_PSADIV_1_gc, CLK_PSBCDIV_1_2_gc );  //Pc为2分频,T0工作在16M
  142.                 do {} while ( CLKSYS_IsReady( OSC_RC32MRDY_bm ) == 0 );
  143.                 CLKSYS_Main_ClockSource_Select( CLK_SCLKSEL_RC32M_gc );
  144. }
  145. //设置系统时钟 为内部RC 2M
  146. //----------------------------------------------------------------------------
  147. void repay_sysclk()
  148. {
  149.                 /*  Select 2 MHz RC oscillator as main clock source and diable
  150.                  *  unused clock.
  151.                  */
  152.                 do {} while ( CLKSYS_IsReady( OSC_RC2MRDY_bm ) == 0 );
  153.                 CLKSYS_Prescalers_Config( CLK_PSADIV_1_gc, CLK_PSBCDIV_1_1_gc );  
  154.                 CLKSYS_Main_ClockSource_Select( CLK_SCLKSEL_RC2M_gc );
  155.                 CLKSYS_Disable( OSC_PLLEN_bm );  //关掉PLL
  156. }
  157. //主程序
  158. //----------------------------------------------------------------------------
  159. int main(void)
  160. {
  161. unsigned char cnt,get_char,signal=0;
  162. unsigned char packNO;
  163. unsigned char crch, crcl;
  164. unsigned int i;

  165. sysclk_initial();   //设定系统时钟

  166. #if LED_En
  167. LED_initial();LED1_ON();
  168. #endif

  169. USART_initial();

  170. putstr((void*)borad_massage); //向串口打印字符
  171. putstr((void*)MCU_massage);

  172. #if Test_The_Uart  //如果置位,则是测试串口 发什么,回什么

  173. while(1){ //hold 用户可以通过串口助手等工具发送数据,看串口是否正常工作 38400 n,8,1
  174. get_char=WaitCom(); //接收数据
  175. WriteCom(get_char); //回送数据
  176. }
  177. #else  //不是测试串口,则编译bootloader
  178.   Timer_initial(); //初始化时间 200MS
  179. //---------------------------------------------
  180. //提示等待密码
  181.   putstr(msg1);
  182.   cnt = TimeOutCnt;
  183.   cl = 0;
  184.   while(1)
  185.   {
  186.     if(TC_GetOverflowFlag(&TCC0))    //T1溢出
  187.     {
  188.       TC_ClearOverflowFlag(&TCC0);

  189.       if(cl == CONNECTCNT)      //判断连接密码
  190.         break;                  //密码正确,跳出本次循环
  191. #if LED_En
  192.       LED1_T();                 //LED指示状态 闪动
  193. #endif
  194.       cnt--;
  195.       if(cnt == 0)              //连接超时
  196.       {
  197.         putstr(msg2);           //提示超时
  198.         quit();                 //退出bootloader
  199.       }
  200.     }

  201.     if(USART_IsRXComplete(&USART))             //接收到连接密码
  202.     {
  203.           get_char=USART_GetChar(&USART);
  204.       if( get_char== ConnectKey[cl])           //比较密码
  205.         cl++;
  206.       else
  207.         cl = 0;
  208.     }
  209.   }
  210.   putstr(msg3);                 //提示等待接收文件
  211. //进入等二环节
  212. //密码对上了,准备接收数据
  213. //------------------------------------------------------------------------------
  214. //每个时隙向PC机发送一个控制字符“C”,等待控制字〈soh〉
  215.   cnt = TimeOutCntC;
  216.   signal=0;
  217.   while(signal==0)
  218.   {
  219.     if(TC_GetOverflowFlag(&TCC0))  //T1溢出
  220.     {
  221.       TC_ClearOverflowFlag(&TCC0);
  222.       WriteCom(XMODEM_RWC) ;    //发送 "C"
  223. #if LED_En
  224.       LED1_T();                 //LED指示状态 闪动
  225. #endif
  226.       cnt--;
  227.       if(cnt == 0)              //超时
  228.       {
  229.         putstr(msg2);           //提示超时
  230.         quit();                 //退出 bootloader
  231.       }
  232.     }
  233.     if(USART_IsRXComplete(&USART)) //收到字符
  234.     {
  235.           get_char=USART_GetChar(&USART);
  236.       if(get_char == XMODEM_SOH)  //XMODEM命令开始
  237.         {signal=1;break;}
  238.     }
  239.   }
  240. //已经收到一个起启字符了 开始更新Application
  241. //----------------------------------------------------------------
  242.   Timer_Stop(); //关闭定时器1
  243.   //开始接受数据
  244.   packNO = 0; //包序号
  245.   bufptr = 0;
  246.   cnt = 0;
  247.   FlashAddr = 0;       //初始地址设置为0
  248.   do
  249.   {
  250.     packNO++;          //包数量自增
  251.     ch =  WaitCom();                          //获取包序号
  252.     cl = ~WaitCom();                          //包序号的反码,XMODEM帧格式定义的
  253.     if ((packNO == ch) && (packNO == cl))    //包序号对上了!
  254.     {
  255.       for(i = BUFFERSIZE; i > 0; i--)      //接收完整一帧数据
  256.       {
  257.         buf[bufptr++] = WaitCom();
  258.       }
  259.       crch = WaitCom();                       //获取校验字节
  260.       crcl = WaitCom();
  261.       //crc16(&buf[bufptr - BUFFERSIZE]);       //计算校验
  262.           crc16(&buf[0],128);          //计算校验
  263.       if((crch == ch) && (crcl == cl))
  264.       {                                       //校验成功
  265. #if BootStart
  266.         if(FlashAddr < BootStart)             //避免写入Boot区 bootloader 不能自杀
  267.         {
  268. #endif
  269.                                             //后面的条件编译太多了,看得不太明白
  270. #if BUFFERSIZE <= SPM_PAGESIZE
  271.           if(bufptr >= SPM_PAGESIZE)          //缓冲区满,写入数据;否则继续接收
  272.           {                                   //接收多个帧,写入一页
  273.             write_one_page(buf);              //写入缓冲区内容到Flash中
  274.             FlashAddr += SPM_PAGESIZE;        //修改Flash页地址
  275.             bufptr = 0;
  276.           }
  277. #else
  278.           while(bufptr > 0)                   //接收一帧,写入多个页面
  279.           {
  280.             write_one_page(&buf[BUFSIZE - bufptr]);
  281.             FlashAddr += SPM_PAGESIZE;        //修改Flash页地址
  282.             bufptr -= SPM_PAGESIZE;
  283.           }
  284. #endif

  285. #if BootStart
  286.         }
  287.         else                                  //超过BootStart范围,忽略写操作
  288.         {
  289.           bufptr = 0;                         //重置接收指针
  290.         }
  291. #endif

  292. //读取写入的Flash内容并和缓冲区的内容做比较
  293. #if (ChipCheck > 0) && (BootStart > 0)
  294. #if (BUFFERSIZE < SPM_PAGESIZE)
  295.         if((bufptr == 0) && (FlashAddr < BootStart))
  296. #else
  297.         if(FlashAddr < BootStart)
  298. #endif
  299.         {
  300.           //boot_rww_enable();                  //允许读用户程序
  301.           cl = 1;                             //清除错误标志位
  302.                   SP_ReadFlashPage(ReadBuffer, FlashAddr-BUFSIZE);        //----------------------
  303.           for(pagptr = 0; pagptr < BUFSIZE; pagptr++)
  304.           {
  305.             if(ReadBuffer[pagptr] != buf[pagptr])
  306.             {
  307.               cl = 0;                         //设置错误标志位
  308.               break;
  309.             }
  310.           }
  311.           if(cl)                              //校验正确,发送正常反馈
  312.           {
  313.             WriteCom(XMODEM_ACK);
  314.                         
  315.             cnt = 0;
  316.           }
  317.           else
  318.           {
  319.                     
  320.             WriteCom(XMODEM_NAK);             //校验错误,请求重发
  321.             cnt++;                            //错误计数加1
  322.                         
  323.             FlashAddr -= BUFSIZE;             //修正FlashAddr地址
  324.           }
  325.         }
  326.         else                                  //不校验Boot区,直接发送正确回应
  327.         {
  328.           WriteCom(XMODEM_ACK);
  329.           cnt = 0;
  330.         }
  331. #else
  332.         WriteCom(XMODEM_ACK);                 //不校验,直接发送正确响应
  333.         cnt = 0;
  334. #endif

  335. #if LED_En
  336.        Display_Proess(packNO);                //LED提示正在升级 这里可以通过修改 LED.h 里面的程序达到不同的效果
  337. #endif

  338.       }
  339.       else //CRC
  340.       {
  341.         WriteCom(XMODEM_NAK);                 //要求重发数据
  342.         cnt++;   //出错计数 ++  因为没有通过效验                           
  343.       }
  344.     }
  345.     else //PackNo  包序号对不上!
  346.     {
  347.       WriteCom(XMODEM_NAK);                   //要求重发数据
  348.       cnt++;     //出错计数 ++  因为号的序号对不上
  349.     }

  350.     if(cnt > 3)                               //连续出错次数太多,中止升级
  351.       break;
  352.   }
  353.   while(WaitCom() != XMODEM_EOT);
  354.   WriteCom(XMODEM_ACK);
  355.   
  356. #if LED_En
  357.   Led_off();                             //LED 指示升级完成 LED灭
  358. #endif

  359. #if VERBOSE
  360.   if(cnt == 0)   //没有错误!
  361.   {
  362.     //升级成功
  363.     putstr(msg4);                             //提示升级成功
  364.   }
  365.   else
  366.   {
  367.     //升级失败
  368.     putstr(msg5);                             //提示升级失败

  369. #if (BootStart > 0)
  370.     (*((void(*)(void))(BootStart)))();        //跳转到 bootloader
  371. #endif
  372. #endif
  373.   }
  374.   quit();                                     //退出bootloader
  375.   return 0;

  376. #endif
  377. }

复制代码

友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
15条回答
zwl773993221
2019-03-26 22:45
我和版主使用的同一套代码,修改为xmega64d4的bootloader,我最近几次的仿真结果是:
packNO = 1;
CH = 0X41(0x41正好是密码的第一个字节)

经过多次仿真,我发现是AVRBUD工具,在发送完密码后,我板子已经解析,板子然后发送"C"字符,AVRUBD工具接收到"C",然后板子等待AVRUBN工具发送XMODEM协议的SOH(就是0x01,XMODEM的开始命令),然而AVRUBD工具还一直在发送密码,始终不发送XMODEM的SOH,不知道为什么

一周热门 更多>