Xmodem的程序可以在linux或者各种Bootloader里找到.
但是下面这个是我自己写的----2年多以前,我还没接触或"开源"的时候,想写一个自动升级程序的程序.当时主要的原因是没有烧录器,公司好几个人,就一台烧录器----感觉搬来搬去的太麻烦,效率很低,于是找了Xmodem的资料,就有了下面的程序.
--------Xmodem.h--------
#ifndef _XMODEM_H_
#define _XMODEM_H_
//Put Data Type Header Here
#include "types.h"//这里面定义了一些变量类型的别名,如U8=unsigned char等
//Put Xmodem send/receive function Header here
#include "hw_MCU.h"//这里面声明了RS_ready,RS_rx,RS_tx,分别是检测是否有在队列中的字符,接收一个字符,传送一个字符的函数,函数类型就不细说了,看下面的调用就知道了.
#define XMODEM_READY() RS_ready()
#define XMODEM_RX() RS_rx()
#define XMODEM_TX(c) RS_tx(c)
#define XMODEM_SOH (0x01)//这几个都是跟Xmodem协议有关的几个常数,如开始,结束,应答等等
#define XMODEM_EOT (0x04)
#define XMODEM_ACK (0x06)
#define XMODEM_NAK (0x15)
#define XMODEM_CAN (0x18)
#define XMODEM_PACKET_SIZE (128)
#define XMODEM_RETRY_TIME (10)
typedef struct XModem_Frame_s//这里根据Xmodem的协议定义了一个包的结构体
{
unsigned char header;
unsigned char packetnum[2];
unsigned char* pbuf;//packet[XMODEM_PACKET_SIZE];
}XModem_Frame_t;
typedef enum XMODEM_STATE//因为使用了状态机,所以就使用了枚举来定义了几个状态
{
e_XMODEM_ENTRY=0,
e_XMODEM_START,
e_XMODEM_PACKETNUMBER,
e_XMODEM_PACKETRECEIVE,
e_XMODEM_CHECKSUM,
e_XMODEM_EXITWITHERROR,
e_XMODEM_EXIT
};
typedef void (*XModem_DataPorcessFunc)(U32,U8*,U8);//这个函数指针是Xmodem里面用来处理接收到的数据的一个函数的指向,因为当时要将这部分独立出来(可以做成库),所以没有直接调用函数,当时是把这个函数指向一个烧录Flash的函数了,因为但是系统里面内存有限,不能传2MB再一次性烧录,所以只好分批烧录了.
extern U32 XModem_FSM(/*U8 debuglevel,*/XModem_DataPorcessFunc pFunc);
#endif
--------Xmodem.c--------
#include "Xmodem.h"
U8 XModem_Buffer[XMODEM_PACKET_SIZE];
U8 XModem_Getchar(void)//接受函数,这里如果接收缓冲里没有数据会一直等,可以实现为超时退出之类的
{
LOOP:
if(1==XMODEM_READY())
return XMODEM_RX();
else
goto LOOP;
}
#define XModem_Sendchar(c) XMODEM_TX(c)
void XModem_delay(U32 x)
{
U32 i=0;
for(i=0;i
}
U32 XModem_FSM(XModem_DataPorcessFunc pFunc)//这就是主函数,也是这部分的对外唯一的接口,唯一的参数就是每接收到128Byte的包(加包头不止128Byte)后,处理的函数指针,怎么处理是你的事情了,看看调用的地方,你就知道应该把什么参数传给它了.
{
U8 while1=1;
U8 state=0,c=0,i=0;
U8 x[2]={0},checksum=0;
U8 retrytime=0;
U32 packetnum=1;
while(while1)
{
switch(state)
{
case e_XMODEM_ENTRY://这一步就是通常我们看到超级终端之类的会不停的每过n秒打印个字符出来,表示可以传数据了
while(RS_ready()!=1)
{
XModem_Sendchar(XMODEM_NAK);//first send , then delay to wait. ----DONT change the sequence.
XModem_delay(120000);
}
state=e_XMODEM_START;
break;
case e_XMODEM_START://这里重新传输的标志或开始标志,更新state的值,来决定下步做什么
c=XModem_Getchar();
if(XMODEM_SOH==c)
{
state=e_XMODEM_PACKETNUMBER;
}
else if(XMODEM_EOT==c)
{
XModem_Sendchar(XMODEM_ACK);
packetnum--;
state=e_XMODEM_EXIT;
}
else
{
state=e_XMODEM_EXITWITHERROR;
}
break;
case e_XMODEM_PACKETNUMBER://这一步接受packet的标号,但是注意Xmodem里面packet号只有8位来表示,所以超过256*128Byte就不能表示出来了,所以这里用一个16bit的变量packetnum来计数
x[0]=XModem_Getchar();
x[1]=XModem_Getchar();
if((U8)0xff==(U8)(x[0]+x[1]))
{
if((U8)packetnum==x[0])
state=e_XMODEM_PACKETRECEIVE;
else
{
XModem_Sendchar(XMODEM_CAN);
state=e_XMODEM_EXITWITHERROR;
}
}
else
{
retrytime++;
if(retrytime
XModem_Sendchar(XMODEM_NAK);
state=e_XMODEM_START;
}
else
{
XModem_Sendchar(XMODEM_CAN);
state=e_XMODEM_EXITWITHERROR;
}
}
break;
case e_XMODEM_PACKETRECEIVE://接收packet
for(i=0;i
XModem_Buffer[i]=XModem_Getchar();
}
if(XMODEM_PACKET_SIZE==i)
{
state=e_XMODEM_CHECKSUM;
}
else
{
XModem_Sendchar(XMODEM_CAN);
state=e_XMODEM_EXITWITHERROR;
}
break;
case e_XMODEM_CHECKSUM://计算checksum,用的是最简单的checksum校验,原始的Xmodem
checksum=XModem_Getchar();
c=0;
for(i=0;i
c+=(XModem_Buffer[i]);
}
if(checksum==c)
{
pFunc(packetnum-1,XModem_Buffer,XMODEM_PACKET_SIZE);//注意,这里是真正处理的地方,处理的时间不能过长,据说发送端有超时结束之类的云云,但是我是烧录128Byte而已,不算很慢,超时应该是秒级的
packetnum++;
retrytime=0;
XModem_Sendchar(XMODEM_ACK);
state=e_XMODEM_START;
}
else
{
retrytime++;
if(retrytime
XModem_Sendchar(XMODEM_NAK);
state=e_XMODEM_START;
}
else
{
XModem_Sendchar(XMODEM_CAN);
state=e_XMODEM_EXITWITHERROR;
}
}
break;
case e_XMODEM_EXITWITHERROR:
//Printf("Exit Error!/r/n");//注释掉Printf的原因很简单,我用的是串口,Printf也是串口,所以我调试的时候用完就不用了
//while1=0;
//break;
case e_XMODEM_EXIT:
//Printf("Exit!/r/n");
while1=0;
break;
}
if(while1==0)
{
//Printf("Exit FSM/r/n");
}
}
return (packetnum*(U32)XMODEM_PACKET_SIZE);
}
--------Code End--------
上面的程序,已经不是我2年多之前最原始的那个版本了,虽然我觉得有不少缺陷,比如内存使用稍微有点多,但是对于表达一个清晰的实现,它很完整,而且写的过程中,我没有(写的时候没有)任何别的东西可以参考,只有一个Xmodem的简要说明.里面用上了我很喜欢的状态机,实际上不用我还不知道怎么写呢,估计很麻烦.
使用这个代码的环境是单片机上,用中断实现的串口驱动(也可以不用,如果有硬件缓冲的话),将接收到的数据放到一个FIFO里面去,RS_rx从FIFO取数据,RS_ready检查FIFO是否是空的.