STM32 Ymodem 协议及代码解析
文章导图:
1. Ymodem 协议传输效果
1.1 发送端软件效果图
- SecureCRT 软件:SecureCRT 支持 Ymodem 协议传输,可以作为接收端也可以作为发送端,这里我们作为发送端发送升级固件给 STM32 接收端。
- 自制发送端软件:这个是我用 PyQt5 制作的 STM32 IAP 升级软件,作为 Ymodem 发送端发送升级固件给 STM32 接收端。
1.2 STM32接收端效果图
STM32 作为 Ymodem 接收端接收升级固件进行升级。
2. Ymodem 协议介绍
YModem 协议是由 XModem 协议演变而来的,每包数据可以达到 1024 字节,是一个非常高效的文件传输协议。我们平常所说的 Ymodem 协议是指的 Ymodem-1K,除此还有 Ymodem-g(没有 CRC 校验,不常用)。YModem-1K 协议用 1024 字节数据帧传输取代了标准的 128 字节数据帧传输,发送的数据会使用 CRC 校验,保证数据传输的正确性。它每传输一个信息块时,就会等待接收端返回 ACK 信号,接收到响应信号后,才会继续传输下一个信息块,从而保证能够接收到全部数据。
3. Ymodem 协议的最低要求
所有声称支持 Ymodem 协议的项目必须满足以下最低要求:
- 发送端应在第一个数据包中发送路径名(文件名)。
- 路径名应为以 “NULL” 结尾的 ASCII 字符串,如下所述:
- 1)除非有特别要求,否则只发送文件名部分。
- 2)不发送驱动器符号。
- 3)不区分文件名中大小写字母的系统应仅以小写形式发送路径名。
- 接收端应使用第一个数据包中发送的路径名作为接收文件名。
- 当接收端接收到第一个数据包并成功打开输出文件后,应该返回一个 “ACK” 字符给发送端用来确认这个数据包,然后接收端再发送 “C” 或 “NAK” 来开始进行正常的 Ymodem 传输。
- 接收端在接收每个文件时要能接受 128 字节和 1024 字节两种数据包的任意混合发送。发送端可以在 1024 字节和 128 字节两种数据包之间任意切换发送。
- 发送端不能更改未确认的数据包的长度。
- 在每次文件传输的末尾,发送端只能发送最多 10 次 “EOT” 字符,直到它收到一个 “ACK” 字符。(这是 Xmodem 规范的一部分。)
- 发送路径名为空的路径名数据包来表示传输会话的结束,该路径名数据包应与其它路径名数据包一样被确认。
没有满足以上要求的程序不兼容 Ymodem 协议,不应被描述为支持 Ymodem 协议。
4. Ymodem 协议帧详解
4.1 帧格式
Ymodem 有两种帧格式,主要区别是数据长度的不同。
名称 | 结构 | 长度(byte) | 标识符 | 备注 |
帧头 | SOH/STX | 1 | 0x01/0x02 | 表示帧格式类型; SOH 为 128 字节帧,STX 为 1024 字节帧; |
帧序号 | FN | 1 | 帧序号为 1 个字节,取值范围为 0~255; 当帧序号大于 255 后,序号重新从 0 开始累加; | |
帧序号反码 | XFN | 1 | 帧序号的反码,数值为 0xFF-FN; | |
数据 | DATA | 128/1024 | 帧头是 SOH 的数据帧,数据为 128 字节,该类型帧的总长度为 133 字节; 帧头是 STX 的数据帧,数据是 1024 字节,该类型帧总长度为 1029 字节; | |
校验 | CRC | 2 | Ymodem 采用的是 CRC16 校验算法,校验值为 2 字节,传输时 CRC 高八位在前,低八位在后; CRC 计算的是数据部分,不包含帧头、帧序号和帧序号反码; |
4.2 帧命令
命令 | 命令码 | 备注 |
SOH | 0x01 | start of 128-byte data packet |
STX | 0x02 | start of 1024-byte data packet |
EOT | 0x04 | end of transmission |
ACK | 0x06 | acknowledge |
NAK | 0x15 | negative acknowledge |
CA | 0x18 | two of these in succession aborts transfer |
C | 0x43 | ‘C’ == 0x43, request command |
ABORT1 | 0x41 | ‘A’ == 0x41, abort by user |
ABORT2 | 0x61 | ‘a’ == 0x61, abort by user |
4.3 起始帧
Ymodem 的起始帧并不直接传输文件的数据,而是将文件名和文件大小放在数据段中进行传输。
它的帧结构如下所示:
名称 | 结构 | 长度(byte) | 标识符 | 备注 |
帧头 | SOH/STX | 1 | 0x01 | 起始帧为128字节帧; |
帧序号 | FN | 1 | 0x00 | 起始帧的帧序号固定为 0x00; |
帧序号反码 | XFN | 1 | 0xFF | 起始帧的帧序号反码固定为 0xFF; |
数据 | FILENAME | 要传输的文件名; | ||
数据 | FILESIZE | 要传输的文件大小; | ||
数据 | NULL | 若 FILENAME 和 FILESIZE 加起来不满 128 字节则以 0x00 填充剩余字节; | ||
校验 | CRC | 2 | 数据部分的 CRC 校验; |
- 帧头为 SOH,表示起始帧中包含着128个字节的数据。
- 起始帧帧序号固定为 0x00,帧序号反码为 0xFF。
- FILENAME 是要传输的文件名,如 Template.bin,它在数据帧中的格式为:0x54,0x65,0x6D,0x70,0x6C,0x61,0x74,0x65,0x2E,0x62,0x69,0x6E,0x00,也就是把 ASCII 码转成十六进制,但是最后一定要在文件名后加上 0x00,表示文件名的结束。
- FILESIZE 是要传输的文件大小,如上面的 Template.bin 大小 107544 byte,转换成十六进制就是 0x1A418。它在数据帧中的格式可以是十进制数的 0x31,0x30,0x37,0x35,0x34,0x34,也可以是十六进制数的 0x30,0x78,0x31,0x41,0x34,0x31,0x38,同样最后要加上 0x00 表示结束。
- NULL 是数据部分若 FILENAME 和 FILESIZE 加起来不满 128 字节则以 0x00 填充剩余字节。
- CRC 表示的是数据部分的 CRC 校验,不包含帧头、帧序号和帧序号反码。校验值为 2 字节,传输时 CRC 高八位在前,低八位在后。
4.4 数据帧
YModem 数据帧会将数据段用来传输文件。
它的帧结构如下所示:
名称 | 结构 | 长度(byte) | 标识符 | 备注 |
帧头 | SOH/STX | 1 | 0x01/0x02 | 数据帧可以是 128 字节帧或 1024 字节帧; |
帧序号 | FN | 1 | 数据帧帧序号为 0~255; | |
帧序号反码 | XFN | 1 | 帧序号的反码,数值为0xFF-FN; | |
数据 | DATA | 要传输的数据; | ||
数据 | FILL | 若 DATA 不满 128/1024 字节则以 0x1A 填充剩余字节; | ||
校验 | CRC | 2 | 数据部分的 CRC 校验; |
- 帧头为 SOH 时,表示数据帧的数据段为 128 个字节;帧头为 STX 时,表示数据帧的数据段为 1024 个字节。
- 数据帧帧序号的取值范围为 0~255,帧序号反码是它的取反;
- DATA 表示要传输的文件数据;
- FILL 是数据部分若 DATA 不满 128/1024 字节则以 0x1A 填充剩余字节。
- CRC 表示的是数据部分的 CRC 校验,不包含帧头、帧序号和帧序号反码。校验值为 2 字节,传输时 CRC 高八位在前,低八位在后。
4.5 结束帧
YModem 的结束帧也采用帧头为 SOH 的 128 字节帧,它的结构如下:
名称 | 结构 | 长度(byte) | 标识符 | 备注 |
帧头 | SOH | 1 | 0x01 | 结束帧为 128 字节帧; |
帧序号 | FN | 1 | 0x00 | 结束帧的帧序号固定为 0x00; |
帧序号反码 | XFN | 1 | 0xFF | 结束帧的帧序号反码固定为 0xFF; |
数据 | DATA | 128 | 结束帧的数据部分不存放任何信息,全部用 0x00 填充; | |
校验 | CRC | 2 | 数据部分的 CRC 校验; |
- 帧头为 SOH,表示结束帧中包含着 128 个字节的数据。
- 结束帧帧序号固定为 0x00,帧序号反码为 0xFF。
- 结束帧的 128 字节的数据部分不存放任何信息,即全部用 0x00 填充。
- CRC 表示的是数据部分的 CRC 校验,不包含帧头、帧序号和帧序号反码。校验值为 2 字节,传输时 CRC 高八位在前,低八位在后。
5. 文件传输过程
文件的传输过程我们用具体的例子来说明。将大小为 107544 byte 的文件 “Template.bin” 作为传输对象,则它的传输过程如下:
- 当发送端选择完发送文件后则进入准备发送文件状态。
- 当接收端想要开始接收文件时发送命令 ‘C’ 请求发送端开始传输文件,然后开始等待发送端的起始帧。如果发送端没有发送起始帧,则接收端可选择超时退出或者继续发送命令 ‘C’ 请求发送端开始传输文件。
- 发送端在开始时处于准备发送文件状态,等待接收端发送命令 ‘C’。在发送端收到命令 ‘C’ 后发送起始帧给接收端,内容如下(文件名为 Template.bin,文件大小为 107544 byte):
SOH 00 FF FILENAME[54 65…6E 00] FILESIZE[31 30…34 00] NULL[108] CRC
然后发送端开始等待接收端发送 ‘ACK’。 - 当接收端收到起始帧,CRC 校验通过且确定要接收此文件后,则发送 ‘ACK’ 给发送端。
- 发送端收到 ‘ACK’ 后则开始等待接收端的“请求开始传输数据”信号,即重新进入等待接收端发送命令 “C” 的状态。
- 接收端发送命令 ‘C’,请求发送端开始传输数据。
- 发送端在收到命令 ‘C’ 后,开始发送数据帧。内容如下:
STX 01 FE DATA[1024] CRC
然后发送端开始等待接收端发送 ‘ACK’。 - 接收端收到数据帧后,返回一个 ‘ACK’,然后等待接收下一个数据帧,再继续返回 ‘ACK’。直到所有数据传输完毕。
- 数据传输完毕后,发送端发送 ‘EOT’ 命令给接收端,接收端第一次收到 ‘EOT’ 命令后返回 ‘NAK’,要求进行二次确认。发送端收到 ‘NAK’ 后,重发 ‘EOT’ 命令。接收端第二次收到 ‘EOT’ 命令,返回 ‘ACK’ 应答,确认文件传输完毕。
- 最后接收端发送命令 ‘C’ 请求发送端开启下一个文件传输,发送端在没有第二个文件要传输的情况下,发送结束帧给接收端。内容如下:
SOH 00 FF NULL[128] CRC
在接收端返回 ‘ACK’ 后,正式结束数据传输。
6. STM32 代码解析
6.1 ymodem_rx 类图
--- End ---