1

edit-2:事实证明,这实际上可能是许多基于 USB 的适配器的限制,因为它们总是一次将多个字节传输到主机,而没有放置有关单个字节的信息的地方。因此,如果任何字节产生某种错误,整个传输将被标记,无法确定是哪个字节引起的。

编辑:我刚刚用 MT7688 的内部 UART 尝试了这段代码,它工作得非常好。所以这似乎是ftdi_sio驱动程序中的错误,甚至可能是硬件本身的错误。我可能会尝试用libftdi1/来实现这一点libftd2xx以排除这种情况。


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

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

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

我可以使用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序列以及设置了第 9 位的字节的实际值来指示。 0xFF0x00

static 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, 。(0x18 恰好是我的数据包的第一个字节),更多时候不是我不会读取转义,而是,(我什至不知道这是怎么可能的,因为永远不会发送 0x00)有时我会收到奇偶校验错误在随机有效载荷字节上。0x000x180xFF0x000x18

我首先怀疑存在竞争条件,因为我可能会在读取时更改 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 是如此不可靠。

4

0 回答 0