I am writing a little serial bus (RS485) monitor in c++. It should read small data packages up to 32 bytes from the serialport an occasionally writes such a databus to the serial port when the user requests it.
Reading works perfectly. I set up a thread for reading using the SetCommMask(hComm, EV_RXCHAR); during initialization and later ReadFile(hComm, RS485PacketBuffer, sizeof(T_PACKET), &nBytes, NULL) to receive the package using timeouts for getting the inter package gaps.
In the main program I use a simple WriteFile(hComm, packet, packet->length + 5, &nBytes, NULL) to write the package.
This WriteFile seems to hang until there are some bytes received from the bus. Only then the package is sent and the bus devices recognize it and answer properly. Why does WriteFile wait for character receiving?
This is my init and thread code
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
And this is the write function, which hangs until character receiving:
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;`
}
I already tried several timout settings. These seems to have no effect to the problem. Maybe it is an issue with the USB/RS485 converter. I think as the RS485 is a bus, the serial port will probably see it's own send bytes. Maybe that causes the problem.
Or maybe the blocking receiver thread which is waiting for characters blocks the whole serial port.
As @Codo mentioned, I tried now to use overlapped IO. I have a complete solution now. See the answer below.