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
相关产品推荐
相关产品推荐

