3. LTE物理层与关键机制

说到AT指令,我刚开始做嵌入式通信那会儿,总觉得这东西太古老了。后来才发现,越是基础的东西,越值得花功夫吃透。AT指令就像是咱们和模组对话的「通用语言」,不管你是用移远、广和通还是SIMCom的模组,这套指令集基本都通用。

说白了,AT指令就是一套文本命令。你通过串口发一句话给模组,模组给你回一句话。就这么简单。但简单背后藏着不少细节,今天咱们就把它掰开揉碎了讲清楚。

核心认知:AT指令是「人机对话」的桥梁。你发什么,模组就回什么。理解了这个交互模型,后面所有开发都会顺很多。

3.1 AT指令到底是什么?

AT是Attention的缩写。早期Hayes调制解调器时代就有了,一直沿用到现在。每条指令都以"AT"开头,模组收到后就知道「哦,这是要跟我说话」。

我在项目中遇到过不少新手,上来就问:「为什么我发AT回车没反应?」嗯,这里要注意——AT指令必须带回车换行(\r\n),模组才能识别指令结束。少了这个,模组会一直等,你以为它坏了,其实它在等你把话说完。

3.2 指令格式详解

AT指令的格式其实就几种,我整理了一张表,你一看就明白:

指令类型 格式示例 说明
测试指令 AT+CMD=? 查询指令支持哪些参数范围
查询指令 AT+CMD? 查询当前参数值
设置指令 AT+CMD=<param> 设置参数值
执行指令 AT+CMD 执行某个操作(不带参数)

举个例子,AT+CSQ是查信号强度的。你发AT+CSQ?,模组会返回当前信号值。你发AT+CSQ=?,它告诉你信号值范围是0到31。你想想看,这设计是不是挺巧妙的?一条指令四种用法,覆盖了所有场景。

我的小技巧:调试时先用AT+CMD=?看看模组支持什么参数,再动手写代码。这能省下不少排查时间。

3.3 常用基础指令一览

做LTE模组开发,有几条指令你几乎天天用。我按使用频率排了个序:

  1. AT —— 测试通信是否正常。模组返回OK,说明串口通了。
  2. AT+CSQ —— 查信号强度。返回值0-31,越大越好。我见过信号值0还能打电话的,但网速就别指望了。
  3. AT+CREG? —— 查网络注册状态。0未注册,1已注册,2正在搜网,5已注册漫游。
  4. AT+CGATT? —— 查GPRS附着状态。1表示已附着,可以上网了。
  5. AT+CGPADDR —— 查IP地址。连上网络后,用这个拿IP。

我记得有一次,客户说模组连不上网。我远程一看,AT+CREG?返回0,模组根本没注册上网络。查了一圈,发现是SIM卡没插好。你看,基础指令有时候比复杂工具管用多了。

3.4 指令响应解析

模组的响应格式也是有规律的。我把它分成三类:

  • OK/ERROR —— 简单指令的最终结果。AT回车,模组回OK,完事。
  • 中间响应 + OK/ERROR —— 比如AT+CSQ,先回+CSQ: 20,0,再回OK。
  • URC(主动上报) —— 模组主动发消息,比如+CMTI: "SM",3表示有新短信。

你写代码解析响应时,我建议用状态机来处理。逐行读,匹配关键字,别用简单的字符串查找——URC可能随时插进来,容易乱。

避坑指南:我曾经踩过一个坑——模组返回的换行符可能是\r\n,也可能是\n。不同固件版本不一样。解析时最好用trim()去掉空白字符,别硬匹配。

3.5 指令交互的完整流程

下面这张图,是我自己画的一个AT指令交互流程。你照着这个思路写代码,基本不会出大问题。

AT指令交互流程图 AT指令交互流程图 发送AT指令 等待模组响应 解析响应 OK 指令执行成功 ERROR 指令执行失败 注意:URC消息可能在任何时刻插入,解析时需做缓冲处理

你看这个流程,其实不复杂。发指令、等响应、解析结果。但实际开发中,最头疼的是超时处理和URC干扰。我一般会给每条指令设个超时时间,比如5秒。超时了就重试,重试3次还不行,就报错。

3.6 实战:写一个AT指令收发函数

光说不练假把式。我写个简单的AT指令收发函数,你感受一下:

// 发送AT指令并等待响应
int at_send_cmd(const char *cmd, const char *expect, int timeout_ms)
{
    char buf[256];
    int ret;
    
    // 清空串口缓冲区,避免残留数据干扰
    uart_flush();
    
    // 发送指令
    uart_send(cmd);
    uart_send("\r\n");
    
    // 等待期望的响应
    ret = uart_wait_for(expect, buf, sizeof(buf), timeout_ms);
    if (ret == 0) {
        printf("收到期望响应: %s\n", buf);
        return 0;  // 成功
    } else {
        printf("超时或响应不匹配\n");
        return -1; // 失败
    }
}

这个函数看着简单,但有几个关键点:

  • 发送前一定要清空缓冲区。不然上次残留的数据会干扰本次解析。
  • 超时时间要根据指令类型调整。查信号这种快的,2秒就够了。拨号上网这种慢的,可能要30秒。
  • 响应匹配用前缀匹配,别用完全匹配。因为模组返回的数据可能带换行、空格。

我的经验:调试阶段,把AT指令的收发日志都打印出来。出问题时,翻日志比猜原因快得多。我习惯用「AT>>」和「AT<<」标记收发方向,一目了然。

好了,AT指令的基础就这些。你把这些吃透了,后面讲TCP/IP、MQTT这些应用层协议时,你会发现——万变不离其宗,都是AT指令的变种。记住那个交互模型,剩下的就是参数不同而已。