vxWorks下串口UART调用select()始终返回0无法读取数据
问题分析与解决方案
针对vxWorks下串口读取使用select()始终返回0的问题,核心排查方向和解决步骤如下:
1. 检查select()调用参数正确性
这是最容易忽略的错误点:
select()的第一个参数必须是最大文件描述符+1,你的串口fd固定为4,所以该参数必须传入5,而非4。如果传入4,select()会忽略fd=4的监控,直接返回0。- 确认
FD_SET正确将this->fd加入读描述符集合,FD_ZERO初始化集合无遗漏。 - 超时时间赋值要准确:400us对应
timeout.tv_sec = 0; timeout.tv_usec = 400;
正确调用示例:
fd_set readFds; struct timeval timeout; FD_ZERO(&readFds); FD_SET(this->fd, &readFds); timeout.tv_sec = 0; timeout.tv_usec = 400; // 第一个参数必须为5 int ret = select(5, &readFds, NULL, NULL, &timeout);
2. 适配vxWorks系统时钟精度限制
vxWorks的系统tick分辨率默认多为1ms(对应CONFIG_VXWORKS_SYS_TICK_RATE=1000),select()的超时时间会被对齐到系统tick的整数倍。400us小于1ms,会被系统视为立即超时,导致还未等到串口数据就返回0。
解决方法:
- 临时调整超时时间到1ms(
timeout.tv_usec = 1000),匹配IMU的1kHz数据发送周期,既能覆盖数据包传输耗时,又能适配系统tick精度。 - 如果必须用更短超时,可通过
sysClkRateGet()查看当前tick频率,若允许则修改系统tick为2000Hz(500us粒度),但该操作可能影响其他系统模块,需谨慎评估。
3. 修正串口终端属性配置
若串口处于规范模式,select()会等待换行符或缓冲区满才触发可读信号,而IMU的二进制数据包不含换行符,导致select()一直不触发。需配置为非规范模式:
struct termios termAttr; tcgetattr(this->fd, &termAttr); // 设置波特率 cfsetispeed(&termAttr, B921600); cfsetospeed(&termAttr, B921600); // 配置数据格式:8位数据、无校验、1位停止位 termAttr.c_cflag &= ~(PARENB | CSTOPB | CSIZE); termAttr.c_cflag |= CS8; // 禁用规范模式、回显、信号 termAttr.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // 禁用流控制 termAttr.c_iflag &= ~(IXON | IXOFF | IXANY); // 禁用输出处理 termAttr.c_oflag &= ~OPOST; // 设置非阻塞读取:有数据就返回,无数据立即退出 termAttr.c_cc[VMIN] = 0; termAttr.c_cc[VTIME] = 0; tcsetattr(this->fd, TCSANOW, &termAttr);
4. 排查串口硬件与接收状态
- 用
ioctl(this->fd, FIONREAD, &bytesAvail)在select()前查询串口接收缓冲区字节数:- 若
bytesAvail>0但select()返回0:说明select()参数配置错误。 - 若
bytesAvail=0:排查硬件连接、串口引脚配置(如RX/TX是否接反)、IMU是否正常发送数据。
- 若
内容的提问来源于stack exchange,提问作者Dark Sorrow
相关产品推荐
相关产品推荐

