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

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;
}

问题分析

  1. O_NDELAY标志的影响:打开串口时使用了O_NDELAY,该标志会将串口设置为非阻塞模式。当无数据可读时,read会直接返回0,这是简单读取调用返回0的直接原因。
  2. VTIME与VMIN的配置逻辑:当前设置VMIN=0(不等待最小字符数)、VTIME=10(等待1秒),结合非阻塞模式,read会立即返回,无数据时返回0,无法满足"接收完所有数据再返回"的需求。
  3. 缺失关键配置位:原代码未设置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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 16:34:53