近红外光谱仪串口指令解析
近红外光谱仪串口指令解析
概述
谱研互联近红外光谱模组和光谱仪支持通过 USB 虚拟串口(HID)进行底层指令通信。掌握串口指令协议,可以在不使用 SDK 的情况下直接与设备交互,适用于嵌入式系统开发、自定义上位机开发等场景。本文介绍串口通信的数据帧结构、校验机制以及常用操作指令。
一、数据帧结构
每条指令由固定格式的数据帧(Packet)组成,帧结构如下:
| 成员变量 | 起始索引 | 字节数 | 说明 |
|---|---|---|---|
| start bytes | 0 | 4 | 帧起始标识:0x41 0x42 0x43 0x44(ASCII "ABCD") |
| checksum | 4 | 4 | 校验和(不含 start/trailer bytes) |
| flag | 8 | 1 | 标志位,固定 0xC0 |
| sequence byte | 9 | 1 | 序列号(用于匹配请求与响应) |
| length | 10 | 2 | 数据长度 = command(2) + data(N) 的字节数 |
| command | 12 | 2 | 指令码 |
| data | 14 | N | 指令附带数据(可为空) |
| trailer bytes | 末尾 | 4 | 帧结束标识:0x44 0x43 0x42 0x41(ASCII "DCBA") |
示例帧(配置扫描参数):
41 42 43 44 ← start bytes
5D 00 00 00 ← checksum
C0 ← flag
00 ← sequence byte
1E 00 ← length = 30
18 02 ← command (NNO_CMD_CONFIG_SCAN)
04 04 02 00 ... ← data(28 bytes)
44 43 42 41 ← trailer bytes
二、校验和计算
校验和用于验证数据传输的完整性。计算方式为:对从 flag 到 data 末尾的所有字节求和(不含 start bytes 和 trailer bytes),取低 4 字节。接收方收到数据后重新计算校验和并与帧中的 checksum 字段比对,不一致则说明传输错误。
三、常用操作指令
3.1 配置扫描参数(NNO_CMD_CONFIG_SCAN)
指令码:0x18 0x02
配置光谱仪的扫描参数,包括积分时间、增益、扫描模式等。发送后设备返回相同的指令帧表示确认。
发送帧示例:
41 42 43 44 5D 00 00 00 C0 00 1E 00 18 02
[28 bytes 配置数据]
44 43 42 41
3.2 执行扫描(NNO_CMD_PERFORM_SCAN)
指令码:0x16 0x02
触发一次光谱扫描。该指令无 data 字段(length = 2)。
发送帧:
41 42 43 44 DA 00 00 00 C0 00 02 00 16 02 44 43 42 41
返回帧:设备返回扫描数据包,data 中包含波长、强度等信息。
3.3 获取扫描状态(NNO_CMD_SCAN_GET_STATUS)
指令码:0x19 0x02
查询扫描是否完成。返回的 data payload:
| 返回值 | 含义 |
|---|---|
0x01 |
扫描完成,数据就绪 |
| 空 | 仍在扫描中,需继续轮询 |
发送帧:
41 42 43 44 DD 00 00 00 C0 00 02 00 19 02 44 43 42 41
3.4 获取返回数据长度(NNO_CMD_FILE_GET_READSIZE)
指令码:0x2D 0x00
读取本次扫描返回的数据包总长度(bytes),用于后续分片读取。
3.5 获取扫描数据(NNO_CMD_FILE_GET_DATA)
指令码:0x2E 0x00
读取完整的扫描数据包。返回的 data 中包含以下信息:
| 字段 | 说明 |
|---|---|
| header_version | 协议版本号 |
| serial_number | 设备序列号(8 字节 ASCII) |
| adc_data_length | ADC 数据总长度 |
| pga | 当前增益倍数 |
| scan_config | 扫描配置参数 |
| wavelength[ ] | 波长数组(864 个 double 值) |
| intensity[ ] | 强度数组(864 个 int 值) |
| length | 有效数据点数(通常为 228) |
四、扫描数据的读取流程
完整的扫描与数据读取流程如下:
1. 发送 CONFIG_SCAN → 配置扫描参数
2. 发送 PERFORM_SCAN → 触发扫描
3. 轮询 GET_STATUS → 等待扫描完成(data[0] == 0x01)
4. 发送 GET_READSIZE → 获取数据包长度
5. 发送 GET_DATA → 读取完整数据包
6. 解析 wavelength[] 和 intensity[] → 获取光谱数据
五、积分时间与像素宽度的换算
积分时间(exposure time)
积分时间的 index 从 0 开始,与真实时间的对应关系:
| index | 积分时间 |
|---|---|
| 0 | 0.635 ms |
| 1 | 1.27 ms |
| 2 | 2.54 ms |
| 3 | 5.08 ms |
| ... | 每级翻倍 |
像素宽度(width_px)
以 DMD 像素(px)为单位计算图案宽度。对于标准波长光机(900–1700 nm):
$$1\text{ px} \approx 1.1709\text{ nm} = \frac{800\text{ nm}}{854\text{ px} \times 80\%}$$
| width_px | 对应光谱宽度 |
|---|---|
| 5 px | ≈ 5.85 nm |
| 6 px | ≈ 7.03 nm |
| 7 px | ≈ 8.20 nm |
对于扩展波长光机(1350–2150 nm / 1600–2400 nm),1 px ≈ 1 nm。
六、常见问题
结构体对齐
在 C/C++ 开发中解析数据包时,需注意结构体的字节对齐问题。以下结构体因包含不同大小的成员(uint8_t、uint16_t、char[ ]),编译器可能自动插入填充字节导致解析偏移:
struct ScanConfig {
uint8_t scan_type;
uint16_t scanConfigIndex; // 可能在 offset 2 而非 offset 1
char serial_number[8];
char config_name[40];
uint16_t num_repeats;
uint8_t num_sections;
};
解决方案:使用 #pragma pack(1) 或 __attribute__((packed)) 强制按 1 字节对齐,或逐字段手动解析偏移位置,不依赖 sizeof 计算。
本文由谱研互联整理编写