使用Xmodem有一段時間了,使用起來移植性能不夠,通過這次徹底拋離了底層通信部分,可以用在任何通信接口上面了,跟底層的通信已經(jīng)無關(guān)了,使用了大量的回調(diào),回調(diào)主要完成通信的收發(fā),以及數(shù)據(jù)存儲等功能,我目前主要使用在STM32 IAP升級(寫入到內(nèi)部flash),app升級(寫入到外部flash W25Q128),字庫以及各種編碼下載(寫入到外部flash W25Q128)。
//數(shù)據(jù)包格式比較簡單
// 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協(xié)議實現(xiàn) ?*?作者:????????????cp1300@139.com ?*?創(chuàng)建時間:????????2014-08-19 ?*?最后修改時間:????2017-09-05 ?*?詳細(xì):????????????使用串口實現(xiàn)Xmodem協(xié)議應(yīng)用層 ????????????????????2017-03-23:修改采用回調(diào)以及句柄方式,可移植性更強 ????????????????????2017-04-04:增加發(fā)送延時 ????????????????????2017-09-05:發(fā)送NAK與結(jié)束增加延時,防止發(fā)送過快,并且修改如果通信超時則發(fā)送NAK,大大提高通信可靠性 ????????????????????2017-09-06:修復(fù)第一包數(shù)據(jù)丟失問題,增加數(shù)據(jù)包id重復(fù)檢查,大大提高數(shù)據(jù)的可靠性,防止重復(fù)的數(shù)據(jù)包 *************************************************************************************************************/ #include?"system.h" #include?"usart.h" #include?"main.h" #include?"Xmodem.h" #if?SYS_WDG_EN_ #include?"wdg.h"???? #endif???? //調(diào)試開關(guān) #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 /************************************************************************************************************************* *?函數(shù)????????????:????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 *?參數(shù)????????????:????pHandle:句柄; ????????????????????pSendData:發(fā)送回調(diào)函數(shù)(pDataBuff:發(fā)送數(shù)據(jù)緩沖區(qū),DataLen:發(fā)送數(shù)據(jù)長度) ????????????????????pReadData:接收數(shù)據(jù)回調(diào)函數(shù),會等待直到數(shù)據(jù)被寫入到接收緩沖區(qū)(pDataBuff:接收數(shù)據(jù)緩沖區(qū),ByteTimeOut:等待的字節(jié)超時時間,單位ms,TimeOut:數(shù)據(jù)包超時時間,單位ms),pReceiveDelay:返回接收延時,單位ms ????????????????????pClearRxData:清除接收數(shù)據(jù)緩沖區(qū)回調(diào)函數(shù) ????????????????????pReceivePacket:收到一包數(shù)據(jù)后的回調(diào)函數(shù),(返回false會退出通信)用于應(yīng)用層對數(shù)據(jù)進(jìn)行存儲(pPackData:接收到的數(shù)據(jù)包,PackSize:包大??;RecDataSize:已經(jīng)接收的數(shù)據(jù)包大小,不含當(dāng)前包的數(shù)據(jù)) ????????????????????pTransEnd:傳輸結(jié)束時回調(diào)函數(shù),(返回false會退出通信)用于傳輸結(jié)束的處理(isTransOK:TRUE,傳輸正常完成,F(xiàn)ALSE:傳輸錯誤結(jié)束;RecDataSize:總共收到的數(shù)據(jù)大小) *?返回????????????:????TRUE:初始化成;FALSE:初始化錯誤 *?依賴????????????:????通信接口 *?作者????????????:????cp1300@139.com *?時間????????????:????2013-05-08 *?最后修改時間?????:?????2017-03-23 *?說明????????????:?????XMODE通信協(xié)議通信接口初始化,默認(rèn)超時時間為等待啟動30秒,數(shù)據(jù)包超時2秒 ;????????????????????????????????//清除接收數(shù)據(jù)緩沖區(qū) *************************************************************************************************************************/ 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;????????????????????????????//接收到的數(shù)據(jù)大小 ????pHandle->RecPackCnt?=?0;????????????????????????????//接收到的數(shù)據(jù)包計數(shù) ????pHandle->pSendData?=?pSendData;????????????????????????//發(fā)送回調(diào)指針,????發(fā)送數(shù)據(jù)回調(diào)函數(shù) ????pHandle->pReadData?=?pReadData;????????????????????????//讀取數(shù)據(jù)回調(diào)指針,>0返回接收到的數(shù)據(jù)長度,否則失敗 ????pHandle->pClearRxData?=?pClearRxData;????????????????//清除接收數(shù)據(jù)緩沖區(qū) ????pHandle->pReceivePacket?=?pReceivePacket;????????????//收到數(shù)據(jù)包回調(diào)指針 ????pHandle->pTransEnd?=?pTransEnd;????????????????????????//傳輸結(jié)束時回調(diào)(可能是出錯結(jié)束) ????pHandle->WaitStartTimeOutSer?=?30;????????????????????//等待啟動傳輸超時時間 ????pHandle->PackTimeOutSer?=?2;????????????????????????//數(shù)據(jù)包超時時間 ????pHandle->pXMODEM_128Pack?=?NULL;????????????????????//128B數(shù)據(jù)包指針 ????pHandle->pXMODEM_1KPack?=?NULL;????????????????????????//1KB數(shù)據(jù)包指針 ????pHandle->TransRetry?=?10;????????????????????????????//失敗重試次數(shù),默認(rèn)10次 ????pHandle->TxByteTimeUs?=?0;????????????????????????????//發(fā)送延時默認(rèn)為0 ???? ????return?TRUE; } /************************************************************************************************************************* *?函數(shù)????????????:????bool?XMODE_SetTimeOut(XMODE_HANDLE?*pHandle,?u16?WaitStartTimeOutSer,?u16?PackTimeOutSer,?u8?TransRetry,u8?TxByteTimeUs) *?功能????????????:????設(shè)置XMODE超時時間 *?參數(shù)????????????:????pHandle:句柄;WaitStartTimeOutSer:等待啟動超時時間,單位秒鐘;PackTimeOutSer:數(shù)據(jù)包超時時間,單位秒鐘;TransRetry:出錯重試次數(shù),1-255次;TxByteTimeUs:發(fā)送字節(jié)延時 *?返回????????????:????TRUE:初始化成;FALSE:初始化錯誤 *?依賴????????????:????通信接口 *?作者????????????:????cp1300@139.com *?時間????????????:????2013-05-08 *?最后修改時間?????:?????2017-03-23 *?說明????????????:?????設(shè)置超時時間 *************************************************************************************************************************/ 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;????????????????????????????//數(shù)據(jù)包超時時間 ????if(pHandle->PackTimeOutSer?<?1)?pHandle->PackTimeOutSer?=?1; ????pHandle->TransRetry?=?TransRetry;????????????????????????????????????//出錯重試次數(shù) ????if(pHandle->TransRetry?<?1)?pHandle->TransRetry?=?1; ????pHandle->TxByteTimeUs?=?TxByteTimeUs;????????????????????????????????//發(fā)送字節(jié)延時,用于RS485接口,發(fā)送后需要進(jìn)行延時 ???? ????return?TRUE; } /************************************************************************************************************************* *?函數(shù)????????????:????int?XMODEM_Start(XMODE_HANDLE?*pHandle,?u8?**pRxBuff) *?功能????????????:????發(fā)送啟動請求 *?參數(shù)????????????:????pHandle:句柄;pRxBuff:接收緩沖區(qū)(存放第一包數(shù)據(jù)) *?返回????????????:????WaitStartTimeOutSer*10;????????????//轉(zhuǎn)換為100m單位 ????int?len; ???? ????if(pHandle?==?NULL)?return?FALSE; ????pHandle->DataBuff[0]?=?X_CRC_MODE;????????????????????????//采用CRC模式的校驗請求頭 ????while(TimeOut?--) ????{ ????????pHandle->pSendData(pHandle->DataBuff,1);????????????//發(fā)送請求信號 ????????if(pHandle->TxByteTimeUs?>?0)?XMODEM_DelayMS((10*pHandle->TxByteTimeUs)/1000+1);????//發(fā)送延時 ????????pHandle->pClearRxData();????????????????????????????//清除接收 ????????len?=?pHandle->pReadData(pRxBuff,10,100,NULL);????????//接收數(shù)據(jù) ????????if(len?>?0)????????????????????//等待接收 ????????{ ????????????pHandle->RecDataSize?=?0;????????????????????????//接收到的數(shù)據(jù)大小清零 ????????????pHandle->RecPackCnt?=?0;????????????????????????//接收到的數(shù)據(jù)包計數(shù)清零 ????????????return?len; ????????}???????????????? ???????????? #if?SYS_WDG_EN_ ????????IWDG_Feed();????????????????????????????????????????//喂狗 #endif???????????? ????} ????return?-1; } //發(fā)送ACK __inline?void?XMODEM_SendACK(XMODE_HANDLE?*pHandle) { ????pHandle->DataBuff[0]?=?(u8)X_ACK; ????pHandle->pSendData(pHandle->DataBuff,1);????????????//發(fā)送請求信號 ????if(pHandle->TxByteTimeUs?>?0)?XMODEM_DelayMS((10*pHandle->TxByteTimeUs)/1000+1);????//發(fā)送延時 } //發(fā)送NAK __inline?void?XMODEM_SendNAK(XMODE_HANDLE?*pHandle) { ????XMODEM_DelayMS(20); ????pHandle->DataBuff[0]?=?(u8)X_NAK; ????pHandle->pSendData(pHandle->DataBuff,1);????????????//發(fā)送請求信號 ????if(pHandle->TxByteTimeUs?>?0)?XMODEM_DelayMS((10*pHandle->TxByteTimeUs)/1000+1);????//發(fā)送延時 } //取消傳輸 __inline?void?XMODEM_CancelTran(XMODE_HANDLE?*pHandle) { ????XMODEM_DelayMS(20); ????pHandle->DataBuff[0]?=?(u8)X_CAN; ????pHandle->pSendData(pHandle->DataBuff,1);????????????//發(fā)送請求信號 ????if(pHandle->TxByteTimeUs?>?0)?XMODEM_DelayMS((10*pHandle->TxByteTimeUs)/1000+1);????//發(fā)送延時 } //判斷是否結(jié)束 __inline?bool?XMODEM_isTranEnd(u8?Data,?XMODE_HANDLE?*pHandle) { ????if(Data?==?X_EOT)?return?TRUE; ????else?return?FALSE; } /************************************************************************************************************************* *?函數(shù)????????????:????u16?XMODEM_CRC16(u8?*pData,?u16?DataLen) *?功能????????????:????crc16校驗 *?參數(shù)????????????:????pData:數(shù)據(jù)緩沖區(qū);DataLen:數(shù)據(jù)長度 *?返回????????????:????crc16結(jié)果 *?依賴????????????:????通信接口 *?作者????????????:????cp1300@139.com *?時間????????????:????2013-05-08 *?最后修改時間?????:?????2017-03-23 *?說明????????????:?????用于通信數(shù)據(jù)校驗,僅用于XMODEM,不可與modbus-rtu協(xié)議用的crc16混用(兩者計算結(jié)果會不一致) ????????????????????多項式碼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); } /************************************************************************************************************************* *?函數(shù)????????????:????u32?XMODEM_DownloadFile(XMODE_HANDLE?*pHandle,?u32?MaxDataSize) *?功能????????????:????使用XMODEM下載文件 *?參數(shù)????????????:????pHandle:句柄;MaxDataSize:限制最大下載數(shù)據(jù)量 *?返回????????????:????0:錯誤;其它:接收的數(shù)據(jù)長度 *?依賴????????????:????底層 *?作者????????????:????cp1300@139.com *?時間????????????:????2013-05-08 *?最后修改時間?????:?????2017-03-23 *?說明????????????:?????使用CRC校驗?zāi)J?,支?28,1K數(shù)據(jù)包 *************************************************************************************************************************/ 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;????????????????????????//用于記錄上一次包序號,每次包序號不能重復(fù) ???? ????len?=?XMODEM_Start(pHandle,?&pRxBuff);????//等待開始傳輸 ????if(len?pClearRxData();????????????????????????????????????????????????????//清除接收緩沖區(qū) ????????????len?=?pHandle->pReadData(&pRxBuff,?2,pHandle->PackTimeOutSer*1000,NULL);????//接收數(shù)據(jù) ????????} ????????isStart?=?FALSE;????????????????//第一次開始傳輸狀態(tài)無效 ????????if(len?pXMODEM_128Pack?=?(XMODEM_128B_PACK?*)pRxBuff;????????????????????//128B數(shù)據(jù)包指針 ????????????pHandle->pXMODEM_1KPack?=?(XMODEM_1KB_PACK?*)pRxBuff;????????????????????//1KB數(shù)據(jù)包指針 ????????????switch(pHandle->pXMODEM_128Pack->X_Start) ????????????{ ????????????????case?X_SOH:????//128 ????????????????{ ????????????????????if(len?<?128)? ????????????????????{ ????????????????????????XMODEM_SendNAK(pHandle);?//發(fā)送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);?//發(fā)送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)????//收到數(shù)據(jù)包,調(diào)用回調(diào) ????????????????????????????????{ ????????????????????????????????????//用戶返回退出下載 ????????????????????????????????????if(pHandle->pTransEnd?!=?NULL)????//判斷回調(diào)是否有效 ????????????????????????????????????{ ????????????????????????????????????????pHandle->pTransEnd(FALSE,?pHandle->RecDataSize);????//傳輸完成,調(diào)用回調(diào),有錯誤 ????????????????????????????????????} ???????????????????????????????????? ????????????????????????????????????XMODEM_CancelTran(pHandle);????????????????????//發(fā)送結(jié)束傳輸 ????????????????????????????????????XMODEM_DelayMS(10); ????????????????????????????????????XMODEM_CancelTran(pHandle);????????????????????//發(fā)送結(jié)束傳輸 ????????????????????????????????????XMODEM_DelayMS(500); ????????????????????????????????????xmodem_debug("用戶取消下載!rn"); ????????????????????????????????????return?0; ????????????????????????????????} ???????????????????????????????? ????????????????????????????????pHandle->RecDataSize?+=?128;????????????????????//接收到的數(shù)據(jù)大小增加 ????????????????????????????????pHandle->RecPackCnt?++;????????????????????????????//接收到的數(shù)據(jù)包計數(shù)增加 ????????????????????????????????if(pHandle->RecDataSize?>?MaxDataSize) ????????????????????????????????{ ????????????????????????????????????if(pHandle->pTransEnd?!=?NULL)????//判斷回調(diào)是否有效 ????????????????????????????????????{ ????????????????????????????????????????pHandle->pTransEnd(FALSE,?pHandle->RecDataSize);????//傳輸完成,調(diào)用回調(diào),有錯誤 ????????????????????????????????????} ???????????????????????????????????? ????????????????????????????????????XMODEM_CancelTran(pHandle);????????????????????//發(fā)送結(jié)束傳輸 ????????????????????????????????????XMODEM_DelayMS(10); ????????????????????????????????????XMODEM_CancelTran(pHandle);????????????????????//發(fā)送結(jié)束傳輸 ????????????????????????????????????XMODEM_DelayMS(500); ????????????????????????????????????xmodem_debug("文件下載失敗,大小超出范圍(%dB)!rn",?MaxDataSize); ????????????????????????????????????return?0; ????????????????????????????????} ????????????????????????????} ????????????????????????????else????//故障,重復(fù)的數(shù)據(jù)包 ????????????????????????????{ ????????????????????????????????XMODEM_DelayMS(10); ????????????????????????????} ????????????????????????????XMODEM_SendACK(pHandle);????????????????????????//發(fā)送ACK響應(yīng) ????????????????????????????retry?=?0; ????????????????????????} ????????????????????}???? ????????????????}break; ????????????????case?X_STX:????//1k ????????????????{ ????????????????????if(len?<?1024)? ????????????????????{ ????????????????????????XMODEM_SendNAK(pHandle);?//發(fā)送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);????????????????????????//發(fā)送NAK? ????????????????????????????retry++; ????????????????????????} ????????????????????????else ????????????????????????{ ????????????????????????????if(LastPackCnt!=pHandle->pXMODEM_128Pack->X_PackNum)?????//包序號不一樣 ????????????????????????????{ ????????????????????????????????LastPackCnt?=?pHandle->pXMODEM_128Pack->X_PackNum;????//記錄上一次的包序號 ???????????????????????????????? ????????????????????????????????if(pHandle->pReceivePacket?!=?NULL)????//判斷回調(diào)是否有效 ????????????????????????????????{ ????????????????????????????????????if(pHandle->pReceivePacket(pHandle->pXMODEM_1KPack->X_PackData,?1024,?pHandle->RecDataSize)==FALSE)????//收到數(shù)據(jù)包,調(diào)用回調(diào) ????????????????????????????????????{ ????????????????????????????????????????//用戶返回退出下載 ????????????????????????????????????????if(pHandle->pTransEnd?!=?NULL)????//判斷回調(diào)是否有效 ????????????????????????????????????????{ ????????????????????????????????????????????pHandle->pTransEnd(FALSE,?pHandle->RecDataSize);????//傳輸完成,調(diào)用回調(diào),有錯誤 ????????????????????????????????????????} ???????????????????????????????????????? ????????????????????????????????????????XMODEM_CancelTran(pHandle);????????????????????//發(fā)送結(jié)束傳輸 ????????????????????????????????????????XMODEM_DelayMS(10); ????????????????????????????????????????XMODEM_CancelTran(pHandle);????????????????????//發(fā)送結(jié)束傳輸 ????????????????????????????????????????XMODEM_DelayMS(500); ????????????????????????????????????????xmodem_debug("用戶取消下載!rn"); ????????????????????????????????????????return?0; ????????????????????????????????????} ????????????????????????????????} ???????????????????????????????? ????????????????????????????????pHandle->RecDataSize?+=?1024;????????????????????//接收到的數(shù)據(jù)大小增加 ????????????????????????????????pHandle->RecPackCnt?++;????????????????????????????//接收到的數(shù)據(jù)包計數(shù)增加 ???????????????????????????????? ????????????????????????????????if(pHandle->RecDataSize?>?MaxDataSize) ????????????????????????????????{ ????????????????????????????????????if(pHandle->pTransEnd?!=?NULL)????//判斷回調(diào)是否有效 ????????????????????????????????????{ ????????????????????????????????????????pHandle->pTransEnd(FALSE,?pHandle->RecDataSize);????//傳輸完成,調(diào)用回調(diào),有錯誤 ????????????????????????????????????} ???????????????????????????? ????????????????????????????????????XMODEM_CancelTran(pHandle);????????????????????//發(fā)送結(jié)束傳輸 ????????????????????????????????????XMODEM_DelayMS(10); ????????????????????????????????????XMODEM_CancelTran(pHandle);????????????????????//發(fā)送結(jié)束傳輸 ????????????????????????????????????XMODEM_DelayMS(500); ????????????????????????????????????xmodem_debug("文件下載失敗,代碼超出范圍(%dB)!rn",?MaxDataSize); ????????????????????????????????????return?0; ????????????????????????????????} ????????????????????????????} ????????????????????????????else????//故障,重復(fù)的數(shù)據(jù)包 ????????????????????????????{ ????????????????????????????????XMODEM_DelayMS(10); ????????????????????????????} ????????????????????????????XMODEM_SendACK(pHandle);????????????????????????//發(fā)送ACK響應(yīng) ????????????????????????????retry?=?0; ????????????????????????} ????????????????????}???? ????????????????}break; ????????????????case?X_EOT:????//傳輸結(jié)束,最后一包 ????????????????{ ????????????????????if(pHandle->pTransEnd?!=?NULL)????//判斷回調(diào)是否有效 ????????????????????{ ????????????????????????if(pHandle->pTransEnd(TRUE,?pHandle->RecDataSize)?==?FALSE)????//傳輸完成,調(diào)用回調(diào),有錯誤則退出 ????????????????????????{ ????????????????????????????XMODEM_CancelTran(pHandle);????????????????????//發(fā)送結(jié)束傳輸 ????????????????????????????XMODEM_DelayMS(10); ????????????????????????????XMODEM_CancelTran(pHandle);????????????????????//發(fā)送結(jié)束傳輸 ????????????????????????????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)????//判斷回調(diào)是否有效 ????????????{ ????????????????pHandle->pTransEnd(FALSE,?pHandle->RecDataSize);????//傳輸完成,調(diào)用回調(diào),有錯誤 ????????????} ???????????????????????? ????????????XMODEM_CancelTran(pHandle);????????????????????????????//取消傳輸 ????????????XMODEM_DelayMS(10); ????????????XMODEM_CancelTran(pHandle);????????????????????????????//取消傳輸 ????????????XMODEM_DelayMS(500); ????????????xmodem_debug("下載失敗,重試次數(shù)過多!rn"); ????????????return?0; ????????} #if?SYS_WDG_EN_ ????????IWDG_Feed();????????????????????????????????????????//喂狗 #endif ????} }
//.h文件
/************************************************************************************************************* ?*?文件名: Xmodem.h ?*?功能: Xmodem協(xié)議實現(xiàn) ?*?作者: cp1300@139.com ?*?創(chuàng)建時間: 2014-08-19 ?*?最后修改時間: 2014-08-19 ?*?詳細(xì): 使用串口實現(xiàn)Xmodem協(xié)議應(yīng)用層 *************************************************************************************************************/ #ifndef?_X_MODEM_H_ #define?_X_MODEM_H_ #include?"system.h" #include?"USART.h" #ifdef?_UCOS_II_ //支持ucos操作系統(tǒng),使用系統(tǒng)延時 #define?XMODEM_DelayMS(x) OSTimeDlyHMSM(0,0,0,x) //延時ms,最大延時999ms #else #define?XMODEM_DelayMS(x) Delay_MS(x) #endif?//_UCOS_II_ //XMODEM?相關(guān)定義說明 #define?X_SOH 0x01 //?Xmodem數(shù)據(jù)頭 #define?X_STX 0x02 //?1K-Xmodem數(shù)據(jù)頭 #define?X_EOT 0x04 //?發(fā)送結(jié)束 #define?X_ACK 0x06 //?認(rèn)可響應(yīng) #define?X_NAK 0x15 //?不認(rèn)可響應(yīng) #define?X_CAN 0x18 //?撤銷傳送 #define?X_EOF 0x1A //?填充數(shù)據(jù)包 //128B數(shù)據(jù)包格式 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數(shù)據(jù)包格式 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; //接收到的數(shù)據(jù)大小 u32?RecPackCnt; //接收到的數(shù)據(jù)包計數(shù) bool?(*?pSendData)(u8?*pDataBuff,?u16?DataLen); //發(fā)送回調(diào)指針, 發(fā)送數(shù)據(jù)回調(diào)函數(shù) int?(*?pReadData)(u8?**pDataBuff,?u8?ByteTimeOut,?u16?TimeOut,?u16?*pReceiveDelay); //讀取數(shù)據(jù)回調(diào)指針,>0返回接收到的數(shù)據(jù)長度,否則失敗 bool?(*pReceivePacket)(u8?*pPackData,u16?PackSize,u32?RecDataSize); //收到數(shù)據(jù)包回調(diào)指針,返回false會退出傳輸 bool?(*pTransEnd)(bool?isTransOK,?u32?RecDataSize); //傳輸結(jié)束回調(diào)指針,返回false會退出傳輸 void?(*pClearRxData)(void); //清除接收數(shù)據(jù)緩沖區(qū) XMODEM_128B_PACK?*pXMODEM_128Pack; //128bit數(shù)據(jù)包格式 XMODEM_1KB_PACK?*pXMODEM_1KPack; //1K數(shù)據(jù)包格式 u16?WaitStartTimeOutSer; //等待啟動傳輸超時時間 u16?PackTimeOutSer; //數(shù)據(jù)包超時時間 u8?TxByteTimeUs; //發(fā)送字節(jié)延時,微秒,用于RS485通信延時 u8?DataBuff[2]; //分配的臨時緩沖區(qū) u8?TransRetry; //失敗重試次數(shù),默認(rèn)10次 }XMODE_HANDLE; //啟動傳輸校驗?zāi)J?typedef?enum { X_CRC_MODE?=?'C', //傳輸使用CRC16校驗?zāi)J? X_ACC_MODE?=?X_NAK, //傳輸使用累加校驗?zāi)J?}XMODEM_START_MODE; //數(shù)據(jù)包大小 typedef?enum { X_PACK128B?=?X_SOH, //128B數(shù)據(jù)包 X_PACK1kB?=?X_STX, //1KB數(shù)據(jù)包 }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?超時設(shè)置 bool?XMODE_SetTimeOut(XMODE_HANDLE?*pHandle,?u16?WaitStartTimeOutSer,?u16?PackTimeOutSer,?u8?TransRetry,u8?TxByteTimeUs); //XMODEM?下載數(shù)據(jù) u32?XMODEM_DownloadFile(XMODE_HANDLE?*pHandle,?u32?MaxDataSize); #endif?/*_X_MODEM_H_*/
下面是2個下載的例子,我主要是示意通信與存儲接口格式,實際使用需要按照自己的平臺進(jìn)行移植
//例子1:下載程序到STM32內(nèi)部flash,主要提供數(shù)據(jù)收發(fā)接口,以及存儲接口示意,這些接口需要根據(jù)自己的平臺做移植 /************************************************************************************************************* ?*?文件名: UpgradeBIOS.c ?*?功能: 升級BIOS相關(guān) ?*?作者: cp1300@139.com ?*?創(chuàng)建時間: 2017-05-16 ?*?最后修改時間: 2017-05-16 ?*?詳細(xì): 使用xmodem直接刷STM32flash,如果失敗了請不要重啟,否則會無法進(jìn)入系統(tǒng) *************************************************************************************************************/ #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程序內(nèi)部flash基址 static?u32?UpgradeBiosDataSaveCnt?=?0; //升級應(yīng)用程序已經(jīng)存儲的數(shù)據(jù)大小 //升級文件接口XMODE句柄 XMODE_HANDLE?UpgradeBIOSHandle; //發(fā)送數(shù)據(jù)接口 bool?UpgradeBiosSendData(u8?DataBuff[],?u16?DataLen) { UARTx_SendData(UART_PRINTF_CH,?DataBuff,?DataLen); return?TRUE; } //接收數(shù)據(jù)接口 int?UpgradeBiosReadData(u8?**pDataBuff,u8?ByteTimeOutMs,?u16?TimeOutMs,?u16?*pReceiveDelayMs) { u32?cnt?=?0; u16?TempTime; UARTx_ClearRxCnt(UART_PRINTF_CH); //清除串口接收緩沖區(qū),開始結(jié)束數(shù)據(jù) if(ByteTimeOutMs?<?1)?ByteTimeOutMs?=?1; //字節(jié)超時時間,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; //接收緩沖區(qū) return?cnt; } #if?SYS_WDG_EN_ IWDG_Feed(); //喂狗 #endif } return?0; } //清除接收緩沖區(qū) void?UpgradeBiosClearData(void) { UARTx_ClearRxCnt(UART_PRINTF_CH); //清除串口緩沖區(qū) } //收到數(shù)據(jù)包回調(diào)-用于寫數(shù)據(jù)到內(nèi)部flash bool?UpgradeBiosReceivePacketExitFlash(u8?*pPackData,u16?PackSize,u32?RecDataSize) { if((PackSize?!=?128)?&&?(PackSize!=1024))?return?FALSE; //xmodem只支持128字節(jié)或1024字節(jié)的數(shù)據(jù)包 //寫入數(shù)據(jù)到內(nèi)部flash STM32FLASH_Write(UP_PROGRAM_STM32_BIOS_ADDR+RecDataSize,?(u16?*)pPackData,?(PackSize+1)/2); UpgradeBiosDataSaveCnt?+=?PackSize; return?TRUE; } //傳輸結(jié)束時回調(diào)(可能是出錯結(jié)束) bool?UpgradeBiosTransEndtExitFlash(bool?isTransOK,?u32?RecDataSize) { if(isTransOK==FALSE)?return?FALSE; //失敗返回 if(UpgradeBiosDataSaveCnt?==?RecDataSize)?return?TRUE; if(UpgradeBiosDataSaveCnt?>?RecDataSize)?return?FALSE; //存儲的數(shù)據(jù)不能大于接收的數(shù)據(jù) if((RecDataSize-UpgradeBiosDataSaveCnt)?>?0)?return?FALSE; //不能有未存儲的數(shù)據(jù) return?FALSE; //存儲出錯 } //使用XMODEM下載數(shù)據(jù)到內(nèi)部flash //返回程序大小,如果失敗了返回<=0 int?XMODEM_DownloadFileToSTM32Flash(u32?MaxFileSize) { XMODE_Init(&UpgradeBIOSHandle,? //句柄 UpgradeBiosSendData,? //發(fā)送數(shù)據(jù)回調(diào)函數(shù) UpgradeBiosReadData,? //讀取數(shù)據(jù)回調(diào)函數(shù) UpgradeBiosClearData,? //清除接收數(shù)據(jù)緩沖回調(diào)函數(shù) UpgradeBiosReceivePacketExitFlash,? //接收到數(shù)據(jù)包回調(diào)函數(shù) UpgradeBiosTransEndtExitFlash //傳輸結(jié)束回調(diào)函數(shù) ); UpgradeBiosDataSaveCnt?=?0; //已經(jīng)存儲的數(shù)據(jù)大小清零 return?XMODEM_DownloadFile(&UpgradeBIOSHandle,?MaxFileSize); } //使能系統(tǒng)命令行 #if?SYS_CMD_EN_ #include?"cmd.h" #include?"string.h" CMD_TYPE?const?CMD_UP_BIOS =?{"UP?BIOS",?0XD3476564,?CMD_UpBIOS,?"tt升級BIOS"}; //升級BIOS程序 //進(jìn)入升級BIOS模式 void?CMD_UpBIOS(char?*pStr) { cmd_printf("已經(jīng)進(jìn)入升級BIOS模式,等待連接,超時10S!rn>"); cmd_printf("請在10S內(nèi)進(jìn)入Xmodem下載模式!rn>"); RTC_DisableInt(); //關(guān)閉RTC中斷,防止喚醒后臺任務(wù) OSTaskSuspend(BACK_TASK_Prio); //掛起后臺任務(wù)線程 OSTaskSuspend(LED_TASK_Prio); //掛起LED任務(wù)線程 OSTaskSuspend(GPRS_TASK_Prio); //掛起GPRS進(jìn)程 OSTaskSuspend(COLL_TASK_Prio); //數(shù)據(jù)采集時間查詢進(jìn)程 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); //恢復(fù)掛起LED任務(wù)線程 OSTaskResume(GPRS_TASK_Prio); //恢復(fù)掛起GPRS進(jìn)程 OSTaskResume(COLL_TASK_Prio); //恢復(fù)數(shù)據(jù)采集時間查詢進(jìn)程 OSTaskResume(OTHER_TASK_Prio); //恢復(fù)OTHER OSTaskResume(MODBUS1_TASK_Prio); //恢復(fù)MODBUS1 OSTaskResume(MODBUS2_TASK_Prio); //恢復(fù)MODBUS2 OSTaskResume(KEY_TASK_Prio); //恢復(fù)KEY RTC_EnableInt(); //恢復(fù)RTC中斷 } #endif?//SYS_CMD_EN_ //例子2:下載字庫編碼到外部flash,使用的是SPI?接口flash,W25Q128,存儲接口稍有不同,因為我每次將數(shù)據(jù)集齊4K才進(jìn)行存儲,這樣可以提高存儲效率,降低flash損耗,當(dāng)然最后一包可能不足4K,會另外進(jìn)行處理的。 /************************************************************************************************************* ?*?文件名: DownFont.c ?*?功能: 下載字庫相關(guān) ?*?作者: cp1300@139.com ?*?創(chuàng)建時間: 2017-03-29 ?*?最后修改時間: 2017-03-29 ?*?詳細(xì): 使用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; //升級應(yīng)用程序已經(jīng)存儲的數(shù)據(jù)大小 //收到數(shù)據(jù)包回調(diào)-用于寫數(shù)據(jù)到外部flash //需要使用到W25X16的4K臨時緩沖區(qū),在升級與校驗的時候確保沒有其它線程訪問W25X16,最好掛起其它線程 bool?DownFontReceivePacketExitFlash(u8?*pPackData,u16?PackSize,u32?RecDataSize) { if((PackSize?!=?128)?&&?(PackSize!=1024))?return?FALSE; //xmodem只支持128字節(jié)或1024字節(jié)的數(shù)據(jù)包 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) //擦除一個扇區(qū) { return?FALSE; } if(W25X16_WriteNoCheck(SPI_FLASH_BUF,?DOWN_FONT_EXIT_SECTOR*4096+RecDataSize+PackSize-4096,?4096)??==?FALSE) //寫入一個扇區(qū) { return?FALSE; } DownFontDataSaveCnt?+=?4096; } return?TRUE; } //傳輸結(jié)束時回調(diào)(可能是出錯結(jié)束) //由于需要4K對齊,因此最后一包不足4K需要單獨進(jìn)行處理 //需要使用到W25X16的4K臨時緩沖區(qū),在升級與校驗的時候確保沒有其它線程訪問W25X16,最好掛起其它線程 bool?DownFontTransEndtExitFlash(bool?isTransOK,?u32?RecDataSize) { if(isTransOK==FALSE)?return?FALSE; //失敗返回 if(DownFontDataSaveCnt?==?RecDataSize)?return?TRUE; if(DownFontDataSaveCnt?>?RecDataSize)?return?FALSE; //存儲的數(shù)據(jù)不能大于接收的數(shù)據(jù) if((RecDataSize-DownFontDataSaveCnt)?>=?4096)?return?FALSE; //未存儲的數(shù)據(jù)大小不能超過4K if(W25X16_EraseSector(DownFontDataSaveCnt/4096?+?DOWN_FONT_EXIT_SECTOR)?==?TRUE) //擦除一個扇區(qū) { if(W25X16_WriteNoCheck(SPI_FLASH_BUF,?DOWN_FONT_EXIT_SECTOR*4096+DownFontDataSaveCnt,?RecDataSize-DownFontDataSaveCnt)??==?TRUE) //寫入一個扇區(qū) { return?TRUE; } } return?FALSE; //存儲出錯 } //使用XMODEM下載字庫數(shù)據(jù)到外部flash //返回程序大小,如果失敗了返回<=0 int?XMODEM_DownloadFontToExitFlash(u32?MaxFileSize) { XMODE_Init(&UpgradeHandle,? //句柄 UpgradeSendData,? //發(fā)送數(shù)據(jù)回調(diào)函數(shù) UpgradeReadData,? //讀取數(shù)據(jù)回調(diào)函數(shù) UpgradeClearData,? //清除接收數(shù)據(jù)緩沖回調(diào)函數(shù) DownFontReceivePacketExitFlash,?//接收到數(shù)據(jù)包回調(diào)函數(shù) DownFontTransEndtExitFlash //傳輸結(jié)束回調(diào)函數(shù) ); DownFontDataSaveCnt?=?0; //已經(jīng)存儲的數(shù)據(jù)大小清零 return?XMODEM_DownloadFile(&UpgradeHandle,?MaxFileSize); } //使能系統(tǒng)命令行 #if?SYS_CMD_EN_ #include?"cmd.h" #include?"string.h" CMD_TYPE?const?CMD_DOWN_BIN =?{"DOWN?BIN",?0X51A36D01,?CMD_DownFont,?"t下載字庫數(shù)據(jù)"}; //下載字庫數(shù)據(jù) //升級應(yīng)用層-需要使用到W25X16的4K臨時緩沖區(qū),在升級與校驗的時候確保沒有其它線程訪問W25X16,最好掛起其它線程 void?CMD_DownFont(char?*pStr) { u32?DataSize; cmd_printf("已經(jīng)進(jìn)入升級程序模式,等待連接,超時10S!rn>"); cmd_printf("請在10S內(nèi)進(jìn)入Xmodem下載字庫模式!rn>"); RTC_DisableInt(); //關(guān)閉RTC中斷,防止喚醒后臺任務(wù) OSTaskSuspend(BACK_TASK_Prio); //掛起后臺任務(wù)線程 OSTaskSuspend(LED_TASK_Prio); //掛起LED任務(wù)線程 OSTaskSuspend(GPRS_TASK_Prio); //掛起GPRS進(jìn)程 OSTaskSuspend(COLL_TASK_Prio); //數(shù)據(jù)采集時間查詢進(jìn)程 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); //恢復(fù)掛起LED任務(wù)線程 OSTaskResume(GPRS_TASK_Prio); //恢復(fù)掛起GPRS進(jìn)程 OSTaskResume(COLL_TASK_Prio);