Linux下USB对接GSM模块写入正常 中断读取数据不稳定排查
Linux下USB转GSM模块串口读取异常排查
你的代码存在语法错误、串口配置错误、异步IO逻辑错误三类核心问题,是读取时好时坏的根本原因,逐点说明:
一、编译级语法错误
open调用存在语法拼接错误:原代码fd=open("/dev/ttyUSB1",O_RDWR|O_NDELAY|O_NOCTTY|O_APPEND,S_IRWXU|S_IRWXG|S_IRWXO);|O_NOCTTY|O_SYNC|O_NDELAY);多了半截无效flag和括号,打开已存在的tty设备不需要传入文件权限位,多余参数会导致open行为异常。- 信号处理函数内
static int gsm_count缺少行尾分号,且该变量从未做增量更新、边界校验,定义后无实际作用,还会引发内存越界风险。 - main函数内
int init_ttyusb0(B9600,0)为函数调用,多余前缀int且缺少行尾分号,会直接引发编译失败。 - 信号处理函数内
fcntl(fd,F_SETFL,!(O_ASYNC));逻辑完全错误:O_ASYNC为非零宏,!O_ASYNC固定返回0,该行执行后会清空所有文件状态标志,直接导致后续异步通知失效。
二、串口termios配置错误
- 配置位归属写错:
ICANON、ISIG、ECHO*属于本地模式标志c_lflag,IXON/IXOFF/IXANY属于输入模式标志c_iflag,代码中误将这些位写到c_cflag、重复写到c_lflag,导致规范模式关闭、流控关闭的配置完全不生效,串口会按默认规则按换行切分数据,和GSM模块返回格式不匹配时就会读不到数据。 VMIN/VTIME配置和异步IO冲突:当前设置VMIN=0, VTIME=10代表读操作最长等待1秒就返回,哪怕没有数据也会返回空值,极易出现数据未到达就触发空读,后续数据到达时无信号触发的问题。- 初始化时未清空
termios结构体、未清空串口缓冲区:结构体残留的随机配置、设备打开时残留的历史数据,都会随机干扰正常读写。 - 多余标志干扰:打开设备时传入的
O_APPEND标志对字符设备无意义,会随机影响读写偏移。
三、异步IO(中断式读取)逻辑错误
sigaction配置不完整:未初始化信号掩码sa_mask、未设置SA_RESTART标志,会导致信号触发时随机打断scanf、usleep等系统调用,程序流程错乱;未屏蔽信号嵌套会引发重入问题。O_ASYNC开关逻辑完全错误:代码每次写完AT指令、固定sleep一段时间后才开启O_ASYNC,但GSM模块返回响应的时间不固定,若模块在开启O_ASYNC前就返回数据,不会触发SIGIO信号,直接读不到数据;信号处理函数中错误关闭O_ASYNC后,若模块返回数据分多段传输,后续数据段不会触发信号,直接丢失。- 信号处理函数逻辑违规:
printf、memset、fcntl都不是异步信号安全函数,不能在信号处理函数中调用;read调用未判断返回值,直接按字符串打印会因为缺少结束符引发乱码、内存越界;读完立即清空缓冲区,主逻辑无法获取有效返回数据。
四、业务逻辑错误
- 写AT指令后靠固定
usleep等响应不可靠:不同AT指令响应时间差异极大,比如AT+CFUN=1需要数秒才能完成模块初始化,固定延时要么太短漏数据,要么太长阻塞流程。 - case 2分支逻辑矛盾:代码中
strcpy写入的是AT+CCID\r,实际write发送的是AT+CPIN?\r,指令和预期不匹配。
修复要点
- 修正open调用:
fd = open("/dev/ttyUSB1", O_RDWR | O_NOCTTY | O_NDELAY);,去掉多余标志和无效参数。 - 修正termios配置:初始化时先清空结构体,将所有配置位写到对应flag字段,配置完成后调用
tcflush(fd, TCIOFLUSH)清空收发缓冲区;设置VMIN=1, VTIME=0,让读操作至少读到1字节再返回。 - 修正异步IO逻辑:串口初始化完成后就开启
O_ASYNC,不要每次发指令后重复开启,也不要在信号处理函数中关闭O_ASYNC;配置sigaction时先清空sa_mask,加上SA_RESTART标志避免系统调用被打断。 - 信号处理函数仅做最小操作:只执行
read将数据追加到全局环形缓冲区,设置数据就绪标志,所有打印、解析、逻辑处理放到主循环中完成,不要在信号处理函数中调用非异步安全函数。 - 去掉所有固定延时等响应的逻辑,完全靠信号触发的读操作接收模块返回数据,所有系统调用都要校验返回值,处理
EINTR、EAGAIN等临时错误。
内容的提问来源于stack exchange,提问作者MKT
相关产品推荐
相关产品推荐

