当前位置:网站首页>软件实现AT命令操作过程
软件实现AT命令操作过程
2022-07-31 15:34:00 【DropLin】
述说
AT命令操作模块的功能已成为硬件功能标准化应用常用的一种实现方式,通过不同的AT命令的组合即可实现一些复杂的应用,而且不用去研究底层的执行逻辑,只需要1组串口既可实现所有功能的操作。比如现在市场上流行的wifi模组,4G模组,NB模组等都推出了官方的AT固件。
AT命令看似简单,但是要用软件来实现这一过程,还是有很多的事情要处理,比如API接口的灵活性,需要适配不同的操作指令,测试命令,设置命令,读取命令等操作,还有需要处理响应数据等,要想完美的解决这些操作,还得下点功夫。今天我们就以移远通信的4G模块EC600U系列为例讲讲软件实现AT命令的操作过程。
实现平台
移远通信的4G模块EC600U
操作方式
AT命令
AT命令类型
| AT 命令类型 | 语句 | 描述 |
|---|---|---|
| 测试命令 | AT+<cmd>=? | 测试是否存在相应的设置命令,并返回有关其参数的类型、值或范围的信息。 |
| 查询命令 | AT+<cmd>? | 查询相应设置命令的当前参数值。 |
| 设置命令 | AT+<cmd>=<p1>[,<p2>[,<p3>[…]]] | 设置用户可定义的参数值。 |
| 执行命令 | AT+<cmd> | 返回特定的参数信息或执行特定的操作。 |
实现思路
先把所有需要操作的AT命令和对应的序号定义好,最好定义成一个列表,方便调用
序号定义
以枚举的方式定义
typedef enum __AT_CMD
{
/*000*/ AC_ATI, //显示MT的ID信息
/*001*/ AC_GMI, //请求制造商信息
/*002*/ AC_GMM, //请求MT型号ID
/*003*/ AC_GMR, //请求TA固件版本ID
/*004*/ AC_CGMI, //请求制造商信息
/*005*/ AC_CGMM, //请求MT型号ID
/*006*/ AC_CGMR, //请求TA固件版本ID
// ......
/*215*/ AC_QISDE, //控制是否回显AT+QISEND要发送的数据
/*216*/ AC_QIGETERROR, //查询上一个AT命令错误代码
/*217*/ AC_QENG, // 查询主服务小区和邻区信息
/*218*/ AC_QCELLINFO, // 获取服务小区和邻区信息
}atCmd_te;
AT命令定义
以结构体变量数组的方式定义
基本单元结构体
typedef struct __AT_COMMAND_STRUCT
{
uint16_t index; //命令在列表中的序号
char *testCmd; //测试命令 AT+<X>=? 该命令用于查询设置命令或内部程序设置的参数以及其取值范围
char *setCmd; //设置命令 AT+<X>=<…> 该命令用于设置用户自定义的参数值
char *readCmd; //查询命令 AT+<X>? 该命令用于返回参数的当前值
char *executeCmd; //执行命令 AT+<X> 该命令用于读取受GSM 模块内部程序控制的不可变参数
char *cmdInfor; //命令注释说明
}atCommandStruct_ts;
结构体变量数组
对比测试命令、设置命令、查询命令、执行命令这四种,可以发现测试命令、设置命令、查询命令这3种命令可以由执行命令按照对应的格式重组而成,重组方式见下一章节。
由于受限于单片机flash的容量,不可能把所有的AT命令都列出来,这样可能会导致序号定义中的序号和AT命令在数组中的位置对应不上,所以在定义的时候先把序号和AT命令绑定在一起,调用的时候再去检索序号index找出该命令在数组中的序号(atCmd_getListIndex()),再通过该序号调用数组中对应命令的AT命令和相关说明。
const atCommandStruct_ts atCommandStructList[] =
{
{
.index = AC_ATI,
.executeCmd = "ATI",
.cmdInfor = "显示MT的ID信息",
},
{
.index = AC_GMI,
.executeCmd = "AT+GMI",
// .cmdInfor = "请求制造商信息",
},
{
.index = AC_GMM,
.executeCmd = "AT+GMM",
// .cmdInfor = "请求MT型号ID",
},
{
.index = AC_GMR,
.executeCmd = "AT+GMR",
.cmdInfor = "请求TA固件版本ID",
},
{
.index = AC_CGMI,
.executeCmd = "AT+CGMI",
.cmdInfor = "请求制造商信息",
},
{
.index = AC_CGMM,
.executeCmd = "AT+CGMM",
.cmdInfor = "请求MT型号ID",
},
{
.index = AC_CGMR,
.executeCmd = "AT+CGMR",
.cmdInfor = "请求TA固件版本ID",
},
// ......
{
.index = AC_QISDE,
.executeCmd = "AT+QISDE",
.cmdInfor = "控制是否回显AT+QISEND要发送的数据",
},
{
.index = AC_QIGETERROR,
.executeCmd = "AT+QIGETERROR",
.cmdInfor = "查询上一个AT命令错误代码",
},
{
.index = AC_QENG,
.executeCmd = "AT+QENG",
.cmdInfor = "查询主服务小区和邻区信息",
},
{
.index = AC_QCELLINFO,
.executeCmd = "AT+QCELLINFO",
.cmdInfor = "获取服务小区和邻区信息",
},
};
AT命令API封装
测试命令:该命令只需要在执行命令的基础上加上=?\r\n即可设置命令:由于设置命令会带有不定参数,所以该命令的组成可以通过格式化函数vsnprintf和可变传参来实现读取命令:该命令只需要在执行命令的基础上加上?\r\n即可执行命令:该命令直接通过序号调用数组列表中的定义即可
uint16_t atCmd_getListIndex(uint16_t cmdIndex)
{
for (int i = 0; atCommandStructList[i].index != 65535; i++)
{
if (atCommandStructList[i].index == cmdIndex)
{
return i;
}
}
return 0;
}
void atCmdExport(cmdType_te cmdType, uint16_t cmdIndex, char *fmt, ...)
{
static uint8_t stepCount = 0;
uint16_t listIndex;
listIndex = atCmd_getListIndex(cmdIndex);
switch(cmdType)
{
case CT_TEST_CMD://测试命令 AT+<X>=? 该命令用于查询设置命令或内部程序设置的参数以及其取值范围
{
atCmd_clearbuffer();
atCmd_printf("%s=?\r\n", atCommandStructList[listIndex].executeCmd);
}break;
case CT_SET_VALUE://设置命令 AT+<X>=<…> 该命令用于设置用户自定义的参数值
{
atCmd_clearbuffer();
bufferIndex = 0;
if (fmt)
{
va_list va;
char tempTab[50];
va_start(va, fmt);
memset(tempTab, 0, sizeof(tempTab));
vsnprintf(tempTab, (int)sizeof(tempTab), fmt, va);
atCmd_printf("%s=%s\r\n",atCommandStructList[listIndex].executeCmd, tempTab);
va_end(va);
}
else
{
atCmd_printf("%s\r\n",atCommandStructList[listIndex].executeCmd);
}
}break;
case CT_READ_VALUE://查询命令 AT+<X>? 该命令用于返回参数的当前值
{
atCmd_printf("%s?\r\n", atCommandStructList[listIndex].executeCmd);
}break;
case CT_EXECUTE_CMD://执行命令 AT+<X> 该命令用于读取受GSM 模块内部程序控制的不可变参数
{
atCmd_clearbuffer();
bufferIndex = 0;
atCmd_printf("%s\r\n", atCommandStructList[listIndex].executeCmd);
}break;
case CT_GET_CMDINFOR://命令注释说明
{
}break;
case CT_PRINTF://直接调用打印
{
va_list va;
char tempTab[255];
va_start(va, fmt);
memset(tempTab, 0, sizeof(tempTab));
vsnprintf(tempTab, (int)sizeof(tempTab), fmt, va);
atCmd_console(tempTab);
atCmd_printf(tempTab);
va_end(va);
atCmd_clearbuffer();
bufferIndex = 0;
}break;
default:
break;
}
}
命令响应处理
AT命令执行之后,不管结果如何模组都会返回参数告知
命令执行后可能出现的结果
- 执行成功:比如
设置命令发送之后可能会返回设置操作的结果和OK,或者读取命令返回了想要的数据 - 执行错误:发送命令后有可能出错,比如非法AT命令,非法参数等原因会返回
ERROR或者其他错误响应 - 执行超时:有些操作比较耗时,或者由于其他原因导致操作失败,可以人为计时等待超时,防止流程阻塞太久导致死机。
边栏推荐
猜你喜欢
随机推荐
RecyclerView的高效使用第一节
R language ggplot2 visualization: use the ggboxplot function of the ggpubr package to visualize the grouped box plot, use the ggpar function to change the graphical parameters (caption, add, modify th
TRACE32——C源码关联
Why don't you make a confession during the graduation season?
Ubantu专题5:设置静态ip地址
7、常见面试口语提问问题汇总
复制延迟案例(3)-单调读
AVH Deployment Practice (1) | Deploying the Flying Paddle Model on Arm Virtual Hardware
Qt实战案例(54)——利用QPixmap设计图片透明度
修改SQL语言实现Mysql 多表关联查询优化
Insert into data table to insert data
Deployment application life cycle and Pod health check
RecyclerView高效使用第二节
Excel quickly aligns the middle name of the table (two-word name and three-word name alignment)
Precautions and solutions when SIGABRT error is reported
为什么黑客领域几乎一片男生?
"Autumn Recruitment Series" MySQL Interview Core 25 Questions (with answers)
MySQL数据库操作
TRACE32——常用操作
After Grafana is installed, the web opens and reports an error









