AirLink 链路层描述
注意, 链路层只保证数据包被 "正确"传输, 并抛弃出错的包, 当前协议层不实现自动重传.
C 语言描述
来源:
components/airlink/include/luat_airlink.h:55-62,components/airlink/src/luat_airlink_linkdata.c
typedef struct airlink_link_data {
uint8_t magic[4];
uint16_t len;
uint16_t crc16;
uint32_t pkgid; // 包序号, 单调递增, 用于丢包诊断与统计, 不用于自动重传
airlink_flags_t flags; // 包头标志, 用于能力协商与流量控制
uint8_t data[0]; // 指向 cmd 帧
} airlink_link_data_t;
字段描述
| 字段 | 长度 | 含义 |
|---|---|---|
| magic | 4B | 魔数, 固定 0xA1 0xB1 0xCA 0x66, 用于帧边界定位 |
| len | 2B | pkgid + flags + data 三段的总长度 (字节), 不含 magic/len/crc16 自身 |
| crc16 | 2B | MODBUS CRC16, 大端存放, 计算范围见下 |
| pkgid | 4B | 包序号, 单调递增, 用于丢包诊断与统计 |
| flags | 4B | 标志位, 见下表 |
| data | NB | 指令层帧 (luat_airlink_cmd_t) |
CRC16 计算
| 项目 | 值 |
|---|---|
| 算法 | MODBUS CRC16 (luat_crc16_modbus) |
| 范围 | 从 pkgid 起始, 共 len + 8 字节 (即 pkgid + flags + data) |
| 字节序 | 大端存放 |
| 实现位置 | src/luat_airlink_linkdata.c:102 |
解包行为 (src/luat_airlink_linkdata.c:32-69): 链路层在一个物理层缓冲中多缓冲扫描 magic. 单个 magic 的 CRC 失败后, 跳过当前 4 字节向后继续搜索; 找到有效 magic 后才提交到指令层. 这保证了一帧 SPI 事务中可能含多个有效 AirLink 链路帧, 也允许部分损坏被跳过.
pkgid
- 32 位单调递增计数器, 由发送端
next_pkg_id++生成 (src/luat_airlink_linkdata.c:18, 84) - 当前协议层不实现自动重传 — 链路层发现 CRC 错误即丢弃整帧
- 用于统计 (
airlink_statistic_t.rx_pkg/tx_pkg) 和对端能力/状态推断, 不作为协议层的可靠传输保证
flags 字段位定义
来源:
components/airlink/include/luat_airlink.h:44-53
typedef struct airlink_flags {
uint32_t mem_is_high: 1; // 本端内存高水位
uint32_t queue_cmd: 1; // 命令队列非空 (SPI 模式)
uint32_t queue_ip: 1; // IP 包队列非空 (SPI 模式)
uint32_t irq_ready: 1; // 请求切换到 IRQ 模式
uint32_t irq_pin: 4; // 中断管脚号 (从 GPIO12/140 起算, 传给 SLAVE)
uint32_t rpc_supported: 1; // 是否支持 nanopb RPC (cmd 0x30)
uint32_t frag_supported: 1; // 是否支持 cmd 0x32 分片
uint32_t reserved: 22; // 保留位, 当前必须为 0
} airlink_flags_t;
| 位 | 名称 | 含义 |
|---|---|---|
| 0 | mem_is_high | 1 = 本端内存紧张, 对端应停止下发 IP 包, 但不影响非 IP 包传输 |
| 1 | queue_cmd | 1 = 命令队列仍有数据待取, 用于 SPI 主机判断何时继续轮询 |
| 2 | queue_ip | 1 = IP 包队列仍有数据待取, 用于 SPI 主机判断何时继续轮询 |
| 3 | irq_ready | 1 = 请求切换到 IRQ 模式 (RDY + IRQ 引脚中断驱动) |
| 4-7 | irq_pin | 4 位中断管脚号, 传给 SLAVE, 由从机最终选定具体 GPIO |
| 8 | rpc_supported | 1 = 本端支持 nanopb RPC (cmd 0x30); 通过 luat_airlink_peer_rpc_supported() 查询 |
| 9 | frag_supported | 1 = 本端支持 cmd 0x32 分片重组; 通过 luat_airlink_peer_frag_supported() 查询 |
| 10-31 | reserved | 保留位, 当前必须为 0 |
能力协商
链路层无显式握手. 能力协商通过每帧 flags 字段隐式完成:
- 发送端在
luat_airlink_data_pack()时设置flags.rpc_supported = 1/flags.frag_supported = 1(src/luat_airlink_linkdata.c:88-92) - 对端
luat_airlink_data_unpack()找到有效帧后, 调用luat_airlink_peer_flags_update(&link->flags)保存对端能力 (src/luat_airlink.c:210) - 上层 API
luat_airlink_peer_rpc_supported()/luat_airlink_peer_frag_supported()查询对端能力, 决定 sdata 走 RPC (cmd 0x30) 还是 raw cmd 0x20, 是否需要分片降级 (src/luat_airlink.c:213-216)
链路层 API
| 函数 | 作用 |
|---|---|
luat_airlink_data_pack(buff, len, dst) |
把 cmd 帧打包为链路帧, 写入 magic/len/pkgid/flags/crc16 |
luat_airlink_data_unpack(buff, len) |
从物理层缓冲中提取第一个有效链路帧 (多缓冲扫描) |
luat_airlink_peer_flags_update(flags) |
由传输层在每个有效帧后调用, 更新对端 flags |
luat_airlink_peer_rpc_supported() |
查询对端是否支持 RPC |
luat_airlink_peer_frag_supported() |
查询对端是否支持分片 |