使用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:接收缓冲区(存放第一包数据)
* 返回 : <=0:响应超时;其它:接收的数据长度
* 依赖 : 通信接口
* 作者 : cp1300@139.com
* 时间 : 2013-05-08
* 最后修改时间 : 2017-03-23
* 说明 : 用于通信数据校验
2017-09-06:增加指针,用于返回第一包数据
*************************************************************************************************************************/
int XMODEM_Start(XMODE_HANDLE *pHandle, u8 **pRxBuff)
{
u32 TimeOut = pHandle->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 <= 0)
{
return 0;
}
isStart = TRUE; //开始传输
while(1)
{
if(isStart != TRUE) //非第一包数据才开始接收,因为第一包数据已经在等待开始时接收到了
{
pHandle->pClearRxData(); //清除接收缓冲区
len = pHandle->pReadData(&pRxBuff, 2,pHandle->PackTimeOutSer*1000,NULL); //接收数据
}
isStart = FALSE; //第一次开始传输状态无效
if(len <= 0)
{
retry ++;
XMODEM_SendNAK(pHandle); //发送重试
}
else
{
pHandle->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 <<= 8;
temp += pHandle->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("用户取消下载!\r\n");
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)!\r\n", 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 <<= 8;
temp += pHandle->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("用户取消下载!\r\n");
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)!\r\n", 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("用户取消下载!\r\n");
return 0;
}
}
XMODEM_SendACK(pHandle);
retry = 0;
XMODEM_DelayMS(10);
XMODEM_SendACK(pHandle);
XMODEM_DelayMS(500);
xmodem_debug("文件下载成功!\r\n");
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("下载失败,重试次数过多!\r\n");
return 0;
}
#if SYS_WDG_EN_
IWDG_Feed(); //喂狗
#endif
}
}
/*************************************************************************************************************
* 文件名: 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 <stdlib.h>
#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, "\t\t升级BIOS"}; //升级BIOS程序
//进入升级BIOS模式
void CMD_UpBIOS(char *pStr)
{
cmd_printf("已经进入升级BIOS模式,等待连接,超时10S!\r\n>");
cmd_printf("请在10S内进入Xmodem下载模式!\r\n>");
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失败,重启后将无法启动,建议重新升级!\r\n");
}
else
{
cmd_printf("升级BIOS成功!\r\n");
}
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 <stdlib.h>
#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!\r\n>");
cmd_printf("请在10S内进入Xmodem下载字库模式!\r\n>");
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("下载字库失败!\r\n>");
}
else
{
cmd_printf("下载字库成功(%dB)!\r\n",DataSize);
}
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_