AT指令框架(C语言)

时间:2024-03-08 12:57:49

本文档记录设计的AT指令框架,使用时只需要简单增加修改指令和执行指令函数

一、指令结构

typedef enum{
    AT_CMD_TEST = 0,     /* 测试指令 */
    AT_CMD_NAME,         /* 设置名称 */
    AT_CMD_DISA,         /* 断开当前连接 */
    AT_CMD_TX_POWER,     /* 设置发送功率 */
    AT_CMD_ADV_INTERVAL, /* 设置广播间隔 */
    AT_CMD_CON_INTERVAL, /* 设置连接间隔 */
    AT_CMD_ADV_ONOFF,    /* 设置打开、关闭广播 */
    AT_GET_VER,          /* 获取版本号 */
    AT_GET_CON_STATE,    /* 获取连接状态 */
    AT_CMD_UART_CFG,     /* 配置UART参数 */
    
    AT_END
}AT_Cmd;

typedef unsigned char (*pFunc)(unsigned char *ptr, unsigned char len);

typedef struct {
    AT_Cmd cmd;          /* 指令序号 */
    unsigned char *str;  /* 指令内容 */
    pFunc  cb;           /* 指令执行 */
}AT_cmd_func;

/* AT指令表 */
const AT_cmd_func at_cmd_func[] = {
    {AT_CMD_TEST,         "AT",                at_cmd_test},
    {AT_CMD_NAME,         "AT+NAME=",          at_cmd_name},
    {AT_CMD_DISA,         "AT+DISC",           NULL},
    {AT_CMD_TX_POWER,     "AT+TX=",            NULL},
    {AT_CMD_ADV_INTERVAL, "AT+ADV_INTERVAL=",  NULL},
    {AT_CMD_CON_INTERVAL, "AT+CON_INTERVAL=",  NULL},
    {AT_CMD_ADV_ONOFF,    "AT_ADV=",           NULL},
    {AT_GET_VER,          "AT+VERSION=",       NULL},
    {AT_GET_CON_STATE,    "AT+STATE",          NULL},
    {AT_CMD_UART_CFG,     "AT+UART_CFG=",      NULL},
    {AT_END,              NULL,                NULL}
};

指令执行函数

/* 指令执行函数 */
unsigned char at_cmd_test(unsigned char *p, unsigned char len){
    
    AT_DEBUG_INFO("AT+OK\r\n");
    return 0;
}

unsigned char at_cmd_name(unsigned char *p, unsigned char len){
    
    if(*p == \'?\'){
        
        AT_DEBUG_INFO("AT+OK BLE-NAME\r\n");
    }else{
    
        AT_DEBUG_INFO("AT+OK\r\n");
    }
    return 0;
}

 

二、指令解析 

/* 查找指令表中对应的指令 */
unsigned char AT_cmd_search(unsigned char *p, unsigned char len){
    
    unsigned char ret = 0;
    unsigned char *pstr;
    unsigned char i, n;
    
    for(i=1; at_cmd_func[i].cmd != AT_END; i++){    
        
        n = mstrlen(at_cmd_func[i].str);
        if(!mstrncmp(p, at_cmd_func[i].str, n)){
            ret = i;
            break;
        }        
    }
    
    return ret;
}

/* AT指令解析 */
unsigned char at_cmd_parse(unsigned char *p, unsigned char len){
    
    unsigned char ret = AT_SUCCESS;
    unsigned char index = 0;
    
    if(len < 4) return AT_ERR; /* 不符合指令最小长度 */
    
    if((p[0] == \'A\') && (p[1] == \'T\') && (p[len-2] == 0x0D) && (p[len-1] == 0x0A)){
        if(len == 4){  /* 测试指令 */
        
            if(at_cmd_func[AT_CMD_TEST].cb != NULL)  
                at_cmd_func[AT_CMD_TEST].cb(NULL, 0);  /* 执行测试指令 */
        }else if(p[2] == \'+\'){ /* 执行指令解析 */
            
            index = AT_cmd_search(p, len); /* 查找匹配的执行指令,0-已匹配,!0-未匹配 */
            if(index){
                if(at_cmd_func[index].cb != NULL){  /* 判断指令对应执行函数是否存在 */
                    unsigned char n;
                    n = mstrlen(at_cmd_func[index].str);
                    ret = at_cmd_func[index].cb(p+n, len-n); /* 执行对应的指令函数, p+n:将指令参数传输执行函数,len-n-2:指令参数有效长度 */
                }else
                    ret = AT_ERR_FUN_UNUSED; /* 没有可执行函数 */
            }else{
                ret = AT_ERR_UNINVAIL; /* 未找到匹配的指令 */
            }
        }
    }else{/* 格式不匹配 */
        
        return AT_ERR;
    }
    
    return ret;
}

 

三、指令测试

/* 测试 */
int main(void){

    unsigned char ret;
    unsigned char i, n, m;
    #define CMD_NUM_MAX (5)    
    char *test_cmd[CMD_NUM_MAX]={
        "AT\r\n",
        "AT+NAME=BLE-TEST\r\n",
        "AT+NAME=?\r\n",
        "AT+DISC\r\n",
        "AT+NBME=?\r\n",
    };
    
    for(i=0; i<CMD_NUM_MAX; i++){
        m = mstrlen(test_cmd[i]);
        ret = at_cmd_parse(test_cmd[i], m);
        if(ret)
            AT_DEBUG_INFO("AT ERR! = %d\r\n", ret);
    }
    
    return 0;
}

结果如下

 

指令解析后返回数据定义

#define AT_SUCCESS         (0) /* 指令正常 */
#define AT_ERR             (1) /* 指令异常 */
#define AT_ERR_UNINVAIL    (2) /* 没有对应指令 */
#define AT_ERR_FUN_UNUSED  (3) /* 没有可执行函数 */

/* 返回值参数内容如上, p-指向解析的指令,len-解析指令长度 */
unsigned char at_cmd_parse(unsigned char *p, unsigned char len);

 

 

注意:上面代码中使用了2个类C库函数 mstrlen, mstrncmp (与C库中strlen, strncmp功能一致),主要是为了移植是脱离平台,编译时使用代码占用空间尽量少

unsigned int mstrlen(const char *s){
    
    const char *ss = s;
    
    while (*ss)
        ss++;
    
    return ss - s;
}

int mstrncmp(const char *s1, const char *s2, int n){
    
    const unsigned char *c1 = (const unsigned char *)s1;
    const unsigned char *c2 = (const unsigned char *)s2;
    unsigned char ch;
    int d = 0;

    while (n--) {
        d = (int)(ch = *c1++) - (int)*c2++;
        if (d || !ch)
            break;
    }

    return d;
}