Linux环境C语言读取Duet2开发板串口出现目标数据丢失问题
C语言读取Duet2开发板串口数据丢失问题分析
问题描述
尝试使用C语言编写程序读取Duet2开发板的串口数据,预期输出为两类内容:
- 以
N开头、后跟若干数值的目标数据行 - 偶发的加热器相关警告信息
实际运行C代码时仅能输出加热器警告内容,N开头的目标数据行完全丢失。已知Duet2开发板串口输出正常,使用Python代码可完整读取所有串口数据。
相关代码
存在问题的C代码
#include <stdio.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <termios.h> int main(int argc, char ** argv) { int fd; struct termios SerialPortSettings; // Open the Port. We want read/write, no "controlling tty" status, and open it no matter what state DCD is in fd = open("/dev/ttyACM0", O_RDWR | O_NOCTTY | O_NDELAY); // setting baud rates cfsetispeed(&SerialPortSettings,B115200); cfsetospeed(&SerialPortSettings,B115200); if (fd == -1) { perror("open_port: Unable to open /dev/ttyACM0 - "); return(-1); } fcntl(fd, F_SETFL, 0); // Read up to 255 characters from the port if they are there char buf[256]; while (1) { int k = read(fd, (void*)buf, 255); if (k < 0) { perror("Read failed - "); return -1; } else if (k == 0) { printf("No data on port\n"); } else { printf("Read line is : %s \n",buf); } } close(fd); return 0; }
可正常运行的Python代码
import serial import time import subprocess LINUX = 1 output = subprocess.check_output("python -m serial.tools.list_ports", shell=True) # sending command to terminal output = output.decode('utf-8') # the output is of type b'somestring' This removes that output = output.splitlines() # so we can access the COM part if LINUX: temp = output[0].split() port = temp[0] else: port = output[0] data = open("Input.txt", "w") ser = serial.Serial(port, 115200, timeout=30) print("Serial Port opened on : " + ser.name) counter = 0 while (1): response = ser.readline().decode('utf-8') print(response) data.write(response) if response == "": break time.sleep(1) data.close()
问题原因
你的C代码存在3个核心错误,共同导致了目标数据丢失:
- 串口配置完全未生效,参数错误:你定义的
SerialPortSettings是未初始化的局部变量,内部全是垃圾值,仅修改了波特率参数,既没有读取当前串口的默认配置,也没有调用tcsetattr将配置写入串口设备。Python的serial库默认配置为8N1(8数据位、1停止位、无奇偶校验)、无流控,C代码未做对应配置的情况下,短的警告行可能凑巧被识别,长的N开头数据行直接因为校验/帧错误被内核丢弃。 - 未给读取到的字节添加字符串结束符:
read函数仅返回读取到的字节数,不会自动在缓冲区末尾添加\0,你直接用printf("%s")打印会导致越界读取栈上的脏数据,长行内容大概率会被破坏。 - 未实现按行读取逻辑:串口是字节流,单次
read调用可能只返回半行数据,你的代码没有缓存拆分的行片段,直接输出会导致长的N行被拆分、内容混乱,无法被识别。
修复建议
- 串口打开成功后,先调用
tcgetattr(fd, &SerialPortSettings)读取当前默认配置,再修改波特率、配置8N1模式、关闭流控、开启原始模式,最后调用tcsetattr(fd, TCSANOW, &SerialPortSettings)让配置生效。 - 每次
read返回后,在buf[k]位置写入\0,保证缓冲区是合法的C字符串。 - 自行实现行缓存逻辑,将每次读取到的字节存入全局缓存,检测到换行符再整行输出。
内容的提问来源于stack exchange,提问作者HDEV5
相关产品推荐
相关产品推荐

