2

I am using a BeagleBone Black to read data coming from a microcontroller(s) via a UART port(s). I need the reading of the UART port to be a blocking call. Additionally, for the usage of this software there will be some non-standard baud rates in use (aka not provided by termios). Additionally, the UART should follow 8-N-1 (8 data bits, no parity, 1 stop bit).

The code I have for the opening the UART port is as follows:

int UART::UART_open(unsigned int baudRate) 
{
    mFd = open(mPath.c_str(), O_RDWR | O_NOCTTY);
    if(mFd < 0)
    {
        return -1;
    }

    struct termios2 tty;

    if(ioctl(mFd, TCGETS2, &tty) == -1)
    {
        return -1;
    }

    tty.c_cc[VMIN] = 1;
    tty.c_cc[VTIME] = 5;
    tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;
    tty.c_cflag &= ~CBAUD;
    tty.c_cflag |= (BOTHER | CREAD | CLOCAL);
    tty.c_ispeed = baudRate;
    tty.c_ospeed = baudRate;

    if(ioctl(mFd, TCSETS2, &tty) == -1)
    {
        return -1;
    }

    return 0;
}

The code I have for reading the UART port is as follows:

int UART::UART_read(unsigned char* buf, int length)
{
    if(mFd < 0)
    {
        return -1;
    }
    if(read(mFd, buf, length) != length)
    {
        return -1;
    }
    return length;
}

There is some odd behavior going on. What happens is, the reading is inconsistent. Sometimes when I test it with an Mbed microcontroller sending data continuously (with small delays in between) via UART to the right port, and a test program to continuously read the UART port on the BeagleBone Black, and print out the data it gets, it works fine, and I am able to print out the data sent and everything works as expected. However, what happens often is the very first read simply blocks forever. No errors occur from the functions, the UART_read function simply hangs. So, to debug the error the first thing I do is I use 'screen' to monitor the /dev/ttyO* port I am trying to read from. What I find is that data is being sent to that port just fine. Then, the odd thing is, after I use screen, if I run my test program to continuously read the UART port, it works fine. This happens consistently too, if I do a quick 'screen' of the port when it is not working, I see data being sent, then my test program works. I have tried changing some of the opening termios2 struct options, to no avail. Any help would be appreciated!

  • 1
    My guess is 'screen' is configuring the port in a way your program is not. You could try printing out all the members of `tty` before and after running screen, and see if there is anything you are missing. – AShelly May 10 '18 at 23:49
  • 1
    Suggest `struct termios2 tty;` --> `struct termios2 tty = {0};` to fully initialize `tty`. – chux - Reinstate Monica May 11 '18 at 00:12
  • @chux whoops that was a mistake! It is now fixed –  May 11 '18 at 00:22

1 Answers1

1

Your termios initialization is obviously incomplete. There's a partial configuration for raw mode, e.g.

tty.c_cc[VMIN] = 1;
tty.c_cc[VTIME] = 5;

yet you never actually enable noncanonical mode.

The "blocks forever" behavior of read() is symptomatic of a serial terminal (mis)configured for canonical mode when only binary data is received (i.e. there is no line terminator to "complete" the request).

Another possible cause of "blocks forever" behavior is failure to disable hardware flow-control when it is not used.

The insertion of the following statements would enable noncanonical mode and disable the HW handshake:

cfmakeraw(&tty);
tty.c_cflag &= ~CRTSCTS;

Refer to this answer for a complete example.


if(read(mFd, buf, length) != length)
{
    return -1;
}

There is no requirement that a read() of a serial terminal will fill the buffer. Depending on the configuration, a "successful" read can return zero or more bytes up to the number requested. Therefore a short read is not actually an error condition.

sawdust
  • 16,103
  • 3
  • 40
  • 50