近红外光谱仪串口指令解析

概述

谱研互联近红外光谱模组和光谱仪支持通过 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 计算。


本文由谱研互联整理编写