OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 4991|回复: 0

单片机裸机环境下编写AT指令程序

[复制链接]

17

主题

56

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
394
金钱
394
注册时间
2015-1-7
在线时间
101 小时
发表于 2021-1-24 16:57:27 | 显示全部楼层 |阅读模式
本帖最后由 zhang062061 于 2021-1-24 17:00 编辑

1.写在前面

    AT指令在各种WIFI模块、2G/4G模块以及一些无线通讯模块中应用广泛。但是用过的朋友都知道,这种方式对于单片机编程来说,并不友好……本篇文章将以ESP8266 WIFI模块为例介绍在单片机裸机环境下编写AT指令程序的一种方式。

2.程序设计

    首先串口底层的收发程序不在这里详细介绍。接收程序一般采用中断方式,采用超时判断的方式判断帧结束。

先简单介绍一个概念:状态机,状态转移图。对于程序来说,就是将程序分为几个状态,不同状态执行不同程序,判断条件进行状态转移。具体到C语言程序中,就是switch-case语句。

       以ESP8266 WIFI模块的AT指令程序为例,将它的状态分为以下几种:

    1.准备发送AT指令

    2.发送AT指令

    3.等待接收回复数据

    4.接收成功

    5.接收超时

将几种状态定义成一个枚举类型数据:

  1. typedef enum
  2. {
  3.     ATCMD_START                    = 0x00U,
  4.     ATCMD_SEND                    = 0x01U,
  5.     ATCMD_WAIT_REV            = 0x02U,
  6.     ATCMD_REVOK                          = 0x03U,
  7.     ATCMD_TIMEOUT                 = 0x04U
  8. } ATCMD_StatusTypeDef;
复制代码

接下来用switch-case语句编写各个状态的程序,如下所示。

  1. //------------------发送AT指令-----------------
  2. //参数1:huart 串口号
  3. //参数2:cmd AT指令内容
  4. //参数3:timeout 单次发送的超时时间
  5. //参数4:res 要判断的返回结果
  6. //参数5:count 尝试次数
  7. //返回值:ATCMD_REVOK 发送且收到回复  ATCMD_TIMEOUT 超时
  8. ATCMD_StatusTypeDef AT_CMD_ESP12(UART_HandleTypeDef *huart,uint8_t* cmd,uint16_t timeout,const char* res,uint8_t count)
  9. {
  10.     static ATCMD_StatusTypeDef atcmd_status=ATCMD_START;
  11.     static uint8_t cnt=1;
  12.     uint16_t temp;
  13.     switch(atcmd_status)
  14.     {
  15.     case ATCMD_START:
  16.         cnt = count;
  17.         atcmd_status=ATCMD_SEND;
  18.         break;
  19.     case ATCMD_SEND:
  20.         Clr_RxBuf();//清除串口接收缓存
  21.         HAL_UART_Transmit_IT(huart,cmd,strlen((const char*)cmd));
  22.         atcmd_status=ATCMD_WAIT_REV;
  23.         Esp12cmd_tick=0;
  24.         break;
  25.     case ATCMD_WAIT_REV:
  26.         if(Esp12cmd_tick < timeout)
  27.         {
  28.             if(Hand((char*)res,&temp,0))//
  29.             {
  30.                 atcmd_status=ATCMD_REVOK;
  31.             }
  32.         }
  33.         else
  34.         {
  35.             if(cnt>0)//再次发送
  36.                 atcmd_status=ATCMD_SEND;
  37.             else
  38.                 atcmd_status=ATCMD_TIMEOUT;
  39.             cnt--;
  40.         }
  41.         break;
  42.     case ATCMD_REVOK:
  43.     case ATCMD_TIMEOUT:
  44.         atcmd_status=ATCMD_START;
  45.     default:
  46.         break;
  47.     }
  48.     return atcmd_status;
  49. }
复制代码

程序逻辑很简单,其中中Esp12cmd_tick变量为毫秒计数器,在SysTick_Handler中断中进行+1操作。

Hand()为判断串口接收数据中是否包含指定字符串的函数,如下:

  1. //判断串口接收缓存中是否包含substr
  2. //参数1 *substr 被判断的字符串
  3. //参数2 *index substr在串口缓存中的位置
  4. //参数3 start_index串口缓存起始判断位置
  5. //返回值  包含返回1  不包含返回0
  6. uint8_t Hand(char* substr,uint16_t *index,uint16_t start_index)
  7. {
  8.     uint16_t i,flag=0;
  9.     char *p;
  10.   if(Uart5.RxFlag != 1)return 0;//一帧数据未接收完成,直接返回
  11.   Uart5.RxFlag = 0;
  12.     for(i=start_index; i<RX_LEN; i++)
  13.     {
  14.         if(Uart5.RxBuf[i]==*substr)
  15.         {
  16.             flag=1;
  17.             p=substr;
  18.             while(*p)
  19.             {
  20.                 if(Uart5.RxBuf[i]==*p)
  21.                 {
  22.                     *p++;
  23.                     i++;
  24.                 }
  25.                 else
  26.                 {
  27.                     flag=0;
  28.                     break;
  29.                 }
  30.             }
  31.             if(flag==1)
  32.             {
  33.                 *index = i;
  34.                 break;
  35.             }
  36.         }
  37.     }
  38.     return flag;
  39. }
复制代码

调用方式

一般设置时都需要多条AT指令,也采用状态机的方式进行设置。以设置WIFI模块为AP模式为例,程序如下:

  1. //-------------------设置为AP模式------------
  2. //设置的SSID  密码 和本机IP
  3. //成功返回0  返回1表示错误  返回2正在设置
  4. uint8_t SetAPMode(UART_HandleTypeDef *huart)
  5. {
  6.   static uint8_t status=0;//
  7.   static ATCMD_StatusTypeDef res;
  8.   
  9.   switch(status)
  10.   {
  11.     case 0:
  12.       res = AT_CMD_ESP12(huart,(uint8_t*)"AT+CWMODE_DEF=2\r\n",1000,"OK",3);//设置为AP模式
  13.       if(res==ATCMD_REVOK)
  14.         status=1;
  15.       else if(res==ATCMD_TIMEOUT)
  16.       {
  17.         status=0;
  18.         return 1;
  19.       }
  20.       break;
  21.     case 1:
  22.       res = AT_CMD_ESP12(huart,(uint8_t*)"AT+CWSAP_DEF="ESP8266","1234567890",5,3\r\n",1000,"OK",3);//设置AP
  23.       if(res==ATCMD_REVOK)
  24.         status=2;
  25.       else if(res==ATCMD_TIMEOUT)
  26.       {
  27.         status=0;
  28.         return 1;
  29.       }
  30.       break;
  31.     case 2:
  32.       res = AT_CMD_ESP12(huart,(uint8_t*)"AT+CIPAP_DEF="192.168.5.1","192.168.5.1","255.255.255.0"\r\n",1000,"OK",3);//设置IP
  33.       if(res==ATCMD_REVOK)
  34.         status=3;
  35.       else if(res==ATCMD_TIMEOUT)
  36.       {
  37.         status=0;
  38.         return 1;
  39.       }
  40.       break;
  41.     case 3://设置UDP
  42.       res = AT_CMD_ESP12(huart,(uint8_t*)"AT+CIPSTART="UDP","192.168.5.255",8899,8266,0\r\n",1000,"OK",3);
  43.       if(res==ATCMD_REVOK)
  44.         status=4;
  45.       else if(res==ATCMD_TIMEOUT)
  46.       {
  47.         status=0;
  48.         return 1;
  49.       }
  50.       break;
  51.     case 4://设置完成
  52.       status=0;
  53.       return 0;  
  54.     default:
  55.       break;
  56.   }
  57.   return 2;  
  58. }
复制代码

上述设置程序中,AT指令接收错误后的操作是返回执行第一条指令,当然也可以进行一些其它操作,比如多次接收错误后模块重新复位等。上述设置程序也可以是WIFI模块主程序的一个状态,WIFI主程序如下。

  1. void ESP12_Task(UART_HandleTypeDef *huart)
  2. {
  3.     switch(WIFI_Status)
  4.     {
  5.     case 0://设置为AP模式
  6.                                 if(SetAPMode(huart) == 0)//设置成功,转到等待接收状态
  7.                                 {
  8.                                         WIFI_Status=1;
  9.                                         }
  10.         break;
  11.     case 1://等待接收数据
  12.                                 if(Uart5.RxFlag == 1)
  13.                                 {
  14.                                         Uart5.RxFlag = 0;
  15.                                         ESP12_Rev(huart);
  16.                                         WIFI_Status = 2;
  17.                                 }
  18.         break;
  19.                 case 2:
  20.                         if(Send_Data(huart,Uart5.TxBuf,Uart5.TxNum)<2)
  21.                         {
  22.                                 Uart5.TxFlag = 0;
  23.                                 WIFI_Status = 1;
  24.                         }
  25.                         break;
  26.     default:
  27.         break;
  28.     }
  29. }
复制代码

该函数在程序主循环中周期循环调用即可。可以完成AT指令的发送,等待的操作,也不影响其它程序的执行。


3.总结

本篇文章其实主要介绍了状态机的概念,层层调用。理解起来并不困难,实际编程中非常实用。可以广泛应用于其它程序的编写


欢迎关注公众号"嵌入式技术开发",大家可以后台给我留言沟通交流。如果觉得该公众号对你有所帮助,也欢迎推荐分享给其他人。


https://shop72315368.taobao.com
承接软硬件项目开发
微信搜索 “嵌入式技术开发”,关注我的公众号
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-25 09:17

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表