欢迎光临
我们一直在努力

USB 级联 CH348 USB转串口物理端口识别方案

一、系统总架构

plaintext

PC USB 主机


┌─────────────────────┐
│ 板1 │
│ 上行USB ◄── PC │
│ 下行USB ──► 板2 │
│ 本地USB ──► 4口USB转串口芯片 CH348
└─────────────────────┘


┌─────────────────────┐
│ 板2 │
│ 上行USB ◄── 板1 │
│ 下行USB ──► 板3 │
│ 本地USB ──► 4口USB转串口芯片 CH348
└─────────────────────┘


┌─────────────────────┐
│ 板3 │
│ 上行USB ◄── 板2 │
│ 下行USB ──► 板4 │
│ 本地USB ──► 4口USB转串口芯片 CH348
└─────────────────────┘


┌─────────────────────┐
│ 板4 │
│ 上行USB ◄── 板3 │
│ 下行USB ──► 空(末端)│
│ 本地USB ──► 4口USB转串口芯片 CH348
└─────────────────────┘

说明:

  • 共 4 块硬件板,板间 USB 级联连接
  • 每块板含:上行 USB、下行 USB、本地 USB 共 3 口
  • 本地 USB 接板载 CH348(4 端口 USB 转串口芯片)
  • PC 仅需 1 根 USB 连接首块板,整套系统共 16 路串口
  • 目标:上位机识别每一路串口对应的物理板号 + 板内串口序号

  • 二、方案一:CH348 烧录唯一序列号 SN 方案

    1. 结构示意图

    plaintext

    PC USB 主机


    ┌─────────────────────┐
    │ 板1 │
    │ 上行USB ◄── PC │
    │ 下行USB ──► 板2 │
    │ 本地USB ──► CH348(SN=BOARD01)
    └─────────────────────┘


    ┌─────────────────────┐
    │ 板2 │
    │ 上行USB ◄── 板1 │
    │ 下行USB ──► 板3 │
    │ 本地USB ──► CH348(SN=BOARD02)
    └─────────────────────┘


    ┌─────────────────────┐
    │ 板3 │
    │ 上行USB ◄── 板2 │
    │ 下行USB ──► 板4 │
    │ 本地USB ──► CH348(SN=BOARD03)
    └─────────────────────┘


    ┌─────────────────────┐
    │ 板4 │
    │ 上行USB ◄── 板3 │
    │ 下行USB ──► 空(末端)│
    │ 本地USB ──► CH348(SN=BOARD04)
    └─────────────────────┘

    2. 逻辑关系

  • 每块板 CH348 单独烧录唯一序列号:板 1-BOARD01、板 2-BOARD02、板 3-BOARD03、板 4-BOARD04
  • 烧录要求:必须单块板单独接 PC 烧录,不可级联烧录
  • 同一块 CH348 的 4 个串口:SN 相同,Interface 号不同 (0/1/2/3)
  • 映射规则:Interface0→串口 1、1→串口 2、2→串口 3、3→串口 4
  • 上位机流程:枚举 COM 口→读取 SN+Interface→映射板号与串口
  • 3. C++ 核心实现代码

    cpp

    运行

    #include <Windows.h>
    #include <SetupAPI.h>
    #include <string>
    #include <vector>
    using namespace std;

    struct ComInfo
    {
    wstring comName;
    wstring sn;
    int interfaceId;
    int boardId;
    int uartId;
    };

    vector<ComInfo> comList;

    int SnToBoardId(const wstring& sn)
    {
    if (sn == L"BOARD01") return 1;
    if (sn == L"BOARD02") return 2;
    if (sn == L"BOARD03") return 3;
    if (sn == L"BOARD04") return 4;
    return 0;
    }

    void EnumAllCH348Com()
    {
    comList.clear();
    HDEVINFO hDevInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_COMPORT,
    nullptr, nullptr, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);

    if (hDevInfo == INVALID_HANDLE_VALUE) return;

    SP_DEVICE_INTERFACE_DATA ifData = { 0 };
    ifData.cbSize = sizeof(ifData);

    for (DWORD i = 0; SetupDiEnumDeviceInterfaces(hDevInfo, nullptr,
    &GUID_DEVINTERFACE_COMPORT, i, &ifData); i++)
    {
    DWORD reqSize = 0;
    SetupDiGetDeviceInterfaceDetail(hDevInfo, &ifData, nullptr, 0, &reqSize, nullptr);
    vector<BYTE> detailBuf(reqSize);
    PSP_DEVICE_INTERFACE_DETAIL_DATA pDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)detailBuf.data();
    pDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
    SP_DEVINFO_DATA devData = { 0 };
    devData.cbSize = sizeof(devData);

    if (!SetupDiGetDeviceInterfaceDetail(hDevInfo, &ifData, pDetail, reqSize, nullptr, &devData))
    continue;

    wstring devicePath = pDetail->DevicePath;
    int infNum = -1;

    if (devicePath.find(L"&0") != wstring::npos) infNum = 0;
    else if (devicePath.find(L"&1") != wstring::npos) infNum = 1;
    else if (devicePath.find(L"&2") != wstring::npos) infNum = 2;
    else if (devicePath.find(L"&3") != wstring::npos) infNum = 3;
    if (infNum < 0) continue;

    wstring sn;
    size_t snPos = devicePath.find(L"SN_");
    if (snPos != wstring::npos)
    sn = devicePath.substr(snPos + 3, 8);

    wchar_t* pCom = wcsstr(devicePath.c_str(), L"COM");
    if (!pCom) continue;

    ComInfo info;
    info.comName = pCom;
    info.sn = sn;
    info.interfaceId = infNum;
    info.boardId = SnToBoardId(sn);
    info.uartId = infNum + 1;
    comList.push_back(info);
    }
    SetupDiDestroyDeviceInfoList(hDevInfo);
    }


    三、方案二:4bit GPIO 硬件地址方案(不烧 SN、不加 MCU)

    1. 结构示意图

    plaintext

    PC USB 主机


    ┌─────────────────────┐
    │ 板1 │
    │ 上行USB ◄── PC │
    │ 下行USB ──► 板2 │
    │ 本地USB ──► CH348(GPIO硬件地址:0001)
    └─────────────────────┘


    ┌─────────────────────┐
    │ 板2 │
    │ 上行USB ◄── 板1 │
    │ 下行USB ──► 板3 │
    │ 本地USB ──► CH348(GPIO硬件地址:0010)
    └─────────────────────┘


    ┌─────────────────────┐
    │ 板3 │
    │ 上行USB ◄── 板2 │
    │ 下行USB ──► 板4 │
    │ 本地USB ──► CH348(GPIO硬件地址:0011)
    └─────────────────────┘


    ┌─────────────────────┐
    │ 板4 │
    │ 上行USB ◄── 板3 │
    │ 下行USB ──► 空(末端)│
    │ 本地USB ──► CH348(GPIO硬件地址:0100)
    └─────────────────────┘

    2. 逻辑关系

  • 每块板使用 4 个上下拉电阻接 CH348 的 4 个 GPIO,设置 4bit 硬件地址
  • 地址定义:板 1-0001、板 2-0010、板 3-0011、板 4-0100
  • PC USB 枚举识别每一颗 CH348 设备
  • 上位机通过 CH348 官方 API 读取 GPIO 电平,得到板号
  • 每颗 CH348 对应 4 个端口:0/1/2/3→板内串口 1/2/3/4
  • 支持级联整体识别,无需单块烧录,无需额外 MCU
  • 3. C++ 核心实现代码

    cpp

    运行

    #include <Windows.h>
    #include "CH348DLL.H"
    #pragma comment(lib,"CH348DLL.LIB")
    #include <vector>
    #include <string>
    using namespace std;

    struct BoardPort
    {
    int boardAddr;
    int uartId;
    wstring comName;
    };
    vector<BoardPort> boardList;

    int GpioToBoard(BYTE gpioData)
    {
    int addr = gpioData & 0x0F;
    if (addr == 1) return 1;
    if (addr == 2) return 2;
    if (addr == 3) return 3;
    if (addr == 4) return 4;
    return 0;
    }

    void ScanAllCH348Board()
    {
    boardList.clear();
    DWORD devCount = 0;
    CH348GetDevices(&devCount);
    if (devCount == 0) return;

    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);

    wchar_t comName[32];
    for (int p = 0; p < 4; p++)
    {
    CH348GetComName(hDev, p, comName);
    BoardPort bp;
    bp.boardAddr = board;
    bp.uartId = p + 1;
    bp.comName = comName;
    boardList.push_back(bp);
    }
    CH348Close(hDev);
    }
    }


    四、两套方案对比总结

    方案一:烧录 SN 方案

    • 优点:无需改动硬件 GPIO 电路
    • 缺点:必须单块单独烧录 SN,不可级联烧录
    • 识别方式:USB 枚举→读 SN+Interface 号完成映射

    方案二:4bit GPIO 硬件地址方案

    • 优点:不烧 SN、硬件统一、支持级联识别、无需额外 MCU
    • 缺点:需硬件设计 4bit GPIO 上下拉电路
    • 识别方式:USB 枚举 CH348→读 GPIO 地址→匹配 4 路串口
    赞(0)
    未经允许不得转载:171主机测评 » USB 级联 CH348 USB转串口物理端口识别方案
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址