Xmodem接收的代码

    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是否是空的.

你可能感兴趣的:(我喜欢的代码)