AT指令框架

通信模块:BC28
主控:HC32F176KATA

背景:
在公司接受相应的通讯模块,发现要频繁的使用AT指令,然而公司使用的AT指令都是一条条的写的,需要逐条维护,十分麻烦。借着写nbiot项目时,便自己写了统一的AT指令框架,便于以后对于通讯模块的统一维护。

思路:
结合状态机原理,建立一个二维数组,即表格样式。里面分别存有at指令当前状态,下一状态,发送指令,接受正确应答指令,指令发送后没有应答的超时时间,重发次数,串口状态,以及特殊处理函数。
特殊处理函数是用来处理非单纯应答正确即可的指令的,比如存储设备的imei号码,我把它放在读取到所有应答数据后的下一步执行。

代码:
定义串口的状态,以及要用到的at指令

typedef enum
{
    IDLE = 0,
    SUCCESS_REC,    //成功
	TIME_OUT,       //超时
	NO_REC          //未收到  
} rec_state_t;

typedef enum
{
    AT = 0,               /*发送AT指令测试*/
    CFUN0,
    NCSEARFCN,
    NCONFIG1,
    NCONFIG2,
    NCONFIG3,
    NCONFIG4,
    NBAND,
    NCDP,
    QREGSWT,
    CFUN1,
    NCCID,
    CIMI,
    CGSN,
    CPSMS,   
    // CEDRXS, 
    NPTWEDRXS,
    NRB,
    CEREG,
    CGATT,
    NNMI,
    CSQ,
    QLWULDATA,
    FINISH
} comd_state_e;

定义at指令的状态机对象:

typedef struct 
{
    comd_state_e cur_state;     // 当前状态
    comd_state_e next_state;    // 下一个状态
    char AtSendStr[128];        // 发送字符串(AT命令)
	char ATRecStr[128];         // 需要返回的正确字符串
    int  wait_time;             // 等待时间,单位为ms
    rec_state_t at_status;      // 接收状态
    int  try_cnt;               // 重试次数
    uint8_t (*recv_deal)(char* data, uint8_t len); //动作:需要对某些返回的数据记录或者错误处理

}fsm_state_t;

定义完整的at指令表格,程序基本按照表格里的at指令顺序执行

fsm_state_t ATCmds[] = 
{
    //参数分别为 
    //当前状态     下一个状态   向NB-IOT发送字符串(AT命令)、                     模块应该返回的正确指令、                          设置超时(毫秒)、AT指令接收状态、设置重发次数
	{AT,          CFUN0,       "AT\r\n",                                         "OK",                                             10000, IDLE, 10,default_deal},// 发送at指令,确定模块是否正常
    {CFUN0,       NCSEARFCN,   "AT+CFUN=0\r\n",                                  "OK",                                             5000,  IDLE, 3, default_deal},// 关闭射频功能(不进行无线通讯)
    {NCSEARFCN,   NCONFIG1,    "AT+NCSEARFCN\r\n",                               "OK",                                             300,   IDLE, 3, default_deal},// 清除存储的频点
    {NCONFIG1,    NCONFIG2,    "AT+NCONFIG=CR_0354_0338_SCRAMBLING,TRUE\r\n",    "OK",                                             300,   IDLE, 3, default_deal},// 打开扰码控制
    {NCONFIG2,    NCONFIG3,    "AT+NCONFIG=CR_0859_SI_AVOID,TRUE\r\n",           "OK",                                             300,   IDLE, 3, default_deal},// 打开扰码控制
    {NCONFIG3,    NCONFIG4,    "AT+NCONFIG=AUTOCONNECT,TRUE\r\n",                "OK",                                             300,   IDLE, 3, default_deal},// 配置模块自动连接网络
    {NCONFIG4,    NBAND,       "AT+NCONFIG=CELL_RESELECTION,TRUE\r\n",           "OK",                                             300,   IDLE, 3, default_deal},// 小区重选
    {NBAND,       NCDP,        "AT+NBAND=5\r\n",                                 "OK",                                             300,   IDLE, 3, default_deal},// 设置频段为电信的频段
    {NCDP,        QREGSWT,     "AT+NCDP=221.229.214.202,5683\r\n",               "OK",                                             300,   IDLE, 3, default_deal},// 云平台接入ip地址及端口设置
    {QREGSWT,     CFUN1,       "AT+QREGSWT=1\r\n",                               "OK",                                             300,   IDLE, 3, default_deal},// 设置为1,模块在重启并连接到网络后会触发自动注册物联网平台
    {CFUN1,       NCCID,       "AT+CFUN=1\r\n",                                  "OK",                                             5000,  IDLE, 10, default_deal},// 开启射频功能
    {NCCID,       CIMI,        "AT+NCCID\r\n",                                   "OK",                                             300,   IDLE, 3, default_deal},// 确认sim卡是否存在
    {CIMI,        CGSN,        "AT+CIMI\r\n",                                    "OK",                                             300,   IDLE, 3, default_deal},// 返回 IMSI 码 
    {CGSN,        CPSMS,       "AT+CGSN=1\r\n",                                  "\r\n+CGSN:",                                     300,   IDLE, 3, default_deal},// 返回 IMEI 码
    {CPSMS,       NPTWEDRXS,   "AT+CPSMS=0\r\n",                                 "OK",                                             300,   IDLE, 3, default_deal},// PSM模式设置
    // {CEDRXS,      NRB,         "AT+CEDRXS=0,5\r\n",                              "OK",                                             300,   IDLE, 3, default_deal},// eDRX模式设置
    {NPTWEDRXS,   NRB,         "AT+NPTWEDRXS=3,5\r\n",                           "OK",                                             300,   IDLE, 3, default_deal},// eDRX模式设置   
    {NRB,         CEREG,       "AT+NRB\r\n",                                     "+QLWEVTIND:3",                                   60000, IDLE, 5, default_deal},// 模块重启
    {CEREG,       CGATT,       "AT+CEREG?\r\n",                                  "\r\n+CEREG:0,1",                                 5000,  IDLE, 10,default_deal},// 查询网络注册状态
    {CGATT,       NNMI,        "AT+CGATT=1\r\n",                                 "OK",                                             300,   IDLE, 3, default_deal},// 使能网络附着
    {NNMI,        CSQ,         "AT+NNMI=1\r\n",                                  "OK",                                             300,   IDLE, 3, default_deal},// 接收到一个下行消息后会发送新消息指示
    {CSQ,         FINISH,      "AT+CSQ\r\n",                                     "\r\n+CSQ:",                                      300,   IDLE, 3, CSQ_deal},// 查询信号强度
    {QLWULDATA,   FINISH,      "AT+QLWULDATA=",                                  "OK",                                             1000,   IDLE, 3, QLWULDATA_deal},// 发送数据
	
};

接下来是AT指令的接收,发送函数:

fsm_state_t cur = {0,0,0,0,0,0,0,0}; // 相当于一个游动指针,表示当前状态,执行完就更新

static void At_send(fsm_state_t cmd)
{
    if(cur.at_status == IDLE)
    {
        Uart_NB.Uart_SendString(cmd.AtSendStr,strlen(cmd.AtSendStr));
        at_recv_time = cmd.wait_time;
    }
}


static void At_recv(fsm_state_t *cmd)
{
    uint8_t i;

    if(cur.try_cnt == 0) //发送次数用完处理
    {
        // 暂定初始化重来
        cur.at_status = NO_REC;
    }
    
    
    // 尚有发送次数时
    else 
    {
        if(at_recv_time > 0)  // 接收时间未超时
        {
            cur.at_status = NO_REC;   // 没收到数据

            //while( (Uart_NB.rx_flag != 1) && (at_recv_time > 0) );
            if(Uart_NB.rx_flag == 1) // 规定时间内nbiot串口接收到数据
            {
                Uart_NB.rx_flag = 0;
                memset(atbuff, 0, sizeof(atbuff));// 清空at指令接收缓存
                for(i=0; i<Uart_NB.rx_cnt;i++)   // 接收到的数据复制到缓存
                {
                    atbuff[i] = U1_RxBuffer[i];
                }
                                                                
                if( strstr(atbuff, cmd->ATRecStr ) != NULL)
                {
                    cur.at_status = SUCCESS_REC;  // 接收状态赋值为成功
                    cmd->recv_deal(atbuff, Uart_NB.rx_cnt);   // 接收nb模块数据处理
                    // 接收到数据后,更新当前执行状态机状态
                    cur.cur_state = cur.next_state;
                    cur.next_state = ATCmds[cur.cur_state].next_state;
                    cur.try_cnt = ATCmds[cur.cur_state].try_cnt;
                    cur.at_status = ATCmds[cur.cur_state].at_status;
                }
                    Uart_NB.rx_cnt = 0;   //串口接收缓存清零
            }
        }
        
        else if(at_recv_time == 0)   // 超时处理
        {
            cur.try_cnt--;
            cur.at_status = IDLE;
        }            
    }
    
}

封装相应的初始化函数和任务函数,后续注册给相应的通讯模块,放进主循环即可:

void AT_init()
{
    cur.cur_state = ATCmds[AT].cur_state;
    cur.next_state = ATCmds[AT].next_state;
    cur.try_cnt = ATCmds[AT].try_cnt;
}



void At_task()
{
    if(cur.cur_state != FINISH )
    {
       At_send(ATCmds[cur.cur_state]);
       At_recv(&ATCmds[cur.cur_state]);
    }
     ZD_NB_transfer();
}
  • 15
    点赞
  • 91
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值