使用CMSPAR进行MARK奇偶校验检测非常不可靠(9位UART)

时间:2017-10-24 15:26:06

标签: c linux uart ftdi 9-bit-serial

edit-2 :事实证明这实际上可能是limitation of many USB based adapters,因为它们总是一次将多个字节传输到主机,而无需放置有关各个字节的信息。因此,如果任何字节产生某些类型的错误,整个传输将被标记,无法确定哪个字节导致它。

编辑:我刚刚使用MT7688的内部UART尝试了这段代码。所以这似乎是ftdi_sio驱动程序中的错误,甚至可能是硬件本身的错误。我可能会尝试使用libftdi1 / libftd2xx来实现此目的。

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

为此我使用的是FT232RL USB串口适配器。

该协议标记第9个(奇偶校验)位置1的帧的第一个字节。

我可以使用CMSPAR选项(标记/空格奇偶校验)进行重现,实际上,通过这种方式发送帧非常可靠。

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

当我在Linux上收到此类数据包时,应通过read 0xFF 0x00static uint16_t uart_read_byte(int fd) { uint8_t c; read(fd, &c, sizeof(c)); if (c == 0xFF) { // got escape byte read(fd, &c, sizeof(c)); if (c == 0x00) { // parity bit set read(fd, &c, sizeof(c)); return c | 0x100; } else if (c != 0xFF) fprintf(stderr, "invalid byte sequence: %x %x\n", 0xFF, c); } return c; } void receive_packets(int fd) { struct pollfd pfd = {.fd = fd, .events = POLLIN}; int bytes = 0; while (1) { poll(&pfd, 1, -1); ++bytes; uint16_t byte = uart_read_byte(fd); if (byte & 0x100) { printf("--[%d bytes]--\n", bytes); bytes = 0; } printf("> %02x|%c\n", byte, byte); } } 和实际的0xFF序列来指示奇偶校验'错误'设置第9位的字节值。

0x00

但这只是“有点”的作用。仅在某些情况下,才会检测到完整序列0x180xFF0x00。 (0x18恰好是我的数据包的第一个字节),更常见的是我不会读取转义0x18而只是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);(我甚至不知道这是怎么回事是可能的,因为永远不会发送0x00),有时我会在随机有效负载字节上得到奇偶校验错误。

我首先怀疑是竞争条件,因为我可能会在读取时更改TTY设置,但即使我使用两个UART和单独的进程(一个仅用于RX,另一个用于TX),我得到的结果如{{ 3}}

要设置TTY,我使用

hterm

screen

现在这是硬件问题吗?我的阅读速度不够快吗?是否有一些我错过设置的标志?

当{{1}}和{{1}}等工具在没有太多错误位的情况下可以正常接收时,我很难相信RX是如此不可靠。

0 个答案:

没有答案