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

1.写在前面

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

2.程序设计

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

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

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

    1.准备发送AT指令

    2.发送AT指令

    3.等待接收回复数据

    4.接收成功

    5.接收超时

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

typedef enum
{
    ATCMD_START    		= 0x00U,
    ATCMD_SEND    		= 0x01U,
    ATCMD_WAIT_REV    	= 0x02U,
    ATCMD_REVOK  			= 0x03U,
    ATCMD_TIMEOUT 		= 0x04U
} ATCMD_StatusTypeDef;

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

//------------------发送AT指令-----------------
//参数1:huart 串口号
//参数2:cmd AT指令内容
//参数3:timeout 单次发送的超时时间
//参数4:res 要判断的返回结果
//参数5:count 尝试次数
//返回值:ATCMD_REVOK 发送且收到回复  ATCMD_TIMEOUT 超时
ATCMD_StatusTypeDef AT_CMD_ESP12(UART_HandleTypeDef *huart,uint8_t* cmd,uint16_t timeout,const char* res,uint8_t count)
{
    static ATCMD_StatusTypeDef atcmd_status=ATCMD_START;
    static uint8_t cnt=1;
    uint16_t temp;
    switch(atcmd_status)
    {
    case ATCMD_START:
        cnt = count;
        atcmd_status=ATCMD_SEND;
        break;
    case ATCMD_SEND:
        Clr_RxBuf();//清除串口接收缓存
        HAL_UART_Transmit_IT(huart,cmd,strlen((const char*)cmd));
        atcmd_status=ATCMD_WAIT_REV;
        Esp12cmd_tick=0;
        break;
    case ATCMD_WAIT_REV:
        if(Esp12cmd_tick < timeout)
        {
            if(Hand((char*)res,&temp,0))//
            {
                atcmd_status=ATCMD_REVOK;
            }
        }
        else
        {
            if(cnt>0)//再次发送
                atcmd_status=ATCMD_SEND;
            else
                atcmd_status=ATCMD_TIMEOUT;
            cnt--;
        }
        break;
    case ATCMD_REVOK:
    case ATCMD_TIMEOUT:
        atcmd_status=ATCMD_START;
    default:
        break;
    }
    return atcmd_status;
}

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

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

//判断串口接收缓存中是否包含substr
//参数1 *substr 被判断的字符串
//参数2 *index substr在串口缓存中的位置
//参数3 start_index串口缓存起始判断位置
//返回值  包含返回1  不包含返回0
uint8_t Hand(char* substr,uint16_t *index,uint16_t start_index)
{
    uint16_t i,flag=0;
    char *p;
  if(Uart5.RxFlag != 1)return 0;//一帧数据未接收完成,直接返回
  Uart5.RxFlag = 0;
    for(i=start_index; i<RX_LEN; i++)
    {
        if(Uart5.RxBuf[i]==*substr)
        {
            flag=1;
            p=substr;
            while(*p)
            {
                if(Uart5.RxBuf[i]==*p)
                {
                    *p++;
                    i++;
                }
                else
                {
                    flag=0;
                    break;
                }
            }
            if(flag==1)
            {
                *index = i;
                break;
            }
        }
    }
    return flag;
}

调用方式

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

//-------------------设置为AP模式------------
//设置的SSID  密码 和本机IP
//成功返回0  返回1表示错误  返回2正在设置
uint8_t SetAPMode(UART_HandleTypeDef *huart)
{
  static uint8_t status=0;//
  static ATCMD_StatusTypeDef res;
  
  switch(status)
  {
    case 0:
      res = AT_CMD_ESP12(huart,(uint8_t*)"AT+CWMODE_DEF=2\r\n",1000,"OK",3);//设置为AP模式
      if(res==ATCMD_REVOK)
        status=1;
      else if(res==ATCMD_TIMEOUT)
      {
        status=0;
        return 1;
      }
      break;
    case 1:
      res = AT_CMD_ESP12(huart,(uint8_t*)"AT+CWSAP_DEF=\"ESP8266\",\"1234567890\",5,3\r\n",1000,"OK",3);//设置AP 
      if(res==ATCMD_REVOK)
        status=2;
      else if(res==ATCMD_TIMEOUT)
      {
        status=0;
        return 1;
      }
      break;
    case 2:
      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 
      if(res==ATCMD_REVOK)
        status=3;
      else if(res==ATCMD_TIMEOUT)
      {
        status=0;
        return 1;
      }
      break;
    case 3://设置UDP
      res = AT_CMD_ESP12(huart,(uint8_t*)"AT+CIPSTART=\"UDP\",\"192.168.5.255\",8899,8266,0\r\n",1000,"OK",3);
      if(res==ATCMD_REVOK)
        status=4;
      else if(res==ATCMD_TIMEOUT)
      {
        status=0;
        return 1;
      }
      break;
    case 4://设置完成
      status=0;
      return 0;  
    default:
      break;
  }
  return 2;  
}

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


void ESP12_Task(UART_HandleTypeDef *huart)
{
    switch(WIFI_Status)
    {
    case 0://设置为AP模式
      if(SetAPMode(huart) == 0)//设置成功,转到等待接收状态
      {
        WIFI_Status=1;
      }
      break;
    case 1://等待接收数据
      if(Uart5.RxFlag == 1)
      {
        Uart5.RxFlag = 0;
        ESP12_Rev(huart);
        WIFI_Status = 2;
      }
      break;
    case 2:
      if(Send_Data(huart,Uart5.TxBuf,Uart5.TxNum)<2)//发送数据
      {
        Uart5.TxFlag = 0;
        WIFI_Status = 1;
      }
      break;
    default:
      break;
    }
}

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

3.总结

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

 

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

图片

  • 8
    点赞
  • 54
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值