Ubuntu Serial Communication: reads failing and the

2019-01-20 06:02发布

问题:

I'm writing a program that runs on a MIO-3260 single board computer running Ubuntu server 14.04 and communicates with a AMC DPRANIE C100A400 drive. The program sends a string of hex codes to the drive and is supposed to receive a response for every message it sends. When I try it in realTerm on windows this works well so I don't think it's an issue with the drive. However, when I try to read from the serial port read() returns -1 almost all the time, until suddenly at a seemingly random point I get a massive dump of messages all at once.

I'm using termios to set up the serial port. Here's my code for that. I've tried setting it up in a blocking configuration but if I do that the code just hangs indefinitely at the first read().

int fd;
fd = open("/dev/ttyS0",O_RDWR | O_NOCTTY | O_NDELAY);
struct termios SerialPortSettings;

tcgetattr(fd, &SerialPortSettings); //get current settings of serial port
cfsetispeed(&SerialPortSettings,B115200);//set input baud rate
cfsetospeed(&SerialPortSettings,B115200);//set output baud rate
SerialPortSettings.c_cflag &= ~PARENB;//clear parity bit (no parity)
SerialPortSettings.c_cflag &= ~CSTOPB;//Stop bits = 1
SerialPortSettings.c_cflag &= ~CSIZE;//clears the mask
SerialPortSettings.c_cflag |= CS8; //set data bits = 8
SerialPortSettings.c_cflag &= ~CRTSCTS; //turn off hardwar based flow ctrl
SerialPortSettings.c_cflag |= CREAD | CLOCAL;//Turn on the reciever

SerialPortSettings.c_iflag &= ~(IXON | IXOFF | IXANY); //Turn off software
//based flow control
SerialPortSettings.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG);//Non-canonical mode
SerialPortSettings.c_oflag &= ~OPOST;//no output processing
//set the termios struct now
if(tcsetattr(fd,TCSANOW,&SerialPortSettings) != 0)
    printf("\n ERROR: setting attributes");
else
    printf("\n Baudrate = 115200 \t Stopbits = 1 \t Parity = none");

To read from the serial port, my code is as follows:

uint8_t buf[1024];//Rx buffer
int bytes_read;
bytes_read = read(fd,&buf,1024);
if(bytes_read != -1){
        for(int i=0;i<bytes_read;i++)    /*printing only the received characters*/
                printf("%02X\t",buf[i]);
        puts("\n");
}

Here's a sample of the kind of message I should be receiving {0xA5 0xFF 0x10 0x01 0x00 0x00 0xD4 0x11}. It should be about 8-14 bytes long. Instead I receive a huge number all at once and none at other times (for example I just received 810 bytes at once after sending 946 commands with no response).

I've been troubleshooting it for the last few days and have no idea what's going on. Sometimes I run it and it responds most of the time and then mysteriously it just stops and then comes back intermittently.

Let me know if there's anymore information I can provide.

Any help would be really appreciated!

UPDATE: I've connected my laptop to the serial port as well so I can spy on the transmissions using RealTerm, and I've verified that commands are being sent by the MIO-3260 correctly AND being responded to correctly by the drive. So the issue seems to be when I try to read from the port.

回答1:

I've tried setting it up in a blocking configuration but if I do that the code just hangs indefinitely at the first read().

The results you describe are similar to blocking canonical mode (when you actually want (blocking) raw mode).
The read() is supposed to block until an EOL (end of line) character is received.

However, when I try to read from the serial port read() returns -1 almost all the time, until suddenly at a seemingly random point I get a massive dump of messages all at once.

The results you describe are similar to non-blocking canonical mode (when you actually want (blocking) raw mode).
When no data (i.e. a complete line) is available in the buffer, the read() will return -1 and errno (which you do not bother to examine) is set to -EAGAIN.
But when the binary data coincidentally matches an EOL (end of line) character, the condition to satisfy the canonical read() is met, and the buffered data is returned.

The reason why the serial terminal is not actually configured for non-canonical mode is because the ICANON and related flags are cleared from the wrong member.

SerialPortSettings.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG);//Non-canonical mode 

ICANON is in the c_lflag member, and not in c_iflag.
So the statement should be

SerialPortSettings.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // Non-canonical mode 

Presumably that serial terminal defaulted to canonical mode, and your program has never successfully changed the mode to anything different.


Additionally, for raw mode, the VMIN and VTIME members need to be defined.
For example:

SerialPortSettings.c_cc[VMIN]  = 1;
SerialPortSettings.c_cc[VTIME] = 1;

Another bug in your code is the use of pointer to array address (i.e. address of address) when the array address would suffice.

bytes_read = read(fd,&buf,1024);

should simply be

bytes_read = read(fd, buf, 1024);

ADDENDUM

The OP's code is remarkably similar to this question, which has the identical bad termios statement.
That poster eventually solved his own problem, but mistakenly attributed the fix to adding an (irrelevant) ECHONL flag and not realizing that he was actually correcting the name of the structure member.


ADDENDUM 2

The origin of this c_iflag and ICANON bug seems to be this serial port tutorial from xanthium.in. The author was notified over two years ago of the bug, but has not fixed it.