ARTICLE · 1326558
YMODEM 串口升级协议技术文档
1概述
1.1协议简介
YMODEM 是由 Chuck Forsberg 于 1985 年在 XMODEM 基础上改进提出的一种面向串行链路的文件传输协议。它在 XMODEM 的帧格式之上引入了 1024 字节大块、CRC-16 校验、文件名与文件大小传输以及批量文件传输(YMODEM Batch)等能力,至今仍是嵌入式设备通过 UART/RS-232/USB 虚拟串口进行固件升级(Bootloader OTA)最常用的协议之一。
协议本质是“停等式”(stop-and-wait)滑动窗口协议:发送方每发一个数据块,必须等待接收方回复 ACK 后才能继续发下一块;任何一块出错或超时未应答,发送方重发当前块。这种机制实现简单、可靠,对底层链路要求低,非常适合 MCU 片上 Bootloader。
1.2典型应用场景
YMODEM 在工业控制、伺服驱动、变频器、仪器仪表等设备的串口固件升级中被广泛采用。典型上位机工具包括 Tera Term、SecureCRT、Xshell、minicom、sz/rz 命令行等,设备侧则由 Bootloader 实现接收端逻辑。
1.3主要特点
(1)数据块长度可选:128 字节(SOH 头)或 1024 字节(STX 头),长块显著提升吞吐率。
(2)校验方式:默认采用 CRC-16(CCITT 多项式 0x1021),替代了早期 XMODEM 的累加和校验,误检率极低。
(3)文件信息随附:第 0 块携带文件名、文件大小、修改时间,接收方可据此分配缓冲区或擦除 Flash 区域。
(4)支持批量传输:一次会话可连续传送多个文件,以空第 0 块作为批结束标志。
(5)实现成本低:Bootloader 侧只需实现串口收发、CRC 计算和简单状态机,代码量通常在 1~3 KB。
2物理层与传输参数
YMODEM 本身不规定物理层,它运行在任意字节透明的串行通道之上。嵌入式升级中常见配置如下:
参数 | 典型取值 | 说明 |
物理接口 | UART TTL / RS-232 / RS-485 / USB-CDC | 要求全双工或可切换方向的半双工链路 |
数据位 | 8 | 标准 8N1 |
校验位 | None | 链路层校验由 YMODEM 自身 CRC 完成 |
停止位 | 1 | 8N1 配置 |
波特率 | 9600 / 115200 / 460800 / 921600 | Bootloader 与上位机必须一致 |
流控 | 通常不使用硬件流控 | 依靠协议层 ACK/NAK 做停等 |
表 1典型串口参数
3控制字符定义
YMODEM 协议使用一组固定的单字节控制符,它们同时也是 ASCII 控制字符,取值不可与数据域混淆(数据域中的相同字节由上层保证不会与帧首冲突)。
符号 | 十六进制 | ASCII 含义 | 方向 | 作用 |
SOH | 0x01 | Start of Heading | 发送方→接收方 | 标识本块数据长度为 128 字节 |
STX | 0x02 | Start of Text | 发送方→接收方 | 标识本块数据长度为 1024 字节 |
EOT | 0x04 | End of Transmission | 发送方→接收方 | 文件内容发送完毕 |
ACK | 0x06 | Acknowledge | 接收方→发送方 | 上一块正确接收,请继续下一块 |
NAK | 0x15 | Negative Acknowledge | 接收方→发送方 | 上一块出错(CRC 错),请重发 |
CAN | 0x18 | Cancel | 双向 | 取消当前传输(连发两次 CAN 才生效) |
C | 0x43 | ASCII 'C' | 接收方→发送方 | 请求以 CRC-16 模式启动传输 |
表 2YMODEM 控制字符一览
注意:早期 XMODEM 以 NAK(0x15)作为启动符,表示请求累加和校验;YMODEM 统一改用字符 “C”(0x43)作为启动符,表示请求 CRC-16 校验。Bootloader 上电后应循环发送 “C” 等待上位机发起传输。
4帧结构
4.1通用帧格式
YMODEM 的每个数据块(Block)由 6 个部分组成,总长为:头(1) + 块号(1) + 补码块号(1) + 数据(N) + CRC16(2)。
字段 | 长度(字节) | 取值说明 |
Header | 1 | SOH(0x01) 表示 128 字节数据;STX(0x02) 表示 1024 字节数据 |
Block Number | 1 | 块号,从 0 开始;数据块从 1 开始;8 位无符号,超过 255 后回绕到 0 |
~Block Number | 1 | 块号的按位取反(即 255 - Block Number),用于校验帧头同步 |
Data | 128 或 1024 | 负载数据;不足长度时按协议规则填充 |
CRC High | 1 | CRC-16 高字节(先发送) |
CRC Low | 1 | CRC-16 低字节(后发送) |
表 3YMODEM 数据帧结构
整帧长度示意(以 1024 字节块为例):
| SOH/STX | BlockNo | ~BlockNo | Data[1024] | CRC_H | CRC_L |
1B1B1B1024B1B1B= 1029 B
4.2块号规则
块号是协议层排序与去重的核心:
(1)第 0 块(Block 0)专用于携带文件名、文件大小等元信息,不携带固件数据。
(2)固件数据从第 1 块(Block 1)开始编号。
(3)块号为 8 位无符号整数,递增加 1;到达 255 后下一块为 0(回绕)。
(4)接收方若收到“预期块号不一致”的块(例如重复块),仍应回复 ACK,但丢弃数据,以维持同步。
(5)补码块号字段用于检测帧头错位:若 BlockNo 与 ~BlockNo 之和不等于 0xFF,说明链路字节错位,应直接 NAK 或丢弃该帧。
4.3CRC-16 校验
YMODEM 使用 CCITT 标准 CRC-16,多项式为 0x1021,初始值为 0x0000(注意:不是 Modbus 常用的 0xFFFF 初始值)。计算范围覆盖整个 Data 字段(128 或 1024 字节),不包含 Header、块号与 CRC 字段本身。
CRC 以“高字节在前、低字节在后”的顺序追加在数据域之后,这一点与 Modbus RTU 的低字节在前不同,实现时需特别注意。
CRC-16/CCITT 参数:多项式= 0x1021初始值= 0x0000输入反射 = False输出反射 = False异或输出 = 0x0000
5块类型详解
5.1第 0 块:文件信息头块
第 0 块在整个传输流程中承担“握手 + 文件元信息”的作用。数据域布局如下:
偏移 | 长度 | 内容 | 说明 |
0 | 变长 | 文件名 | ASCIZ 字符串(以 0x00 结尾),通常为固件名如 app.bin |
紧随文件名 | 变长 | 文件大小 | 十进制 ASCII 数字字符串,如“123456”,以 0x00 结尾 |
紧随大小 | 变长 | 修改时间 | 可选,Unix 时间戳八进制 ASCII,以 0x00 结尾 |
紧随时间 | 变长 | 文件模式 | 可选,八进制 ASCII,如“0644” |
剩余 | 填充 | 0x00 | 数据域剩余字节全部填 0 |
表 4第 0 块数据域布局
示例:一个大小为 0x1E000(122880 字节)、文件名为 app.bin 的头块数据域前若干字节为:
61 70 70 2E 62 69 6E 00 31 32 32 38 38 30 00 00 00 ...|"app.bin"| | "122880" | 后续全部 0x00 填充
批传输结束标志:当一个第 0 块的数据域第一个字节就是 0x00(即文件名为空),表示当前没有更多文件可传,接收方据此结束整个会话。
5.2数据块(Block 1 起)
从 Block 1 开始,数据域直接承载固件二进制内容。关于块长度与填充规则:
(1)默认使用 1024 字节长块(STX),以提高吞吐;128 字节短块(SOH)用于低速或不可靠链路。
(2)最后一个数据块若不足 1024 字节,剩余字节必须以 0x1A(Ctrl+Z)填充,这是 YMODEM 与 XMODEM 兼容的历史约定;接收方在写入 Flash 时应按第 0 块声明的文件大小截断,丢弃填充字节。
(3)接收方每收到一个正确数据块,应立即将数据写入目标 Flash 地址,再回复 ACK;这种“边收边写”策略可避免占用大块 RAM。
5.3EOT 结束
发送方发完最后一个数据块并收到 ACK 后,发送单字节 EOT(0x04)。接收方收到 EOT 后,第一次应回复 NAK(0x15),发送方随后再次发送 EOT,接收方再回复 ACK。这种“EOT → NAK → EOT → ACK”的两次握手是 YMODEM的标准约定,用于防止 EOT 字节在链路中丢失而导致死等。
5.4批传输结束空块
单文件升级场景中,Bootloader 收到 EOT 的 ACK 后即可直接跳转应用。若上位机支持批量传输,则 EOT 流程结束后,上位机会再发送一个第 0 块(数据域全 0),接收方回复 ACK 后整个会话结束。Bootloader 通常只实现单文件模式,收到空第 0 块后直接结束即可。
6完整升级流程
6.1时序总览
以下以 Bootloader 为接收方、上位机(如Tera Term)为发送方,描述一次完整的单文件 YMODEM 升级时序:
Bootloader(接收)上位机(发送)|||--- "C" (循环发送, 每 1~3s) -->||| 收到 "C"|<-- SOH Blk0 ~Blk0 "app.bin 122880" CRC --||解析文件名/大小, 擦除 Flash||--- ACK ----------------------->||--- "C" ------------------------>||| 开始发数据|<-- STX Blk1 ~Blk1 data[1024] CRC ---||写 Flash, 校验通过||--- ACK ----------------------->||<-- STX Blk2 ~Blk2 data[1024] CRC ---||--- ACK ----------------------->||... 重复 ...||<-- STX BlkN ~BlkN data[剩余+0x1A] CRC||--- ACK ----------------------->||||<-- EOT (0x04) ------------------||--- NAK (0x15) ----------------->||<-- EOT (0x04) ------------------||--- ACK ------------------------>||--- "C" (可选, 等下一文件) ----->||<-- SOH Blk0 全 0 (批结束) -------||--- ACK ------------------------>||校验固件, 跳转应用|
6.2启动阶段
(1)设备上电或复位后,Bootloader 先判断是否进入升级模式(例如检测某 GPIO 为低电平、按键按下、或应用区标志位无效)。
(2)进入升级模式后,Bootloader 通过串口循环发送字符 “C”(0x43),发送间隔通常为 1~3 秒,持续若干次(例如 10 次),超时则放弃升级、跳转应用。
(3)上位机在收到第一个 “C” 后,立即发送第 0 块(文件信息块)。
6.3文件头传输
(1)Bootloader 接收第 0 块,校验 SOH 标志、块号 0 / ~0 = 0xFF、CRC-16。
(2)解析数据域:提取文件名(ASCIZ)与文件大小(十进制ASCII)。文件大小是后续写入Flash 的关键依据。
(3)根据文件大小擦除目标 Flash 扇区,准备接收数据。
(4)回复 ACK,并再次发送 “C”,通知上位机开始发送数据块。
6.4数据块传输
(1)Bootloader 进入“等块号 1”状态。
(2)收到一帧后依次检查:帧头(SOH/STX)、块号是否等于预期值、~块号、CRC-16。
(3)全部通过:将数据写入 Flash(按声明的文件大小截断),预期块号加 1(回绕),回复 ACK。
(4)CRC 错误:不写 Flash,回复 NAK,请求重发同一号块。
(5)块号重复(例如 ACK 丢失导致发送方重发):不写 Flash,直接回复 ACK,不推进预期块号。
6.5结束阶段
(1)收到 EOT:回复 NAK,等待第二次 EOT。
(2)再次收到 EOT:回复 ACK,表示文件接收完成。
(3)(可选)继续发送 “C” 等待下一文件;若收到全 0 的第 0 块,则回复 ACK 后结束会话。
(4)Bootloader 对刚写入的固件做完整性校验(例如 CRC 或 SHA),通过后设置启动标志并跳转应用入口。
7错误处理与重传机制
异常情况 | 接收方动作 | 发送方动作 |
CRC 校验失败 | 不写 Flash,回复 NAK | 重发当前号块 |
块号不匹配(非预期块) | 丢弃数据,回复 ACK | 收到下一 ACK 后推进 |
ACK 丢失 | 已回复 ACK 但发送方未收到 | 超时后重发当前号块;接收方识别为重复块并回 ACK |
EOT 丢失 | 未收到 EOT,持续等待 | 超时后重发 EOT |
连续 NAK 超过上限(如 10 次) | 放弃传输,回复 CAN 两次 | 收到双 CAN 后终止 |
接收方等待超时(如 3 min 无数据) | 自动跳转应用或进入空闲 | — |
用户主动取消 | 连发两次 CAN | 收到双 CAN 后终止传输 |
表 5错误处理与重传策略
CAN 必须连发两次(0x18 0x18)才被视为有效取消命令,这是为了避免链路上偶然出现的单个 0x18 字节被误判为取消。
8Bootloader 状态机设计
Bootloader 侧的 YMODEM 接收逻辑可建模为以下有限状态机,各状态与转移条件清晰,便于在 MCU 上实现:
状态 | 进入条件 | 主要动作 | 退出转移 |
WAIT_C | 进入升级模式 | 循环发“C”,超时计数 | 收到第 0 块 -> WAIT_FILE_ACK |
WAIT_FILE_ACK | 收到第 0 块 | 解析文件名/大小,擦 Flash | 发 ACK + “C” -> WAIT_DATA |
WAIT_DATA | 准备好接收数据 | 收帧、校验、写 Flash、回 ACK/NAK | 收到 EOT -> WAIT_EOT2 |
WAIT_EOT2 | 第一次收到 EOT | 回 NAK | 再次收到 EOT -> END |
END | 第二次 EOT 已回 ACK | 校验固件、跳转应用 | — |
ABORT | 任意状态出错超限或双 CAN | 恢复串口,跳转应用 | — |
表 6Bootloader YMODEM 接收状态机
9XMODEM / YMODEM / YMODEM-G 对比
特性 | XMODEM | YMODEM | YMODEM-G |
数据块长度 | 128 字节 | 128 / 1024 字节 | 1024 字节 |
校验方式 | 累加和 | CRC-16 | CRC-16 |
启动字符 | NAK (0x15) | 'C' (0x43) | 'C' (0x43) |
文件名/大小 | 无 | 第 0 块携带 | 第 0 块携带 |
停等 ACK | 是 | 是 | 否(连续发送) |
批量文件 | 不支持 | 支持 | 支持 |
适用场景 | 低速不可靠链路 | 通用串口升级 | 可靠链路(如 USB-CDC) |
典型吞吐 | ~900 B/s @9600 | ~8 KB/s @115200 | 接近波特率上限 |
表 7XMODEM 家族主要协议对比
10嵌入式实现要点
10.1缓冲区设计
(1)必须为 1024 字节长块准备完整接收缓冲区。RAM 紧张的 MCU 可采用 DMA 双缓冲或边收边写 Flash 的流式方案。
(2)帧的 1029 字节必须在一个连续接收窗口内收齐,串口超时(如 1 秒未收到新字节)即视为帧不完整,应丢弃并 NAK。
10.2Flash 写入策略
(1)先按第 0 块声明的文件大小擦除对应扇区,避免边擦边写导致的数据丢失。
(2)每收到一个正确数据块,立即写入 Flash 当前偏移地址,并推进写指针。
(3)最后一块按文件大小截断写入,0x1A 填充字节不得写入 Flash。
10.3超时与看门狗
(1)Bootloader 进入升级模式后,应设置整体超时(例如 3 分钟),超时未完成则自动跳转应用,避免设备变砖。
(2)长时间写 Flash 或等待 ACK 期间需喂狗,否则看门狗复位会导致升级中断。
10.4健壮性建议
(1)串口接收使用环形缓冲区 + 中断,避免轮询丢字节。
(2)对块号、补码、CRC 做三重校验,任何一项失败都按帧错误处理。
(3)升级完成后必须对固件区做整体 CRC 校验,校验失败则保留旧固件并跳转应用。
(4)建议在 Bootloader 中预留“升级失败回滚”机制:双备份 Flash 或启动标志位。
11上位机调试要点
(1)Tera Term:菜单“File → Transfer → YMODEM → Send...”,选择固件文件即可;串口设置为 8N1,波特率与 Bootloader 一致。
(2)命令行 sz/rz:在 Linux/minicom 下使用 `rz -b`(二进制模式)发送文件,接收端会自动发起 YMODEM 握手。
(3)调试时建议先用 115200 波特率调通流程,再根据可靠性提升到 460800 或 921600。
(4)若出现“发完第 0 块后无响应”,通常是 Bootloader 未正确解析文件名/大小或 Flash 擦除失败;可用串口示波器或逻辑分析仪抓取字节流定位。
(5)若出现“一直重发同一块”,优先检查 CRC-16 多项式与初始值是否正确(Bootloader 与上位机必须一致:多项式 0x1021,初始值 0x0000)。
12附录:典型字节流示例
以下为一次升级的关键字节流(HEX),供对照实现:
Bootloader -> Host:43; "C" 请求 CRCHost -> Boot:01 00 FF 61 70 70 2E 62 69 6E 00 ...; SOH Blk0Boot -> Host:06; ACKBoot -> Host:43; "C"Host -> Boot:02 01 FE <data[1024]> CRC_H CRC_L; STX Blk1Boot -> Host:06; ACK...Host -> Boot:02 NN ~NN <data> CRC_H CRC_LBoot -> Host:06Host -> Boot:04; EOTBoot -> Host:15; NAKHost -> Boot:04; EOTBoot -> Host:06; ACKBoot -> Host:43; "C" (等下一文件)Host -> Boot:01 00 FF 00 ...; SOH Blk0 全 0 (批结束)Boot -> Host:06; ACK















































































