存储访问协议用于上位机通过蓝牙透传链路按字节访问从机 MCU 的存储区域。协议为单主单从模型,不带从机地址字段,不属于标准 Modbus RTU。
小程序中该协议与标准 Modbus RTU、Bootloader 升级协议平级:
CMD + ADDR + LEN + DATA + CRC16-CCITT-FALSE。OP=0x05,不保留旧同步区域入口。除 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 字节外的整帧内容。
每帧第 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=1 且 MODE=0x7 组合为特殊指令,即 CMD=0x4F |
| RSV | 0 | 当前版本固定为 0 |
| RW | 0 | 读操作 |
| RW | 1 | 写操作 |
普通内存命令生成规则:
读请求 : CMD = MODE
写请求 : CMD = 0x08 | MODE
异常响应 : CMD_ERR = CMD | 0x80
CMD=0x4F 固定保留为特殊指令帧,不作为普通存储区域。
| AREA | 名称 | 读 | 写 | 用途 |
|---|---|---|---|---|
0x00 |
保留 | 禁止 | 禁止 | 保留 |
0x01 |
DATA | 支持 | 支持 | 内部直接寻址 RAM 区 |
0x02 |
IDATA | 支持 | 支持 | 内部间接寻址 RAM 区 |
0x03 |
XDATA | 支持 | 支持 | 外部数据空间或扩展 RAM |
0x04 |
CODE | 支持 | 禁止 | 程序存储区 |
0x05..0x06 |
保留 | 禁止 | 禁止 | 保留 |
0x07 |
ADDR32 | 支持 | 支持 | 32 位统一地址模式 |
写 CODE 区必须返回写保护异常。
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 字节。
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 + LEN 或 7 + LEN 字节。
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 + LEN 或 7 + LEN 字节。
响应必须回显请求中的 CMD、ADDR、LEN。
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 字节。
响应必须回显请求中的 CMD、ADDR、LEN。
CMD_ERR EXCEPTION_CODE CRC_H CRC_L
长度固定 4 字节。CMD_ERR 为原请求 CMD | 0x80。特殊指令异常响应使用 0xCF。
特殊指令使用固定 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 | 执行失败 |
| 异常码 | 名称 | 说明 |
|---|---|---|
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 | 不支持的操作 |
CodeInfo 同步分两步:
OP=0x05 读取 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 信息块本体时使用的地址长度,只允许 16 或 32 |
ENDIAN_MARK |
2 | 目标内存变量字节序标记;原始字节 55 AA 表示大端,原始字节 AA 55 表示小端 |
MAX_PACKET |
2 | 从机允许的最大完整协议帧长度,包含 CMD/ADDR/LEN/DATA/CRC;为 0 表示未声明 |
ENDIAN_MARK 只影响目标内存变量的多字节值显示和编辑,例如结构体字段、单独变量、enum 底层整数。CMD、ADDR、普通读写 LEN、CODE_LEN、MAX_PACKET 等多字节协议控制字段始终按大端序解析;TLV LEN 为单字节字段,不涉及大小端。
上位机继续发送普通读请求:
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_WIDTH 与 MAX_PACKET;当 MAX_PACKET 非 0 时,设置页最大包长和设备声明包长取较小值。
最大数据长度计算:
ADDR_WIDTH=32: 读响应单帧数据上限 = max_frame_bytes - 9
ADDR_WIDTH=16: 读响应单帧数据上限 = max_frame_bytes - 7
CodeInfo TLV 信息块位于 bootstrap 描述符给出的 CODE 区地址。该信息块不再定义固定 C 结构体头,也不再包含 format_version、board_info_format、struct_entry_len 或 struct_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 字节。
板卡信息全部为可选项。某块板不存在对应硬件信息时,不需要放该 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 汉字 |
结构体实例和单独变量也是 TLV 项,不再额外保存 entry_kind 字段:
| TYPE | 类型 | 地址宽度 | 小程序处理 |
|---|---|---|---|
0x20 |
结构体实例 | 16 位 | 创建结构体组;未导入定义时按 00、01、02... 字节占位 |
0x21 |
单独变量 | 16 位 | 创建单变量组;1/2/4 字节默认按 uint8_t、uint16_t、uint32_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 |
小程序读取完整 CodeInfo TLV 信息块后:
TYPE LEN VALUE 遍历 TLV 项,未知类型跳过。TYPE=0x20/0x28 创建结构体组;结构体定义未导入时按字节占位。TYPE=0x21/0x29 创建单独变量组。TYPE 解析内存入口地址;0x20/0x21 为 16 位地址,0x28/0x29 为 32 位地址。MEMORY_ENDIAN 解释结构体字段、单独变量和 enum 底层整数的多字节值;单字节值不受影响。mem_type、byte_addr、byte_len、TYPE、type_name 创建参数组。TYPE=0x20/0x28 且 type_name 与结构体定义名一致、长度一致时才补全结构体字段;结构体字段类型为 enum 时,读回值按枚举项显示。TYPE=0x21/0x29 如果 type_name 匹配 enum 类型名,或导入文件中存在 EnumType variable_name; 且变量名匹配 type_name,读回值按枚举项显示。type_name 但 mem_type 或 byte_addr 不同的入口,应视为不同参数组。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
假设描述符返回:
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 位统一地址模式。
结构体实例:
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
LEN 单位始终为字节,字段固定 16 位,有效范围为 1..0xFFFF;CodeInfo TLV 项内的 LEN 为 1 字节,只表示单项 VALUE 长度。ADDR 单位始终为字节;MODE=0x07 时地址字段为 32 位,MODE=0x01..0x04 时地址字段为 16 位;MODE=0x00/0x05/0x06 保留。CMD、ADDR、LEN。CMD、ADDR、LEN、CRC,并确认数据长度等于 LEN。CODE 区只读,写入必须返回 WRITE_PROTECT。STATUS。OP=0x05 bootstrap 描述符,再按 bootstrap ADDR_WIDTH 读取信息块;内存入口地址宽度由 TLV TYPE 决定,目标变量值按 bootstrap MEMORY_ENDIAN 解释。普通内存读写协议不额外携带变量位宽字段,只传输“字节地址 + 字节长度 + 字节数据”。变量显示和编辑位宽由以下信息决定:
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 |
0x01、0x02、0x04 |
单个值 8/16/32 位 |
COUNT |
16 位 | 值数量,不是字节数 |
DATA |
VALUE_WIDTH * COUNT 字节 |
按协议大端序 |