发送特定字节模式时串口数据损坏
Serial port data corrupted when sending a specific pattern of bytes
我有一个发送数据流的 python 脚本,然后我有一个 Linux 嵌入式计算机接收数据(用 C++ 编写的代码)。大多数时候它都有效,但是,我注意到在发送 特定的字节模式 时,我的数据被破坏了。纠结了好久,不知道怎么解决。
Python 脚本(发件人):
serial = serial.Serial("COM2", 115200, timeout=5)
all_bytes = [0x63,0x20,0x72,0x69,0x67,0x68,0x74,0x73,0x20,0x61,0x6e,0x64,0x20,0x72,0x65,0x73,0x74,0x72,0x69,0x63,0x74,0x69,0x6f,0x6e,0x73,0x20,0x69,0x6e,0x0a,0x68,0x6f,0x77,0xff,0x20,0xf0,0x8b]
fmt = "B"*len(all_bytes)
byte_array = struct.pack(fmt,*all_bytes)
serial.write(byte_array)
C++代码(接收器)
typedef std::vector<uint8_t> ustring; // ustring = vector containing a bunch of uint8_t elements
// configure the port
int UART::configure_port()
{
struct termios port_settings; // structure to store the port settings in
cfsetispeed(&port_settings, B115200); // set baud rates
cfsetospeed(&port_settings, B115200);
port_settings.c_cflag &= ~PARENB; // set no parity, stop bits, data bits
port_settings.c_cflag &= ~CSTOPB;
port_settings.c_cflag &= ~CSIZE;
port_settings.c_cflag |= CS8;
port_settings.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines
port_settings.c_cc[VTIME] = 10; // n seconds read timeout
//port_settings.c_cc[VMIN] = 0; // blocking read until 1 character arrives
port_settings.c_iflag &= ~(IXON | IXOFF | IXANY); // turn off s/w flow ctrl
port_settings.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // make raw
port_settings.c_oflag &= ~OPOST; // make raw
tcsetattr(fd, TCSANOW, &port_settings); // apply the settings to the port
return(fd);
}
int UART::uart_read(ustring *data,int buffer_size)
{
// Buffer
uint8_t * buf = new uint8_t[buffer_size];
// Flush contents of the serial port
//tcflush(fd, TCIOFLUSH);
//usleep(1000);
ustring data_received;
// Read
int n_bytes = 0;
while (n_bytes < buffer_size)
{
int n = read( fd, buf , buffer_size );
// Some bytes were read!
if (n > 0)
{
n_bytes+=n;
// Add to buffer new data!
for( int i=0; i<n; i++ )
{
data_received.push_back(buf[i]);
}
}
}
// String received
*data = data_received;
cout << "Data received..." << endl;
print_ustring(data_received);
delete[] buf;
return read_valid;
}
int main()
{
UART uart_connection;
vector<uint8_t> data;
vector<uint8_t> *data_ptr = &data;
int status = uart_connection.uart_read(data_ptr,36);
return 0;
}
事情是这样的:
如果我发送以下字节(来自 python):
0x632072696768747320616e64207265737472696374696f6e7320696e0a686f77ff20f08b
这是我收到的(在 C++ 程序中):
0x632072696768747320616e64207265737472696374696f6e7320696e0a686f77ffff20f0
如您所见,末尾(CRC)有几个字节发生了变化,其余的似乎没问题。但这并不总是发生,它只在发送某些特定的字节模式时发生。
假设我发送了以下内容(一些其他模式):
0x6868686868686868686868686868686868686868686868686868686868686868b18cf5b2
我完全明白我在上述模式中发送的内容!
您认为可能是 Pyserial 将我的无符号字节更改为 ASCII 吗?我不知道发生了什么。我已经为此苦苦挣扎了好几天!
编辑
对于任何感兴趣的人来说,问题显然是 struct termios 需要在声明后立即初始化。
这是解决问题的代码:
// configure the port
int UART::configure_port()
{
struct termios port_settings; // structure to store the port settings in
tcgetattr(fd, &port_settings);
// Open ttys4
fd = open("/dev/ttyS4", O_RDWR | O_NOCTTY );
if(fd == -1) // if open is unsucessful
{
//perror("open_port: Unable to open /dev/ttyS0 - ");
printf("open_port: Unable to open /dev/ttyS4. \n");
}
else
{
fcntl(fd, F_SETFL, 0);
/* get the current options */
printf("port is open.\n");
cfsetispeed(&port_settings, B9600); // set baud rates
cfsetospeed(&port_settings, B9600);
port_settings.c_cflag &= ~PARENB; // set no parity, stop bits, data bits
port_settings.c_cflag &= ~CSTOPB; //Stop bits = 1
port_settings.c_cflag &= ~CSIZE; // clear mask
port_settings.c_cflag |= CS8; // data bits = 8
port_settings.c_cflag &= ~CRTSCTS; // Turn off hardware flow control
port_settings.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines
port_settings.c_cc[VMIN] = 0; // blocking read until 1 character arrives
// port_settings.c_cc[VTIME] = 10; // n seconds read timeout
port_settings.c_iflag &= ~(IXON | IXOFF | IXANY); // turn off s/w flow ctrl
port_settings.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // make raw -- NON Cannonical mode
// port_settings.c_iflag |= IGNPAR; // Input parity options
// port_settings.c_oflag &= ~OPOST; // make raw
tcsetattr(fd, TCSANOW, &port_settings); // apply the settings to the port
}
return(fd);
}
您的程序未能正确执行 termios 初始化。
1. struct termios port_settings
需要在任何修改前通过调用 tcgetattr() 进行初始化。
2. 由于您配置了非规范模式,因此需要定义 VMIN。无论出于何种原因,您已将声明注释掉。
As you can see there are a few bytes at the end (the CRC) that are changed,...
看起来值 0xFF 的输入字节是重复的。
当设置了 PARMRK 和 INPCK 而未设置 IGNPAR 时会发生这种情况。
根据 termios 手册页:
PARMRK If this bit is set, input bytes with parity or framing errors are marked when passed to the program.
...
Therefore, a valid byte 7 is passed to the program as two bytes, 7 7, in this case.
由于您的 termios 结构从未正确初始化,这些设置可能是为您的程序配置的。
附录
So I called "tcgetattr(fd, &port_settings);" RIGHT AFTER declaring it, and RIGHT BEFORE opening the port.
否,尽管任何积极的结果都是不合逻辑的代码。
文件描述符无效,如果检查 return 代码(所有系统调用都应该检查),您会发现 tcgetattr() 失败。
宏 cfmakeraw() 是指示非规范模式的正确 termios 配置的显着值的指南:
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;
所以你的程序应该是这样的:
...
if (tcgetattr(fd, &port_settings) < 0) {
printf("Error from tcgetattr: %s\n", strerror(errno));
return -1;
}
cfsetospeed(&port_settings, B9600);
cfsetispeed(&port_settings, B9600);
port_settings.c_cflag |= (CLOCAL | CREAD); /* ignore modem controls */
port_settings.c_cflag &= ~CSIZE;
port_settings.c_cflag |= CS8; /* 8-bit characters */
port_settings.c_cflag &= ~PARENB; /* no parity bit */
port_settings.c_cflag &= ~CSTOPB; /* only need 1 stop bit */
port_settings.c_cflag &= ~CRTSCTS; /* no hardware flowcontrol */
/* setup for non-canonical mode */
port_settings.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
port_settings.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
port_settings.c_oflag &= ~OPOST;
/* fetch bytes as they become available */
port_settings.c_cc[VMIN] = 1;
port_settings.c_cc[VTIME] = 1;
if (tcsetattr(fd, TCSANOW, &port_settings) != 0) {
printf("Error from tcsetattr: %s\n", strerror(errno));
return -1;
}
我有一个发送数据流的 python 脚本,然后我有一个 Linux 嵌入式计算机接收数据(用 C++ 编写的代码)。大多数时候它都有效,但是,我注意到在发送 特定的字节模式 时,我的数据被破坏了。纠结了好久,不知道怎么解决。
Python 脚本(发件人):
serial = serial.Serial("COM2", 115200, timeout=5)
all_bytes = [0x63,0x20,0x72,0x69,0x67,0x68,0x74,0x73,0x20,0x61,0x6e,0x64,0x20,0x72,0x65,0x73,0x74,0x72,0x69,0x63,0x74,0x69,0x6f,0x6e,0x73,0x20,0x69,0x6e,0x0a,0x68,0x6f,0x77,0xff,0x20,0xf0,0x8b]
fmt = "B"*len(all_bytes)
byte_array = struct.pack(fmt,*all_bytes)
serial.write(byte_array)
C++代码(接收器)
typedef std::vector<uint8_t> ustring; // ustring = vector containing a bunch of uint8_t elements
// configure the port
int UART::configure_port()
{
struct termios port_settings; // structure to store the port settings in
cfsetispeed(&port_settings, B115200); // set baud rates
cfsetospeed(&port_settings, B115200);
port_settings.c_cflag &= ~PARENB; // set no parity, stop bits, data bits
port_settings.c_cflag &= ~CSTOPB;
port_settings.c_cflag &= ~CSIZE;
port_settings.c_cflag |= CS8;
port_settings.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines
port_settings.c_cc[VTIME] = 10; // n seconds read timeout
//port_settings.c_cc[VMIN] = 0; // blocking read until 1 character arrives
port_settings.c_iflag &= ~(IXON | IXOFF | IXANY); // turn off s/w flow ctrl
port_settings.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // make raw
port_settings.c_oflag &= ~OPOST; // make raw
tcsetattr(fd, TCSANOW, &port_settings); // apply the settings to the port
return(fd);
}
int UART::uart_read(ustring *data,int buffer_size)
{
// Buffer
uint8_t * buf = new uint8_t[buffer_size];
// Flush contents of the serial port
//tcflush(fd, TCIOFLUSH);
//usleep(1000);
ustring data_received;
// Read
int n_bytes = 0;
while (n_bytes < buffer_size)
{
int n = read( fd, buf , buffer_size );
// Some bytes were read!
if (n > 0)
{
n_bytes+=n;
// Add to buffer new data!
for( int i=0; i<n; i++ )
{
data_received.push_back(buf[i]);
}
}
}
// String received
*data = data_received;
cout << "Data received..." << endl;
print_ustring(data_received);
delete[] buf;
return read_valid;
}
int main()
{
UART uart_connection;
vector<uint8_t> data;
vector<uint8_t> *data_ptr = &data;
int status = uart_connection.uart_read(data_ptr,36);
return 0;
}
事情是这样的:
如果我发送以下字节(来自 python):
0x632072696768747320616e64207265737472696374696f6e7320696e0a686f77ff20f08b
这是我收到的(在 C++ 程序中):
0x632072696768747320616e64207265737472696374696f6e7320696e0a686f77ffff20f0
如您所见,末尾(CRC)有几个字节发生了变化,其余的似乎没问题。但这并不总是发生,它只在发送某些特定的字节模式时发生。
假设我发送了以下内容(一些其他模式):
0x6868686868686868686868686868686868686868686868686868686868686868b18cf5b2
我完全明白我在上述模式中发送的内容!
您认为可能是 Pyserial 将我的无符号字节更改为 ASCII 吗?我不知道发生了什么。我已经为此苦苦挣扎了好几天!
编辑
对于任何感兴趣的人来说,问题显然是 struct termios 需要在声明后立即初始化。
这是解决问题的代码:
// configure the port
int UART::configure_port()
{
struct termios port_settings; // structure to store the port settings in
tcgetattr(fd, &port_settings);
// Open ttys4
fd = open("/dev/ttyS4", O_RDWR | O_NOCTTY );
if(fd == -1) // if open is unsucessful
{
//perror("open_port: Unable to open /dev/ttyS0 - ");
printf("open_port: Unable to open /dev/ttyS4. \n");
}
else
{
fcntl(fd, F_SETFL, 0);
/* get the current options */
printf("port is open.\n");
cfsetispeed(&port_settings, B9600); // set baud rates
cfsetospeed(&port_settings, B9600);
port_settings.c_cflag &= ~PARENB; // set no parity, stop bits, data bits
port_settings.c_cflag &= ~CSTOPB; //Stop bits = 1
port_settings.c_cflag &= ~CSIZE; // clear mask
port_settings.c_cflag |= CS8; // data bits = 8
port_settings.c_cflag &= ~CRTSCTS; // Turn off hardware flow control
port_settings.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines
port_settings.c_cc[VMIN] = 0; // blocking read until 1 character arrives
// port_settings.c_cc[VTIME] = 10; // n seconds read timeout
port_settings.c_iflag &= ~(IXON | IXOFF | IXANY); // turn off s/w flow ctrl
port_settings.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // make raw -- NON Cannonical mode
// port_settings.c_iflag |= IGNPAR; // Input parity options
// port_settings.c_oflag &= ~OPOST; // make raw
tcsetattr(fd, TCSANOW, &port_settings); // apply the settings to the port
}
return(fd);
}
您的程序未能正确执行 termios 初始化。
1. struct termios port_settings
需要在任何修改前通过调用 tcgetattr() 进行初始化。
2. 由于您配置了非规范模式,因此需要定义 VMIN。无论出于何种原因,您已将声明注释掉。
As you can see there are a few bytes at the end (the CRC) that are changed,...
看起来值 0xFF 的输入字节是重复的。
当设置了 PARMRK 和 INPCK 而未设置 IGNPAR 时会发生这种情况。
根据 termios 手册页:
PARMRK If this bit is set, input bytes with parity or framing errors are marked when passed to the program.
...
Therefore, a valid byte 7 is passed to the program as two bytes, 7 7, in this case.
由于您的 termios 结构从未正确初始化,这些设置可能是为您的程序配置的。
附录
So I called "tcgetattr(fd, &port_settings);" RIGHT AFTER declaring it, and RIGHT BEFORE opening the port.
否,尽管任何积极的结果都是不合逻辑的代码。
文件描述符无效,如果检查 return 代码(所有系统调用都应该检查),您会发现 tcgetattr() 失败。
宏 cfmakeraw() 是指示非规范模式的正确 termios 配置的显着值的指南:
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;
所以你的程序应该是这样的:
...
if (tcgetattr(fd, &port_settings) < 0) {
printf("Error from tcgetattr: %s\n", strerror(errno));
return -1;
}
cfsetospeed(&port_settings, B9600);
cfsetispeed(&port_settings, B9600);
port_settings.c_cflag |= (CLOCAL | CREAD); /* ignore modem controls */
port_settings.c_cflag &= ~CSIZE;
port_settings.c_cflag |= CS8; /* 8-bit characters */
port_settings.c_cflag &= ~PARENB; /* no parity bit */
port_settings.c_cflag &= ~CSTOPB; /* only need 1 stop bit */
port_settings.c_cflag &= ~CRTSCTS; /* no hardware flowcontrol */
/* setup for non-canonical mode */
port_settings.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
port_settings.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
port_settings.c_oflag &= ~OPOST;
/* fetch bytes as they become available */
port_settings.c_cc[VMIN] = 1;
port_settings.c_cc[VTIME] = 1;
if (tcsetattr(fd, TCSANOW, &port_settings) != 0) {
printf("Error from tcsetattr: %s\n", strerror(errno));
return -1;
}