1. 项目背景与需求分析
在工业自动化、实验室设备集群等场景中,经常需要构建多板卡级联的串口通信系统。传统方案需要为每个串口设备单独连接USB线缆到主机,不仅布线复杂,还占用大量主机USB接口资源。通过USB级联方案,只需用一根USB线连接首块板卡,即可扩展出多个串口通道,极大简化了系统架构。
本项目的核心挑战在于:当使用CH348这类多端口USB转串口芯片构建级联系统时,如何让上位机准确识别每个串口对应的物理位置(板号+端口号)。例如在16路串口的系统中,当收到COM5的数据时,需要快速确定这是来自第2块板的第1个串口,还是第1块板的第2个串口。
2. 系统架构设计
2.1 硬件拓扑结构
系统采用四级级联设计,每块板卡包含:
- 1个上行USB接口(连接PC或上级板卡)
- 1个下行USB接口(连接下级板卡)
- 1个本地USB接口(连接板载CH348芯片)
plaintext复制PC USB主机
│
▼
┌─────────────────────┐
│ 板1 │
│ 上行USB ◄── PC │
│ 下行USB ──► 板2 │
│ 本地USB ──► CH348(4串口)
└─────────────────────┘
│
▼
┌─────────────────────┐
│ 板2 │
│ 上行USB ◄── 板1 │
│ 下行USB ──► 板3 │
│ 本地USB ──► CH348(4串口)
└─────────────────────┘
│
▼
┌─────────────────────┐
│ 板3 │
│ 上行USB ◄── 板2 │
│ 下行USB ──► 板4 │
│ 本地USB ──► CH348(4串口)
└─────────────────────┘
│
▼
┌─────────────────────┐
│ 板4 │
│ 上行USB ◄── 板3 │
│ 下行USB ──► 空(末端)│
│ 本地USB ──► CH348(4串口)
└─────────────────────┘
2.2 CH348芯片特性
CH348是沁恒推出的USB转8串口芯片(本项目使用4串口模式),具有以下关键特性:
- 支持USB2.0全速12Mbps通信
- 每个物理芯片可虚拟出多个串口(本项目用4个)
- 支持修改设备序列号(SN)
- 提供GPIO扩展功能(方案二关键)
- 工作电流约100mA,适合总线供电
3. 方案一:SN烧录识别方案
3.1 实现原理
为每块板卡的CH348芯片烧录唯一序列号(SN),上位机通过解析设备路径中的SN和接口号(Interface)实现物理定位。
plaintext复制设备示例路径:
USB\VID_1A86&PID_55D4\SN_BOARD03&0
其中:
SN_BOARD03 → 第3块板
&0 → 接口0(对应板内串口1)
3.2 烧录流程与注意事项
-
单板烧录模式:
- 必须断开级联,单独连接每块板卡到PC
- 使用沁恒提供的CH348BurnTool工具
- 按顺序烧录:BOARD01、BOARD02等
-
烧录参数配置:
plaintext复制
VID: 1A86(保持默认) PID: 55D4(保持默认) SN: BOARDxx(xx为板号) 其他参数保持出厂默认
重要提示:绝对禁止在级联状态下烧录SN!这会导致多块板卡SN冲突,造成系统无法识别。
3.3 上位机识别代码解析
核心是通过Windows SetupAPI获取设备接口详情,从中提取SN和接口号:
cpp复制// 设备信息结构体
struct ComInfo {
wstring comName; // COM端口名
wstring sn; // 序列号
int interfaceId; // 接口号(0-3)
int boardId; // 解析出的板号
int uartId; // 板内串口号(1-4)
};
// SN到板号的转换
int SnToBoardId(const wstring& sn) {
if (sn == L"BOARD01") return 1;
if (sn == L"BOARD02") return 2;
// ...其他板号
}
// 枚举所有CH348串口
void EnumAllCH348Com() {
HDEVINFO hDevInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_COMPORT,
nullptr, nullptr, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
// 遍历所有设备接口
while (SetupDiEnumDeviceInterfaces(...)) {
// 获取设备路径
wstring devicePath = pDetail->DevicePath;
// 提取接口号
if (devicePath.find(L"&0") != npos) infNum = 0;
// ...其他接口
// 提取SN
size_t snPos = devicePath.find(L"SN_");
if (snPos != npos)
sn = devicePath.substr(snPos + 3, 8);
// 保存到结构体
ComInfo info;
info.comName = ...;
info.sn = sn;
info.interfaceId = infNum;
info.boardId = SnToBoardId(sn);
info.uartId = infNum + 1; // 接口0→串口1
comList.push_back(info);
}
}
3.4 方案优缺点
优势:
- 无需硬件改动,适合已有成品板卡
- 识别逻辑简单直接
局限:
- 生产环节繁琐(必须单板烧录)
- 后期维护困难(更换板卡需重新烧录)
- 无法热插拔识别(需重新枚举)
4. 方案二:GPIO硬件编码方案
4.1 硬件设计要点
通过4个GPIO的上下拉组合设置板卡地址:
plaintext复制板1:GPIO0=上拉,其他=下拉 → 0001
板2:GPIO1=上拉,其他=下拉 → 0010
板3:GPIO0+1=上拉 → 0011
板4:GPIO2=上拉 → 0100
电路设计注意事项:
- 使用10kΩ电阻实现可靠上/下拉
- GPIO线路需加100nF滤波电容
- 避免与其他功能引脚冲突
4.2 识别原理
上位机通过CH348官方DLL读取GPIO状态:
- 枚举所有CH348设备
- 对每个设备调用CH348GetGPIO()
- 解析4bit地址得到板号
- 关联对应的4个串口
4.3 核心代码实现
cpp复制#include "CH348DLL.H"
#pragma comment(lib,"CH348DLL.LIB")
// GPIO到板号的转换
int GpioToBoard(BYTE gpioData) {
int addr = gpioData & 0x0F; // 取低4位
switch(addr) {
case 1: return 1; // 0001
case 2: return 2; // 0010
case 3: return 3; // 0011
case 4: return 4; // 0100
default: return 0;
}
}
void ScanAllCH348Board() {
DWORD devCount = 0;
CH348GetDevices(&devCount);
for (DWORD i = 0; i < devCount; i++) {
HANDLE hDev = CH348Open(i);
if (hDev == INVALID_HANDLE_VALUE) continue;
BYTE gpio = 0;
CH348GetGPIO(hDev, &gpio);
int board = GpioToBoard(gpio);
// 获取该设备对应的4个串口
wchar_t comName[32];
for (int p = 0; p < 4; p++) {
CH348GetComName(hDev, p, comName);
// 保存板号、串口号、COM名称
BoardPort bp = {board, p+1, comName};
boardList.push_back(bp);
}
CH348Close(hDev);
}
}
4.4 方案优势与局限
突出优势:
- 支持热插拔识别
- 无需单独烧录步骤
- 板卡可任意更换位置
- 识别速度快(毫秒级)
实施难点:
- 需要硬件电路配合
- GPIO资源占用
- 地址空间有限(4bit最多16块板)
5. 生产与调试指南
5.1 方案选择建议
| 考量维度 | SN烧录方案 | GPIO编码方案 |
|---|---|---|
| 硬件改动 | 无 | 需要 |
| 生产便利性 | 差 | 优 |
| 维护便利性 | 差 | 优 |
| 识别可靠性 | 高 | 中高 |
| 扩展性 | 低 | 中 |
推荐选择:
- 小批量固定安装:SN方案
- 大批量/需维护场景:GPIO方案
5.2 常见问题排查
SN方案典型问题:
-
所有板卡显示相同SN
- 原因:级联状态下烧录
- 解决:单板重新烧录
-
部分串口无法识别
- 检查设备管理器是否显示黄色感叹号
- 重新安装CH348驱动
GPIO方案典型问题:
-
板号识别错误
- 测量GPIO电压是否符合预期
- 检查电阻焊接是否正常
-
级联通信不稳定
- 检查USB线缆质量
- 在末端板卡USB D+/-加22Ω匹配电阻
5.3 性能优化建议
- 枚举加速技巧:
cpp复制// 并行初始化多串口
for (auto& port : boardList) {
CreateFile(port.comName.c_str(), ...);
PurgeComm(hCom, PURGE_RXCLEAR);
}
- 电源管理:
- 每块板卡增加470μF钽电容
- 级联超过4块板时建议外接电源
6. 扩展应用场景
本方案可应用于:
- 工业控制柜多设备监控
- 实验室仪器集群数据采集
- 智能货架电子标签系统
- 分布式传感器网络
对于更大规模系统,可考虑:
- 改用CH482(16串口芯片)
- 采用树形级联拓扑
- 增加I2C总线辅助识别
在实际项目中,我们使用GPIO方案成功实现了32路串口(8块板卡)的可靠识别,系统连续运行6个月无故障。关键经验是:
- 给每块板卡增加电源指示灯
- 使用镀金USB连接器
- 上位机增加心跳检测机制