Hey, I'm having some problems with serial communication. I need to communicate a Windows PC with 2 devices, both requiring reading and writing. These devices are RS422 and use a baud rate of 12800. They require data sent approximately every 100 ms of 84 bytes. They will send data back to be received by the Windows PC whenever it feels like it (the document for the device provides no help on that one). The receive size is variable - a multiple of 6 bytes - and usually approximately 54 bytes long.
Two questions:
First, what threading model would be suggested for this? I seem to be running out of cycles to keep these devices going. So far, it has worked best to have a single thread per device, doing both reading and writing to one device on each thread. It would seem that some form of threading would be necessary because at a 12800 baud rate, the 84 bytes should take ~52.5 ms, and since they both need to send this, that leaves very little room for the receive before the next pass of send (send every 100 ms).
Secondly, and perhaps most importantly, the WriteFile calls in PortMon (tool for debugging and monitoring communication) sporadically take 100 ms. The ReadFile occasionally also has timeouts. This makes the two threads not have enough cycles to both get their tasks done fast enough and one thread randomly will get behind and my serial device loses data because it was not receiving at 100 ms. Why would a WriteFile take this long? The ReadFile timeout is most likely due to the WriteFile taking all the cycles. Code as follows:
Creation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
|
bool SerialPort::Create(const string &szPort, unsigned long uBaudRate, unsigned char uByteSize, unsigned char uParity, unsigned char uStopBits)
{
// Already Created
if(*m_port != INVALID_HANDLE_VALUE)
return false;
// Create Port
*m_port = CreateFile(szPort.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if(*m_port == INVALID_HANDLE_VALUE)
return false;
// Get Comm State
if(!GetCommState(*m_port, &m_tDCB))
{
// Close
Close();
return false;
}
// Set Port Properties
m_tDCB.BaudRate = uBaudRate;
m_tDCB.ByteSize = uByteSize;
m_tDCB.Parity = uParity;
m_tDCB.StopBits = uStopBits;
// Set Comm State
if(!SetCommState(*m_port, &m_tDCB))
{
// Close
Close();
return false;
}
// Success
return true;
}
|
Writing Asynchronous - (called every 100 ms on the thread)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
int SerialPort::SendBytesA(const char *pBytes, int nByteCount)
{
// Invalid Port
if(*m_port == INVALID_HANDLE_VALUE)
return 0;
OVERLAPPED tOverlappedSend = {0};
unsigned long uBytesWritten = 0;
// Create Overlapped Event
tOverlappedSend.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (tOverlappedSend.hEvent == NULL)
return 0;
// Write Bytes
if(!WriteFile(*m_port, pBytes, nByteCount, &uBytesWritten, &tOverlappedSend))
{
// Reset Bytes
uBytesWritten = 0;
// Check Pending
if(GetLastError() == ERROR_IO_PENDING)
{
// Get Signalled - Wait Indefinately
if(!GetOverlappedResult(*m_port, &tOverlappedSend, &uBytesWritten, TRUE))
uBytesWritten = 0;
}
}
// Close Handle
CloseHandle(tOverlappedSend.hEvent);
// Return
return (int)uBytesWritten;
}
|
Reading Asynchronous - (this function called in a loop in the thread)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
|
int SerialPort::ReceiveBytesA(char *pBytes, int nReadSize, int &nByteCount)
{
// Reset Bytes
nByteCount = 0;
// Invalid Port
if(*m_port == INVALID_HANDLE_VALUE)
return 0;
// Check Pending
unsigned long uBytesRead = 0;
if(!m_bReceivePending)
{
// Create Event
memset(&m_tOverlappedReceive, 0, sizeof(m_tOverlappedReceive));
m_tOverlappedReceive.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (m_tOverlappedReceive.hEvent == NULL)
return 0;
// Receive
if(!ReadFile(*m_port, pBytes, nReadSize, &uBytesRead, &m_tOverlappedReceive))
{
// Validate Pending
if(GetLastError() == ERROR_IO_PENDING)
m_bReceivePending = true;
}
else
{
// Set Bytes
nByteCount = (int)uBytesRead;
}
// Close Event
if(!m_bReceivePending)
{
CloseHandle(m_tOverlappedReceive.hEvent);
m_tOverlappedReceive.hEvent = NULL;
}
}
else if(HasOverlappedIoCompleted(&m_tOverlappedReceive))
{
// Get Signalled Bytes
if(GetOverlappedResult(*m_port, &m_tOverlappedReceive, &uBytesRead, FALSE))
{
// Set Bytes
nByteCount = (int)uBytesRead;
}
// No Longer Pending
m_bReceivePending = false;
// Close Event
CloseHandle(m_tOverlappedReceive.hEvent);
m_tOverlappedReceive.hEvent = NULL;
}
// Success
return nByteCount;
}
|