2025年XModem协议

XModem协议出处 XModem 协议 XModem 协议介绍 XModem 是一种在串口通信中广泛使用的异步文件传输协议 分为 XModem 和 1k XModem 协议两种 前者使用 128 字节的数据块 后者使用 1024 字节即 1k 字节的数据块 一 XModem 校验和协议 1 XModem 信息包格式 XModem 协议最早由 Ward

大家好,我是讯享网,很高兴认识大家。

出处:XModem协议

XModem协议介绍:
XModem是一种在串口通信中广泛使用的异步文件传输协议,分为XModem和1k-XModem协议两种,前者使用128字节的数据块,后者使用1024字节即1k字节的数据块。


一、XModem校验和协议

1. XModem信息包格式
XModem协议最早由Ward Christensen在20世纪70年代提出并实现的,传输数据单位为信息包,信息包格式如下:

--------------------------------------------------------------------------- |     Byte1     |    Byte2    |    Byte3      |Byte4~Byte131| Byte132   | |-------------------------------------------------------------------------| |Start Of Header|Packet Number|~(Packet Number)| Packet Data |  Check Sum | --------------------------------------------------------------------------- 

讯享网

 

2. 校验和的计算
所有的数据字节都将参与和运算,由于校验和只占一个字节,如果累加的和超过255将从零开始继续累加。


3. 字段定义
<SOH> 01H
<EOT> 04H
<ACK> 06H
<NAK> 15H
<CAN> 18H

4. 校验和方式的XModem传输流程
传输流程如图所示:

讯享网------------------------------------------------------------------------------ |               SENDER                | |          RECIEVER        | |                                     |  <---    |  NAK                      | |                                     |          |  Time out after 3 second  | |                                     |  <---    |  NAK                      | | SOH|0x01|0xFE|Data[0~127]|CheckSum| |  --->    |                           | |                                    |  <---    |  ACK                      | | SOH|0x02|0xFD|Data[0~127]|CheckSum| |  --->    |                           | |                                     |  <---    |  NAK                      | | SOH|0x02|0xFD|Data[0~127]|CheckSum| |  --->    |                           | |                                    |  <---    |  ACK                      | | SOH|0x03|0xFC|Data[0~127]|CheckSum| |  --->    |                           | |                                     |  <---    |  ACK                      | | .                                  |          |  .                        | | .                                   |          |  .                        | | .                                   |          |  .                        | |                                    |  <---    |  ACK                      | | EOT                                |  --->    |                           | |                                    |  <---    |  ACK                      | ------------------------------------------------------------------------------

对于发送方仅仅支持校验和的传输方式,接收方应首先发送NAK信号来发起传输,如果发送方没有数据发送过来,需要超时等待3秒之后再发起NAK信号来进行数据传输。对于数据传输正确,接收方需要发送ACK信号来进行确认,如果数据传输有误,则发送NAK信号,发送方在接收到NAK信号之后需要重新发起该次数据传输,如果数据已近传输完成,发送方需要发送EOT信号,来结束数据传输。

5. 如何取消数据传输
当接收方发送CAN表示无条件结束本次传输过程,发送方收到CAN后,无需发送EOT来确认,直接停止数据的发送。


二、XModem-CRC16协议

1. XModem-CRC16信息包格式
XModem协议在90年代做过一次修改,将132字节处的校验和改成双字节的CRC16校验,CRC16校验的信息包格式如下:

------------------------------------------------------------------------------ |    Byte1     |    Byte2   |     Byte3      |Byte4~Byte131|Byte132~Byte133| |----------------------------------------------------------------------------| |Start Of Header|Packet Number|~(Packet Number)| Packet Data |   16Bit CRC   | ------------------------------------------------------------------------------ 

 

2. CRC16的计算
比较复杂,表示看不懂,以后有时间再研究吧,先给出一份源代码,来自:
http://web.mit.edu/6.115/www/miscfiles/amulet/amulet-help/xmodem.htm

 

[cpp] view plain copy

  1. int calcrc(char *ptr, int count)  
  2. {  
  3.     int crc;  
  4.     char i;  
  5.   
  6.     crc = 0;  
  7.     while (--count >= 0)  
  8.     {  
  9.         crc = crc ^ (int) *ptr++ << 8;  
  10.         i = 8;  
  11.         do  
  12.         {  
  13.             if (crc & 0x8000)  
  14.                 crc = crc << 1 ^ 0x1021;  
  15.             else  
  16.                 crc = crc << 1;  
  17.         } while (--i);  
  18.     }  
  19.   
  20.     return (crc);  
  21. }  

需要注意的是,在发送方,CRC是高字节在前,低字节在后。

 

3. CRC16校验的XModem传输流程
传输流程如图所示:

 

讯享网--------------------------------------------------------------------------- |               SENDER             | |           RECIEVER        | |                                  |  <---    |  'C'                      | |                                  |          |  Time out after 3 second  | |                                  |  <---    |  'C'                      | | SOH|0x01|0xFE|Data[0~127]|CRC16| |  --->    |                           | |                                  |  <---    |  ACK                      | | SOH|0x02|0xFD|Data[0~127]|CRC16| |  --->    |                           | |                                  |  <---    |  NAK                      | | SOH|0x02|0xFD|Data[0~127]|CRC16| |  --->    |                           | |                                  |  <---    |  ACK                      | | SOH|0x03|0xFC|Data[0~127]|CRC16| |  --->    |                           | |                                  |  <---    |  ACK                      | | .                                |          |  .                        | | .                                |          |  .                        | | .                                |          |  .                        | |                                  |  <---    |  ACK                      | | EOT                              |  --->    |                           | |                                  |  <---    |  ACK                      | --------------------------------------------------------------------------- 

 

和校验和方式不同的是,当接收方要求发送方以CRC16校验方式发送数据时以'C'来请求,发送方对此做出应答,流程就如上图所示。当发送方仅仅支持校验和方式时,则接收方要发送NAK来请求,要求以校验和方式来发送数据,如果仅仅支持CRC16校验方式,则只能发送'C'来请求。如果两者都支持的话,优先发送'C'来请求,流程如图所示:

------------------------------------------------------------------------------ |               SENDER                | |           RECIEVER        | |                                     |  <---    |  'C'                      | |                                     |          |  Time out after 3 second  | |                                     |  <---    |  NAK                      | |                                     |          |  Time out after 3 second  | |                                     |  <---    |  'C'                      | |                                     |          |  Time out after 3 second  | |                                     |  <---    |  NAK                      | | SOH|0x01|0xFE|Data[0~127]|CheckSum| |  --->    |                           | |                                     |  <---    |  ACK                      | | SOH|0x02|0xFD|Data[0~127]|CheckSum| |  --->    |                           | |                                     |  <---    |  NAK                      | | SOH|0x02|0xFD|Data[0~127]|CheckSum| |  --->    |                           | |                                     |  <---    |  ACK                      | | SOH|0x03|0xFC|Data[0~127]|CheckSum| |  --->    |                           | |                                     |  <---    |  ACK                      | | .                                   |          |  .                        | | .                                   |          |  .                        | | .                                   |          |  .                        | |                                     |  <---    |  ACK                      | | EOT                                 |  --->    |                           | |                                     |  <---    |  ACK                      | ------------------------------------------------------------------------------ 

最后,如果信息包中的数据如果不足128字节,剩余的部分要以0x1A(Ctrl-Z)来填充。


三、1k-XModem协议
1k-XModem协议同XModem-CRC16协议差不多,只是数据块长度变成了1024字节即1k,同时每个信息报的第一个字节的SOH变成了STX,STX定义为 <STX> 0x02,能有效的加快数据传输速率。

 

 

 

使用Java实现Xmodem协议

 

 

 

Xmodem协议
讯享网

 

1.介绍

标准Xmodem协议(使用128字节的数据块)帧格式:

Byte1 Byte2 Byte3 Byte4 ~ byte131 Byte132
控制字符 包序号 包序号的反码 数据 校验和

如果你对串口通信还不太了解,可以看下我写的这篇博客使用Java实现串口通信。

2.实现

讯享网 public class Xmodem { // 开始 private final byte SOH = 0x01; // 结束 private final byte EOT = 0x04; // 应答 private final byte ACK = 0x06; // 重传 private final byte NAK = 0x15; // 无条件结束 private final byte CAN = 0x18; // 以128字节块的形式传输数据 private final int SECTOR_SIZE = 128; // 最大错误(无应答)包数 private final int MAX_ERRORS = 10; // 输入流,用于读取串口数据 private InputStream inputStream; // 输出流,用于发送串口数据 private OutputStream outputStream; public Xmodem(InputStream inputStream, OutputStream outputStream) { this.inputStream = inputStream; this.outputStream = outputStream; } / * 发送数据 * * @param filePath * 文件路径 */ public void send(final String filePath) { new Thread() { public void run() { try { // 错误包数 int errorCount; // 包序号 byte blockNumber = 0x01; // 校验和 int checkSum; // 读取到缓冲区的字节数量 int nbytes; // 初始化数据缓冲区 byte[] sector = new byte[SECTOR_SIZE]; // 读取文件初始化 DataInputStream inputStream = new DataInputStream( new FileInputStream(filePath)); while ((nbytes = inputStream.read(sector)) > 0) { // 如果最后一包数据小于128个字节,以0xff补齐 if (nbytes < SECTOR_SIZE) { for (int i = nbytes; i < SECTOR_SIZE; i++) { sector[i] = (byte) 0xff; } } // 同一包数据最多发送10次 errorCount = 0; while (errorCount < MAX_ERRORS) { // 组包 // 控制字符 + 包序号 + 包序号的反码 + 数据 + 校验和 putData(SOH); putData(blockNumber); putData(~blockNumber); checkSum = CRC16.calc(sector) & 0x00ffff; putChar(sector, (short) checkSum); outputStream.flush(); // 获取应答数据 byte data = getData(); // 如果收到应答数据则跳出循环,发送下一包数据 // 未收到应答,错误包数+1,继续重发 if (data == ACK) { break; } else { ++errorCount; } } // 包序号自增 blockNumber = (byte) ((++blockNumber) % 256); } // 所有数据发送完成后,发送结束标识 boolean isAck = false; while (!isAck) { putData(EOT); isAck = getData() == ACK; } } catch (Exception e) { e.printStackTrace(); } }; }.start(); } / * 接收数据 * * @param filePath * 文件路径 * @return 是否接收完成 * @throws IOException * 异常 */ public boolean receive(String filePath) throws Exception { // 错误包数 int errorCount = 0; // 包序号 byte blocknumber = 0x01; // 数据 byte data; // 校验和 int checkSum; // 初始化数据缓冲区 byte[] sector = new byte[SECTOR_SIZE]; // 写入文件初始化 DataOutputStream outputStream = new DataOutputStream( new FileOutputStream(filePath)); // 发送字符C,CRC方式校验 putData((byte) 0x43); while (true) { if (errorCount > MAX_ERRORS) { outputStream.close(); return false; } // 获取应答数据 data = getData(); if (data != EOT) { try { // 判断接收到的是否是开始标识 if (data != SOH) { errorCount++; continue; } // 获取包序号 data = getData(); // 判断包序号是否正确 if (data != blocknumber) { errorCount++; continue; } // 获取包序号的反码 byte _blocknumber = (byte) ~getData(); // 判断包序号的反码是否正确 if (data != _blocknumber) { errorCount++; continue; } // 获取数据 for (int i = 0; i < SECTOR_SIZE; i++) { sector[i] = getData(); } // 获取校验和 checkSum = (getData() & 0xff) << 8; checkSum |= (getData() & 0xff); // 判断校验和是否正确 int crc = CRC16.calc(sector); if (crc != checkSum) { errorCount++; continue; } // 发送应答 putData(ACK); // 包序号自增 blocknumber++; // 将数据写入本地 outputStream.write(sector); // 错误包数归零 errorCount = 0; } catch (Exception e) { e.printStackTrace(); } finally { // 如果出错发送重传标识 if (errorCount != 0) { putData(NAK); } } } else { break; } } // 关闭输出流 outputStream.close(); // 发送应答 putData(ACK); return true; } / * 获取数据 * * @return 数据 * @throws IOException * 异常 */ private byte getData() throws IOException { return (byte) inputStream.read(); } / * 发送数据 * * @param data * 数据 * @throws IOException * 异常 */ private void putData(int data) throws IOException { outputStream.write((byte) data); } / * 发送数据 * * @param data * 数据 * @param checkSum * 校验和 * @throws IOException * 异常 */ private void putChar(byte[] data, short checkSum) throws IOException { ByteBuffer bb = ByteBuffer.allocate(data.length + 2).order( ByteOrder.BIG_ENDIAN); bb.put(data); bb.putShort(checkSum); outputStream.write(bb.array()); } } 

CRC16校验算法,采用的是查表法。

 public class CRC16 { private static final char crctable[] = { 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 }; public static char calc(byte[] bytes) { char crc = 0x0000; for (byte b : bytes) { crc = (char) ((crc << 8) ^ crctable[((crc >> 8) ^ b) & 0x00ff]); } return (char) (crc); } } 

3.使用

讯享网 // serialPort为串口对象 Xmodem xmodem = new Xmodem(serialPort.getInputStream(),serialPort.getOutputStream()); // filePath为文件路径 // ./bin/xxx.bin xmodem.send(filePath); 

4.写在最后

完整的代码下载



作者:容华谢后
链接:https://www.jianshu.com/p/6dabbfe61495
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

 

 

2018.06.08 10.58

      xmodem协议模块今天测试完毕了,说下我的思路和感受。写之前一定要有一个大题的思路,

这个东西做成什么样子(API接口设计,状态机设计),然后就是画各个状态图->合并状态图->源码实现。

其实我比较懒,懒得动笔画,我习惯写之前把状态枚举出来,然后写的过程中会根据思路增加或者减少状态。

       xmodem的API接口:

bool xmodem_init( xmodem_t *ptXmodem,user_api_t *ptApi); bool xmodem_start_rx( xmodem_t *ptXmodem,xmodem_select_check_mode_t tCheckMode,uint16_t hwFrameLong); bool xmodem_cfg_tx_mode(xmodem_t *ptXmodem,uint16_t hwFrameLong); bool xmodem_cancel_rx( xmodem_t *ptXmodem); fsm_rt_t xmodem_tx( xmodem_t *ptXmodem); fsm_rt_t xmodem_check( void *ptXmodem,bool *pbIsRequestDrop,queue_peek_byte_t* ptReadByteHandler);

    模式:

讯享网typedef enum{ XMODEM_CHECKOUT_SUM = 0, XMODEM_CHECKOUT_CRC16, XMODEM_CHECKOUT_AUTO, }xmodem_select_check_mode_t;

注:

1、发送模式自适应,无需配置模式;

2、接收模式三种模式都可以配置,如果配置为自适应,则'C'和NAK每隔3s交替发送;

3、帧长只做了1024和非1024(最大帧长为1024),非1024按着128格式走;

4、不支持收和发文件同时进行;   

5、接收线程由协议解析引擎驱动(每次进入接收都需要用户启动);

6、发送函数需要由用户发送线程驱动(开始发送前需要配置帧长,默认128);

 

      接收状态机,相对于发送状态机要复杂一些,其中有几个状态是独立:强制停止接收态,发送模式

接收协议解析,这几个状态是通过API强制改变的。发送模式简单分为三大部分:发送启动字符(每隔3s发一次),

第一帧协议接收(这里面可能跳转到发送启动字符状态),后续帧接收(这里面有发送ACK和NAK)。

typedef enum{ XMODEM_CHECK_STATE_START = 0, XMODEM_CHECK_STATE_WAIT_START_RX_FLAG, XMODEM_CHECK_STATE_START_UP_TX_C, XMODEM_CHECK_STATE_START_UP_TX_NAK, XMODEM_CHECK_STATE_START_UP_WAIT_FRAME_START, XMODEM_CHECK_STATE_START_UP_WAIT_FRAME_NUM, XMODEM_CHECK_STATE_START_UP_WAIT_FRAME_CHECK_NUM, XMODEM_CHECK_STATE_START_UP_WAIT_FRAME_DATA, XMODEM_CHECK_STATE_START_UP_FRAME_CHECK, XMODEM_CHECK_STATE_WAIT_FRAME_START, XMODEM_CHECK_STATE_WAIT_FRAME_DATA, XMODEM_CHECK_STATE_WAIT_FRAME_CHECK, XMODEM_CHECK_STATE_WAIT_RETURN_ACK, XMODEM_CHECK_STATE_WAIT_RETURN_NAK, XMODEM_CHECK_STATE_RX_EOT, XMODEM_CHECK_STATE_USER_FORCE_STOP_RX, //TX XMODEM_CHECK_STATE_TX_CHECK_START_FLAG, XMODEM_CHECK_STATE_TX_WAIT_RETURN, }xmodem_check_state_t;

        xmodem的接收模式就相对简单了,读取字符串流,打包发送,等待返回字符。

讯享网typedef enum{ XMODEM_TX_STATE_START = 0, XMODEM_TX_STATE_IS_INIT_API, XMODEM_TX_STATE_WAIT_START_FLAG, XMODEM_TX_STATE_READ_BUFFER, XMODEM_TX_STATE_TX, XMODEM_TX_STATE_WAIT_RETURN, XMODEM_TX_STATE_TX_EOT, }xmodem_tx_state_t;

       xmodem需要外部提供一些API借口,如下:

typedef void start_timer(uint16_t hwDelayMs); typedef bool check_timer_flag(void); typedef bool user_buffer_write(uint8_t *pchBuffer,uint16_t hwNum); typedef uint16_t user_buffer_read( uint8_t *pchBuffer,uint16_t hwNum); typedef fsm_rt_t xmodem_serial_out(uint8_t *pchBuffer,uint16_t hwSize); typedef struct _user_api_t user_api_t; struct _user_api_t{ //延时相关 start_timer *ptStartTimer; check_timer_flag *ptCheckTimerFlag; //APP层对接 user_buffer_write *ptWriteBuffer; user_buffer_read *ptReadBuffer; //输出数据 xmodem_serial_out *ptSerialOut; };

说明:

1、start_timer和check_timer_flag延时相关函数;

2、user_buffer_write向用户输出数据流,完整一帧协议;

3、user_buffer_read读取用户要发送数据,每次读取一帧长度,返回实际读取数据长度,如果为0,则表示文件发送完成;

4、xmodem_serial_out向UART的发送Buffer写入数据,状态机方式,主要是考虑UART的发送buffer小于帧长;

源码我就不发布了,没啥意义,需要配合我的协议解析引擎和queue队列才能使用。

 

2018.07.30

前几天看了遍代码,总是感觉哪里不对,但是又说不上来,今天看了个相似历程,算是明白自己的代码哪里有问题了:

1、状态机太"复杂"了,这里的复杂是指一个函数里面跑了几个状态机,都在一个平面;不是说一个函数不能跑几个状态机,

而是状态机应该是嵌套关系,不应该是平铺的关系;

2、数据流程没有去抽象,用了一个很大的函数去处理,也就是没有所谓的层次感;

总结就是,写代码应该是像搭积木,用一块块积木去拼凑自己的模型;而不应该是像摊大饼一样,平铺在一起。

修改代码,回头再发上来。

2018.08.02

今天代码改造完了,有了纵向的层次感,想积木一样,一层一层搭建起来。但是里面仍然有一些根据初始化配置不同而处理

逻辑不同,比如:长度是128还是1K,校验方式是校验和还是CRC16。这些怎么处理?

我们可以这样处理,逻辑层抽象出通用的API接口,把上述四种情况封装成四个处理函数,然后在初始化时候根据不同的

配置初始化指针,这样对于逻辑层来说处理逻辑完全是通用的。

 

2018.08.16

今天抽个时间把代码改了下,以前的处理逻辑是这样的:

模式定义:

讯享网typedef enum{ XMODEM_128_SUM = 0, XMODEM_128_CRC16, XMODEM_1K_SUM, XMODEM_1K_CRC16, }xmodem_select_mode_t;

初始化:

bool xmodem_init(xmodem_t *ptXmodem,xmodem_cfg_t *ptCfg,user_api_t *ptApi) { CLASS(xmodem_t) *ptThis = (CLASS(xmodem_t) *)ptXmodem; if(NULL == ptThis || NULL == ptCfg || NULL == ptApi){ return false; } if(NULL == ptApi->ptWriteBuffer || NULL == ptApi->ptStartTimer || NULL == ptApi->ptCheckTimerFlag || NULL == ptApi->ptSerialRead || NULL == ptApi->ptSerialWrite || NULL == ptCfg->pchBuffer ){ this.ptUserApi = NULL; this.tUserApiIsInitFlag = false; return false; } this.tXmodemSelectMode = ptCfg->tXmodemSelectMode; this.pchBuffer = ptCfg->pchBuffer; this.chByte = 0x00; this.ptUserApi = ptApi; this.tUserApiIsInitFlag = true; return true; }

下面的处理逻辑:

讯享网 switch(this.tXmodemSelectMode){ case XMODEM_128_SUM: //break; case XMODEM_128_CRC16: if(SOH == this.pchBuffer[s_hwRevCnt]){ s_tState = XMODEM_REV_FRAME_DATA; s_hwRevCnt++; }else if(EOT == this.pchBuffer[s_hwRevCnt]){ RST_XMODEM_REV_FRAME_FSM(); return XMODEM_REV_FRAME_RX_CPL; }else{ //RST_XMODEM_REV_FRAME_FSM(); return XMODEM_REV_FRAME_DROP; } break; case XMODEM_1K_SUM: //break; case XMODEM_1K_CRC16: if(STX == this.pchBuffer[s_hwRevCnt]){ s_tState = XMODEM_REV_FRAME_DATA; s_hwRevCnt++; }else if(EOT == this.pchBuffer[s_hwRevCnt]){ RST_XMODEM_REV_FRAME_FSM(); return XMODEM_REV_FRAME_RX_CPL; }else{ //RST_XMODEM_REV_FRAME_FSM(); return XMODEM_REV_FRAME_DROP; } break; default: while(1); }
 if(XMODEM_REV_FRAME_CPL == tTemp){ s_hwRevCnt++; switch(this.tXmodemSelectMode){ case XMODEM_128_SUM: if(132 > s_hwRevCnt){ break; } chTemp = calsum(this.pchBuffer,s_hwRevCnt-1); if(chTemp == this.pchBuffer[s_hwRevCnt-1]){ RST_XMODEM_REV_FRAME_FSM(); return XMODEM_REV_FRAME_CPL; }else{ RST_XMODEM_REV_FRAME_FSM(); return XMODEM_REV_FRAME_CHECK_ERROR; } break; case XMODEM_128_CRC16: if(133 > s_hwRevCnt){ break; } hwTemp = calcrc(this.pchBuffer,s_hwRevCnt-2); tCrc16.chCrcL = this.pchBuffer[s_hwRevCnt-1]; tCrc16.chCrcH = this.pchBuffer[s_hwRevCnt-2]; if(tCrc16.hwCrc16 == hwTemp){ RST_XMODEM_REV_FRAME_FSM(); return XMODEM_REV_FRAME_CPL; }else{ RST_XMODEM_REV_FRAME_FSM(); return XMODEM_REV_FRAME_CHECK_ERROR; } break; case XMODEM_1K_SUM: if(1028 > s_hwRevCnt){ break; } chTemp = calsum(this.pchBuffer,s_hwRevCnt-1); if(chTemp == this.pchBuffer[s_hwRevCnt-1]){ RST_XMODEM_REV_FRAME_FSM(); return XMODEM_REV_FRAME_CPL; }else{ RST_XMODEM_REV_FRAME_FSM(); return XMODEM_REV_FRAME_CHECK_ERROR; } break; case XMODEM_1K_CRC16: if(1029 > s_hwRevCnt){ break; } hwTemp = calcrc(this.pchBuffer,s_hwRevCnt-2); tCrc16.chCrcL = this.pchBuffer[s_hwRevCnt-1]; tCrc16.chCrcH = this.pchBuffer[s_hwRevCnt-2]; if(tCrc16.hwCrc16 == hwTemp){ RST_XMODEM_REV_FRAME_FSM(); return XMODEM_REV_FRAME_CPL; }else{ RST_XMODEM_REV_FRAME_FSM(); return XMODEM_REV_FRAME_CHECK_ERROR; } break; default: while(1); }

有几处代码都是类似的形式,看着是不是辣眼睛?后来改为了这样:

初始化:

讯享网bool xmodem_init(xmodem_t *ptXmodem,xmodem_cfg_t *ptCfg,user_api_t *ptApi) { CLASS(xmodem_t) *ptThis = (CLASS(xmodem_t) *)ptXmodem; if(NULL == ptThis || NULL == ptCfg || NULL == ptApi){ return false; } if(NULL == ptApi->ptWriteBuffer || NULL == ptApi->ptStartTimer || NULL == ptApi->ptCheckTimerFlag || NULL == ptApi->ptSerialRead || NULL == ptApi->ptSerialWrite || NULL == ptCfg->pchBuffer ){ this.ptUserApi = NULL; this.tUserApiIsInitFlag = false; return false; } switch(ptCfg->tXmodemSelectMode){ case XMODEM_128_SUM: this.cRxFrameHead = SOH; this.hwRxFrameSize = 128+3+1; this.cTxStartUp = SUM_SAT; this.ptXmodemCheck = xmodem_sum_check; break; case XMODEM_128_CRC16: this.cRxFrameHead = SOH; this.hwRxFrameSize = 128+3+2; this.cTxStartUp = CRC16_SAT; this.ptXmodemCheck = xmodem_crc_check; break; case XMODEM_1K_SUM: this.cRxFrameHead = STX; this.hwRxFrameSize = 1024+3+1; this.cTxStartUp = SUM_SAT; this.ptXmodemCheck = xmodem_sum_check; break; case XMODEM_1K_CRC16: this.cRxFrameHead = STX; this.hwRxFrameSize = 1024+3+2; this.cTxStartUp = CRC16_SAT; this.ptXmodemCheck = xmodem_crc_check; break; default: return false; } this.pchBuffer = ptCfg->pchBuffer; this.chByte = 0x00; this.ptUserApi = ptApi; this.tUserApiIsInitFlag = true; return true; }

然后下面的处理:

 if(this.cRxFrameHead == this.pchBuffer[s_hwRevCnt]){ s_tState = XMODEM_REV_FRAME_DATA; s_hwRevCnt++; }else if(EOT == this.pchBuffer[s_hwRevCnt]){ RST_XMODEM_REV_FRAME_FSM(); return XMODEM_REV_FRAME_RX_CPL; }else{ //RST_XMODEM_REV_FRAME_FSM(); return XMODEM_REV_FRAME_DROP; }
讯享网 s_hwRevCnt++; if(this.hwRxFrameSize > s_hwRevCnt){ break; } if(this.ptXmodemCheck(ptXmodem)){ RST_XMODEM_REV_FRAME_FSM(); return XMODEM_REV_FRAME_CPL; }else{ RST_XMODEM_REV_FRAME_FSM(); return XMODEM_REV_FRAME_CHECK_ERROR; }

这样看是不是舒服很多。。。。。。

小讯
上一篇 2025-03-09 15:00
下一篇 2025-01-05 09:36

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/30085.html