使用Xmodem有一段時間了,使用起來移植性能不夠,通過這次徹底拋離了底層通信部分,可以用在任何通信接口上面了,跟底層的通信已經(jīng)無關了,使用了大量的回調,回調主要完成通信的收發(fā),以及數(shù)據(jù)存儲等功能,我目前主要使用在STM32 IAP升級(寫入到內部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
?*?詳細:????????????使用串口實現(xiàn)Xmodem協(xié)議應用層
????????????????????2017-03-23:修改采用回調以及句柄方式,可移植性更強
????????????????????2017-04-04:增加發(fā)送延時
????????????????????2017-09-05:發(fā)送NAK與結束增加延時,防止發(fā)送過快,并且修改如果通信超時則發(fā)送NAK,大大提高通信可靠性
????????????????????2017-09-06:修復第一包數(shù)據(jù)丟失問題,增加數(shù)據(jù)包id重復檢查,大大提高數(shù)據(jù)的可靠性,防止重復的數(shù)據(jù)包
*************************************************************************************************************/
#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
/*************************************************************************************************************************
*?函數(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ā)送回調函數(shù)(pDataBuff:發(fā)送數(shù)據(jù)緩沖區(qū),DataLen:發(fā)送數(shù)據(jù)長度)
????????????????????pReadData:接收數(shù)據(jù)回調函數(shù),會等待直到數(shù)據(jù)被寫入到接收緩沖區(qū)(pDataBuff:接收數(shù)據(jù)緩沖區(qū),ByteTimeOut:等待的字節(jié)超時時間,單位ms,TimeOut:數(shù)據(jù)包超時時間,單位ms),pReceiveDelay:返回接收延時,單位ms
????????????????????pClearRxData:清除接收數(shù)據(jù)緩沖區(qū)回調函數(shù)
????????????????????pReceivePacket:收到一包數(shù)據(jù)后的回調函數(shù),(返回false會退出通信)用于應用層對數(shù)據(jù)進行存儲(pPackData:接收到的數(shù)據(jù)包,PackSize:包大??;RecDataSize:已經(jīng)接收的數(shù)據(jù)包大小,不含當前包的數(shù)據(jù))
????????????????????pTransEnd:傳輸結束時回調函數(shù),(返回false會退出通信)用于傳輸結束的處理(isTransOK:TRUE,傳輸正常完成,F(xiàn)ALSE:傳輸錯誤結束;RecDataSize:總共收到的數(shù)據(jù)大?。?*?返回????????????:????TRUE:初始化成;FALSE:初始化錯誤
*?依賴????????????:????通信接口
*?作者????????????:????cp1300@139.com
*?時間????????????:????2013-05-08
*?最后修改時間?????:?????2017-03-23
*?說明????????????:?????XMODE通信協(xié)議通信接口初始化,默認超時時間為等待啟動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ā)送回調指針,????發(fā)送數(shù)據(jù)回調函數(shù)
????pHandle->pReadData?=?pReadData;????????????????????????//讀取數(shù)據(jù)回調指針,>0返回接收到的數(shù)據(jù)長度,否則失敗
????pHandle->pClearRxData?=?pClearRxData;????????????????//清除接收數(shù)據(jù)緩沖區(qū)
????pHandle->pReceivePacket?=?pReceivePacket;????????????//收到數(shù)據(jù)包回調指針
????pHandle->pTransEnd?=?pTransEnd;????????????????????????//傳輸結束時回調(可能是出錯結束)
????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ù),默認10次
????pHandle->TxByteTimeUs?=?0;????????????????????????????//發(fā)送延時默認為0
????
????return?TRUE;
}
/*************************************************************************************************************************
*?函數(shù)????????????:????bool?XMODE_SetTimeOut(XMODE_HANDLE?*pHandle,?u16?WaitStartTimeOutSer,?u16?PackTimeOutSer,?u8?TransRetry,u8?TxByteTimeUs)
*?功能????????????:????設置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
*?說明????????????:?????設置超時時間
*************************************************************************************************************************/
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ā)送后需要進行延時
????
????return?TRUE;
}
/*************************************************************************************************************************
*?函數(shù)????????????:????int?XMODEM_Start(XMODE_HANDLE?*pHandle,?u8?**pRxBuff)
*?功能????????????:????發(fā)送啟動請求
*?參數(shù)????????????:????pHandle:句柄;pRxBuff:接收緩沖區(qū)(存放第一包數(shù)據(jù))
*?返回????????????:????WaitStartTimeOutSer*10;????????????//轉換為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ā)送延時
}
//判斷是否結束
__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結果
*?依賴????????????:????通信接口
*?作者????????????:????cp1300@139.com
*?時間????????????:????2013-05-08
*?最后修改時間?????:?????2017-03-23
*?說明????????????:?????用于通信數(shù)據(jù)校驗,僅用于XMODEM,不可與modbus-rtu協(xié)議用的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);
}
/*************************************************************************************************************************
*?函數(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校驗模式,支持128,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;????????????????????????//用于記錄上一次包序號,每次包序號不能重復
????
????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ù)包,調用回調
????????????????????????????????{
????????????????????????????????????//用戶返回退出下載
????????????????????????????????????if(pHandle->pTransEnd?!=?NULL)????//判斷回調是否有效
????????????????????????????????????{
????????????????????????????????????????pHandle->pTransEnd(FALSE,?pHandle->RecDataSize);????//傳輸完成,調用回調,有錯誤
????????????????????????????????????}
????????????????????????????????????
????????????????????????????????????XMODEM_CancelTran(pHandle);????????????????????//發(fā)送結束傳輸
????????????????????????????????????XMODEM_DelayMS(10);
????????????????????????????????????XMODEM_CancelTran(pHandle);????????????????????//發(fā)送結束傳輸
????????????????????????????????????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)????//判斷回調是否有效
????????????????????????????????????{
????????????????????????????????????????pHandle->pTransEnd(FALSE,?pHandle->RecDataSize);????//傳輸完成,調用回調,有錯誤
????????????????????????????????????}
????????????????????????????????????
????????????????????????????????????XMODEM_CancelTran(pHandle);????????????????????//發(fā)送結束傳輸
????????????????????????????????????XMODEM_DelayMS(10);
????????????????????????????????????XMODEM_CancelTran(pHandle);????????????????????//發(fā)送結束傳輸
????????????????????????????????????XMODEM_DelayMS(500);
????????????????????????????????????xmodem_debug("文件下載失敗,大小超出范圍(%dB)!rn",?MaxDataSize);
????????????????????????????????????return?0;
????????????????????????????????}
????????????????????????????}
????????????????????????????else????//故障,重復的數(shù)據(jù)包
????????????????????????????{
????????????????????????????????XMODEM_DelayMS(10);
????????????????????????????}
????????????????????????????XMODEM_SendACK(pHandle);????????????????????????//發(fā)送ACK響應
????????????????????????????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)????//判斷回調是否有效
????????????????????????????????{
????????????????????????????????????if(pHandle->pReceivePacket(pHandle->pXMODEM_1KPack->X_PackData,?1024,?pHandle->RecDataSize)==FALSE)????//收到數(shù)據(jù)包,調用回調
????????????????????????????????????{
????????????????????????????????????????//用戶返回退出下載
????????????????????????????????????????if(pHandle->pTransEnd?!=?NULL)????//判斷回調是否有效
????????????????????????????????????????{
????????????????????????????????????????????pHandle->pTransEnd(FALSE,?pHandle->RecDataSize);????//傳輸完成,調用回調,有錯誤
????????????????????????????????????????}
????????????????????????????????????????
????????????????????????????????????????XMODEM_CancelTran(pHandle);????????????????????//發(fā)送結束傳輸
????????????????????????????????????????XMODEM_DelayMS(10);
????????????????????????????????????????XMODEM_CancelTran(pHandle);????????????????????//發(fā)送結束傳輸
????????????????????????????????????????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)????//判斷回調是否有效
????????????????????????????????????{
????????????????????????????????????????pHandle->pTransEnd(FALSE,?pHandle->RecDataSize);????//傳輸完成,調用回調,有錯誤
????????????????????????????????????}
????????????????????????????
????????????????????????????????????XMODEM_CancelTran(pHandle);????????????????????//發(fā)送結束傳輸
????????????????????????????????????XMODEM_DelayMS(10);
????????????????????????????????????XMODEM_CancelTran(pHandle);????????????????????//發(fā)送結束傳輸
????????????????????????????????????XMODEM_DelayMS(500);
????????????????????????????????????xmodem_debug("文件下載失敗,代碼超出范圍(%dB)!rn",?MaxDataSize);
????????????????????????????????????return?0;
????????????????????????????????}
????????????????????????????}
????????????????????????????else????//故障,重復的數(shù)據(jù)包
????????????????????????????{
????????????????????????????????XMODEM_DelayMS(10);
????????????????????????????}
????????????????????????????XMODEM_SendACK(pHandle);????????????????????????//發(fā)送ACK響應
????????????????????????????retry?=?0;
????????????????????????}
????????????????????}????
????????????????}break;
????????????????case?X_EOT:????//傳輸結束,最后一包
????????????????{
????????????????????if(pHandle->pTransEnd?!=?NULL)????//判斷回調是否有效
????????????????????{
????????????????????????if(pHandle->pTransEnd(TRUE,?pHandle->RecDataSize)?==?FALSE)????//傳輸完成,調用回調,有錯誤則退出
????????????????????????{
????????????????????????????XMODEM_CancelTran(pHandle);????????????????????//發(fā)送結束傳輸
????????????????????????????XMODEM_DelayMS(10);
????????????????????????????XMODEM_CancelTran(pHandle);????????????????????//發(fā)送結束傳輸
????????????????????????????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("下載失敗,重試次數(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
?*?詳細: 使用串口實現(xiàn)Xmodem協(xié)議應用層
*************************************************************************************************************/
#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?相關定義說明
#define?X_SOH 0x01 //?Xmodem數(shù)據(jù)頭
#define?X_STX 0x02 //?1K-Xmodem數(shù)據(jù)頭
#define?X_EOT 0x04 //?發(fā)送結束
#define?X_ACK 0x06 //?認可響應
#define?X_NAK 0x15 //?不認可響應
#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ā)送回調指針, 發(fā)送數(shù)據(jù)回調函數(shù)
int?(*?pReadData)(u8?**pDataBuff,?u8?ByteTimeOut,?u16?TimeOut,?u16?*pReceiveDelay); //讀取數(shù)據(jù)回調指針,>0返回接收到的數(shù)據(jù)長度,否則失敗
bool?(*pReceivePacket)(u8?*pPackData,u16?PackSize,u32?RecDataSize); //收到數(shù)據(jù)包回調指針,返回false會退出傳輸
bool?(*pTransEnd)(bool?isTransOK,?u32?RecDataSize); //傳輸結束回調指針,返回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ù),默認10次
}XMODE_HANDLE;
//啟動傳輸校驗模式
typedef?enum
{
X_CRC_MODE?=?'C', //傳輸使用CRC16校驗模式
X_ACC_MODE?=?X_NAK, //傳輸使用累加校驗模式
}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?超時設置
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個下載的例子,我主要是示意通信與存儲接口格式,實際使用需要按照自己的平臺進行移植
//例子1:下載程序到STM32內部flash,主要提供數(shù)據(jù)收發(fā)接口,以及存儲接口示意,這些接口需要根據(jù)自己的平臺做移植
/*************************************************************************************************************
?*?文件名: UpgradeBIOS.c
?*?功能: 升級BIOS相關
?*?作者: cp1300@139.com
?*?創(chuàng)建時間: 2017-05-16
?*?最后修改時間: 2017-05-16
?*?詳細: 使用xmodem直接刷STM32flash,如果失敗了請不要重啟,否則會無法進入系統(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程序內部flash基址
static?u32?UpgradeBiosDataSaveCnt?=?0; //升級應用程序已經(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ū),開始結束數(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ù)包回調-用于寫數(shù)據(jù)到內部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ù)到內部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; //存儲的數(shù)據(jù)不能大于接收的數(shù)據(jù)
if((RecDataSize-UpgradeBiosDataSaveCnt)?>?0)?return?FALSE; //不能有未存儲的數(shù)據(jù)
return?FALSE; //存儲出錯
}
//使用XMODEM下載數(shù)據(jù)到內部flash
//返回程序大小,如果失敗了返回<=0
int?XMODEM_DownloadFileToSTM32Flash(u32?MaxFileSize)
{
XMODE_Init(&UpgradeBIOSHandle,? //句柄
UpgradeBiosSendData,? //發(fā)送數(shù)據(jù)回調函數(shù)
UpgradeBiosReadData,? //讀取數(shù)據(jù)回調函數(shù)
UpgradeBiosClearData,? //清除接收數(shù)據(jù)緩沖回調函數(shù)
UpgradeBiosReceivePacketExitFlash,? //接收到數(shù)據(jù)包回調函數(shù)
UpgradeBiosTransEndtExitFlash //傳輸結束回調函數(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程序
//進入升級BIOS模式
void?CMD_UpBIOS(char?*pStr)
{
cmd_printf("已經(jīng)進入升級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); //數(shù)據(jù)采集時間查詢進程
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); //恢復數(shù)據(jù)采集時間查詢進程
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,存儲接口稍有不同,因為我每次將數(shù)據(jù)集齊4K才進行存儲,這樣可以提高存儲效率,降低flash損耗,當然最后一包可能不足4K,會另外進行處理的。
/*************************************************************************************************************
?*?文件名: DownFont.c
?*?功能: 下載字庫相關
?*?作者: cp1300@139.com
?*?創(chuàng)建時間: 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; //升級應用程序已經(jīng)存儲的數(shù)據(jù)大小
//收到數(shù)據(jù)包回調-用于寫數(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;
}
//傳輸結束時回調(可能是出錯結束)
//由于需要4K對齊,因此最后一包不足4K需要單獨進行處理
//需要使用到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ù)回調函數(shù)
UpgradeReadData,? //讀取數(shù)據(jù)回調函數(shù)
UpgradeClearData,? //清除接收數(shù)據(jù)緩沖回調函數(shù)
DownFontReceivePacketExitFlash,?//接收到數(shù)據(jù)包回調函數(shù)
DownFontTransEndtExitFlash //傳輸結束回調函數(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ù)
//升級應用層-需要使用到W25X16的4K臨時緩沖區(qū),在升級與校驗的時候確保沒有其它線程訪問W25X16,最好掛起其它線程
void?CMD_DownFont(char?*pStr)
{
u32?DataSize;
cmd_printf("已經(jīng)進入升級程序模式,等待連接,超時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); //數(shù)據(jù)采集時間查詢進程
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); 




