问题
I am sending binary data from an arduino to a serial port where this code is running. Using cutecom in hex mode can clearly read what I expect on that serial port. As shown below.
00000000: 24 04 85 ab 47 43 04 04 24 04 85 ab 47 43 04 04
00000010: 24 04 85 ab 47 43 04 04 24 04 85 ab 47 43 04 04
No problems up to here. I don't believe I need to give the arduino code.
I am trying to read the same thing with c. However the code below only prints this:
24 85 ab 47 43 24 85 ab 47 43 24 85 ab 47 43
For some reasons it is skipping the 04. Any ideas?
#include <stdio.h>
#include <fcntl.h> /* File Control Definitions */
#include <termios.h> /* POSIX Terminal Control Definitions */
#include <unistd.h> /* UNIX Standard Definitions */
#include <errno.h> /* ERROR Number Definitions */
#include <signal.h>
#include <string.h>
#include <stdint.h>
int open_serial(char *port, int baud);
void main(void)
{
int tty = open_serial("/dev/ttyUSB0", B115200);
uint8_t buff[256]; /* Buffer to store the data received */
int n; /* Number of bytes read by the read() system call */
while (1) {
n = read(tty, &buff, sizeof buff);
if (n > 0){
//printf("-%d-\n ", n);
for(int i=0;i<n;i++){
printf("%02x ", buff[i]);
}
fflush(stdout);
}
}
}
int open_serial(char *port, int baud)
{
int fd = open( port, O_RDWR | O_NOCTTY);
if(fd == -1) /* Error Checking */
printf("\n Error! in Opening tty ");
struct termios SerialPortSettings; /* Create the structure */
tcgetattr(fd, &SerialPortSettings); /* Get the current attributes of the Serial port */
/* Setting the Baud rate */
cfsetispeed(&SerialPortSettings,B115200); /* Set Read Speed as 115200 */
cfsetospeed(&SerialPortSettings,B115200); /* Set Write Speed as 115200 */
/* 8N1 Mode */
SerialPortSettings.c_cflag &= ~PARENB; /* Disables the Parity Enable bit(PARENB),So No Parity */
SerialPortSettings.c_cflag &= ~CSTOPB; /* CSTOPB = 2 Stop bits,here it is cleared so 1 Stop bit */
SerialPortSettings.c_cflag &= ~CSIZE; /* Clears the mask for setting the data size */
SerialPortSettings.c_cflag |= CS8; /* Set the data bits = 8 */
SerialPortSettings.c_cflag &= ~CRTSCTS; /* No Hardware flow Control */
SerialPortSettings.c_cflag |= CREAD | CLOCAL; /* Enable receiver,Ignore Modem Control lines */
SerialPortSettings.c_iflag &= ~(IXON | IXOFF | IXANY); /* Disable XON/XOFF flow control both i/p and o/p */
SerialPortSettings.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG); /* Non Cannonical mode */
SerialPortSettings.c_oflag &= ~OPOST;/*No Output Processing*/
/* Setting Time outs */
SerialPortSettings.c_cc[VMIN] = 10; /* Read at least 10 characters */
SerialPortSettings.c_cc[VTIME] = 0; /* Wait indefinetly */
if((tcsetattr(fd,TCSANOW,&SerialPortSettings)) != 0) /* Set the attributes to the termios structure*/
printf("\n ERROR ! in Setting attributes");
else
printf("\n BaudRate = 115200 StopBits = 1 Parity = none\n");
/*------------------------------- Read data from serial port -----------------------------*/
tcflush(fd, TCIFLUSH); /* Discards old data in the rx buffer */
return fd;
}
回答1:
I am sending binary data from an arduino to a serial port where this code is running.
Transmission of binary data necessitates that the POSIX serial terminal be configured for non-canonical (aka raw) mode.
For some reasons it is skipping the 04. Any ideas?
Consistent loss of data that have values in the range of ASCII control characters (i.e. 0x00 through 0x1F) almost always indicates improper termios configuration.
Since the data loss seems to be on the reception side, the first thing to verify is the proper specification of non-canonical input mode.
Your code has
SerialPortSettings.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG); /* Non Cannonical mode */
The attributes on the right-hand side of the assignment all belong to the c_lflag
of the termios structure, rather than the c_iflag
that is coded.
So presumably the default mode of the serial terminal is canonical mode, and your program fails to reconfigure to non-canonical input mode because of this typo.
Your workaround of augmenting your code with a cfmakeraw() call is not ideal.
The use of cfmakeraw() is convenient for avoiding mistakes like the one you have.
But the buggy statement should be corrected, or even better the operations that are made redundant by the cfmakeraw() call can be removed.
cfmakeraw() sets the terminal to something like the "raw" mode of the old
Version 7 terminal driver: input is available character by character, echoing is
disabled, and all special processing of terminal input and output characters is
disabled. The terminal attributes are set as follows:
termios_p->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
| INLCR | IGNCR | ICRNL | IXON);
termios_p->c_oflag &= ~OPOST;
termios_p->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
termios_p->c_cflag &= ~(CSIZE | PARENB);
termios_p->c_cflag |= CS8;
BTW your code does use the recommended/preferred method of calling tcgetattr(), and then setting/modifying the terminal attributes using Boolean operations. The suggestion of using a "clean struc" is not considered portable.
ADDENDUM
The origin of this c_iflag and ICANON bug seems to be this serial port tutorial from xanthium.in. The author was notified back in 2016 of the bug, but has not bothered to fix it.
回答2:
Got it to work adding
cfmakeraw(&SerialPortSettings);
just above the tcsetattr function call.
来源:https://stackoverflow.com/questions/49556279/binary-serial-port-read-missing-bytes-in-c