使用CMSPAR的标记奇偶校验检测非常不可靠(9位UART)
edit-2:实际上这可能是一个,因为它们总是一次将多个字节传输到主机,而没有一个位置来放置有关单个字节的信息。因此,如果任何字节产生某种类型的错误,整个传输都将被标记,无法确定是哪个字节导致了错误 编辑:我刚刚用MT7688的内部UART尝试了这段代码,它的工作原理非常好。因此,这似乎是ftdi_sio驱动程序中的一个缺陷,甚至可能是硬件本身。我可能会尝试使用libftdi1/libftd2x来实现这一点,以排除这种可能性 我正在尝试与使用Linux的串行协议的嵌入式设备通信 为此,我使用FT232RL USB串行适配器 协议使用第9奇偶校验位集标记帧的第一个字节 我可以用CMSPAR选项mark/space奇偶校验重现这种情况,事实上,以这种方式发送帧效果良好且非常可靠使用CMSPAR的标记奇偶校验检测非常不可靠(9位UART),c,linux,uart,ftdi,9-bit-serial,C,Linux,Uart,Ftdi,9 Bit Serial,edit-2:实际上这可能是一个,因为它们总是一次将多个字节传输到主机,而没有一个位置来放置有关单个字节的信息。因此,如果任何字节产生某种类型的错误,整个传输都将被标记,无法确定是哪个字节导致了错误 编辑:我刚刚用MT7688的内部UART尝试了这段代码,它的工作原理非常好。因此,这似乎是ftdi_sio驱动程序中的一个缺陷,甚至可能是硬件本身。我可能会尝试使用libftdi1/libftd2x来实现这一点,以排除这种可能性 我正在尝试与使用Linux的串行协议的嵌入式设备通信 为此,我使用FT
static void _write_byte_mark(int fd, unsigned char c) {
struct termios tio = {0};
if (tcgetattr(fd, &tio))
printf("%s:%d : error %d\n", __func__, __LINE__, errno);
tio.c_cflag |= PARODD;
if (tcsetattr(fd, TCSADRAIN, &tio))
printf("%s:%d : error %d\n", __func__, __LINE__, errno);
// write byte with MARK parity
if (write(fd, &c, sizeof(c)) <= 0)
printf("%s:%d : error %d\n", __func__, __LINE__, errno);
// set back to SPACE parity
tio.c_cflag &= ~PARODD;
if (tcsetattr(fd, TCSADRAIN, &tio))
printf("%s:%d : error %d\n", __func__, __LINE__, errno);
}
void send_packet(int fd, const unsigned char* pkg, size_t size) {
const unsigned char preamble = 0xff;
// write preamble to sync UARTs (protocol will ignore this)
// write(fd, &preamble, sizeof(preamble));
// send first byte with 9th bit set to indicate start of frame
_write_byte_mark(fd, pkg[0]);
// send the rest of the pkg
write(fd, pkg + 1, size - 1);
}
然而,这只是“一种”有效。仅在某些情况下,检测到完整序列0xFF、0x00、0x18。0x18恰好是我的数据包的第一个字节,通常情况下,我不会读取转义的0xFF,而只是0x00,0x18我甚至不确定这是怎么可能的,因为0x00从未被发送,有时我会在随机负载字节上出现奇偶校验错误
我首先怀疑是竞争条件,因为我可能会在读取时更改TTY设置,但即使我使用两个UART和单独的进程,一个仅用于RX,另一个仅用于TX,我也会得到类似的结果
要设置TTY,我使用
int uart_init_linux(const char* dev, unsigned long baudrate) {
#define XMIT_FIFO_SIZE 14
int fd = open(dev, O_RDWR | O_NOCTTY);
if (fd < 0) {
printf("can't open %s: error %d\n", dev, errno);
return fd;
}
// Set port Settings
struct termios tio;
tcgetattr(fd, &tio);
cfmakeraw(&tio);
cfsetspeed(&tio, baudrate);
tio.c_cflag |= CMSPAR; // Set "stick" parity (either mark or space)
tio.c_cflag &= ~PARODD; // Select space parity so that only address byte causes error
// NOTE: The following block overrides PARMRK and PARENB bits cleared by cfmakeraw.
tio.c_cflag |= PARENB; // Enable parity generation
tio.c_iflag |= INPCK; // Enable parity checking
tio.c_iflag |= PARMRK; // Enable in-band marking
tio.c_iflag &= ~IGNPAR; // Make sure input parity errors are not ignored
if (tcsetattr(fd, TCSADRAIN, &tio))
printf("tcsetattr failed on %s (fh %d)!\n", dev, fd);
struct serial_struct serial;
// set xmit fifo size
ioctl(fd, TIOCGSERIAL, &serial);
printf("\tchange xmit buffer from %d to %d\n", serial.xmit_fifo_size, XMIT_FIFO_SIZE);
serial.xmit_fifo_size = XMIT_FIFO_SIZE;
ioctl(fd, TIOCSSERIAL, &serial);
return fd;
}
使用uart_init_linux/dev/ttyUSB0,B500000
这是硬件问题吗?我读得不够快吗?有一些旗帜我错过设置吗
我很难相信,当hterm和screen这样的工具可以接收到非常好的数据而没有那么多错误位时,RX是如此不可靠。可能是TX端的竞争条件;在更改奇偶校验设置和通过USB或UART芯片内的不同路径发送数据之间。测试这一点的一种蹩脚、探索性的方法可能是在奇偶校验位每次更改之前和之后睡觉;这改变了吗?编辑:哦,但这并不能解释0x00,0x18…为了排除这种可能性,我已经尝试了两个物理上不同的UART,一个只负责发送,一个负责接收。因此,我认为发送路径工作正常。我也看到了同样的问题,当我在Linux上只使用接收路径receive_数据包,并继续使用我的嵌入式设备STM32发送数据包时,该设备以处理这种“9位模式”而闻名。hterm和screen是否使用与您使用的相同硬件和协议工作?USB到串行设备过去因未能正确覆盖更不寻常的配置而臭名昭著。你能试着降低两端的比特率吗?现在听起来这个问题可能在硬件或RX端的驱动程序中超出了你的控制,可能是因为当它们在FIFO中排队时,它不能可靠地发出哪个字节有奇偶校验错误的信号?可能是TX端的竞争条件;在更改奇偶校验设置和通过USB或UART芯片内的不同路径发送数据之间。测试这一点的一种蹩脚、探索性的方法可能是在奇偶校验位每次更改之前和之后睡觉;这改变了吗?编辑:哦,但这并不能解释0x00,0x18…为了排除这种可能性,我已经尝试了两个物理上不同的UART,一个只负责发送,一个负责接收。因此,我认为发送路径工作正常。我也看到了同样的问题,当我在Linux上只使用接收路径receive_数据包,并继续使用我的嵌入式设备STM32发送数据包时,该设备以处理这种“9位模式”而闻名。hterm和screen是否使用与您使用的相同硬件和协议工作?USB到串行设备过去因未能正确覆盖更不寻常的配置而臭名昭著。你能试着降低两端的比特率吗?现在听起来这个问题可能在硬件或RX端的驱动程序中超出了你的控制,也许当它们在FIFO中排队时,它不能可靠地发出哪个字节有奇偶校验错误的信号?
int uart_init_linux(const char* dev, unsigned long baudrate) {
#define XMIT_FIFO_SIZE 14
int fd = open(dev, O_RDWR | O_NOCTTY);
if (fd < 0) {
printf("can't open %s: error %d\n", dev, errno);
return fd;
}
// Set port Settings
struct termios tio;
tcgetattr(fd, &tio);
cfmakeraw(&tio);
cfsetspeed(&tio, baudrate);
tio.c_cflag |= CMSPAR; // Set "stick" parity (either mark or space)
tio.c_cflag &= ~PARODD; // Select space parity so that only address byte causes error
// NOTE: The following block overrides PARMRK and PARENB bits cleared by cfmakeraw.
tio.c_cflag |= PARENB; // Enable parity generation
tio.c_iflag |= INPCK; // Enable parity checking
tio.c_iflag |= PARMRK; // Enable in-band marking
tio.c_iflag &= ~IGNPAR; // Make sure input parity errors are not ignored
if (tcsetattr(fd, TCSADRAIN, &tio))
printf("tcsetattr failed on %s (fh %d)!\n", dev, fd);
struct serial_struct serial;
// set xmit fifo size
ioctl(fd, TIOCGSERIAL, &serial);
printf("\tchange xmit buffer from %d to %d\n", serial.xmit_fifo_size, XMIT_FIFO_SIZE);
serial.xmit_fifo_size = XMIT_FIFO_SIZE;
ioctl(fd, TIOCSSERIAL, &serial);
return fd;
}