使用CMSPAR的标记奇偶校验检测非常不可靠(9位UART)

使用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

edit-2:实际上这可能是一个,因为它们总是一次将多个字节传输到主机,而没有一个位置来放置有关单个字节的信息。因此,如果任何字节产生某种类型的错误,整个传输都将被标记,无法确定是哪个字节导致了错误

编辑:我刚刚用MT7688的内部UART尝试了这段代码,它的工作原理非常好。因此,这似乎是ftdi_sio驱动程序中的一个缺陷,甚至可能是硬件本身。我可能会尝试使用libftdi1/libftd2x来实现这一点,以排除这种可能性

我正在尝试与使用Linux的串行协议的嵌入式设备通信

为此,我使用FT232RL USB串行适配器

协议使用第9奇偶校验位集标记帧的第一个字节

我可以用CMSPAR选项mark/space奇偶校验重现这种情况,事实上,以这种方式发送帧效果良好且非常可靠

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;
}