![](/img/trans.png)
[英]WriteFile to a serial port always times out and the number of bytes written is zero
[英]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.