You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WinApi与Raspberry Pi Pico串口通信异常问题排查

Raspberry Pi Pico USB串口通信卡在WaitCommEvent的解决方案

问题分析

你的程序卡在WaitCommEvent的核心原因是:Raspberry Pi Pico的USB CDC串口默认需要Windows端断言DTR(数据终端就绪)信号才会启动数据发送。Putty在打开串口时会自动设置DTR信号,触发Pico开始发送数据;关闭Putty后DTR虽然撤销,但Pico会持续发送直到重启,因此你的程序后续能正常读取。而你的初始代码没有处理DTR信号,导致Pico始终未启动数据传输,Windows端自然检测不到EV_RXCHAR事件。

另外,串口缓冲区的残留数据也可能干扰事件检测,需要在初始化时清空。

解决方案

1. 启用DTR/RTS信号

在设置完串口参数后,主动触发DTR和RTS信号,让Pico开始发送数据:

// 设置完SetCommState后添加
if (!EscapeCommFunction(hComm, SETDTR)) {
    cout << "\nError setting DTR!\n";
}
if (!EscapeCommFunction(hComm, SETRTS)) {
    cout << "\nError setting RTS!\n";
}

也可以直接在DCB参数中配置自动启用:

dcbParams.fDtrControl = DTR_CONTROL_ENABLE;
dcbParams.fRtsControl = RTS_CONTROL_ENABLE;

2. 初始化时清空串口缓冲区

打开串口后立即清空输入输出缓冲区,避免残留数据影响事件检测:

// 打开串口成功后添加
if (!PurgeComm(hComm, PURGE_TXCLEAR | PURGE_RXCLEAR)) {
    cout << "\nError purging comm buffers!\n";
}

3. 优化WaitCommEvent与ReadFile的配合

每次事件触发后,先确认缓冲区状态再读取数据,同时处理可能的错误重试:

for (int i = 0; i <= 99; i+=1)
{
    if (!WaitCommEvent(hComm, &dwEventMask, NULL))
    {
        DWORD err = GetLastError();
        cout << "\n    Error in WaitCommEvent! Code: " << err << "\n";
        // 重置事件掩码,继续等待
        SetCommMask(hComm, EV_RXCHAR);
        continue;
    }
    
    if (dwEventMask & EV_RXCHAR)
    {
        COMSTAT cs;
        DWORD err;
        ClearCommError(hComm, &err, &cs);
        if (cs.cbInQue > 0)
        {
            ReadFile(hComm, &readBuffer, min(sizeof(readBuffer), cs.cbInQue), &noBytesRead, NULL);
            cout << "           " << string(readBuffer, noBytesRead);
            // 清空缓冲区,避免残留数据干扰下次读取
            memset(readBuffer, 0, sizeof(readBuffer));
        }
    }
}

修改后的完整代码片段

hComm = CreateFileA(commPortName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (hComm == INVALID_HANDLE_VALUE)
    cout << "\nError Port " << commPortName << " can't be opened!!!\n";
else
{
    cout << "\nPort " << commPortName << " is opened!!\n";
    
    // 清空串口缓冲区
    if (!PurgeComm(hComm, PURGE_TXCLEAR | PURGE_RXCLEAR))
        cout << "\nError purging comm buffers!\n";
}

dcbParams.DCBlength = sizeof(dcbParams);
if (!GetCommState(hComm, &dcbParams))
    cout << "\nError in GetCommState()\n";
dcbParams.BaudRate = CBR_115200; 
dcbParams.ByteSize = 8; 
dcbParams.StopBits = ONESTOPBIT; 
dcbParams.Parity = NOPARITY;
// 自动启用DTR/RTS
dcbParams.fDtrControl = DTR_CONTROL_ENABLE;
dcbParams.fRtsControl = RTS_CONTROL_ENABLE;

if (!SetCommState(hComm, &dcbParams))
    cout << "\nError in setting Device-Control Block(DCB)\n";
else
{
    cout << "\n\n    Setting DCB Structure Successfull" 
         << "\n       Baudrate --> " << dcbParams.BaudRate 
         << "\n       ByteSize --> " << (unsigned int)dcbParams.ByteSize 
         << "\n       StopBits --> " << (unsigned int)dcbParams.StopBits 
         << "\n       Parity   --> " << (unsigned int)dcbParams.Parity;
}

// 手动断言DTR/RTS(可选,若DCB配置已生效可省略)
if (!EscapeCommFunction(hComm, SETDTR))
    cout << "\nError setting DTR!\n";
if (!EscapeCommFunction(hComm, SETRTS))
    cout << "\nError setting RTS!\n";

timeouts.ReadIntervalTimeout = 50;
timeouts.ReadTotalTimeoutConstant = 50;
timeouts.ReadTotalTimeoutMultiplier = 10;
if (!SetCommTimeouts(hComm, &timeouts))
    cout << "\n\n    Error! in Setting Time Outs\n";
else
    cout << "\n\n    Setting Serial Port Timeouts Successfull\n";

if (!SetCommMask(hComm, EV_RXCHAR))
    cout << "\n\n    Error! in Setting Communication Mask\n";
else
    cout << "\n\n    Setting Communication Mask successfull\n";

cout << "\n\n    Waiting for Data Reception....\n";
for (int i = 0; i <= 99; i+=1)
{
    if (!WaitCommEvent(hComm, &dwEventMask, NULL))
    {
        DWORD err = GetLastError();
        cout << "\n    Error in WaitCommEvent! Code: " << err << "\n";
        SetCommMask(hComm, EV_RXCHAR);
        continue;
    }
    
    if (dwEventMask & EV_RXCHAR)
    {
        COMSTAT cs;
        DWORD err;
        ClearCommError(hComm, &err, &cs);
        if (cs.cbInQue > 0)
        {
            ReadFile(hComm, &readBuffer, min(sizeof(readBuffer), cs.cbInQue), &noBytesRead, NULL);
            cout << "           " << string(readBuffer, noBytesRead);
            memset(readBuffer, 0, sizeof(readBuffer));
        }
    }
}
CloseHandle(hComm);

额外说明

  • Pico的USB CDC驱动设计:部分Pico串口程序会等待DTR信号才启动数据发送,这是为了兼容传统串口设备的握手逻辑。
  • Putty的行为:Putty默认会在打开串口时设置DTR/RTS,关闭时撤销,但Pico一旦开始发送就会持续输出直到重启。

内容的提问来源于stack exchange,提问作者Luzian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 10:14:59