You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux/C串口编程:termios结构VTIME与VMIN超时参数失效问题

解决Linux串口(USB转RS485)VTIME/VMIN配置失效问题

核心问题排查与修复

1. 代码中的明显变量名错误

你的代码存在变量名笔误:定义的接收缓冲区是_bufReception,但read()调用时使用了未定义的_bufReceptionx,这会导致未定义行为,直接影响read()的返回结果:

// 定义的缓冲区
unsigned char _bufReception[50];
// 错误的read调用(变量名错误)
num_bytes = read(serial_port, _bufReceptionx, sizeof(_bufReceptionx));
// 修正为
num_bytes = read(serial_port, _bufReception, sizeof(_bufReception));

2. VTIME/VMIN的正确配置逻辑

VTIME(超时,单位0.1秒)和VMIN(最小接收字节数)的生效依赖非规范模式(你已通过tty.c_lflag &= ~ICANON;开启),需严格遵循以下规则:

  • 当VMIN > 0且VTIME > 0:read()会阻塞直到收到VMIN字节,或在收到第一个字节后等待VTIME时长超时,以先触发的条件为准。
  • 你配置的VTIME=10(1秒超时)、VMIN=1(收到任意字节即返回)逻辑是正确的,但需确保没有其他配置干扰。

3. USB转RS485适配器的特殊处理

RS485是半双工通信,多数USB转RS485适配器需要通过RTS引脚切换收发状态,这是很多开发者忽略的关键点:

  • 发送数据前,需切换RTS引脚使适配器进入发送模式;
  • 发送完成后,必须切回接收模式,否则无法接收数据。

你可以通过两种方式控制RTS:

// 方式1:开启硬件流控自动切换(部分适配器支持)
tty.c_cflag |= CRTSCTS;

// 方式2:手动控制RTS(需包含<sys/ioctl.h>)
int rts_state = TIOCM_RTS;
// 发送前切换到发送模式(部分适配器需用TIOCMBIC,可自行测试)
ioctl(serial_port, TIOCMBIS, &rts_state);
write(serial_port, _bufEmission, index+4);
// 等待发送完成,确保数据全部发出
tcdrain(serial_port);
// 切换回接收模式
ioctl(serial_port, TIOCMBIC, &rts_state);

4. 终端属性设置后的必要操作

设置完termios后,建议清空输入输出缓冲区,避免旧数据干扰新通信:

// 清空所有缓冲区
tcflush(serial_port, TCIOFLUSH);

修正后的完整核心代码

#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <unistd.h>
#include <sys/ioctl.h> // 用于RTS控制

const int longueur_bufemission = 30;
unsigned char _bufEmission[longueur_bufemission];   
unsigned char _bufReception[50];
int serial_port;
struct termios tty;

int main() {
    serial_port = open("/dev/ttyS4", O_RDWR);
    if (serial_port < 0) {
        printf("Error %i from open: %s\n", errno, strerror(errno));
        return 1;
    }

    // 先获取当前终端属性再修改,避免清零丢失默认配置
    if(tcgetattr(serial_port, &tty) != 0) {
        printf("Error %i from tcgetattr: %s\n", errno, strerror(errno));
        close(serial_port);
        return 1;
    } 

    // 串口基础配置
    tty.c_cflag &= ~PARENB;
    tty.c_cflag &= ~CSTOPB;
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;
    tty.c_cflag &= ~CRTSCTS; // 手动控制RTS时关闭硬件流控
    tty.c_cflag |= CREAD | CLOCAL;

    tty.c_lflag &= ~ICANON;
    tty.c_lflag &= ~ECHO;
    tty.c_lflag &= ~ECHOE;
    tty.c_lflag &= ~ECHONL;
    tty.c_lflag &= ~ISIG;

    tty.c_iflag &= ~(IXON | IXOFF | IXANY);
    tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL);

    tty.c_oflag &= ~OPOST;
    tty.c_oflag &= ~ONLCR;

    // 配置超时与最小接收字节数
    tty.c_cc[VTIME] = 10; // 1秒超时(10*0.1秒)
    tty.c_cc[VMIN] = 1;   // 收到至少1字节就返回,或超时返回

    cfsetispeed(&tty, B9600);
    cfsetospeed(&tty, B9600);

    if (tcsetattr(serial_port, TCSANOW, &tty) != 0) {   
        printf("Error %i from tcsetattr: %s\n", errno, strerror(errno));
        close(serial_port);
        return 1;
    }

    // 清空缓冲区
    tcflush(serial_port, TCIOFLUSH);

    // --- 发送数据 ---
    int index = 0; // 假设index已正确赋值
    int rts_state = TIOCM_RTS;
    // 切换到发送模式(若无效可尝试TIOCMBIC)
    ioctl(serial_port, TIOCMBIS, &rts_state);
    ssize_t write_bytes = write(serial_port, _bufEmission, index+4);
    if (write_bytes < 0) {
        printf("Error %i from write: %s\n", errno, strerror(errno));
    }
    // 等待发送完成
    tcdrain(serial_port);
    // 切换回接收模式
    ioctl(serial_port, TIOCMBIC, &rts_state);

    // --- 接收数据 ---
    ssize_t num_bytes = read(serial_port, _bufReception, sizeof(_bufReception));
    if (num_bytes < 0) {
        printf("Error %i from read: %s\n", errno, strerror(errno));
    } else {
        printf("Received %zd bytes\n", num_bytes);
    }

    close(serial_port);
    return 0;
}

额外注意事项

  • 不同USB转RS485适配器的RTS引脚逻辑可能相反(高电平发送/低电平发送),若切换后仍无法接收,可互换TIOCMBIS和TIOCMBIC的调用。
  • 若超时问题仍存在,可尝试增大VTIME值,或通过dmesg查看适配器的驱动挂载日志,确认硬件是否正常。

内容的提问来源于stack exchange,提问作者PAvel Tchoudakov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 01:43:12