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 位的字节的实际值来指示。 0xFF
0x00
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)有时我会收到奇偶校验错误在随机有效载荷字节上。0x00
0x18
0xFF
0x00
0x18
我首先怀疑存在竞争条件,因为我可能会在读取时更改 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 是如此不可靠。