Reputation: 19
I have an inconsistency problem while I read in my serial port using Linux and C.
The code that I use to configure the serial port is this:
serial = open("/dev/ttymxc1", O_RDWR | O_NOCTTY | O_SYNC); //Open in non blocking read/write mode
if (serial == -1)
{
//ERROR - CAN'T OPEN SERIAL PORT
printf("Error - Unable to open UART. Ensure it is not in use by another application\n");
}
struct termios tty;
memset (&tty, 0, sizeof tty);
if (tcgetattr (serial, &tty) != 0)
{
printf("error from tcgetattr");
return -1;
}
cfsetospeed (&tty, B115200);
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; // 8-bit chars
// disable IGNBRK for mismatched speed tests; otherwise receive break
// as \000 chars
//tty.c_iflag &= ~IGNBRK; // disable break processing
tty.c_lflag = 0; // no signaling chars, no echo,
// no canonical processing
tty.c_oflag = 0; // no remapping, no delays
tty.c_cc[VMIN] = 0; // read doesn't block
tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl
tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls,
// enable reading
tty.c_cflag &= ~(PARENB | PARODD); // shut off parity
tty.c_cflag |= 0;
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CRTSCTS;
if (tcsetattr (serial, TCSANOW, &tty) != 0)
{
printf("error from tcsetattr");
return -1;
}
Then I poll the UART (from the same thread which has configured the serial port) with the following code:
while(1)
{
if (serial != -1)
{
//memset(rx, 0, sizeof(rx));
int rx_length = read(serial, &rx, MAX_TXRX_BUF); //Filestream, buffer to store in, number of bytes to read (max)
if(rx_length > 0){
//Bytes received
//rx[rx_length] = '\0';
printf("1) %i bytes read : %s\n", rx_length, rx);
//forward_data(rx, rx_length);
printf("2) %i bytes read : %s\n", rx_length, rx);
//tcflush(serial, TCIOFLUSH);
}
// else: NO DATA
}
else{
fprintf(stderr, "TEST: %s SERIAL FAIL\n", __func__);
releaseUart();
}
}
The problem is that this print:
printf("1) %i bytes read : %s\n", rx_length, rx);
always WORKS and print the correct data read from serial. While the second print:
printf("2) %i bytes read : %s\n", rx_length, rx);
which is immediately below the first one, sometimes works and other items it just prints an unknown character.
Below I show you the ouput but in the case in which it works and in the case in which it does not work:
Correct:
1) 2 bytes read : gv
2) 2 bytes read gv
Wrong:
1) 2 bytes read : gv
2) 2 bytes read: �
Why even if the two prints are one below the other sometimes I get these kind of inconsistencies in printing the SAME buffer?
Thanks in advance very much for your help.
Best regards, Marco
Upvotes: 0
Views: 1372
Reputation: 140836
I cannot explain why the output changes between the first printf
and the second. However, read
does not NUL-terminate its buffer, which means, after you do
int rx_length = read(serial, &rx, MAX_TXRX_BUF);
you do not have a valid C string in rx
. You need to do something like this instead:
char rx[MAX_TXRX_BUF + 1]; // extra space for terminator
ssize_t rx_length = read(serial, rx, MAX_TXRX_BUF);
if (rx_length >= 0) {
rx[rx_length] = '\0';
// use rx here
// if rx_length == 0, handle EOF
} else {
// read error
}
Even though I don't know why the output changes, I predict that if you put your two printf
statements where I have // use rx here
, they will always print the same thing.
N.B. taking the address of an entire array, as you did with &rx
, is almost always a mistake. In most cases what you actually want, in order to refer to an array in C, is the address of the first element, which is what you get with just rx
. (For reasons too tedious and tangential to get into here, read
doesn't care which one of the two you supply it, but in other cases it can lead to subtle bugs. Therefore, best practice in C is to use the address of the first element even in cases where it doesn't matter; reserve &rx
for the rare cases where you do specifically need the address of the entire array.)
Upvotes: 1