网上的通用xmega的bootloader移植出来的,但是发送XModem并不能完成升级。求指教!
- #include "usart.h"
- #include "bootcfg.h"
- #include "sp_driver.h"
- #include "XMODEM.h"
- #include "LED.h"
- #include "timer.h"
- #include "clksys_driver.h"
- #define Test_The_Uart 1 //如果这个标志为1那么程序只发送字符,用于测试串口 正常设置为0
- /*
- Xmega Bootloaber 移植AVRUBD
- 上位机,AVRUDB
- 系统时钟:内部RC32MHZ,波特率38400 (32M 时钟时,波特率误差较小)
- */
- //使用UARTD0 定义下载串口
- #define USART USARTD0
- #define USART_PORT PORTD
- #define USART_TX PIN3_bm
- #define USART_RX PIN2_bm
- // SPM_PAGESIZE/PROG_START 已经在avr/io.h里面定义了
- #define BUFSIZE SPM_PAGESIZE
- //用户程序起始地
- #define PROG_START PROGMEM_START
- //串口打印字符
- #define borad_massage "Xmega Bootload Programer Is Runing..."
- #define MCU_massage "Replant by chiplab7.com(lisn3188@163.com)"
- //----------------------------------------------------------------------------
- //接收缓冲区
- unsigned char buf[BUFSIZE];
- unsigned char ReadBuffer[BUFSIZE];
- unsigned int bufptr, pagptr;
- unsigned char ch, cl;
- //当前Flash地址指针
- unsigned long int FlashAddr;
- //----------------------------------------------------------------------------
- void putstr(const char *str);
- void repay_sysclk();
- void Timer_initial(void);
- void Timer_Stop(void);
- //----------------------------------------------------------------------------
- //更新一个Flash页
- void write_one_page(unsigned char *buf)
- {
- //数据填入Flash缓冲页
- /* Load the flashbuffer with the test buffer. */
- SP_LoadFlashPage(buf);
- /* Perform page erase. *///将缓冲页数据写入一个Flash页
- SP_EraseWriteApplicationPage(FlashAddr);
- /* Wait for NVM to finish. */
- SP_WaitForSPM();//等待页编程完成
- }
- //----------------------------------------------------------------------------
- //跳转到用户程序
- void quit()
- {
- //SP_LockSPM(); //locks the SPM instruction 如果这个开启,那么一但执行,bootloader只能等待下一个复位才能对flash写
- putstr(msg7); //提示进入Application
- repay_sysclk(); //把系统时钟返回为内部2M RC (XMEGA 复位后系统运行在内部2M RC )
- (*((void(*)(void))PROG_START))(); //跳转,这样比'jmp 0'节省空间
- }
- //----------------------------------------------------------------------------
- //串口初始化38400,8,N,1,@32M CPU时钟
- void USART_initial(void)
- {
- /* (TXD) as output. */
- USART_PORT.DIRSET = USART_TX;
- /* (RXD) as input. */
- USART_PORT.DIRCLR = USART_RX;
- /* USART, 8 Data bits, No Parity, 1 Stop bit. */
- USART_Format_Set(&USART, USART_CHSIZE_8BIT_gc, USART_PMODE_DISABLED_gc, 0);
- /* Set Baudrate to 38400 bps:
- * Use the default I/O clock fequency that is 32 MHz.
- * Do not use the baudrate scale factor
- * Baudrate select = (1/(16*(((I/O clock frequency)/Baudrate)-1)
- * = 25
- */
- USART_Baudrate_Set(&USART, 25 , 0); //38400
- /* Enable both RX and TX. */
- USART_Rx_Enable(&USART);
- USART_Tx_Enable(&USART);
- }
- //----------------------------------------------------------------------------
- //写入数据到串口
- void WriteCom(unsigned char data)
- {
- do{
- /* Wait until it is possible to put data into TX data register.
- * NOTE: If TXDataRegister never becomes empty this will be a DEADLOCK. */
- }while(!USART_IsTXDataRegisterEmpty(&USART));
- USART_PutChar(&USART, data);
- }
- //----------------------------------------------------------------------------
- //发送字符串并添加回车
- void putstr(const char *str)
- {
- while(*str)
- WriteCom(*str++);
- WriteCom(0x0D);
- WriteCom(0x0A); //发送回车
- }
- //----------------------------------------------------------------------------
- //等待串口数据
- unsigned char WaitCom(void)
- {
- do{
- /* Wait until data received */
- }while(!USART_IsRXComplete(&USART));
- return(USART_GetChar(&USART));
- }
- //----------------------------------------------------------------------------
- //计算CRC校验:1021
- void crc16(uint8_t *ptr, int count)
- {
- int crc = 0;
- char i;
-
- while (--count >= 0)
- {
- crc = crc ^ (int) *ptr++ << 8;
- i = 8;
- do
- {
- if (crc & 0x8000)
- crc = crc << 1 ^ 0x1021;
- else
- crc = crc << 1;
- } while(--i);
- }
- //return (crc);
- ch = crc / 256;
- cl = crc % 256; //已定义全局变量
- }
- //设置系统时钟 为内部RC 32M
- //----------------------------------------------------------------------------
- void sysclk_initial()
- {
- /* Enable internal 32 MHz ring oscillator and wait until it's
- * stable. Divide clock by two with the prescaler C and set the
- * 32 MHz ring oscillator as the main clock source.
- */
- CLKSYS_Enable( OSC_RC32MEN_bm );
- CLKSYS_Prescalers_Config( CLK_PSADIV_1_gc, CLK_PSBCDIV_1_2_gc ); //Pc为2分频,T0工作在16M
- do {} while ( CLKSYS_IsReady( OSC_RC32MRDY_bm ) == 0 );
- CLKSYS_Main_ClockSource_Select( CLK_SCLKSEL_RC32M_gc );
- }
- //设置系统时钟 为内部RC 2M
- //----------------------------------------------------------------------------
- void repay_sysclk()
- {
- /* Select 2 MHz RC oscillator as main clock source and diable
- * unused clock.
- */
- do {} while ( CLKSYS_IsReady( OSC_RC2MRDY_bm ) == 0 );
- CLKSYS_Prescalers_Config( CLK_PSADIV_1_gc, CLK_PSBCDIV_1_1_gc );
- CLKSYS_Main_ClockSource_Select( CLK_SCLKSEL_RC2M_gc );
- CLKSYS_Disable( OSC_PLLEN_bm ); //关掉PLL
- }
- //主程序
- //----------------------------------------------------------------------------
- int main(void)
- {
- unsigned char cnt,get_char,signal=0;
- unsigned char packNO;
- unsigned char crch, crcl;
- unsigned int i;
- sysclk_initial(); //设定系统时钟
- #if LED_En
- LED_initial();LED1_ON();
- #endif
- USART_initial();
- putstr((void*)borad_massage); //向串口打印字符
- putstr((void*)MCU_massage);
- #if Test_The_Uart //如果置位,则是测试串口 发什么,回什么
- while(1){ //hold 用户可以通过串口助手等工具发送数据,看串口是否正常工作 38400 n,8,1
- get_char=WaitCom(); //接收数据
- WriteCom(get_char); //回送数据
- }
- #else //不是测试串口,则编译bootloader
- Timer_initial(); //初始化时间 200MS
- //---------------------------------------------
- //提示等待密码
- putstr(msg1);
- cnt = TimeOutCnt;
- cl = 0;
- while(1)
- {
- if(TC_GetOverflowFlag(&TCC0)) //T1溢出
- {
- TC_ClearOverflowFlag(&TCC0);
- if(cl == CONNECTCNT) //判断连接密码
- break; //密码正确,跳出本次循环
- #if LED_En
- LED1_T(); //LED指示状态 闪动
- #endif
- cnt--;
- if(cnt == 0) //连接超时
- {
- putstr(msg2); //提示超时
- quit(); //退出bootloader
- }
- }
- if(USART_IsRXComplete(&USART)) //接收到连接密码
- {
- get_char=USART_GetChar(&USART);
- if( get_char== ConnectKey[cl]) //比较密码
- cl++;
- else
- cl = 0;
- }
- }
- putstr(msg3); //提示等待接收文件
- //进入等二环节
- //密码对上了,准备接收数据
- //------------------------------------------------------------------------------
- //每个时隙向PC机发送一个控制字符“C”,等待控制字〈soh〉
- cnt = TimeOutCntC;
- signal=0;
- while(signal==0)
- {
- if(TC_GetOverflowFlag(&TCC0)) //T1溢出
- {
- TC_ClearOverflowFlag(&TCC0);
- WriteCom(XMODEM_RWC) ; //发送 "C"
- #if LED_En
- LED1_T(); //LED指示状态 闪动
- #endif
- cnt--;
- if(cnt == 0) //超时
- {
- putstr(msg2); //提示超时
- quit(); //退出 bootloader
- }
- }
- if(USART_IsRXComplete(&USART)) //收到字符
- {
- get_char=USART_GetChar(&USART);
- if(get_char == XMODEM_SOH) //XMODEM命令开始
- {signal=1;break;}
- }
- }
- //已经收到一个起启字符了 开始更新Application
- //----------------------------------------------------------------
- Timer_Stop(); //关闭定时器1
- //开始接受数据
- packNO = 0; //包序号
- bufptr = 0;
- cnt = 0;
- FlashAddr = 0; //初始地址设置为0
- do
- {
- packNO++; //包数量自增
- ch = WaitCom(); //获取包序号
- cl = ~WaitCom(); //包序号的反码,XMODEM帧格式定义的
- if ((packNO == ch) && (packNO == cl)) //包序号对上了!
- {
- for(i = BUFFERSIZE; i > 0; i--) //接收完整一帧数据
- {
- buf[bufptr++] = WaitCom();
- }
- crch = WaitCom(); //获取校验字节
- crcl = WaitCom();
- //crc16(&buf[bufptr - BUFFERSIZE]); //计算校验
- crc16(&buf[0],128); //计算校验
- if((crch == ch) && (crcl == cl))
- { //校验成功
- #if BootStart
- if(FlashAddr < BootStart) //避免写入Boot区 bootloader 不能自杀
- {
- #endif
- //后面的条件编译太多了,看得不太明白
- #if BUFFERSIZE <= SPM_PAGESIZE
- if(bufptr >= SPM_PAGESIZE) //缓冲区满,写入数据;否则继续接收
- { //接收多个帧,写入一页
- write_one_page(buf); //写入缓冲区内容到Flash中
- FlashAddr += SPM_PAGESIZE; //修改Flash页地址
- bufptr = 0;
- }
- #else
- while(bufptr > 0) //接收一帧,写入多个页面
- {
- write_one_page(&buf[BUFSIZE - bufptr]);
- FlashAddr += SPM_PAGESIZE; //修改Flash页地址
- bufptr -= SPM_PAGESIZE;
- }
- #endif
- #if BootStart
- }
- else //超过BootStart范围,忽略写操作
- {
- bufptr = 0; //重置接收指针
- }
- #endif
- //读取写入的Flash内容并和缓冲区的内容做比较
- #if (ChipCheck > 0) && (BootStart > 0)
- #if (BUFFERSIZE < SPM_PAGESIZE)
- if((bufptr == 0) && (FlashAddr < BootStart))
- #else
- if(FlashAddr < BootStart)
- #endif
- {
- //boot_rww_enable(); //允许读用户程序
- cl = 1; //清除错误标志位
- SP_ReadFlashPage(ReadBuffer, FlashAddr-BUFSIZE); //----------------------
- for(pagptr = 0; pagptr < BUFSIZE; pagptr++)
- {
- if(ReadBuffer[pagptr] != buf[pagptr])
- {
- cl = 0; //设置错误标志位
- break;
- }
- }
- if(cl) //校验正确,发送正常反馈
- {
- WriteCom(XMODEM_ACK);
-
- cnt = 0;
- }
- else
- {
-
- WriteCom(XMODEM_NAK); //校验错误,请求重发
- cnt++; //错误计数加1
-
- FlashAddr -= BUFSIZE; //修正FlashAddr地址
- }
- }
- else //不校验Boot区,直接发送正确回应
- {
- WriteCom(XMODEM_ACK);
- cnt = 0;
- }
- #else
- WriteCom(XMODEM_ACK); //不校验,直接发送正确响应
- cnt = 0;
- #endif
- #if LED_En
- Display_Proess(packNO); //LED提示正在升级 这里可以通过修改 LED.h 里面的程序达到不同的效果
- #endif
- }
- else //CRC
- {
- WriteCom(XMODEM_NAK); //要求重发数据
- cnt++; //出错计数 ++ 因为没有通过效验
- }
- }
- else //PackNo 包序号对不上!
- {
- WriteCom(XMODEM_NAK); //要求重发数据
- cnt++; //出错计数 ++ 因为号的序号对不上
- }
- if(cnt > 3) //连续出错次数太多,中止升级
- break;
- }
- while(WaitCom() != XMODEM_EOT);
- WriteCom(XMODEM_ACK);
-
- #if LED_En
- Led_off(); //LED 指示升级完成 LED灭
- #endif
- #if VERBOSE
- if(cnt == 0) //没有错误!
- {
- //升级成功
- putstr(msg4); //提示升级成功
- }
- else
- {
- //升级失败
- putstr(msg5); //提示升级失败
- #if (BootStart > 0)
- (*((void(*)(void))(BootStart)))(); //跳转到 bootloader
- #endif
- #endif
- }
- quit(); //退出bootloader
- return 0;
- #endif
- }
复制代码
AVR的flash是128的倍数,应该不存在不足的情况吧。不足128字节的情况还没有处理过,如果是规定那样,接收到0x1a后丢弃就行了,或者将0x1A转为0xFF,0xFF是无效指令,这样写入Flash才不会出错。
如果数据接收不全,那边升级就会失败,只能重新升级了。
- for(i=0;i<128;i++)
- {
- if((XmodemBuffer[i]==XMODEM_EOF)&&(XmodemBuffer[i]==XmodemBuffer[127])) //如果最后需要发送的数据不足128个字节,用<eof>填满一个数据块。
- XmodemBuffer[i]=0xff;
- }
复制代码恩比如flash512 只收到一个块只有128 那后面的如何处理?
接收到1a后怎么判断是程序的一个1a 还是后面都是1a了
xmega不太清楚,AVR中是这样,Flash是需要块写入的,但是块的大小与AVR的型号有关,不是每种都一样,从16字节到128直接都有。所以需要等收完一个块后,计算校验后才能写入flash。
正常情况下,是不会出现数据不足128字节的。编译后生成的HEX文件,一般都会补足128字节。如果不足128字节,可以让上位机来补足,避免单片机去完成这个事情。毕竟底层的资源少。
问题解决!
boot代码里是 每次收满512个才往flash里写一次,因为xmega128的每个page就是512bytes
而串口助手只是会以128字节为单元去补满,导致最后一个不满512的page丢失。
希望能帮到后来人。
再次感谢dcexpert斑竹!
一周热门 更多>