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

Linux下C语言操作串口设备无响应,但终端命令可正常工作

串口C代码读写无响应问题排查与解决

问题背景

尝试连接/dev/ttymxc3串口设备,写入十六进制指令\xfa\x03\xaa后无法收到响应,设备可正常打开但读写无效。但在终端执行以下命令可正常读取响应(需执行两次echo指令):

stty -F /dev/ttymxc3 -echo -onlcr 9600
cat /dev/ttymxc3 >rear.bin&
echo -ne '\xfa\x03\xaa' > /dev/ttymxc3
echo -ne '\xfa\x03\xaa' > /dev/ttymxc3

当前测试C代码如下:

#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <stdio.h>
#include <strings.h>
#include <time.h>

#define BAUDRATE B9600
#define DEVICE "/dev/ttymxc3"
#define HEX_DATA_LEN 3
#define HEX_DATA_SEND "\xfa\x03\xaa"
#define READ_INTERVAL_SEC 5
#define READ_COUNT 20

int main()
{
    int fd;
    struct termios oldtio, newtio;
    unsigned char buf[256];

    printf("Opening device %s\n", DEVICE);
    fd = open(DEVICE, O_RDWR | O_NOCTTY);
    if (fd < 0) {
        perror(DEVICE);
        return -1;
    }

    printf("Setting terminal attributes...\n");
    tcgetattr(fd, &oldtio);
    bzero(&newtio, sizeof(newtio));
    newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD;
    newtio.c_iflag = IGNPAR;
    newtio.c_oflag = 0;
    newtio.c_lflag = 0;
    newtio.c_cc[VTIME] = 0;
    newtio.c_cc[VMIN] = 1;
    tcflush(fd, TCIFLUSH);
    tcsetattr(fd, TCSANOW, &newtio);

    printf("Sending hex data...\n");
    write(fd, HEX_DATA_SEND, HEX_DATA_LEN);

    sleep(1);
    printf("Reading response...\n");
    int n = read(fd, buf, sizeof(buf));
    if (n > 0) {
        printf("Received response: ");
        for (int i = 0; i < n; i++) {
            printf("0x%02x ", buf[i]);
        }
        printf("\n");
    }

    printf("Reading from device every %d seconds for %d times...\n", READ_INTERVAL_SEC, READ_COUNT);
    for (int i = 0; i < READ_COUNT; i++) {
        sleep(READ_INTERVAL_SEC);
        n = read(fd, buf, sizeof(buf));
        if (n > 0) {
            printf("Received data: ");
            for (int j = 0; j < n; j++) {
                printf("0x%02x ", buf[j]);
            }
            printf("\n");
        }
    }

    printf("Closing device...\n");
    tcsetattr(fd, TCSANOW, &oldtio);
    close(fd);

    return 0;
}

问题分析与解决方案

1. 对齐终端的串口参数设置

终端命令中设置了-echo和-onlcr,但C代码的termios配置未匹配这些参数,这是核心差异:

  • -echo:关闭回显,需清除c_lflag中的ECHO位
  • -onlcr:禁止输出时将换行转换为回车+换行,需清除c_oflag中的ONLCR位

修改后的串口属性配置:

tcgetattr(fd, &oldtio);
bzero(&newtio, sizeof(newtio));
newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD;
newtio.c_iflag = IGNPAR;
newtio.c_oflag &= ~ONLCR;  // 匹配stty -onlcr
newtio.c_lflag &= ~ECHO;   // 匹配stty -echo
newtio.c_cc[VTIME] = 10;   // 设置1秒超时(VTIME单位为0.1秒)
newtio.c_cc[VMIN] = 0;     // 有数据立即返回,无数据超时返回0
tcflush(fd, TCIOFLUSH);    // 同时刷新输入输出缓冲区(原代码仅刷输入)
tcsetattr(fd, TCSANOW, &newtio);

2. 模拟终端的两次指令发送

终端中需执行两次echo才能触发响应,因此C代码也需要连续发送两次指令,并确保数据完全发送:

printf("Sending hex data twice...\n");
// 第一次发送
write(fd, HEX_DATA_SEND, HEX_DATA_LEN);
tcdrain(fd);  // 等待所有输出数据发送完成
usleep(100000); // 给设备100ms处理时间
// 第二次发送
write(fd, HEX_DATA_SEND, HEX_DATA_LEN);
tcdrain(fd);

3. 优化读取逻辑

原代码单次read可能因数据未完全到达而读取不完整,改为循环读取直到超时:

printf("Reading response...\n");
int total = 0;
memset(buf, 0, sizeof(buf));
// 循环读取直到超时
while (1) {
    int n = read(fd, buf + total, sizeof(buf) - total);
    if (n <= 0) break;
    total += n;
}
if (total > 0) {
    printf("Received response: ");
    for (int i = 0; i < total; i++) {
        printf("0x%02x ", buf[i]);
    }
    printf("\n");
}

完整修改后的代码

#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <stdio.h>
#include <strings.h>
#include <time.h>

#define BAUDRATE B9600
#define DEVICE "/dev/ttymxc3"
#define HEX_DATA_LEN 3
#define HEX_DATA_SEND "\xfa\x03\xaa"
#define READ_INTERVAL_SEC 5
#define READ_COUNT 20

int main()
{
    int fd;
    struct termios oldtio, newtio;
    unsigned char buf[256];

    printf("Opening device %s\n", DEVICE);
    fd = open(DEVICE, O_RDWR | O_NOCTTY);
    if (fd < 0) {
        perror(DEVICE);
        return -1;
    }

    printf("Setting terminal attributes...\n");
    tcgetattr(fd, &oldtio);
    bzero(&newtio, sizeof(newtio));
    newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD;
    newtio.c_iflag = IGNPAR;
    newtio.c_oflag &= ~ONLCR;
    newtio.c_lflag &= ~ECHO;
    newtio.c_cc[VTIME] = 10;
    newtio.c_cc[VMIN] = 0;
    tcflush(fd, TCIOFLUSH);
    tcsetattr(fd, TCSANOW, &newtio);

    printf("Sending hex data twice...\n");
    write(fd, HEX_DATA_SEND, HEX_DATA_LEN);
    tcdrain(fd);
    usleep(100000);
    write(fd, HEX_DATA_SEND, HEX_DATA_LEN);
    tcdrain(fd);

    printf("Reading response...\n");
    int total = 0;
    memset(buf, 0, sizeof(buf));
    while (1) {
        int n = read(fd, buf + total, sizeof(buf) - total);
        if (n <= 0) break;
        total += n;
    }
    if (total > 0) {
        printf("Received response: ");
        for (int i = 0; i < total; i++) {
            printf("0x%02x ", buf[i]);
        }
        printf("\n");
    }

    printf("Reading from device every %d seconds for %d times...\n", READ_INTERVAL_SEC, READ_COUNT);
    for (int i = 0; i < READ_COUNT; i++) {
        sleep(READ_INTERVAL_SEC);
        total = 0;
        memset(buf, 0, sizeof(buf));
        while (1) {
            int n = read(fd, buf + total, sizeof(buf) - total);
            if (n <= 0) break;
            total += n;
        }
        if (total > 0) {
            printf("Received data: ");
            for (int j = 0; j < total; j++) {
                printf("0x%02x ", buf[j]);
            }
            printf("\n");
        }
    }

    printf("Closing device...\n");
    tcsetattr(fd, TCSANOW, &oldtio);
    close(fd);

    return 0;
}

额外排查点

  • 编译命令:gcc serial_test.c -o serial_test,无需额外库
  • 设备权限:若执行失败,尝试用sudo ./serial_test测试
  • 同步写入:可在open时添加O_SYNC参数,确保写入同步:fd = open(DEVICE, O_RDWR | O_NOCTTY | O_SYNC);

内容的提问来源于stack exchange,提问作者MAGIC KB

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 13:35:13