Petalinux下RS232串口通信读取异常及配置优化咨询
Petalinux下RS232串口read返回0的问题解决
问题描述
在Petalinux系统中开发C程序实现RS232串口通信时,串口写入功能正常,但使用read(uart1_fd, (void*) rx_buffer, 32)调用时返回0,无法读取数据,仅能通过自定义循环读取函数获取数据。怀疑串口配置存在问题,期望read能在接收完所有传入数据后再返回。
用户提供的串口配置代码:
int open_uart() { uart1_fd = open("/dev/ttyUL2", O_RDWR | O_NOCTTY | O_NDELAY); if (uart1_fd == -1) { printf("--- no uart ---\n"); return 0; } else { printf("--- open uart ok ---\n"); } struct termios tty; tcgetattr(uart1_fd, &tty); tty.c_cflag &= ~PARENB; // Disable parity tty.c_cflag &= ~CSTOPB; // One stop bit tty.c_cflag &= ~CSIZE; // Clear size bits tty.c_cflag |= CS8; // 8 bits per byte tty.c_cflag &= ~CRTSCTS; // No hardware flow control tty.c_iflag &= ~(IXON | IXOFF | IXANY); // Disable software flow control tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP); // Disable special characters tty.c_oflag &= ~OPOST; // No post-processing tty.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN); // Disable canonical mode tty.c_cc[VTIME] = 10; // Wait up to 10 deciseconds for data tty.c_cc[VMIN] = 0; // Don't wait for minimum number of characters if (cfsetispeed(&tty, B38400) < 0 || cfsetospeed(&tty, B38400) < 0) { printf("error\n"); } if (tcsetattr(uart1_fd, TCSANOW, &tty) < 0) { printf("error\n"); } return 1; }
用户的简单读取代码:
rx_length = read(uart1_fd, (void*) rx_buffer, 32);
用户的循环读取函数:
int recvMsg(char* pMsg, int16_t lng) { int nb = -1; char rx = 0; int m = 0; memset(pMsg, 0, lng); while (rx != 0xfa && m < lng && nb <= 10000) { nb++; if (read(uart1_fd, &rx, 1) > 0) pMsg[m++] = rx; } printf("Réception de: "); for (int i = 0; i < m; i++) { printf("%02x ", (uint8_t) pMsg[i]); } printf("\r\n"); return m; }
问题分析
- O_NDELAY标志的影响:打开串口时使用了
O_NDELAY,该标志会将串口设置为非阻塞模式。当无数据可读时,read会直接返回0,这是简单读取调用返回0的直接原因。 - VTIME与VMIN的配置逻辑:当前设置
VMIN=0(不等待最小字符数)、VTIME=10(等待1秒),结合非阻塞模式,read会立即返回,无数据时返回0,无法满足"接收完所有数据再返回"的需求。 - 缺失关键配置位:原代码未设置
CREAD | CLOCAL,CREAD是启用串口接收的必要配置,CLOCAL用于忽略调制解调器状态线,避免串口被意外挂起。
解决方案
1. 修改串口打开方式
移除O_NDELAY标志,使用阻塞模式打开串口(若需非阻塞可后续通过fcntl调整):
uart1_fd = open("/dev/ttyUL2", O_RDWR | O_NOCTTY);
2. 调整VTIME与VMIN参数
根据数据接收需求选择对应配置:
- 固定长度数据包:若每次接收固定32字节数据,设置
VMIN=32、VTIME=0,read会阻塞直到收到32字节数据才返回:tty.c_cc[VTIME] = 0; // 无限等待,直到收到VMIN个字符 tty.c_cc[VMIN] = 32; // 等待至少32个字符 - 可变长度带结束标志:若数据包长度不固定但以
0xfa为结束标志,设置VMIN=1、VTIME=10,read会先等待至少1个字符,之后1秒内无新数据则返回已接收内容:tty.c_cc[VTIME] = 10; // 收到第一个字符后启动1秒超时 tty.c_cc[VMIN] = 1; // 至少收到1个字符才返回
3. 完整修改后的配置代码
int open_uart() { uart1_fd = open("/dev/ttyUL2", O_RDWR | O_NOCTTY); if (uart1_fd == -1) { printf("--- no uart ---\n"); return 0; } else { printf("--- open uart ok ---\n"); } struct termios tty; // 清空结构体,避免未初始化字段干扰 memset(&tty, 0, sizeof(tty)); if (tcgetattr(uart1_fd, &tty) != 0) { printf("tcgetattr error\n"); close(uart1_fd); return 0; } tty.c_cflag &= ~PARENB; // 禁用奇偶校验 tty.c_cflag &= ~CSTOPB; // 1个停止位 tty.c_cflag &= ~CSIZE; // 清除数据位设置 tty.c_cflag |= CS8; // 8位数据位 tty.c_cflag &= ~CRTSCTS; // 禁用硬件流控 tty.c_cflag |= CREAD | CLOCAL; // 启用接收、忽略调制解调器状态线 tty.c_iflag &= ~(IXON | IXOFF | IXANY); // 禁用软件流控 tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP); // 禁用特殊字符处理 tty.c_oflag &= ~OPOST; // 禁用输出后处理 tty.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG); // 禁用规范模式、回显等 // 根据需求选择配置: // 情况1:固定32字节数据包 tty.c_cc[VTIME] = 0; tty.c_cc[VMIN] = 32; // 情况2:可变长度带结束标志 // tty.c_cc[VTIME] = 10; // tty.c_cc[VMIN] = 1; if (cfsetispeed(&tty, B38400) < 0 || cfsetospeed(&tty, B38400) < 0) { printf("set baud rate error\n"); close(uart1_fd); return 0; } if (tcsetattr(uart1_fd, TCSANOW, &tty) < 0) { printf("tcsetattr error\n"); close(uart1_fd); return 0; } return 1; }
4. 对应读取方式
- 固定长度读取:直接调用
read即可,成功时返回32:rx_length = read(uart1_fd, rx_buffer, 32); - 可变长度带结束标志读取:循环读取直到收到结束标志:
int read_until_end(char* buffer, int max_len) { int total = 0; char c; while (total < max_len) { int ret = read(uart1_fd, &c, 1); if (ret == -1) { perror("read error"); return -1; } else if (ret == 0) { // 超时无数据,退出 break; } buffer[total++] = c; if (c == 0xfa) { // 收到结束标志,退出 break; } } return total; }
内容的提问来源于stack exchange,提问作者Remi45
相关产品推荐
相关产品推荐

