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
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
序列来指示奇偶校验'错误'设置第9位的字节值。
0x00
但这只是“有点”的作用。仅在某些情况下,才会检测到完整序列0x18
,0xFF
,0x00
。 (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是如此不可靠。