簡體   English   中英

WriteFile to Serial port in Windows 等待時間長

[英]WriteFile to Serial port in Windows waits a long time

我正在 c++ 中編寫一個小型串行總線 (RS485) 監視器。它應該從串行端口讀取最多 32 字節的小數據包,偶爾會在用戶請求時將這樣的數據總線寫入串行端口。

閱讀效果很好。 我使用 SetCommMask(hComm, EV_RXCHAR); 設置了一個讀取線程在初始化和稍后的 ReadFile(hComm, RS485PacketBuffer, sizeof(T_PACKET), &nBytes, NULL) 期間使用超時接收 package 以獲取 package 間隔。

在主程序中,我使用一個簡單的 WriteFile(hComm, packet, packet->length + 5, &nBytes, NULL) 來寫入 package。

這個 WriteFile 似乎掛起,直到從總線接收到一些字節。 只有這樣 package 才會被發送,總線設備才能識別並正確應答。 為什么 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

這是寫入 function,它會掛起直到收到字符:

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。我現在有一個完整的解決方案。 請參閱下面的答案。

感謝所有的提示。 @Codo 的第一句話就是解決方案。 我必須使用重疊,因為 Windows 在接收方等待時也會阻止發送方。 愚蠢,但真實。 由於重疊 IO 有點復雜,我將我的解決方案發布給她以供參考。 現在我必須在 C# 中嘗試相同的方法。:-)

HANDLE uart_init(char *portname, int baudrate)
{
    HANDLE hComm;
    BOOL Result;
    DCB dcbSerialParams;
    COMMTIMEOUTS timeouts = { 0 };
    wchar_t wstr[50];
    MultiByteToWideChar(CP_UTF8, 0, portname, -1, wstr, 50);
    hComm = CreateFile(wstr,  //port name
        GENERIC_READ | GENERIC_WRITE,   //Read/Write
        0,                              // No Sharing
        NULL,                           // No Security
        OPEN_EXISTING, // Open existing port only
        FILE_FLAG_OVERLAPPED,           // 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");

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

    if (Result == 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
    dcbSerialParams.fBinary         = TRUE;       // has to be TRUE in Windows
    dcbSerialParams.fParity         = FALSE;      // No parity
    dcbSerialParams.fOutxCtsFlow    = FALSE;      // No CTS flow control
    dcbSerialParams.fOutxDsrFlow    = FALSE;      // No DSR flow control
    dcbSerialParams.fDtrControl     = FALSE;      // No DTR low control
    dcbSerialParams.fDsrSensitivity = FALSE;      // Ignore DSR
    dcbSerialParams.fOutX           = FALSE;      // No XON/XOFF flow control
    dcbSerialParams.fInX            = FALSE;      // No XON/XOFF flow control
    dcbSerialParams.fErrorChar      = FALSE;      // do not replace errors
    dcbSerialParams.fNull           = FALSE;      // allow NULL bytes
    dcbSerialParams.fRtsControl     = RTS_CONTROL_ENABLE; // Enable RTS pin
    dcbSerialParams.fAbortOnError   = FALSE;      // do not stop on error

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

    if (Result == 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;
    }

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

    if (Result == 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()
    DWORD dwRes;
    DWORD dwRead;
    BOOL fWaitingOnRead = FALSE;
    OVERLAPPED osRead = { 0 };

    puts("Serial Thread started");
    if (hComm = uart_init((char *)data, 1200))
    {
        printf("Waiting for Data Reception...\n");

        // Create the overlapped event. Must be closed before exiting
        // to avoid a handle leak.
        osRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
        if (osRead.hEvent == NULL)
        {
            CloseHandle(hComm);//Closing the Serial Port
            puts("Serial Thread stopped");
            return 0;
        }

        while (1)
        {
            if (!fWaitingOnRead) 
            {
                // Issue read operation.
                if (!ReadFile(hComm, RS485PacketBuffer, sizeof(T_PACKET), &dwRead, &osRead)) 
                {
                    if (GetLastError() != ERROR_IO_PENDING)     // read not delayed?
                        printf("wrong character");
                    else
                        fWaitingOnRead = TRUE;
                }
                else {
                    // read completed immediately
                    RS485PrintPacket(RS485PacketBuffer, RS485PacketBuffer->length + 4, stdout);
                }
            }

            if (fWaitingOnRead)
            {
                dwRes = WaitForSingleObject(osRead.hEvent, INFINITE);
                switch (dwRes)
                {
                    // Read completed.
                case WAIT_OBJECT_0:
                    if (!GetOverlappedResult(hComm, &osRead, &dwRead, FALSE))
                        printf("wrong character");
                    else
                        // Read completed successfully.
                        RS485PrintPacket(RS485PacketBuffer, RS485PacketBuffer->length + 4, stdout);

                    //  Reset flag so that another opertion can be issued.
                    fWaitingOnRead = FALSE;
                    break;

                case WAIT_TIMEOUT:
                    // Operation isn't complete yet. fWaitingOnRead flag isn't
                    // changed since I'll loop back around, and I don't want
                    // to issue another read until the first one finishes.
                    //
                    // This is a good time to do some background work.
                    break;

                default:
                    // Error in the WaitForSingleObject; abort.
                    // This indicates a problem with the OVERLAPPED structure's
                    // event handle.
                    break;
                }
            }
        }
        CloseHandle(hComm); //Closing the Serial Port
    }
    puts("Serial Thread stopped");
    return 0;
}

/*
 * Send a data packet
 * use this only for Windows
 */
uint8_t sendPacket(T_PACKET* packet)
{
    DWORD nBytes;
    uint8_t fRes = 0;
    OVERLAPPED osWrite = { 0 };

    //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
    osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (osWrite.hEvent == NULL)
        // error creating overlapped event handle
        return ERR_COMM_WR;

    if (!WriteFile(hComm, packet, packet->length + 5, &nBytes, &osWrite))
    {
        if (GetLastError() != ERROR_IO_PENDING)     // read not delayed?
        {
            fRes = ERR_COMM_WR;
        }
        else
        {
            if (!GetOverlappedResult(hComm, &osWrite, &nBytes, TRUE))
            {
                fRes = ERR_COMM_WR;
            }
            else
            {
                // Read completed successfully.
                RS485PrintPacket(packet, packet->length + 4, stdout);
            }
        }
    }
    CloseHandle(osWrite.hEvent);
    // PORTB |= (1 << TRANSENABLE); // ToDo: How do I disable transmitter on PC
    return fRes;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM