PatrioticStripey
PatrioticStripey

Reputation: 21

Can't get RS-232 flow control to work in C (Windows)

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

Answers (0)

Related Questions