Xmodem 协议封装,用于字库编码下载,软件升级
扫描二维码
随时随地手机看文章
使用Xmodem有一段时间了,使用起来移植性能不够,通过这次彻底抛离了底层通信部分,可以用在任何通信接口上面了,跟底层的通信已经无关了,使用了大量的回调,回调主要完成通信的收发,以及数据存储等功能,我目前主要使用在STM32 IAP升级(写入到内部flash),app升级(写入到外部flash W25Q128),字库以及各种编码下载(写入到外部flash W25Q128)。
//数据包格式比较简单
// Xmodem 包格式 // Byte1 Byte2 Byte3 Byte4~131 Byte132~133 // Start Of Hearder Packet Number ~(Packet Number) Packet Data 16-Bit CRC //1K-Xmodem 包格式 // Byte1 Byte2 Byte3 Byte4~1027 Byte1028~1029 // Start Of Hearder Packet Number ~(Packet Number) Packet Data 16-Bit CRC
//c文件
/************************************************************************************************************* * 文件名: Xmodem.c * 功能: Xmodem协议实现 * 作者: cp1300@139.com * 创建时间: 2014-08-19 * 最后修改时间: 2017-09-05 * 详细: 使用串口实现Xmodem协议应用层 2017-03-23:修改采用回调以及句柄方式,可移植性更强 2017-04-04:增加发送延时 2017-09-05:发送NAK与结束增加延时,防止发送过快,并且修改如果通信超时则发送NAK,大大提高通信可靠性 2017-09-06:修复第一包数据丢失问题,增加数据包id重复检查,大大提高数据的可靠性,防止重复的数据包 *************************************************************************************************************/ #include "system.h" #include "usart.h" #include "main.h" #include "Xmodem.h" #if SYS_WDG_EN_ #include "wdg.h" #endif //调试开关 #define XMODEM_DBUG 0 #if XMODEM_DBUG #include "system.h" #define xmodem_debug(format,...) uart_printf(format,##__VA_ARGS__) #else #define xmodem_debug(format,...) / / #endif //XMODEM_DBUG // Xmodem 包格式 // Byte1 Byte2 Byte3 Byte4~131 Byte132~133 // Start Of Hearder Packet Number ~(Packet Number) Packet Data 16-Bit CRC //1K-Xmodem 包格式 // Byte1 Byte2 Byte3 Byte4~1027 Byte1028~1029 // Start Of Hearder Packet Number ~(Packet Number) Packet Data 16-Bit CRC /************************************************************************************************************************* * 函数 : bool XMODE_Init(XMODE_HANDLE *pHandle, bool (* pSendData)(u8 *pDataBuff, u16 DataLen), int (* pReadData)(u8 **pDataBuff, u8 ByteTimeOut, u16 TimeOut, u16 *pReceiveDelay), void (*pClearRxData)(void), bool (*pReceivePacket)(u8 *pPackData,u16 PackSize,u32 RecDataSize), bool (*pTransEnd)(bool isTransOK, u32 RecDataSize)) * 功能 : 初始化XMODE * 参数 : pHandle:句柄; pSendData:发送回调函数(pDataBuff:发送数据缓冲区,DataLen:发送数据长度) pReadData:接收数据回调函数,会等待直到数据被写入到接收缓冲区(pDataBuff:接收数据缓冲区,ByteTimeOut:等待的字节超时时间,单位ms,TimeOut:数据包超时时间,单位ms),pReceiveDelay:返回接收延时,单位ms pClearRxData:清除接收数据缓冲区回调函数 pReceivePacket:收到一包数据后的回调函数,(返回false会退出通信)用于应用层对数据进行存储(pPackData:接收到的数据包,PackSize:包大小;RecDataSize:已经接收的数据包大小,不含当前包的数据) pTransEnd:传输结束时回调函数,(返回false会退出通信)用于传输结束的处理(isTransOK:TRUE,传输正常完成,FALSE:传输错误结束;RecDataSize:总共收到的数据大小) * 返回 : TRUE:初始化成;FALSE:初始化错误 * 依赖 : 通信接口 * 作者 : cp1300@139.com * 时间 : 2013-05-08 * 最后修改时间 : 2017-03-23 * 说明 : XMODE通信协议通信接口初始化,默认超时时间为等待启动30秒,数据包超时2秒 ; //清除接收数据缓冲区 *************************************************************************************************************************/ bool XMODE_Init(XMODE_HANDLE *pHandle,bool (* pSendData)(u8 *pDataBuff, u16 DataLen), int (* pReadData)(u8 **pDataBuff, u8 ByteTimeOut, u16 TimeOut, u16 *pReceiveDelay), void (*pClearRxData)(void),bool (*pReceivePacket)(u8 *pPackData,u16 PackSize,u32 RecDataSize),bool (*pTransEnd)(bool isTransOK, u32 RecDataSize)) { if(pHandle == NULL) return FALSE; //错误,无效的指针 pHandle->RecDataSize = 0; //接收到的数据大小 pHandle->RecPackCnt = 0; //接收到的数据包计数 pHandle->pSendData = pSendData; //发送回调指针, 发送数据回调函数 pHandle->pReadData = pReadData; //读取数据回调指针,>0返回接收到的数据长度,否则失败 pHandle->pClearRxData = pClearRxData; //清除接收数据缓冲区 pHandle->pReceivePacket = pReceivePacket; //收到数据包回调指针 pHandle->pTransEnd = pTransEnd; //传输结束时回调(可能是出错结束) pHandle->WaitStartTimeOutSer = 30; //等待启动传输超时时间 pHandle->PackTimeOutSer = 2; //数据包超时时间 pHandle->pXMODEM_128Pack = NULL; //128B数据包指针 pHandle->pXMODEM_1KPack = NULL; //1KB数据包指针 pHandle->TransRetry = 10; //失败重试次数,默认10次 pHandle->TxByteTimeUs = 0; //发送延时默认为0 return TRUE; } /************************************************************************************************************************* * 函数 : bool XMODE_SetTimeOut(XMODE_HANDLE *pHandle, u16 WaitStartTimeOutSer, u16 PackTimeOutSer, u8 TransRetry,u8 TxByteTimeUs) * 功能 : 设置XMODE超时时间 * 参数 : pHandle:句柄;WaitStartTimeOutSer:等待启动超时时间,单位秒钟;PackTimeOutSer:数据包超时时间,单位秒钟;TransRetry:出错重试次数,1-255次;TxByteTimeUs:发送字节延时 * 返回 : TRUE:初始化成;FALSE:初始化错误 * 依赖 : 通信接口 * 作者 : cp1300@139.com * 时间 : 2013-05-08 * 最后修改时间 : 2017-03-23 * 说明 : 设置超时时间 *************************************************************************************************************************/ bool XMODE_SetTimeOut(XMODE_HANDLE *pHandle, u16 WaitStartTimeOutSer, u16 PackTimeOutSer, u8 TransRetry,u8 TxByteTimeUs) { if(pHandle == NULL) return FALSE; //错误,无效的指针 pHandle->WaitStartTimeOutSer = WaitStartTimeOutSer; //等待启动传输超时时间 if(pHandle->WaitStartTimeOutSer < 1) pHandle->WaitStartTimeOutSer = 1; pHandle->PackTimeOutSer = PackTimeOutSer; //数据包超时时间 if(pHandle->PackTimeOutSer < 1) pHandle->PackTimeOutSer = 1; pHandle->TransRetry = TransRetry; //出错重试次数 if(pHandle->TransRetry < 1) pHandle->TransRetry = 1; pHandle->TxByteTimeUs = TxByteTimeUs; //发送字节延时,用于RS485接口,发送后需要进行延时 return TRUE; } /************************************************************************************************************************* * 函数 : int XMODEM_Start(XMODE_HANDLE *pHandle, u8 **pRxBuff) * 功能 : 发送启动请求 * 参数 : pHandle:句柄;pRxBuff:接收缓冲区(存放第一包数据) * 返回 : WaitStartTimeOutSer*10; //转换为100m单位 int len; if(pHandle == NULL) return FALSE; pHandle->DataBuff[0] = X_CRC_MODE; //采用CRC模式的校验请求头 while(TimeOut --) { pHandle->pSendData(pHandle->DataBuff,1); //发送请求信号 if(pHandle->TxByteTimeUs > 0) XMODEM_DelayMS((10*pHandle->TxByteTimeUs)/1000+1); //发送延时 pHandle->pClearRxData(); //清除接收 len = pHandle->pReadData(pRxBuff,10,100,NULL); //接收数据 if(len > 0) //等待接收 { pHandle->RecDataSize = 0; //接收到的数据大小清零 pHandle->RecPackCnt = 0; //接收到的数据包计数清零 return len; } #if SYS_WDG_EN_ IWDG_Feed(); //喂狗 #endif } return -1; } //发送ACK __inline void XMODEM_SendACK(XMODE_HANDLE *pHandle) { pHandle->DataBuff[0] = (u8)X_ACK; pHandle->pSendData(pHandle->DataBuff,1); //发送请求信号 if(pHandle->TxByteTimeUs > 0) XMODEM_DelayMS((10*pHandle->TxByteTimeUs)/1000+1); //发送延时 } //发送NAK __inline void XMODEM_SendNAK(XMODE_HANDLE *pHandle) { XMODEM_DelayMS(20); pHandle->DataBuff[0] = (u8)X_NAK; pHandle->pSendData(pHandle->DataBuff,1); //发送请求信号 if(pHandle->TxByteTimeUs > 0) XMODEM_DelayMS((10*pHandle->TxByteTimeUs)/1000+1); //发送延时 } //取消传输 __inline void XMODEM_CancelTran(XMODE_HANDLE *pHandle) { XMODEM_DelayMS(20); pHandle->DataBuff[0] = (u8)X_CAN; pHandle->pSendData(pHandle->DataBuff,1); //发送请求信号 if(pHandle->TxByteTimeUs > 0) XMODEM_DelayMS((10*pHandle->TxByteTimeUs)/1000+1); //发送延时 } //判断是否结束 __inline bool XMODEM_isTranEnd(u8 Data, XMODE_HANDLE *pHandle) { if(Data == X_EOT) return TRUE; else return FALSE; } /************************************************************************************************************************* * 函数 : u16 XMODEM_CRC16(u8 *pData, u16 DataLen) * 功能 : crc16校验 * 参数 : pData:数据缓冲区;DataLen:数据长度 * 返回 : crc16结果 * 依赖 : 通信接口 * 作者 : cp1300@139.com * 时间 : 2013-05-08 * 最后修改时间 : 2017-03-23 * 说明 : 用于通信数据校验,仅用于XMODEM,不可与modbus-rtu协议用的crc16混用(两者计算结果会不一致) 多项式码0x1021 *************************************************************************************************************************/ u16 XMODEM_CRC16(u8 *pData, u16 DataLen) { u16 crc = 0; char i; u16 j; for(j = 0;j < DataLen;j ++) { crc = crc ^ (int) *pData++ << 8; i = 8; do { if (crc & 0x8000) crc = crc << 1 ^ 0x1021; else crc = crc << 1; } while (--i); } return (crc); } /************************************************************************************************************************* * 函数 : u32 XMODEM_DownloadFile(XMODE_HANDLE *pHandle, u32 MaxDataSize) * 功能 : 使用XMODEM下载文件 * 参数 : pHandle:句柄;MaxDataSize:限制最大下载数据量 * 返回 : 0:错误;其它:接收的数据长度 * 依赖 : 底层 * 作者 : cp1300@139.com * 时间 : 2013-05-08 * 最后修改时间 : 2017-03-23 * 说明 : 使用CRC校验模式,支持128,1K数据包 *************************************************************************************************************************/ u32 XMODEM_DownloadFile(XMODE_HANDLE *pHandle, u32 MaxDataSize) { u16 crc16; u16 temp; u16 retry = 0; int len; bool isStart = FALSE; u8 *pRxBuff; u8 LastPackCnt = 0; //用于记录上一次包序号,每次包序号不能重复 len = XMODEM_Start(pHandle, &pRxBuff); //等待开始传输 if(len pClearRxData(); //清除接收缓冲区 len = pHandle->pReadData(&pRxBuff, 2,pHandle->PackTimeOutSer*1000,NULL); //接收数据 } isStart = FALSE; //第一次开始传输状态无效 if(len pXMODEM_128Pack = (XMODEM_128B_PACK *)pRxBuff; //128B数据包指针 pHandle->pXMODEM_1KPack = (XMODEM_1KB_PACK *)pRxBuff; //1KB数据包指针 switch(pHandle->pXMODEM_128Pack->X_Start) { case X_SOH: //128 { if(len < 128) { XMODEM_SendNAK(pHandle); //发送NAK retry++; } else { crc16 = XMODEM_CRC16(pHandle->pXMODEM_128Pack->X_PackData, 128); temp = pHandle->pXMODEM_128Pack->X_CRC16H; temp <pXMODEM_128Pack->X_CRC16L; if(crc16 != temp) //CRC校验错误,重传 { XMODEM_SendNAK(pHandle); //发送NAK retry++; } else { if(LastPackCnt!=pHandle->pXMODEM_128Pack->X_PackNum) //包序号不一样 { LastPackCnt = pHandle->pXMODEM_128Pack->X_PackNum; //记录上一次的包序号 if(pHandle->pReceivePacket(pHandle->pXMODEM_128Pack->X_PackData, 128, pHandle->RecDataSize)==FALSE) //收到数据包,调用回调 { //用户返回退出下载 if(pHandle->pTransEnd != NULL) //判断回调是否有效 { pHandle->pTransEnd(FALSE, pHandle->RecDataSize); //传输完成,调用回调,有错误 } XMODEM_CancelTran(pHandle); //发送结束传输 XMODEM_DelayMS(10); XMODEM_CancelTran(pHandle); //发送结束传输 XMODEM_DelayMS(500); xmodem_debug("用户取消下载!rn"); return 0; } pHandle->RecDataSize += 128; //接收到的数据大小增加 pHandle->RecPackCnt ++; //接收到的数据包计数增加 if(pHandle->RecDataSize > MaxDataSize) { if(pHandle->pTransEnd != NULL) //判断回调是否有效 { pHandle->pTransEnd(FALSE, pHandle->RecDataSize); //传输完成,调用回调,有错误 } XMODEM_CancelTran(pHandle); //发送结束传输 XMODEM_DelayMS(10); XMODEM_CancelTran(pHandle); //发送结束传输 XMODEM_DelayMS(500); xmodem_debug("文件下载失败,大小超出范围(%dB)!rn", MaxDataSize); return 0; } } else //故障,重复的数据包 { XMODEM_DelayMS(10); } XMODEM_SendACK(pHandle); //发送ACK响应 retry = 0; } } }break; case X_STX: //1k { if(len < 1024) { XMODEM_SendNAK(pHandle); //发送NAK retry++; } else { crc16 = XMODEM_CRC16(pHandle->pXMODEM_1KPack->X_PackData, 1024); temp = pHandle->pXMODEM_1KPack->X_CRC16H; temp <pXMODEM_1KPack->X_CRC16L; if(crc16 != temp) //CRC校验错误,重传 { XMODEM_SendNAK(pHandle); //发送NAK retry++; } else { if(LastPackCnt!=pHandle->pXMODEM_128Pack->X_PackNum) //包序号不一样 { LastPackCnt = pHandle->pXMODEM_128Pack->X_PackNum; //记录上一次的包序号 if(pHandle->pReceivePacket != NULL) //判断回调是否有效 { if(pHandle->pReceivePacket(pHandle->pXMODEM_1KPack->X_PackData, 1024, pHandle->RecDataSize)==FALSE) //收到数据包,调用回调 { //用户返回退出下载 if(pHandle->pTransEnd != NULL) //判断回调是否有效 { pHandle->pTransEnd(FALSE, pHandle->RecDataSize); //传输完成,调用回调,有错误 } XMODEM_CancelTran(pHandle); //发送结束传输 XMODEM_DelayMS(10); XMODEM_CancelTran(pHandle); //发送结束传输 XMODEM_DelayMS(500); xmodem_debug("用户取消下载!rn"); return 0; } } pHandle->RecDataSize += 1024; //接收到的数据大小增加 pHandle->RecPackCnt ++; //接收到的数据包计数增加 if(pHandle->RecDataSize > MaxDataSize) { if(pHandle->pTransEnd != NULL) //判断回调是否有效 { pHandle->pTransEnd(FALSE, pHandle->RecDataSize); //传输完成,调用回调,有错误 } XMODEM_CancelTran(pHandle); //发送结束传输 XMODEM_DelayMS(10); XMODEM_CancelTran(pHandle); //发送结束传输 XMODEM_DelayMS(500); xmodem_debug("文件下载失败,代码超出范围(%dB)!rn", MaxDataSize); return 0; } } else //故障,重复的数据包 { XMODEM_DelayMS(10); } XMODEM_SendACK(pHandle); //发送ACK响应 retry = 0; } } }break; case X_EOT: //传输结束,最后一包 { if(pHandle->pTransEnd != NULL) //判断回调是否有效 { if(pHandle->pTransEnd(TRUE, pHandle->RecDataSize) == FALSE) //传输完成,调用回调,有错误则退出 { XMODEM_CancelTran(pHandle); //发送结束传输 XMODEM_DelayMS(10); XMODEM_CancelTran(pHandle); //发送结束传输 XMODEM_DelayMS(500); xmodem_debug("用户取消下载!rn"); return 0; } } XMODEM_SendACK(pHandle); retry = 0; XMODEM_DelayMS(10); XMODEM_SendACK(pHandle); XMODEM_DelayMS(500); xmodem_debug("文件下载成功!rn"); return pHandle->RecDataSize; } default:XMODEM_SendNAK(pHandle); retry++;break; } } if(retry > pHandle->TransRetry) //重传过多 { if(pHandle->pTransEnd != NULL) //判断回调是否有效 { pHandle->pTransEnd(FALSE, pHandle->RecDataSize); //传输完成,调用回调,有错误 } XMODEM_CancelTran(pHandle); //取消传输 XMODEM_DelayMS(10); XMODEM_CancelTran(pHandle); //取消传输 XMODEM_DelayMS(500); xmodem_debug("下载失败,重试次数过多!rn"); return 0; } #if SYS_WDG_EN_ IWDG_Feed(); //喂狗 #endif } }
//.h文件
/************************************************************************************************************* * 文件名: Xmodem.h * 功能: Xmodem协议实现 * 作者: cp1300@139.com * 创建时间: 2014-08-19 * 最后修改时间: 2014-08-19 * 详细: 使用串口实现Xmodem协议应用层 *************************************************************************************************************/ #ifndef _X_MODEM_H_ #define _X_MODEM_H_ #include "system.h" #include "USART.h" #ifdef _UCOS_II_ //支持ucos操作系统,使用系统延时 #define XMODEM_DelayMS(x) OSTimeDlyHMSM(0,0,0,x) //延时ms,最大延时999ms #else #define XMODEM_DelayMS(x) Delay_MS(x) #endif //_UCOS_II_ //XMODEM 相关定义说明 #define X_SOH 0x01 // Xmodem数据头 #define X_STX 0x02 // 1K-Xmodem数据头 #define X_EOT 0x04 // 发送结束 #define X_ACK 0x06 // 认可响应 #define X_NAK 0x15 // 不认可响应 #define X_CAN 0x18 // 撤销传送 #define X_EOF 0x1A // 填充数据包 //128B数据包格式 typedef struct { u8 X_Start; u8 X_PackNum; u8 X_bPackNum; u8 X_PackData[128]; u8 X_CRC16H; u8 X_CRC16L; }XMODEM_128B_PACK; //1024B数据包格式 typedef struct { u8 X_Start; u8 X_PackNum; u8 X_bPackNum; u8 X_PackData[1024]; u8 X_CRC16H; u8 X_CRC16L; }XMODEM_1KB_PACK; typedef struct { u32 RecDataSize; //接收到的数据大小 u32 RecPackCnt; //接收到的数据包计数 bool (* pSendData)(u8 *pDataBuff, u16 DataLen); //发送回调指针, 发送数据回调函数 int (* pReadData)(u8 **pDataBuff, u8 ByteTimeOut, u16 TimeOut, u16 *pReceiveDelay); //读取数据回调指针,>0返回接收到的数据长度,否则失败 bool (*pReceivePacket)(u8 *pPackData,u16 PackSize,u32 RecDataSize); //收到数据包回调指针,返回false会退出传输 bool (*pTransEnd)(bool isTransOK, u32 RecDataSize); //传输结束回调指针,返回false会退出传输 void (*pClearRxData)(void); //清除接收数据缓冲区 XMODEM_128B_PACK *pXMODEM_128Pack; //128bit数据包格式 XMODEM_1KB_PACK *pXMODEM_1KPack; //1K数据包格式 u16 WaitStartTimeOutSer; //等待启动传输超时时间 u16 PackTimeOutSer; //数据包超时时间 u8 TxByteTimeUs; //发送字节延时,微秒,用于RS485通信延时 u8 DataBuff[2]; //分配的临时缓冲区 u8 TransRetry; //失败重试次数,默认10次 }XMODE_HANDLE; //启动传输校验模式 typedef enum { X_CRC_MODE = 'C', //传输使用CRC16校验模式 X_ACC_MODE = X_NAK, //传输使用累加校验模式 }XMODEM_START_MODE; //数据包大小 typedef enum { X_PACK128B = X_SOH, //128B数据包 X_PACK1kB = X_STX, //1KB数据包 }XMODEM_PACK_MODE; //XMODEM 通信初始化 bool XMODE_Init(XMODE_HANDLE *pHandle, bool (* pSendData)(u8 *pDataBuff, u16 DataLen), int (* pReadData)(u8 **pDataBuff, u8 ByteTimeOut, u16 TimeOut, u16 *pReceiveDelay), void (*pClearRxData)(void),bool (*pReceivePacket)(u8 *pPackData,u16 PackSize,u32 RecDataSize),bool (*pTransEnd)(bool isTransOK, u32 RecDataSize)); //XMODEM 超时设置 bool XMODE_SetTimeOut(XMODE_HANDLE *pHandle, u16 WaitStartTimeOutSer, u16 PackTimeOutSer, u8 TransRetry,u8 TxByteTimeUs); //XMODEM 下载数据 u32 XMODEM_DownloadFile(XMODE_HANDLE *pHandle, u32 MaxDataSize); #endif /*_X_MODEM_H_*/
下面是2个下载的例子,我主要是示意通信与存储接口格式,实际使用需要按照自己的平台进行移植
//例子1:下载程序到STM32内部flash,主要提供数据收发接口,以及存储接口示意,这些接口需要根据自己的平台做移植 /************************************************************************************************************* * 文件名: UpgradeBIOS.c * 功能: 升级BIOS相关 * 作者: cp1300@139.com * 创建时间: 2017-05-16 * 最后修改时间: 2017-05-16 * 详细: 使用xmodem直接刷STM32flash,如果失败了请不要重启,否则会无法进入系统 *************************************************************************************************************/ #include "system.h" #include "usart.h" #include "main.h" #include "xmodem.h" #include "UpgradeBIOS.h" #include "STM32Flash.h" #include "w25x16.h" #include "STM32_CRC.h" #include "rtu.h" #include "board.h" #if SYS_WDG_EN_ #include "wdg.h" #endif #include#define UP_PROGRAM_STM32_BIOS_ADDR STM32_FLASH_BASE //BIOS程序内部flash基址 static u32 UpgradeBiosDataSaveCnt = 0; //升级应用程序已经存储的数据大小 //升级文件接口XMODE句柄 XMODE_HANDLE UpgradeBIOSHandle; //发送数据接口 bool UpgradeBiosSendData(u8 DataBuff[], u16 DataLen) { UARTx_SendData(UART_PRINTF_CH, DataBuff, DataLen); return TRUE; } //接收数据接口 int UpgradeBiosReadData(u8 **pDataBuff,u8 ByteTimeOutMs, u16 TimeOutMs, u16 *pReceiveDelayMs) { u32 cnt = 0; u16 TempTime; UARTx_ClearRxCnt(UART_PRINTF_CH); //清除串口接收缓冲区,开始结束数据 if(ByteTimeOutMs < 1) ByteTimeOutMs = 1; //字节超时时间,2个帧之间的间隔最小时间 TimeOutMs /= ByteTimeOutMs; TimeOutMs += 1; TempTime = TimeOutMs; while(TimeOutMs --) { cnt = UARTx_GetRxCnt(UART_PRINTF_CH); OSTimeDlyHMSM(0,0,0,ByteTimeOutMs);; if((cnt > 0) && (cnt == UARTx_GetRxCnt(UART_PRINTF_CH))) { if(pReceiveDelayMs!=NULL) //需要返回延时 { *pReceiveDelayMs = (TempTime-TimeOutMs)*ByteTimeOutMs; } *pDataBuff = SysCommBuff; //接收缓冲区 return cnt; } #if SYS_WDG_EN_ IWDG_Feed(); //喂狗 #endif } return 0; } //清除接收缓冲区 void UpgradeBiosClearData(void) { UARTx_ClearRxCnt(UART_PRINTF_CH); //清除串口缓冲区 } //收到数据包回调-用于写数据到内部flash bool UpgradeBiosReceivePacketExitFlash(u8 *pPackData,u16 PackSize,u32 RecDataSize) { if((PackSize != 128) && (PackSize!=1024)) return FALSE; //xmodem只支持128字节或1024字节的数据包 //写入数据到内部flash STM32FLASH_Write(UP_PROGRAM_STM32_BIOS_ADDR+RecDataSize, (u16 *)pPackData, (PackSize+1)/2); UpgradeBiosDataSaveCnt += PackSize; return TRUE; } //传输结束时回调(可能是出错结束) bool UpgradeBiosTransEndtExitFlash(bool isTransOK, u32 RecDataSize) { if(isTransOK==FALSE) return FALSE; //失败返回 if(UpgradeBiosDataSaveCnt == RecDataSize) return TRUE; if(UpgradeBiosDataSaveCnt > RecDataSize) return FALSE; //存储的数据不能大于接收的数据 if((RecDataSize-UpgradeBiosDataSaveCnt) > 0) return FALSE; //不能有未存储的数据 return FALSE; //存储出错 } //使用XMODEM下载数据到内部flash //返回程序大小,如果失败了返回<=0 int XMODEM_DownloadFileToSTM32Flash(u32 MaxFileSize) { XMODE_Init(&UpgradeBIOSHandle, //句柄 UpgradeBiosSendData, //发送数据回调函数 UpgradeBiosReadData, //读取数据回调函数 UpgradeBiosClearData, //清除接收数据缓冲回调函数 UpgradeBiosReceivePacketExitFlash, //接收到数据包回调函数 UpgradeBiosTransEndtExitFlash //传输结束回调函数 ); UpgradeBiosDataSaveCnt = 0; //已经存储的数据大小清零 return XMODEM_DownloadFile(&UpgradeBIOSHandle, MaxFileSize); } //使能系统命令行 #if SYS_CMD_EN_ #include "cmd.h" #include "string.h" CMD_TYPE const CMD_UP_BIOS = {"UP BIOS", 0XD3476564, CMD_UpBIOS, "tt升级BIOS"}; //升级BIOS程序 //进入升级BIOS模式 void CMD_UpBIOS(char *pStr) { cmd_printf("已经进入升级BIOS模式,等待连接,超时10S!rn>"); cmd_printf("请在10S内进入Xmodem下载模式!rn>"); RTC_DisableInt(); //关闭RTC中断,防止唤醒后台任务 OSTaskSuspend(BACK_TASK_Prio); //挂起后台任务线程 OSTaskSuspend(LED_TASK_Prio); //挂起LED任务线程 OSTaskSuspend(GPRS_TASK_Prio); //挂起GPRS进程 OSTaskSuspend(COLL_TASK_Prio); //数据采集时间查询进程 OSTaskSuspend(OTHER_TASK_Prio); //OTHER OSTaskSuspend(MODBUS1_TASK_Prio); //MODBUS1 OSTaskSuspend(MODBUS2_TASK_Prio); //MODBUS2 OSTaskSuspend(KEY_TASK_Prio); //KEY OSTimeDlyHMSM(0,0,0,500); //延时500毫秒 if(XMODEM_DownloadFileToSTM32Flash(100*1024) == 0)//写入BIOS程序 { cmd_printf("升级BIOS失败,重启后将无法启动,建议重新升级!rn"); } else { cmd_printf("升级BIOS成功!rn"); } CMD_Help(NULL); OSTaskResume(LED_TASK_Prio); //恢复挂起LED任务线程 OSTaskResume(GPRS_TASK_Prio); //恢复挂起GPRS进程 OSTaskResume(COLL_TASK_Prio); //恢复数据采集时间查询进程 OSTaskResume(OTHER_TASK_Prio); //恢复OTHER OSTaskResume(MODBUS1_TASK_Prio); //恢复MODBUS1 OSTaskResume(MODBUS2_TASK_Prio); //恢复MODBUS2 OSTaskResume(KEY_TASK_Prio); //恢复KEY RTC_EnableInt(); //恢复RTC中断 } #endif //SYS_CMD_EN_ //例子2:下载字库编码到外部flash,使用的是SPI 接口flash,W25Q128,存储接口稍有不同,因为我每次将数据集齐4K才进行存储,这样可以提高存储效率,降低flash损耗,当然最后一包可能不足4K,会另外进行处理的。 /************************************************************************************************************* * 文件名: DownFont.c * 功能: 下载字库相关 * 作者: cp1300@139.com * 创建时间: 2017-03-29 * 最后修改时间: 2017-03-29 * 详细: 使用xmodem或tFileModem下载文件 *************************************************************************************************************/ #include "system.h" #include "usart.h" #include "main.h" #include "xmodem.h" #include "tFileModem.h" #include "upgrade.h" #include "STM32Flash.h" #include "STM32_CRC.h" #if SYS_WDG_EN_ #include "wdg.h" #endif #include#include "DownFont.h" #include "RTU.h" #include "BOARD.h" #define DOWN_FONT_EXIT_SECTOR FLASH_BIN_SECTOR //存储到外部flash的位置 static u32 DownFontDataSaveCnt = 0; //升级应用程序已经存储的数据大小 //收到数据包回调-用于写数据到外部flash //需要使用到W25X16的4K临时缓冲区,在升级与校验的时候确保没有其它线程访问W25X16,最好挂起其它线程 bool DownFontReceivePacketExitFlash(u8 *pPackData,u16 PackSize,u32 RecDataSize) { if((PackSize != 128) && (PackSize!=1024)) return FALSE; //xmodem只支持128字节或1024字节的数据包 memcpy(&SPI_FLASH_BUF[RecDataSize%4096], pPackData, PackSize); if(((RecDataSize+PackSize)%4096) == 0) //4K对齐,一次写入到外部flash { if(W25X16_EraseSector((RecDataSize+PackSize)/4096-1 + DOWN_FONT_EXIT_SECTOR) == FALSE) //擦除一个扇区 { return FALSE; } if(W25X16_WriteNoCheck(SPI_FLASH_BUF, DOWN_FONT_EXIT_SECTOR*4096+RecDataSize+PackSize-4096, 4096) == FALSE) //写入一个扇区 { return FALSE; } DownFontDataSaveCnt += 4096; } return TRUE; } //传输结束时回调(可能是出错结束) //由于需要4K对齐,因此最后一包不足4K需要单独进行处理 //需要使用到W25X16的4K临时缓冲区,在升级与校验的时候确保没有其它线程访问W25X16,最好挂起其它线程 bool DownFontTransEndtExitFlash(bool isTransOK, u32 RecDataSize) { if(isTransOK==FALSE) return FALSE; //失败返回 if(DownFontDataSaveCnt == RecDataSize) return TRUE; if(DownFontDataSaveCnt > RecDataSize) return FALSE; //存储的数据不能大于接收的数据 if((RecDataSize-DownFontDataSaveCnt) >= 4096) return FALSE; //未存储的数据大小不能超过4K if(W25X16_EraseSector(DownFontDataSaveCnt/4096 + DOWN_FONT_EXIT_SECTOR) == TRUE) //擦除一个扇区 { if(W25X16_WriteNoCheck(SPI_FLASH_BUF, DOWN_FONT_EXIT_SECTOR*4096+DownFontDataSaveCnt, RecDataSize-DownFontDataSaveCnt) == TRUE) //写入一个扇区 { return TRUE; } } return FALSE; //存储出错 } //使用XMODEM下载字库数据到外部flash //返回程序大小,如果失败了返回<=0 int XMODEM_DownloadFontToExitFlash(u32 MaxFileSize) { XMODE_Init(&UpgradeHandle, //句柄 UpgradeSendData, //发送数据回调函数 UpgradeReadData, //读取数据回调函数 UpgradeClearData, //清除接收数据缓冲回调函数 DownFontReceivePacketExitFlash, //接收到数据包回调函数 DownFontTransEndtExitFlash //传输结束回调函数 ); DownFontDataSaveCnt = 0; //已经存储的数据大小清零 return XMODEM_DownloadFile(&UpgradeHandle, MaxFileSize); } //使能系统命令行 #if SYS_CMD_EN_ #include "cmd.h" #include "string.h" CMD_TYPE const CMD_DOWN_BIN = {"DOWN BIN", 0X51A36D01, CMD_DownFont, "t下载字库数据"}; //下载字库数据 //升级应用层-需要使用到W25X16的4K临时缓冲区,在升级与校验的时候确保没有其它线程访问W25X16,最好挂起其它线程 void CMD_DownFont(char *pStr) { u32 DataSize; cmd_printf("已经进入升级程序模式,等待连接,超时10S!rn>"); cmd_printf("请在10S内进入Xmodem下载字库模式!rn>"); RTC_DisableInt(); //关闭RTC中断,防止唤醒后台任务 OSTaskSuspend(BACK_TASK_Prio); //挂起后台任务线程 OSTaskSuspend(LED_TASK_Prio); //挂起LED任务线程 OSTaskSuspend(GPRS_TASK_Prio); //挂起GPRS进程 OSTaskSuspend(COLL_TASK_Prio); //数据采集时间查询进程 OSTaskSuspend(OTHER_TASK_Prio); //OTHER OSTaskSuspend(MODBUS1_TASK_Prio); //MODBUS1 OSTaskSuspend(MODBUS2_TASK_Prio); //MODBUS2 OSTaskSuspend(KEY_TASK_Prio); //KEY DataSize = XMODEM_DownloadFontToExitFlash(FLASH_BIN_SIZE);//下载字库 if(DataSize==0) { cmd_printf("下载字库失败!rn>"); } else { cmd_printf("下载字库成功(%dB)!rn",DataSize); } CMD_Help(NULL); OSTaskResume(LED_TASK_Prio); //恢复挂起LED任务线程 OSTaskResume(GPRS_TASK_Prio); //恢复挂起GPRS进程 OSTaskResume(COLL_TASK_Prio);