本协议用于上位机通过蓝牙透传链路按字节访问从机 MCU 的存储区域。协议为单主单从模型,不包含从机地址字段,不属于 Modbus RTU。
小程序中该协议与标准 Modbus、Bootloader 升级协议平级。标准 Modbus 继续使用原有从机地址、功能码和 Modbus CRC;存储访问协议统一使用本文定义的 CMD + ADDR + LEN + DATA + CRC 格式,0x0F info 也按只读区域处理。
除 CRC 算法内部计算外,协议字段均为大端序。
16 位地址: ADDR_H ADDR_L
16 位长度: LEN_H LEN_L
CRC 输出 : CRC_H CRC_L
CRC 使用 CRC16-CCITT-FALSE:
多项式: 0x1021
初值 : 0xFFFF
反转 : 输入不反转,输出不反转
异或 : 0x0000
顺序 : 高字节在前
每帧第 1 字节为 CMD。
bit7 ERR 异常标志
bit6 RW 读写标志
bit5~bit0 AREA 存储区域编号
含义如下:
| 位 | 值 | 含义 |
|---|---|---|
| ERR | 0 | 正常请求或正常响应 |
| ERR | 1 | 异常响应 |
| RW | 0 | 读操作 |
| RW | 1 | 写操作 |
命令生成规则:
读命令 CMD = AREA
写命令 CMD = 0x40 | AREA
异常 CMD_ERR = CMD | 0x80
| AREA | 名称 | 读 | 写 | 用途 |
|---|---|---|---|---|
0x01 |
data | 支持 | 支持 | 内部直接寻址 RAM 区 |
0x02 |
idata | 支持 | 支持 | 内部间接寻址 RAM 区 |
0x03 |
xdata | 支持 | 支持 | 外部数据空间或扩展 RAM |
0x04 |
code | 支持 | 禁止 | 程序存储区 |
0x0F |
info | 支持 | 禁止 | 连接后同步 code 区关键数据地址与长度 |
0x0F info 是一个只读信息区域,不直接承载完整 Modbus_Code_Info_t。小程序连接设备后先通过 0x0F 按地址和长度读取 4 字节关键数据,再使用 AREA=0x04 code 读取真正的 Modbus_Code_Info_t。
info 同步请求与普通读请求一致,地址和长度单位均为字节。当前定义固定读取 ADDR=0x0000、LEN=0x0004:
0F 00 00 00 04 CRC_H CRC_L
回复与普通读响应一致,仍包含本次读取的 ADDR 与 LEN,随后 4 字节数据为 code 区内信息块的地址和长度:
0F 00 00 00 04 CODE_ADDR_H CODE_ADDR_L CODE_LEN_H CODE_LEN_L CRC_H CRC_L
响应中的前两个字节地址 00 00 和长度 00 04 是本次 info 区读取的地址与长度。CODE_ADDR 和 CODE_LEN 是返回数据,表示 code 区内 Modbus_Code_Info_t 的字节地址和字节长度。
CMD ADDR_H ADDR_L LEN_H LEN_L CRC_H CRC_L
长度固定 7 字节。
CMD ADDR_H ADDR_L LEN_H LEN_L DATA... CRC_H CRC_L
长度为 7 + LEN 字节。
CMD ADDR_H ADDR_L LEN_H LEN_L DATA... CRC_H CRC_L
长度为 7 + LEN 字节。
CMD ADDR_H ADDR_L LEN_H LEN_L CRC_H CRC_L
长度固定 7 字节。
CMD_ERR EXCEPTION_CODE CRC_H CRC_L
长度固定 4 字节。
| 异常码 | 名称 | 说明 |
|---|---|---|
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 | 不支持的操作 |
连接后同步 code 区关键数据时,上位机先读取 AREA=0x0F info 的只读 4 字节数据,再访问 AREA=0x04 code 读取真实数据。
读请求:
0F 00 00 00 04 CRC_H CRC_L
从机返回:
0F 00 00 00 04 CODE_ADDR_H CODE_ADDR_L CODE_LEN_H CODE_LEN_L CRC_H CRC_L
返回数据字段含义:
| 字段 | 长度 | 说明 |
|---|---|---|
| CODE_ADDR | 2 字节 | Modbus_Code_Info_t 在 code 区内的字节地址 |
| CODE_LEN | 2 字节 | Modbus_Code_Info_t 的字节长度 |
上位机根据 info 返回数据继续读取:
AREA = 0x04
ADDR = CODE_ADDR
LEN = CODE_LEN
如果 CODE_LEN 超过当前链路最大包长,小程序按字节地址自动分片。例如最大帧长为 64 字节时,单帧读响应可承载的数据长度为:
64 - 7 = 57 字节
小程序会依次读取:
CODE_ADDR + 0x0000 ~ CODE_ADDR + 0x0038
CODE_ADDR + 0x0039 ~ CODE_ADDR + 0x0071
...
每个分片均使用 AREA=0x04 code,地址为 code 区内真实字节地址。
Modbus_Code_Info_t 位于 info 返回数据给出的 code 区地址,布局如下:
typedef struct
{
uint16_t byte_len;
uint8_t cave_freq;
uint8_t ref_volt;
uint16_t amp_gain;
uint16_t rs_shunt;
float bus_div;
float along_div;
uint8_t chip_model[8];
uint8_t model[16];
uint16_t struct_count;
uint16_t struct_entry_len;
struct
{
uint16_t byte_addr;
uint16_t byte_len;
uint8_t mem_type;
uint8_t type_name[MODBUS_CODE_INFO_STRUCT_NAME_BYTE_LEN];
} struct_table[MODBUS_CODE_INFO_STRUCT_COUNT];
} Modbus_Code_Info_t;
固定头长度为 44 字节:
| 偏移 | 字段 | 长度 |
|---|---|---|
| 0 | byte_len | 2 |
| 2 | cave_freq | 1 |
| 3 | ref_volt | 1 |
| 4 | amp_gain | 2 |
| 6 | rs_shunt | 2 |
| 8 | bus_div | 4 |
| 12 | along_div | 4 |
| 16 | chip_model | 8 |
| 24 | model | 16 |
| 40 | struct_count | 2 |
| 42 | struct_entry_len | 2 |
| 44 | struct_table | struct_count * struct_entry_len |
struct_table 中 mem_type 使用同一套 AREA 编号:
| mem_type | 结构体实例所在区域 |
|---|---|
0x01 |
data |
0x02 |
idata |
0x03 |
xdata |
0x04 |
code |
struct_table 不应把结构体实例标为 0x0F,因为 0x0F info 只用于同步 code 区信息块地址和长度,不作为普通变量读写区域。
小程序读取完整 Modbus_Code_Info_t 后执行以下步骤:
struct_count 和 struct_entry_len。struct_table。mem_type、byte_addr、byte_len、type_name 创建结构体组。0x01 data0x02 idata0x03 xdata0x04 codetype_name 自动补全字段、位域、数组和数据类型。示例:
struct_table[0]:
byte_addr = 0x2000
byte_len = 64
mem_type = 0x03
type_name = motor_runtime_t
则后续读取该结构体实例时发送:
03 20 00 00 40 CRC_H CRC_L
含义为读取 xdata 区 0x2000 开始的 64 字节。
code 和 info 区默认只读。LEN 单位始终为字节,不是 Modbus 寄存器。0x0F info 只用于连接同步信息块地址和长度,请求/回复均按普通只读帧处理,不作为普通变量读写区域。Modbus_Code_Info_t 布局,应同步更新本文档和小程序解析器。