1
0

存储访问协议.md 16 KB

存储访问协议

1. 协议定位

存储访问协议用于上位机通过蓝牙透传链路按字节访问从机 MCU 的存储区域。协议为单主单从模型,不带从机地址字段,不属于标准 Modbus RTU。

小程序中该协议与标准 Modbus RTU、Bootloader 升级协议平级:

  • 标准 Modbus RTU 使用从机地址、功能码和 Modbus CRC。
  • 存储访问协议使用 CMD + ADDR + LEN + DATA + CRC16-CCITT-FALSE
  • 复位、启动、停止、控制参考值、CodeInfo 描述符读取使用特殊指令帧。
  • CodeInfo 同步只使用特殊指令 OP=0x05,不保留旧同步区域入口。

2. 字节序与 CRC

除 CRC 算法内部计算外,所有多字节协议控制字段均为大端序。目标内存变量的多字节值不跟随协议控制字段字节序,而是按第一次同步 bootstrap 描述符中的 MEMORY_ENDIAN 解释和编辑。

32 位地址 : ADDR_3 ADDR_2 ADDR_1 ADDR_0
16 位长度 : LEN_H LEN_L
CRC 输出  : CRC_H CRC_L

CRC 使用 CRC16-CCITT-FALSE

参数
多项式 0x1021
初值 0xFFFF
输入反转
输出反转
结果异或 0x0000
输出顺序 高字节在前

CRC 覆盖除最后两个 CRC 字节外的整帧内容。

3. CMD 定义

每帧第 1 字节为 CMD

bit7      ERR       异常标志
bit6      CTL       特殊指令标志位
bit5~bit4 RSV       保留,普通读写保持 0
bit3      RW        读写标志,0=读,1=写
bit2~bit0 MODE      地址模式或存储区域
含义
ERR 0 正常请求或正常响应
ERR 1 异常响应
CTL 0 普通内存读写
CTL 1 bit3=1MODE=0x7 组合为特殊指令,即 CMD=0x4F
RSV 0 当前版本固定为 0
RW 0 读操作
RW 1 写操作

普通内存命令生成规则:

读请求   : CMD = MODE
写请求   : CMD = 0x08 | MODE
异常响应 : CMD_ERR = CMD | 0x80

CMD=0x4F 固定保留为特殊指令帧,不作为普通存储区域。

4. AREA 定义

AREA 名称 用途
0x00 保留 禁止 禁止 保留
0x01 DATA 支持 支持 内部直接寻址 RAM 区
0x02 IDATA 支持 支持 内部间接寻址 RAM 区
0x03 XDATA 支持 支持 外部数据空间或扩展 RAM
0x04 CODE 支持 禁止 程序存储区
0x05..0x06 保留 禁止 禁止 保留
0x07 ADDR32 支持 支持 32 位统一地址模式

CODE 区必须返回写保护异常。

5. 普通内存访问帧

5.1 读请求

MODE=0x07: CMD ADDR_3 ADDR_2 ADDR_1 ADDR_0 LEN_H LEN_L CRC_H CRC_L
MODE=0x01..0x04: CMD ADDR_H ADDR_L LEN_H LEN_L CRC_H CRC_L

长度分别为 9 字节或 7 字节。

5.2 写请求

MODE=0x07: CMD ADDR_3 ADDR_2 ADDR_1 ADDR_0 LEN_H LEN_L DATA... CRC_H CRC_L
MODE=0x01..0x04: CMD ADDR_H ADDR_L LEN_H LEN_L DATA... CRC_H CRC_L

长度分别为 9 + LEN7 + LEN 字节。

5.3 正常读响应

MODE=0x07: CMD ADDR_3 ADDR_2 ADDR_1 ADDR_0 LEN_H LEN_L DATA... CRC_H CRC_L
MODE=0x01..0x04: CMD ADDR_H ADDR_L LEN_H LEN_L DATA... CRC_H CRC_L

长度分别为 9 + LEN7 + LEN 字节。

响应必须回显请求中的 CMDADDRLEN

5.4 正常写响应

MODE=0x07: CMD ADDR_3 ADDR_2 ADDR_1 ADDR_0 LEN_H LEN_L CRC_H CRC_L
MODE=0x01..0x04: CMD ADDR_H ADDR_L LEN_H LEN_L CRC_H CRC_L

长度分别为 9 字节或 7 字节。

响应必须回显请求中的 CMDADDRLEN

5.5 异常响应

CMD_ERR EXCEPTION_CODE CRC_H CRC_L

长度固定 4 字节。CMD_ERR 为原请求 CMD | 0x80。特殊指令异常响应使用 0xCF

6. 特殊指令帧

特殊指令使用固定 CMD=0x4F

请求: 4F OP DATA... CRC_H CRC_L
响应: 4F OP STATUS DATA... CRC_H CRC_L
异常: CF EXCEPTION_CODE CRC_H CRC_L

STATUS=0x00 表示指令执行成功;非 0 表示从机识别了指令,但拒绝执行或执行失败。

OP 名称 请求 DATA 成功响应 DATA 说明
0x01 RESET 复位
0x02 START 启动
0x03 STOP 停止
0x04 SET_CONTROL_REF REF_H REF_L 控制参考值,int16_t,大端序
0x05 READ_CODE_INFO_DESCRIPTOR CODE_ADDR32 CODE_LEN16 ADDR_WIDTH8 MEMORY_ENDIAN16 MAX_PACKET16 读取 CodeInfo bootstrap 描述符

特殊指令状态码:

STATUS 名称 说明
0x00 OK 成功
0x01 UNSUPPORTED 不支持的指令
0x02 INVALID_PARAM 参数无效
0x03 BUSY 设备忙
0x04 FAILED 执行失败

7. 异常码

异常码 名称 说明
0x01 ILLEGAL_COMMAND 非法命令
0x02 ILLEGAL_AREA 非法区域
0x03 ILLEGAL_ADDRESS 非法地址
0x04 ILLEGAL_LENGTH 非法长度
0x05 WRITE_PROTECT 写保护
0x06 DEVICE_BUSY 设备忙
0x07 FORMAT_ERROR 格式错误
0x08 ACCESS_DENIED 访问拒绝
0x09 INTERNAL_ERROR 内部错误
0x0A ALIGNMENT_ERROR 对齐错误
0x0B RANGE_OVERFLOW 地址范围溢出
0x0C UNSUPPORTED_OPERATION 不支持的操作

8. CodeInfo 同步流程

CodeInfo 同步分两步:

  1. 使用特殊指令 OP=0x05 读取 bootstrap 描述符。
  2. 按 bootstrap 返回的地址长度、目标内存字节序和最大包长读取完整 CodeInfo TLV 信息块。

8.1 读取 bootstrap 描述符

请求:

4F 05 CRC_H CRC_L

从机成功响应:

4F 05 00 CODE_ADDR_3 CODE_ADDR_2 CODE_ADDR_1 CODE_ADDR_0 CODE_LEN_H CODE_LEN_L ADDR_WIDTH ENDIAN_MARK_H ENDIAN_MARK_L MAX_PACKET_H MAX_PACKET_L CRC_H CRC_L

响应 DATA 字段:

字段 长度 说明
CODE_ADDR 4 CodeInfo TLV 信息块在 CODE 区内的字节地址
CODE_LEN 2 CodeInfo TLV 信息块的字节长度
ADDR_WIDTH 1 读取 CodeInfo 信息块本体时使用的地址长度,只允许 1632
ENDIAN_MARK 2 目标内存变量字节序标记;原始字节 55 AA 表示大端,原始字节 AA 55 表示小端
MAX_PACKET 2 从机允许的最大完整协议帧长度,包含 CMD/ADDR/LEN/DATA/CRC;为 0 表示未声明

ENDIAN_MARK 只影响目标内存变量的多字节值显示和编辑,例如结构体字段、单独变量、enum 底层整数。CMDADDR、普通读写 LENCODE_LENMAX_PACKET 等多字节协议控制字段始终按大端序解析;TLV LEN 为单字节字段,不涉及大小端。

8.2 读取完整 CodeInfo

上位机继续发送普通读请求:

ADDR_WIDTH = 32: MODE = 0x07 ADDR32
ADDR_WIDTH = 16: MODE = 0x04 CODE
ADDR = CODE_ADDR
LEN  = CODE_LEN

ADDR_WIDTH=16 时,下发普通读帧只携带 CODE_ADDR 的低 16 位。

如果 CODE_LEN 超过当前 BLE 包长可承载的数据长度,小程序按协议自动分片读取。后续分片使用 bootstrap 返回的 ADDR_WIDTHMAX_PACKET;当 MAX_PACKET 非 0 时,设置页最大包长和设备声明包长取较小值。

最大数据长度计算:

ADDR_WIDTH=32: 读响应单帧数据上限 = max_frame_bytes - 9
ADDR_WIDTH=16: 读响应单帧数据上限 = max_frame_bytes - 7

9. CodeInfo TLV 信息块

CodeInfo TLV 信息块位于 bootstrap 描述符给出的 CODE 区地址。该信息块不再定义固定 C 结构体头,也不再包含 format_versionboard_info_formatstruct_entry_lenstruct_table。当前测试阶段只维护一种 TLV 字节格式,字段是否存在由 TLV TYPE 决定,信息块总长度由 OP=0x05 bootstrap 返回的 CODE_LEN 决定。

TLV 项连续排列,直到累计长度等于 CODE_LEN

TYPE LEN VALUE...
字段 长度 说明
TYPE 1 TLV 类型
LEN 1 VALUE 字节长度,单项最大 255 字节
VALUE LEN 类型对应的数据

上位机解析到未知 TYPE 时跳过该项。任何 TLV 项声明长度超过 CODE_LEN 剩余字节时,整段 CodeInfo 视为格式错误。CODE_LEN 仍由 bootstrap 使用 16 位字段给出,因此整段 CodeInfo 可以超过 255 字节,只是单个 TLV 项不能超过 255 字节。

9.1 板卡信息 TLV

板卡信息全部为可选项。某块板不存在对应硬件信息时,不需要放该 TLV;小程序卡片和转换公式只使用实际同步到的字段。

TYPE 名称 LEN VALUE 格式 显示/用途
0x01 cave_freq 1 uint8_t 载波频率,单位 KHz
0x02 ref_volt 1 uint8_t 基准电压实际值乘 10,显示时除以 10,单位 V
0x03 amp_gain 1 uint8_t 运放倍数,无单位
0x04 rs_shunt 2 uint16_t 采样电阻,单位 mΩ
0x05 bus_div 4 float32 母线电压分压比,大端序
0x06 along_div 4 float32 模拟输入电压分压比,大端序
0x07 chip_model 可变 UTF-8 或 ASCII 字符串 芯片型号,建议 0 结尾或 0 填充
0x08 model 可变 UTF-8 或 ASCII 字符串 电机型号,建议最多 30 字节,可容纳至少 7 个常见 UTF-8 汉字

9.2 内存入口 TLV

结构体实例和单独变量也是 TLV 项,不再额外保存 entry_kind 字段:

TYPE 类型 地址宽度 小程序处理
0x20 结构体实例 16 位 创建结构体组;未导入定义时按 000102... 字节占位
0x21 单独变量 16 位 创建单变量组;1/2/4 字节默认按 uint8_tuint16_tuint32_t 显示,导入 enum 后可按枚举项显示
0x28 结构体实例 32 位 创建结构体组,统一 32 位字节地址
0x29 单独变量 32 位 创建单变量组,统一 32 位字节地址

内存入口 VALUE 的地址宽度由 TLV TYPE 决定,不再由 bootstrap 的 ADDR_WIDTH 决定。bootstrap ADDR_WIDTH 只表示“读取 CodeInfo 信息块本体”时使用 16 位 CODE 地址还是 32 位统一地址。名称字段为 UTF-8 或 ASCII,建议 0 结尾或 0 填充。

TYPE=0x20/0x21 的 16 位地址入口:

字段 长度 说明
mem_type 1 所在存储区域,使用 AREA 编号
byte_addr 2 结构体实例或单独变量所在区域的字节地址
byte_len 2 结构体实例或单独变量的字节长度
type_name LEN - 5 结构体定义名、变量名或 enum 类型名

TYPE=0x28/0x29 的 32 位地址入口:

字段 长度 说明
byte_addr 4 结构体实例或单独变量所在统一地址空间内的字节地址
byte_len 2 结构体实例或单独变量的字节长度
type_name LEN - 6 结构体定义名、变量名或 enum 类型名

32 位地址入口不携带 mem_type,小程序同步出的参数组统一使用 MODE=0x07 ADDR32。16 位地址入口按 mem_type=0x01..0x04 使用 DATA/IDATA/XDATA/CODE。手动存储访问卡片始终允许选择 ADDR32/DATA/IDATA/XDATA/CODE,不再因为 bootstrap ADDR_WIDTH 被锁定。

mem_type

区域
0x01 DATA
0x02 IDATA
0x03 XDATA
0x04 CODE

10. 小程序同步后的处理

小程序读取完整 CodeInfo TLV 信息块后:

  1. TYPE LEN VALUE 遍历 TLV 项,未知类型跳过。
  2. 解析可选板卡信息,更新通讯页 CodeInfo 卡片;不存在的字段不显示、不进入转换公式上下文。
  3. 遇到 TYPE=0x20/0x28 创建结构体组;结构体定义未导入时按字节占位。
  4. 遇到 TYPE=0x21/0x29 创建单独变量组。
  5. 按 TLV TYPE 解析内存入口地址;0x20/0x21 为 16 位地址,0x28/0x29 为 32 位地址。
  6. 按 bootstrap MEMORY_ENDIAN 解释结构体字段、单独变量和 enum 底层整数的多字节值;单字节值不受影响。
  7. 根据 mem_typebyte_addrbyte_lenTYPEtype_name 创建参数组。
  8. 如果导入了 C 结构体和 enum 定义,只有结构体入口 TYPE=0x20/0x28type_name 与结构体定义名一致、长度一致时才补全结构体字段;结构体字段类型为 enum 时,读回值按枚举项显示。
  9. 单独变量入口 TYPE=0x21/0x29 如果 type_name 匹配 enum 类型名,或导入文件中存在 EnumType variable_name; 且变量名匹配 type_name,读回值按枚举项显示。
  10. 如果当前已有同地址、同区域、同名称、同长度的结构体组,并且已经导入过结构体定义,则保留当前导入结构,只更新来源地址、区域、轮询开关等状态。
  11. 如果存在相同 type_namemem_typebyte_addr 不同的入口,应视为不同参数组。

11. 示例

11.1 读取 XDATA 中 64 字节结构体

AREA = 0x03 XDATA
ADDR = 0x2000
LEN = 0x0040
读请求 = 03 20 00 00 40 CRC_H CRC_L
正常响应 = 03 20 00 00 40 DATA(64B) CRC_H CRC_L

11.2 读取 CODE 中 CodeInfo

假设描述符返回:

CODE_ADDR = 0x00123456
CODE_LEN  = 0x0120
ADDR_WIDTH = 32
MEMORY_ENDIAN = big,bootstrap 原始标记 55 AA

读取请求:

07 00 12 34 56 01 20 CRC_H CRC_L

0x07 表示 32 位统一地址模式。

11.3 CodeInfo TLV 示例

结构体实例:

TYPE = 0x20
LEN = 0x14
VALUE = 03 20 00 00 40 4D 6F 74 6F 72 5F 52 75 6E 74 69 6D 65 5F 74
含义 = XDATA 0x2000 / 64 bytes / Motor_Runtime_t

单独变量:

TYPE = 0x29
LEN = 0x0F
VALUE = 00 00 21 00 00 02 73 70 65 65 64 5F 72 65 66
含义 = ADDR32 0x00002100 / 2 bytes / speed_ref

12. 实现约束

  1. 普通内存读写帧的 LEN 单位始终为字节,字段固定 16 位,有效范围为 1..0xFFFF;CodeInfo TLV 项内的 LEN 为 1 字节,只表示单项 VALUE 长度。
  2. ADDR 单位始终为字节;MODE=0x07 时地址字段为 32 位,MODE=0x01..0x04 时地址字段为 16 位;MODE=0x00/0x05/0x06 保留。
  3. 普通读写响应必须回显请求的 CMDADDRLEN
  4. 上位机必须校验响应 CMDADDRLEN、CRC,并确认数据长度等于 LEN
  5. CODE 区只读,写入必须返回 WRITE_PROTECT
  6. 特殊指令正常响应必须包含 STATUS
  7. CodeInfo 同步必须走 OP=0x05 bootstrap 描述符,再按 bootstrap ADDR_WIDTH 读取信息块;内存入口地址宽度由 TLV TYPE 决定,目标变量值按 bootstrap MEMORY_ENDIAN 解释。
  8. 固件更新 CodeInfo TLV 类型或内存入口格式时,必须同步更新本文档和小程序解析器。

13. 读写位宽说明

普通内存读写协议不额外携带变量位宽字段,只传输“字节地址 + 字节长度 + 字节数据”。变量显示和编辑位宽由以下信息决定:

  • TYPE=0x20/0x28 的结构体实例由导入的 C 结构体定义决定字段类型。
  • TYPE=0x21/0x29 的单独变量由 byte_len 默认推断:1 字节为 uint8_t,2 字节为 uint16_t,4 字节为 uint32_t
  • enum 不改变普通内存读写帧,只是在导入定义后给整数值增加枚举映射;显示格式为 枚举项 (数值),写入时可输入枚举项名称或数字。
  • 参数页可在寄存器/变量配置中进一步设置公式、单位、范围和显示类型。

如果后续从机需要原子读写、硬件寄存器位宽或对齐语义,可以新增 typed 特殊指令,不改变普通内存访问帧:

读: 4F 06 AREA ADDR_3 ADDR_2 ADDR_1 ADDR_0 VALUE_WIDTH COUNT_H COUNT_L CRC_H CRC_L
写: 4F 07 AREA ADDR_3 ADDR_2 ADDR_1 ADDR_0 VALUE_WIDTH COUNT_H COUNT_L DATA... CRC_H CRC_L

建议字段:

字段 建议值 说明
ADDR 4 字节 typed 扩展建议使用 32 位字节地址
VALUE_WIDTH 0x010x020x04 单个值 8/16/32 位
COUNT 16 位 值数量,不是字节数
DATA VALUE_WIDTH * COUNT 字节 按协议大端序