ymodem文件传输(Air780EPM不支持)
一、Ymodem 协议概述
Ymodem 是一种基于串行通信的文件传输协议,由 Chuck Forsberg 于 1980 年代初期在 Xmodem 协议基础上改进而来。其设计目标是解决 Xmodem 在大文件传输效率低(仅支持 128 字节数据块)、缺乏批处理能力等问题,通过引入 1024 字节数据块和批量文件传输机制,显著提升传输效率与可靠性,成为早期嵌入式系统和计算机间文件交换的核心技术之一。
1.1 核心特性
特性 | 说明 |
数据块大小 | 支持 128 字节(SOH 模式)和 1024 字节(STX 模式) |
错误检测 | 使用 CRC-16 循环冗余校验,校验范围包含数据块内容 |
批处理传输 | 可一次性发送多个文件,支持文件名和文件大小元数据传递 |
流量控制 | 通过 ACK/NAK 机制实现自动重传,防止数据溢出 |
填充规则 | 数据不足时用 0x1A 填充,结束帧用 0x00 填充 |
1.2 帧格式说明
1.2.1 起始帧(133 字节)
字段 | 字节数 | 值 | 描述 |
SOH | 1 | 0x01 | 起始符(固定为 SOH) |
块编号 | 1 | 0x00 | 固定为 00 |
块编号反码 | 1 | 0xFF | 块编号的二进制反码 |
文件名 | 变长 | foo.bin | 以 0x00 结尾的字符串 |
文件大小 | 变长 | 1024 | 十进制数值 + 0x00 结尾 |
填充区 | 剩余 | 0x00 | 补满至 128 字节 |
CRC 校验码 | 2 | CRCH CRCL | 高字节在前,低字节在后 |
1.2.2 数据帧(1029/133 字节)
字段 | 字节数 | 值 | 描述 |
STX/SOH | 1 | 0x02/0x01 | 1024 字节用 STX,128 用 SOH |
块编号 | 1 | 0x01 | 从 01 递增 |
块编号反码 | 1 | 0xFE | 块编号的二进制反码 |
数据区 | 1024/128 | 原始数据 | 不足时用 0x1A 填充 |
CRC 校验码 | 2 | CRCH CRCL | 校验数据区内容 |
1.2.3 结束帧(133 字节)
字段 | 字节数 | 值 | 描述 |
SOH | 1 | 0x01 | 固定为 SOH |
块编号 | 1 | 0x00 | 固定为 00 |
块编号反码 | 1 | 0xFF | 固定为 FF |
填充区 | 128 | 0x00 | 全 0 填充 |
CRC 校验码 | 2 | 0x0000 | 固定为 0 |
1.3 控制字符表
字符 | ASCII 码 | 功能描述 |
SOH | 0x01 | 128 字节数据块起始标志 |
STX | 0x02 | 1024 字节数据块起始标志 |
EOT | 0x04 | 传输结束标志 |
ACK | 0x06 | 确认接收成功 |
NAK | 0x15 | 请求重传(校验失败/超时) |
CAN | 0x18 | 取消传输 |
‘C’ | 0x43 | 接收方发起传输请求 |
1.4 传输流程
二、演示功能概述
本篇文章演示的内容为:使用 Air780EHM 核心板的 UART1 连接 PC 端的串口调试仿真工具 SecureCRT,通过 Ymodem 协议接收文件传输。
三、准备硬件环境
参考:硬件环境清单第二章节内容,准备以及组装好硬件环境。
3.1 准备一块 Air780EHM 核心板:点击购买
3.2 连线对应表
Air780EPM开发板 | USB 转 TTL 线 |
uart1_tx | 白线 |
uart1_rx | 绿线 |
gnd | 黑线 |
3.3 硬件连接图
使用 USB 转 TTL 线连接 780EHM 核心板的 U1TX、U1RX 和 GND
四、准备软件环境
烧录工具:Luatools 工具
Air780EPM 烧录需要的固件和脚本文件:
LuatOS 运行所需要的 lib 文件:
使用 Luatools 烧录时,勾选 添加默认 lib 选项,使用默认 lib 脚本文件
PC 端工具 SecureCRT:下载链接
五、代码 API 和代码解析
5.1 代码 API
5.2 代码解析
1.定义一个 ymodem_to 函数,用于发送 C 字符,并重置 ymodem 处理程序
-- 定义一个ymodem_to函数,用于发送C字符,并重置ymodem处理程序
local function ymodem_to()
while true do
-- 如果ymodem协议没有在运行,则发送请求;并重置ymodem处理程序
if not ymodem_running then
uart.write(uartid, "C")
ymodem.reset(ymodem_handler)
end
sys.wait(500)
end
end
2.定义一个 ymodem_rx 函数,用于接收数据
-- 定义一个ymodem_rx函数,用于接收数据
local function ymodem_rx(id,len)
-- 从uart接收数据到缓冲区
uart.rx(id,rxbuff)
-- 打印缓冲区已使用的大小
log.info(rxbuff:used())
-- 调用ymodem.receive函数,接收数据
local result,ack,flag,file_done,all_done = ymodem.receive(ymodem_handler,rxbuff)
ymodem_running = result
log.info("result:",ymodem_running,ack,flag,file_done,all_done)
rxbuff:del()
if result then
rxbuff:copy(0, ack,flag)
uart.tx(id, rxbuff)
end
-- 所有数据都接收完毕
if all_done then
-- 判断/save.bin文件是否存在
local exists=io.exists("/save.bin")
-- 判断/save.bin文件是否存在,存在则打印日志,显示/save.bin文件大小;不存在则打印日志,显示/save.bin文件不存在
if exists then
log.info("io", "save.bin file exists:", exists)
log.info("io", "save.bin file size:", io.fileSize("/save.bin"))
else
log.info("io", "save.bin file not exists")
end
--ymodem_running置为false,再次开始接收
ymodem_running = false
end
rxbuff:del()
end
六、运行结果展示
6.1 完整代码
--[[
@module main
@summary ymodem 接收文件应用功能模块
@version 1.0
@date 2025.07.01
@author 杨鹏
@usage
本文件为Air780EHM核心板演示ymodem功能的代码示例,核心业务逻辑为:
1. 初始化串口通信
2. 创建Ymodem处理对象
local ymodem_handler = ymodem.create("/","save.bin")
3. 启动请求发送任务
uart.write(uartid, "C")
4. 监听串口数据接收
uart.on(uartid, "receive", ymodem_rx)
5. 处理Ymodem数据包
local function ymodem_rx(id,len)
]]
-- 根据实际设备选取不同的uartid
local uartid = 1
--初始化
local result = uart.setup(
uartid,--串口id
115200,--波特率
8,--数据位
1--停止位
)
local taskName = "ymodem_to"
-- 处理未识别的消息
local function ymodem_to_cb(msg)
log.info("ymodem_to_cb", msg[1], msg[2], msg[3], msg[4])
end
-- 定义一个局部变量,用于表示Ymodem协议是否正在运行
local ymodem_running = false
-- 创建一个缓冲区,大小为1024 + 32
local rxbuff = zbuff.create(1024 + 32)
-- 创建一个ymodem处理程序,保存路径为"/",文件名为"save.bin"
local ymodem_handler = ymodem.create("/","save.bin")
-- 定义一个ymodem_to函数,用于发送C字符,并重置ymodem处理程序
local function ymodem_to()
while true do
-- 如果ymodem协议没有在运行,则发送请求;并重置ymodem处理程序
if not ymodem_running then
uart.write(uartid, "C")
ymodem.reset(ymodem_handler)
end
sys.wait(500)
end
end
-- 定义一个ymodem_rx函数,用于接收数据
local function ymodem_rx(id,len)
-- 从uart接收数据到缓冲区
uart.rx(id,rxbuff)
-- 打印缓冲区已使用的大小
log.info(rxbuff:used())
-- 调用ymodem.receive函数,接收数据
local result,ack,flag,file_done,all_done = ymodem.receive(ymodem_handler,rxbuff)
ymodem_running = result
log.info("result:",ymodem_running,ack,flag,file_done,all_done)
rxbuff:del()
if result then
rxbuff:copy(0, ack,flag)
uart.tx(id, rxbuff)
end
-- 所有数据都接收完毕
if all_done then
-- 判断/save.bin文件是否存在
local exists=io.exists("/save.bin")
-- 判断/save.bin文件是否存在,存在则打印日志,显示/save.bin文件大小;不存在则打印日志,显示/save.bin文件不存在
if exists then
log.info("io", "save.bin file exists:", exists)
log.info("io", "save.bin file size:", io.fileSize("/save.bin"))
else
log.info("io", "save.bin file not exists")
end
--ymodem_running置为false,再次开始接收
ymodem_running = false
end
rxbuff:del()
end
-- 监听串口接收事件
uart.on(uartid, "receive", ymodem_rx)
-- 监听串口发送事件
uart.on(uartid, "sent", function(id)
log.info("uart", "sent", id)
end)
--创建并且启动一个task
--运行这个task的主函数ymodem_to
sysplus.taskInitEx(ymodem_to, taskName,ymodem_to_cb)
6.2 结果展示
6.2.1 SecureCRT 使用指南:
6.2.2 1k 文件数据传输:
发送端:传输完成
模组接收端:
6.2.3 10K 文件数据传输:
发送端:传输完成
模组接收端:
七、总结
本文演示如何在 Air780EHM 核心板上面,通过 UART1 连接 PC 端的串口调试工具 SecureCRT,进行 Ymodem 协议的文件传输测试。