软件实现AT命令操作过程

述说

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封装

  1. 测试命令:该命令只需要在执行命令的基础上加上=?\r\n即可
  2. 设置命令:由于设置命令会带有不定参数,所以该命令的组成可以通过格式化函数vsnprintf和可变传参来实现
  3. 读取命令:该命令只需要在执行命令的基础上加上?\r\n即可
  4. 执行命令:该命令直接通过序号调用数组列表中的定义即可
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命令执行之后,不管结果如何模组都会返回参数告知

命令执行后可能出现的结果

  1. 执行成功:比如设置命令发送之后可能会返回设置操作的结果和OK,或者读取命令返回了想要的数据
  2. 执行错误:发送命令后有可能出错,比如非法AT命令,非法参数等原因会返回ERROR或者其他错误响应
  3. 执行超时:有些操作比较耗时,或者由于其他原因导致操作失败,可以人为计时等待超时,防止流程阻塞太久导致死机。
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值