当前位置:首页 > 芯闻号 > 充电吧
[导读]使用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);
本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

智能合灯控制系统由环境光照检测、人体接近检测、语音识别、按键控制、合灯控制、数据显示、蓝牙通信、报警提示功能模块组成。

关键字: STM32 智能台灯

捡球机的移动装置以直流电机驱动,寻球装置以摄像头图像传感器模块构成,并搭配以图像检测算法。避障装置以红外检测和报警模块为主,以防在行动过程中触碰障碍物。

关键字: STM32 捡球机

ST于近期发布了“STM32WBA”无线MCU、“STM32U0”超低功耗入门级MCU、“STM32H7R/S”高性能MCU和“STM32MP2”四大重磅新品,还透露将会在今年推出18nm的STM32新品。

关键字: STM32 MCU NPU AI 超低功耗

在微控制器领域,MSP430与STM32无疑是两颗璀璨的明星。它们各自凭借其独特的技术特点和广泛的应用领域,在市场上占据了重要的位置。本文将深入解析MSP430与STM32之间的区别,探讨它们在不同应用场景下的优势和局限...

关键字: MSP430 STM32 单片机

STM32是由意法半导体公司(STMicroelectronics)推出的基于ARM Cortex-M内核的32位微控制器系列,以其高性能、低功耗、丰富的外设接口和强大的生态系统深受广大嵌入式开发者喜爱。本文将详细介绍S...

关键字: STM32 单片机

STM32与51单片机之间有什么差异呢?两者可以说是一场科技与性能的较量了。在科技飞速发展的今天,微控制器(MCU)已广泛应用于各类电子设备和系统中,发挥着举足轻重的作用。其中,STM32和51单片机作为两种常见的微控制...

关键字: STM32 51单片机 MCU

电磁铁是一种利用电流产生磁场的装置,具有快速响应、易于控制等特点,在工业自动化、电子设备、科学实验等领域有着广泛的应用。STM32是一款功能强大的微控制器,具有高性能、低功耗、易于编程等优点,是控制电磁铁的理想选择。本文...

关键字: 电磁铁 微控制器 STM32

边缘人工智能的实现涉及到三个基本 要素:安全性,连接性、自主性,而其中自主性是AI能力的体现,也是边缘AI有别于其他传统的物联网的关键。而通过ST Edge AI套件,就可以帮助各种不同类型的开发者实现覆盖全硬件平台的全...

关键字: 边缘人工智能 AI STM32

今天,小编将在这篇文章中为大家带来STM32单片机最小系统的有关报道,通过阅读这篇文章,大家可以对它具备清晰的认识,主要内容如下。

关键字: 单片机 单片机最小系统 STM32

STM32是一款由STMicroelectronics生产的微控制器系列,具有高性能、低功耗和丰富的外设资源。其中,串口通信是一种常用的通信方式,可以实现与其他设备之间的数据传输。

关键字: STM32 串口通信 微控制器
关闭
关闭