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

Windows下串口WriteFile写操作阻塞,需接收字节后才执行问题排查

关于RS485串口监控程序WriteFile挂起的问题

问题场景

我用C++编写了一个小型RS485串口总线监控程序,功能是从串口读取最大32字节的小数据包,并在用户请求时偶尔发送同类数据包。读取功能运行正常:初始化时通过SetCommMask(hComm, EV_RXCHAR)设置读取线程,借助ReadFile配合超时机制识别数据包间隔。但主程序中调用WriteFile发送数据包时,会挂起直到总线上有字节被接收,之后总线设备才能识别并正常响应。

相关代码

串口初始化与接收线程代码

HANDLE uart_init(char *portname, int baudrate)
{
    HANDLE hComm;
    BOOL Write_Status, Read_Status;
    DCB dcbSerialParams;
    COMMTIMEOUTS timeouts = { 0 };

    hComm = CreateFile(L"com8",  //port name
        GENERIC_READ | GENERIC_WRITE,   //Read/Write
        0,                              // No Sharing
        NULL,                           // No Security
        OPEN_EXISTING, // Open existing port only
        0,             // Non Overlapped I/O
        NULL);         // Null for Comm Devices

    if (hComm == INVALID_HANDLE_VALUE)
        printf("Error in opening serial port\n");
    else
        printf("opening serial port successful\n");

    Write_Status = GetCommState(hComm, &dcbSerialParams);     //retreives  the current settings

    if (Write_Status == FALSE) 
    {
        printf("   Error in GetCommState()\n");
        CloseHandle(hComm);
        return NULL;
    }


    dcbSerialParams.BaudRate = baudrate;      // Setting BaudRate = 1200
    dcbSerialParams.ByteSize = 8;             // Setting ByteSize = 8
    dcbSerialParams.StopBits = ONESTOPBIT;    // Setting StopBits = 1
    dcbSerialParams.Parity   = NOPARITY;      // Setting Parity = None

    Write_Status = SetCommState(hComm, &dcbSerialParams);  //Configuring the port according to settings in DCB

    if (Write_Status == FALSE)
    {
        printf("   Error! in Setting DCB Structure\n");
        CloseHandle(hComm);
        return NULL;
    }
    else
    {
        printf("   Setting DCB Structure Successful\n");
        printf("       Baudrate = %d\n", dcbSerialParams.BaudRate);
        printf("       ByteSize = %d\n", dcbSerialParams.ByteSize);
        printf("       StopBits = %d\n", dcbSerialParams.StopBits);
        printf("       Parity   = %d\n\n", dcbSerialParams.Parity);
    }

    // Set COM port timeout settings
    timeouts.ReadIntervalTimeout = 50;
    timeouts.ReadTotalTimeoutConstant = 0;
    timeouts.ReadTotalTimeoutMultiplier = 0;
    timeouts.WriteTotalTimeoutConstant = 0;
    timeouts.WriteTotalTimeoutMultiplier = 0;
    if (SetCommTimeouts(hComm, &timeouts) == 0)
    {
        printf("Error setting timeouts\n");
        CloseHandle(hComm);
        return NULL;
    }

    Read_Status = SetCommMask(hComm, EV_RXCHAR); //Configure Windows to Monitor the serial device for Character Reception

    if (Read_Status == FALSE)
        printf("    Error! in Setting CommMask\n\n");
    else
        printf("    Setting CommMask successfull\n\n");
    return hComm;
}

unsigned int __stdcall RS485Receiver(void* data)
{
    BOOL status = 0;
    DWORD dwEventMask = 0;
    DWORD nBytes;                     // Bytes read by ReadFile()

    puts("Serial Thread started");
    if (hComm = uart_init("COM8", 1200))
    {
        printf("Waiting for Data Reception...\n");
        status = WaitCommEvent(hComm, &dwEventMask, NULL); //Wait for the character to be received
        if (status == FALSE)
        {
            printf("Error! in Setting WaitCommEvent()\n");
        }
        else //If  WaitCommEvent()==True Read the RXed data using ReadFile();
        {
            _putch('.'); fflush(stdout);
            do
            {
                //Read_Status = ReadFile(hComm, &TempChar, sizeof(TempChar), &NoBytesRead, NULL);
                if ((!ReadFile(hComm, RS485PacketBuffer, sizeof(T_PACKET), &nBytes, NULL)))
                {
                    printf("wrong character");
                }
                else if (nBytes)
                {
                    RS485PrintPacket(RS485PacketBuffer, RS485PacketBuffer->length + 4, stdout);
                }
            } while (1);
        }
        CloseHandle(hComm);//Closing the Serial Port
    }
    puts("Serial Thread stopped");
    return 0;
}
#endif

会挂起的发送函数代码

uint8_t sendPacket(T_PACKET* packet)
{
    DWORD nBytes;

    //calculate checksum
    packet->d[packet->length] = crc8((uint8_t*)packet, packet->length + 4);

    // PORTB &= ~(1 << TRANSENABLE); // ToDo: How do I enable transmitter on PC
    Sleep(10); // short pause to stabilize bus transceiver
    printf("Sende Paket, length = %u\n", packet->length+5);
    if (!WriteFile(hComm, packet, packet->length + 5, &nBytes, NULL))
    {
        printf("Error writing text to RS485 port\n");
        return 6;
    }
    printf("gesendet\n"); fflush(stdout);
    if (nBytes != packet->length + 5) return 7;
    printf("kein Fehler\n");
    // PORTB |= (1 << TRANSENABLE); // ToDo: How do I disable transmitter on PC
    return 0;
}

排查尝试与猜测

  • 尝试过多种超时设置,未解决问题。
  • 怀疑是USB/RS485转换器的问题,或者RS485总线中串口检测到自身发送的字节导致异常。
  • 猜测阻塞的接收线程导致整个串口被阻塞。

解决方案方向

按照@Codo的建议,尝试使用重叠IO(Overlapped IO),已得到完整解决方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 12:20:36