AT命令框架(裸机)

本篇文章于 2022-8-31 从notion迁移到hugo

本篇文章主要是参考了韦东山老师的MQTT课程和CSDN大佬得到框架后得到的思路下开始写的。相关的链接我会附在文末。

背景

之前做了一个项目,用到了移远的EC200S模块,机器的自检、数据的发送都是通过命令来设置的。刚开始觉得这种模式挺好的,但是到了应用的时候才发现这种一问一答的形式其实有点难用到单片机上面。当然,之前都是采用一条一条命令的方式来发送,用起来也算能够实现想要的需求。最近又要节接触到这个模块了,但是我不想再像之前那样通过一来一回这样的模式来操作这个模块了,所以我想着能不能有一套AT命令解析的框架,能够把AT命令的发送解析做成一个模块化的东西,这样子使用起来也就很方便了,所以有了这篇文章的内容。

简述AT指令工作模式

这里我只使用过EC200S模块,就拿它来说说我的理解。

AT指令的工作方式就像是一问一答,你需要查询什么或者设置什么,你就向模块中发送相应的命令,模块在收到命令之后会返回相应的结果给你。以下是我的以下调试信息,从中应该也能看出它的工作模式。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
[2022-08-12 20:46:13.354]
TXAT                       //查询模块就绪状态

[2022-08-12 20:46:14.366]
RX
OK                          //返回OK

[2022-08-12 20:46:17.622]
TXATE0                    //取消模块的回显

[2022-08-12 20:46:18.635]
RX
OK

[2022-08-12 20:46:24.960]
TXAT+CPIN?                //查询卡状态

[2022-08-12 20:46:25.980]
RX
+CPIN: READY

OK

[2022-08-12 20:46:44.667]
TXAT+CIMI

[2022-08-12 20:46:45.678]
RX
460081249306781

OK

[2022-08-12 [20:48:24.043]
TXAT+CSQ                     //查询信号质量

[2022-08-12 20:48:25.052]
RX
+CSQ: 15,99

OK](https://www.notion.so/AT-92a8ca820b3a4d48bfe93298d089eed9)

裸机的操作模式

刚开始接触AT指令的时候,我主要是把它用在逻辑程序上面,而实现的方式呢?无非就是使用串口发送一条命令之后就在那里一直等着这条命令的结果返回,然后判断收到的字符中有没有期待的返回值,如果没有的话就是说这条指令有问题了。大概流程就是这样子。

1
2
3
4
5
Uart2_SendStr("AT\r\n");
delay_ms(300);
printf("rec:");
strx = strstr((const char *)buf_uart2.buf, (const char *)"OK"); //返回OK
Clear_Buffer();

这就是一问一答的方式了,用起来好像也比较方便,但是,如果你要发很多条命令怎么办?那你就要重复上面的代码很多遍,这样子实在是太麻烦了,所以把它做成模块化就比较重要了。

裸机模块化

怎么模块化呢?

我们可以把命令和期待得到的返回值都传入到一个函数里面,然后在里面执行上面的才做就可以了。而下面的代码我为了防止数据丢失,使用了环形缓冲区来接收串口的数据,然后在解析AT命令的时候,再把收到的数据从环形缓冲区中拿回来。具体怎么实现的就看代码的注释吧,这里不罗嗦了。关于环形缓冲区这个,我后面看看有没有时间,如果有的化就写一篇相关的笔记吧。

这部分代码主要是参考了这位博主的,非常感谢=⇒(15条消息) STM32–ESP8266–AT指令使用例程_liefyuan的博客-CSDN博客_esp8266 stm32 例程

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**
 *
 * [EC200S_SendCmd 发送命令到EC200S]
 * @param  cmd   [需要发送的AT指令]
 * @param  reply [期望模块回显的内容]
 * @param  wait  [等待的时间(ms)]
 * @return       [期望内容等于实际回显内容返回1,否则0]
 *
 */
int EC200S_SendCmd(char *cmd, char *reply, int wait)
{
		/*初始化环形缓冲区,准备接收串口数据*/
    struct_usart2.USART_Length = 0;
    init_buff();

    printf("[EC200S_SendCmd] %s\r\n", cmd);

    uart2_send_buff(cmd);               //发送AT命令

    delay_ms(wait);                     //等待数据返回
		
		/*将数据从环形缓冲区取出,放入struct_usart2.USART_BUFF中,这里其实就是一个处理的数组,随便用个数组就可以*/
    struct_usart2.USART_Length = ringbuff.lenth;
    move_data(struct_usart2.USART_BUFF);

		/*解析返回值是否和期待的相同*/
		//如果期待的返回值为空,直接返回
    if (strcmp(reply, "") == 0)
    {
        return 0;
    }

    if (struct_usart2.USART_Length != 0)
    {
        struct_usart2.USART_BUFF[struct_usart2.USART_Length] = '\0';   //添加字符串结束符号

				//如果和期待返回值一致,返回成功
        if (strstr((char *)struct_usart2.USART_BUFF, reply))
        {
            printf("\r\n%s+++YES\r\n", struct_usart2.USART_BUFF);

            return 1;
        }
        else
        {
            printf("\r\n%s+++NO\r\n", struct_usart2.USART_BUFF);

            return 0;
        }
    }
}

这里补充一点,上面代码用到了一个函数 strstr 这个函数是C语言中的字符串函数,作用是在一个母串中找到子串第一次出现的位置,返回值是子串出现的地址。所以有了这个函数我们就可以非常方便查看串口接收到的数据中有没有我们期待的返回值了。

裸机处理AT指令的弊端

上面的代码用起来其实还是蛮可以的,但是有些地方是很鸡肋的。

  1. 命令等待采用死等的方式,处理方式不灵活
  2. 对于被动接受类数据不友好,响应速度不够

弊端一

首先,先说第一个问题。从上面的代码中我们可以发现,我们发送AT指令之后,就采用一种死等的方式在等待串口的返回数据,等它接收完成我们再去处理这些数据。好,那么数据什么时候接收完成呢?我们不知道呀!如果数据还没有接收完全,我们就把数据搬过来判断,那么很大可能这条命令是没有正确地解析的,就会造成出错。所以只能延时一个相对比较长的时间,或者添加一种机制,让他没有得到想要的返回值时就重复发这条命令。

弊端二

那么,很显然,这种处理方式是有一点不好的,虽然我们可以使用,但是总感觉不能真正解决问题。

再来说第二个存在的问题。我们上面讲过有一种数据是被动接收的,就是服务器下发下来的数据。那么,对于这种数据,我们甚至都不知道它什么时候要来,那我们怎么处理呢?总不能说让整颗芯片都在等你发数据过来吧,那这个单片机就什么都干不了了,只能在这等你的数据了。

但是,也不是说没有解决的办法。我们服务器下发的数据是会带有前缀的,就比如下面这条

1
+QMTRECV: 0,0,"smart_parking","ab"

他会带上**+QMTRECV** 这个前缀,所以我们可以再串口接收中断中每收到一个字节之后判断一下我收到的数据中有没有这个前缀,如果有的活我就调用处理的函数去处理它,没有的话我就继续收数据。

但是,这种方法也是有缺陷的,是什么呢?当你再中断中去判断有没有这个前缀的时候,串口还是会有源源不断的数据过来的,而这时,你把CPU用在了判断有没有前缀这件事情上,那么这个时候到来的数据就会丢失掉。当然,你也可以等他全部接收完之后再去判断,但是问题就在于你也不知道它什么时候接收完,所以这时候你可以把空闲中断用上来,具体的就不细说了,就讲到这。

总之,在我看来啊,裸机处理AT指令其实是不太方便的,在我的使用过程中就经常出现丢失数据的现象,当然,那时的我对单片机和AT指令都不是很熟悉,也许让我再写一份会好很多。不过我现在不喜欢使用裸机的方式来操作AT指令了,而是用RTOS,至于这个,我另作一篇文章吧。这篇文章就到这里。

参考内容

0%