简体   繁体   中英

Can't communicate when software flow control is disabled for serial port

My application should be able to communicate with different serial port settings which user chooses from UI. Every option works except software flow control. When I turn XON/XOFF on, it works with other peer. But when I turn off XON/XOFF, peer receives random amount of 0x00 bytes.

Is there anything I'm missing?

Note: I do exact same configuration to the peer as well. So when I turn on or off XON/XOFF option, I do the same for the peer.

int serialDevice = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_SYNC | O_NONBLOCK);

Serial_Params_t serialSettings;
serialSettings.baudRate = 12; // means 115200
serialSettings.dataBits = 8;
serialSettings.hardwareFlowControl = 0;
serialSettings.parity = 0;
serialSettings.parityOdd = 0;
serialSettings.stopBits = 1;
serialSettings.xonxoff = 0;

setSerialSettings(serialDevice, &serialSettings);
//---------------------------------------------------------------
int8_t setSerialSettings(int serialDevice, Serial_Params_t *settings)
{
    struct termios2 tty;
    memset(&tty, 0, sizeof tty);

    // get current serial settings
    if (ioctl(serialDevice, TCGETS2, &tty) == -1)
    {
        sendLog("Can't get serial attributes | setSerialSettings", LOG_TYPE_ERROR);
        return FALSE;
    }

    // baudrate
    int32_t baudrates[] = {B110, B150, B300, B600, B1200, B2400, B4800, B9600, B9600, B19200, B38400, B57600, B115200};
    cfsetospeed(&tty, (speed_t)baudrates[settings->baudRate]);
    cfsetispeed(&tty, (speed_t)baudrates[settings->baudRate]);

    // enable input parity check
    tty.c_cflag |= INPCK;

    // data bits: CS5, CS6, CS7, CS8
    tty.c_cflag &= ~CSIZE;
    switch (settings->dataBits)
    {
    case 5:
        tty.c_cflag |= CS5;
        break;
    case 6:
        tty.c_cflag |= CS6;
        break;
    case 7:
        tty.c_cflag |= CS7;
        break;
    case 8:
    default:
        tty.c_cflag |= CS8;
        break;
    }

    // stop bit
    switch (settings->stopBits)
    {
    case 1:
    default:
        tty.c_cflag &= ~CSTOPB;
        break;
    case 2:
        tty.c_cflag |= CSTOPB;
    }

    // parity
    if (settings->parity == 1)
        tty.c_cflag |= PARENB;
    else
        tty.c_cflag &= ~PARENB;

    // odd/even parity
    if (settings->parityOdd == 1)
        tty.c_cflag |= PARODD;
    else
        tty.c_cflag &= ~PARODD;

    // flow control
    // XON/XOFF
    if (settings->xonxoff == 1)
        tty.c_cflag |= (IXON | IXOFF | IXANY);
    else
        tty.c_cflag &= ~(IXON | IXOFF | IXANY);

    // enable RTS/CTS
    if (settings->hardwareFlowControl == 1)
        tty.c_cflag |= CRTSCTS;
    else
        tty.c_cflag &= ~CRTSCTS;

    tty.c_cc[VMIN] = 1;            // raise read flag even you read 1 byte
    tty.c_cc[VTIME] = 50;          // 1 seconds read timeout (deciseconds)
    tty.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines

    // non-canonical mode
    tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL);
    tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
    tty.c_oflag &= ~OPOST;

    // flush port & apply attributes
    tcflush(serialDevice, TCIFLUSH);
    if (tcsetattr(serialDevice, TCSANOW, &tty) != 0)
    {
        sendLog("Can't set serial attributes | setSerialSettings", LOG_TYPE_ERROR);
        return FALSE;
    }
    return TRUE;
}

I cannot explain specifically why your peer is receiving extraneous 0x00 bytes because you do not present any code responsible for sending anything at all, but the code you present for setting the serial port properties is faulty several times over. Issues include:

  • Use of mismatched system interfaces . You may use appropriate ioctls to retrieve and update terminal attributes, or you may use tcgetattr and tcsetattr , but you must not mix & match. The data structures in which the Linux ioctls deal are not interchangeable with the one that GLIBC's termios functions work with, though they are similar enough, especially in the type, names, and order of the flag members, that that might not reliably cause noticeable errors. You must have played some tricks and / or ignored some important compiler warnings to even get your code to compile.

    Note that mixing & matching applies not only to retrieving and setting the overall terminal settings, but also to all use of the termios.h functions for modifying a terminal settings structure, such as cfgetispeed and cfgetospeed . You may use these with a settings structure obtained via tcgetattr or, less usefully, with a directly-declared instance of the struct termios defined by termios.h , but not with one obtained via ioctl . On the flip side, a structure suitable for use with any of the termios.h functions is not suitable for use with any of the ioctls for manipulating terminal properties.

  • Setting flag bits in the wrong flag variables . Of particular relevance to the question, you are setting the IXON , IXOFF , and IXANY bits of c_cflag , but these apply to the c_iflag member, not c_cflag . The leading I in their names is mnemonic for that, though not all of the flag macros follow that convention. You are also setting INPCK in the wrong member.

But when I turn off XON/XOFF, peer receives random amount of 0x00 bytes.

This may mean the sender is sending data faster than the receiver can cope with it.

Either

  • Reduce the rate of sending data (This is not so much a baud thing, more like do not send so much data at once.)

  • Use hardware flow control (and the wire/cables that support it).

  • Improve the handling the receivers incoming data.


Note: Other possibilities exit to explain the zeroes.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM