跳转至

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 字段隐式完成:

  1. 发送端在 luat_airlink_data_pack() 时设置 flags.rpc_supported = 1 / flags.frag_supported = 1 (src/luat_airlink_linkdata.c:88-92)
  2. 对端 luat_airlink_data_unpack() 找到有效帧后, 调用 luat_airlink_peer_flags_update(&link->flags) 保存对端能力 (src/luat_airlink.c:210)
  3. 上层 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() 查询对端是否支持分片
问一下 AI