最近终于有机会操作模块,以下内容均由AT指令操作引发
关于AT指令适用范围
模块作为独立工作单元,任何拥有串口通信的上位机都能对其进行操作。
手头有一带GPS功能的4G Modem,网上找一份可以解析NMEA数据的代码,虽然工作正常,但还是有一些有趣的细节值得推敲。
下列现象引起了我的注意(问题1):
chat -t 3 -e '' 'AT\r' OK >> /dev/ttyUSB2 < /dev/ttyUSB2
AT
OK
请注意这里莫名多空两行,再看下从网上找的代码gps.c,执行一下网上下载的程序
open GPS_AT_port success!
init GPS_port success!
nwrite=10,AT+QGPS=1
nread=10,AT+QGPS=1
================1======================
nwrite=11,AT+QGPSEND
nread=6,
OK
...
再执行AT指令
chat -t 3 -e '' 'AT\r' OK >> /dev/ttyUSB2 < /dev/ttyUSB2
AT
OK
结果变得整洁了,没有多余的空行
咋一看除了不美观,貌似也能用,再进行下一步,
读取GPS数据,然而有那么几次,需要二次read才能开始返回正确数据;
可能是读取速度太快了,数据还没准备好
咱sleep两秒…这回看起来好多了,但如果要靠延时来获取数据,太不靠谱了(指令不同,执行时间不同,虽然模块手册标明最大返回时间,但使用最大返回时间意味着效率往往最低)
看看网上关于如何正确地从串口读取/写入AT指令的讨论
You should never, never, ever use sleep as a substitute for waiting for the Final result code from the modem. Just as you would not write a http client that completely ignores all responses from the http server, you should not send AT commands to a modem and completely ignore the responses it sends back. You must read and parse everything the modem sends back to you. Nothing else will work reliably.
AT Command Handling 0
Time delay between at commands for sending sms
A program that will execute AT commands in sequence and capture the response from the modem.
Github atchannel.c
上文指出使用延迟来读取串口数据有两个潜在问题(不用讲这肯定有问题,问题大or小就是了):
引出atinout.c程序,原理不复杂,返回字串挨个判断,符合AT指令规范的结束符就返回。
现在,回到刚才的问题1,atinout貌似很牛逼啊,看看能不能借此去掉两个多余空行
让atinout分别在gps.c程序运行前/后执行,比较结果
# chat -t 3 -e '' 'AT\r' OK >> /dev/ttyUSB2 < /dev/ttyUSB2
AT
OK
# echo AT | atinout - /dev/ttyUSB2 -
AT
OK
AT
OK
AT
OK
......
atinout停不下来了。。。没找到结束符估计是
紧接着执行gps.c程序
# gps
open GPS_AT_port success!
init GPS_port success!
nwrite=10,AT+QGPS=1
nread=10,AT+QGPS=1
================1======================
nwrite=11,AT+QGPSEND
nread=6,
OK
...
再次执行AT指令测试
# chat -t 3 -e '' 'AT\r' OK >> /dev/ttyUSB2 < /dev/ttyUSB2
AT
OK
# echo AT | atinout - /dev/ttyUSB2 -
AT
OK
多次实验,atinout返回数据完整且无重复
从此处可以判断:
在其它条件不变的情况下,
gps.c程序里做了某个操作,使得串口数据处理变得不一样了
调试gps.c源码并结合其他博主的经验总结,产生差别的原因出在串口初始化操作
new_cfg.c_oflag &= ~(ONLCR | OCRNL);
CRLF是Carriage-Return Line-Feed的缩写,意思是回车换行,换行在有的ASCII码表也用newline(简NL)来进行表示,这里的LF是line feed的概念,意思是一样的
有时候,在用write发送数据时没有键入回车,信息就发送不出去,这主要是因为我们在输入输出时是按照规范模式接收到回车或换行才发送,而更多情况下我们是不必键入回车或换行的。此时应转换到行方式输入,不经处理直接发送,设置如下:
Opt.c_lflag &= ~ (ICANON | ECHO | ECHOE | ISIG);配置
还存在这样的情况:发送字符0X0d的时候,往往接收端得到的字符是0X0a,原因是因为在串口设置中c_iflag和c_oflag中存在从NL-CR和CR-NL的映射,即串口能把回车和换行当成同一个字符,可以进行如下设置屏蔽之:
Opt.c_iflag &= ~ (INLCR | ICRNL | IGNCR);
Opt.c_oflag &= ~(ONLCR | OCRNL);
《linux串口编程参数配置》
Canonical mode 规范模式,也称交互模式,Linux默认采用该模式初始化串口,Linux假定串口应用于人机交互
注意:该模式使能ICRNL,CR-to-NL,即回车符CR 转 换行符NL
换行符 LF
回车符 CR
三大操作系统:
至此,可以解释为什么会有空两行及read调用中途返回的现象:模式不匹配
chat -t 3 -e '' 'AT\r' OK >> /dev/ttyUSB2 < /dev/ttyUSB2
echo -e 'AT\r' > /dev/ttyUSB2
Linux串口编程说明
Linux串口编程(termios)
Linux下串口编程入门
《Serial Programming Guide for POSIX Operating Systems》
原始输入/输出(串口通信)
串口转TCP
socat -d -d /dev/ttyUSB1,raw,nonblock,ignoreeof,cr,echo=0 TCP4-LISTEN:5555,reuseaddr