Reputation: 21
I am writing a program in C to communicate with an HP 7550 pen plotter over RS-232, and am having trouble getting flow control to work. When using a terminal program (such as TeraTerm) to send data to the plotter, XON/XOFF flow control works fine. However, when I attempt to send data using my program, the flow control is ignored and the plotter's (very small) buffer gets overflowed. I can see the XOFF character get trasmitted by the plotter by watching the LED on the USB <-> RS-232 adapter, but the PC ignores it and keeps sending data.
Here are the two functions that set up and open the serial port:
// Function openPort
// - Opens a serial port with a port name provided in the format "\\\\.\\COM#"
HANDLE openPort(char *portName) {
HANDLE serialPort;
COMMTIMEOUTS timeouts = {0};
// open the port
//printf("opening serial port . . . ");
serialPort = CreateFile(portName, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, NULL); // open the port
if(serialPort == INVALID_HANDLE_VALUE) {
//printf("failed\n");
return NULL;
} else
//printf("ok\n");
// set up timeouts
//printf("Setting up timeouts . . . ");
timeouts.ReadIntervalTimeout = 50;
timeouts.ReadTotalTimeoutConstant = 50;
timeouts.ReadTotalTimeoutMultiplier = 10;
timeouts.WriteTotalTimeoutConstant = 50;
timeouts.WriteTotalTimeoutMultiplier = 10;
if(!SetCommTimeouts(serialPort, &timeouts)) {
//printf("failed - could not set timeouts\n");
return NULL;
}
if(!SetCommMask(serialPort, EV_RXCHAR)) {
//printf("failed - could not set comm mask\n");
return NULL;
}
//printf("ok\n");
return serialPort;
}
// Function setupPort
// - Sets up the paramters of the serial port
bool setupPort(HANDLE *serialPort, int baudRate, int byteSize, int stopBits, char parityType[], char flowControl[]) {
DCB dcb = {0};
bool error = false;
if(GetCommState(serialPort, &dcb)) {
//printf("Setting up serial port parameters . . .\n");
// basic setup parameters
dcb.fBinary = true;
dcb.fDtrControl = DTR_CONTROL_ENABLE;
dcb.fDsrSensitivity = false;
dcb.fTXContinueOnXoff = false;
dcb.fOutX = false;
dcb.fInX = false;
dcb.fErrorChar = false;
dcb.fNull = false;
dcb.fRtsControl = RTS_CONTROL_ENABLE;
dcb.fAbortOnError = false;
dcb.fOutxCtsFlow = false;
dcb.fOutxDsrFlow = false;
// set up baud rate and byte size
dcb.BaudRate = baudRate;
dcb.ByteSize = byteSize;
// set up stop bits
//printf("Setting up stop bits . . . ");
switch(stopBits) {
case 1:
dcb.StopBits = ONESTOPBIT;
//printf("ok\n");
break;
case 2:
dcb.StopBits = TWOSTOPBITS;
//printf("ok\n");
break;
default:
//printf("Invalid number of stop bits\n");
error = true;
}
// set up parity
//printf("Configuring %s parity . . . ", parityType);
if(parityType == "NONE") {
dcb.Parity = NOPARITY;
//printf("ok\n");
} else if(parityType == "ODD") {
dcb.Parity = ODDPARITY;
//printf("ok\n");
} else if(parityType == "EVEN") {
dcb.Parity = EVENPARITY;
//printf("ok\n");
} else if(parityType == "MARK") {
dcb.Parity = MARKPARITY;
//printf("ok\n");
} else if(parityType == "SPACE") {
dcb.Parity = SPACEPARITY;
//printf("ok\n");
} else {
//printf("Invalid parity type\n");
error = true;
}
// set up flow control
//printf("Configuring %s flow control . . . ", flowControl);
if(flowControl == "NONE") {
//printf("ok\n");
} else if(flowControl == "XONXOFF") {
dcb.fOutX = true;
dcb.fInX = true;
//printf("ok\n");
} else if(flowControl == "RTS/CTS") {
dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
dcb.fOutxCtsFlow = true;
//printf("ok\n");
} else if(flowControl == "DSR/DTR") {
dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;
dcb.fOutxDsrFlow = true;
//printf("ok\n");
} else {
//printf("Invaid flow control type\n");
error = true;
}
}
if(!SetCommState(serialPort, &dcb)) {
error = true;
}
return error;
}
Here is the function that writes data to the serial port:
// Function writeData
// - Writes an array of chars to the port
bool writeData(HANDLE serialPort, const char *data) {
DWORD dataSize = strlen(data);
DWORD bytesWritten;
return WriteFile(serialPort, data, dataSize, &bytesWritten, NULL);
}
I am initializing the port in my main program in the following function:
// Function initSerial():
// This function initializes the serial connection for the plotter to communicate
// It takes five arguments:
// - portNumber (1-255)
// - baudRate (110, 220, 440, 680, 1200, 2400, 4800, or 9600 baud)
// - dataBits (7 or 8)
// - parity (NONE, ODD, EVEN, MARK, SPACE)
// - flowControl(NONE, XONXOFF, RTS/CTS, DSR/DTR)
HANDLE initSerial(int portNumber, int baudRate, int dataBits, char* parity, char* flowControl) {
char s[13];
sprintf(s, "\\\\.\\COM%d", portNumber); // format COM port number
HANDLE serialPort = openPort(s);
setupPort(serialPort, baudRate, dataBits, 1, parity, flowControl);
return serialPort;
}
and that function gets called using the following line:
HANDLE port = initSerial(12, 9600, 8, "NONE", "XONXOFF");
The rest of the program basically consists of repeated calls to writeData() with various HPGL commands to instruct the plotter what to draw.
I was under the impression that flow control was handled by the serial driver, hence why I specify "XONXOFF" flow control to Windows when I set up the port. I've tried XON/XOFF and RTS/CTS flow control with the same results. I have tried two different RS-232 adapters and two different PCs, one running Windows 7 Ultimate and the other Windows 10 Professional.
I realize my C code may not be particularly excellent, but hopefully it's clear enough to be readable.
Upvotes: 1
Views: 351