Search code examples
windowsvisual-c++serial-portuart

send and receive via Serial port Windows


I have a application running on windows, which will send data over serial port. Here is the code:

m_hCommPort= ::CreateFile(L"\\\\.\\COM3",
        GENERIC_READ|GENERIC_WRITE, 0, 0, OPEN_EXISTING,0,0);

    if(m_hCommPort == INVALID_HANDLE_VALUE) 
    {
        printf("COM error: %d\n", GetLastError());
    }
    config.DCBlength = sizeof(config);


    if((GetCommState(m_hCommPort, &config) == 0))
    {
        CloseHandle(m_hCommPort);
        printf("Get configuration port has a problem.\n");
        return FALSE;
    }

    config.BaudRate = 9600;
    config.StopBits = ONESTOPBIT;
    config.Parity = PARITY_NONE; 
    config.ByteSize = DATABITS_8;
    config.fDtrControl = 0;
    config.fRtsControl = 0;

    if (!SetCommState(m_hCommPort, &config))
    {
        CloseHandle(m_hCommPort);
        printf( "Failed to Set Comm State Reason: %d\n",GetLastError());
        return E_FAIL;
    }

Here is the code for Send only (Working) (continuously sending )

while(1)
    {
        Sleep(5000);
        int isWritten = WriteFile(m_hCommPort, txData, 9/*(DWORD)sizeof(txData)*/, &dwBytesWritten, NULL);
            printf("isWritten: %d, dwBytesWritten: %d \n", isWritten, dwBytesWritten);

    }

After this I have added code for Receive data too, then send is NOT WORKING. I mean not able to send data over UART. WriteFile() seems not executed, its stuck. Here I have added a thread to receive data, is thread causing the problem ? or do I need to do something else ?

void ReceiverThread(void *param)
{
    DWORD dwRead=0;
    BOOL fWaitingOnRead = FALSE;
    OVERLAPPED osReader = {0};

    osReader.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

    if (osReader.hEvent == NULL)
        printf("Error creating overlapped event; abort.\n");
    while(1)
    {
        if (!ReadFile(m_hCommPort, &Byte, 1, &dwRead, &osReader)) {
            if (GetLastError() != ERROR_IO_PENDING)     // read not delayed?
                printf("Error in communications; report it.\n");
            else
                fWaitingOnRead = TRUE;
        }
        else {    

            rxData[rxHeadIndex++]= Byte;
            rxHeadIndex = (rxHeadIndex) & QUEUE_MASK;
        }


    }

}


SetCommMask (m_hCommPort, EV_RXCHAR/ | EV_ERR); //receive character event


    _beginthread(ReceiverThread,0,NULL); 


    while(1)
    {
        Sleep(5000);
        int isWritten = WriteFile(m_hCommPort, txData, 9/*(DWORD)sizeof(txData)*/, &dwBytesWritten, NULL);
            printf("isWritten: %d, dwBytesWritten: %d \n", isWritten, dwBytesWritten);

    }

Thanks in advance. Ashok


Solution

  • I've encountered a similar problem a while ago.

    I found out that WriteFile(..) blocks if a ReadFile(..) is currently in progress for a serial port. So that's a problem if ReadFile(..) blocks if there is no data to be read.

    I've solved the problem by checking if there is data available within the serial buffer to be read by using the function ClearCommError(..). This ensures that ReadFile(..) can read something immediately and does not unnecessarily block the device. Try changing your ReceiverThread into something like this:

    void ReceiverThread(void *param)
    {
        DWORD dwRead=0;
        COMSTAT comStat;
        char buffer[1024];
    
        while (m_hCommPort != INVALID_HANDLE_VALUE)
        {
            if ((ClearCommError(m_hCommPort, NULL, &comStat) != FALSE) && (comStat.cbInQue > 0))
            {
                /* todo: ensure buffer is big enough to contain comStat.cbInQue bytes! */
    
                if (ReadFile(m_hCommPort, buffer, comStat.cbInQue, &dwRead, NULL) != FALSE)
                {
                    /* do something with data in buffer */
                }
            }
    
            /* avoid busy-wait */
            if (comStat.cbInQue == 0)
            {
                SleepEx(1, FALSE);
            }
        }
    }
    

    This way ReadFile(..) is only called if data is available and meanwhile WriteFile(..) can send data without being blocked.

    Unfortunately I've not been able to make ClearCommError(..) blocking so I used the SleepEx(1, FALSE); work-around to avoid a busy-wait and therefore prefenting the ReceiverThread to eat up the CPU.