簡易AT指令解析器創建與分析

  1. 動機:

為什麼會想分享簡易AT指令解析器(AT Command Parser)? 本人大多都是使用AT指令進行功能控制,從沒自訂過AT指令,因此遇到需要創建自家AT指令時,卻不知從何創起,因此經過一番研究後發現,創建自家AT指令時必須要有一套AT指令解析器;雖然知道AT指令解析器只需簡單的字串字元進行比較即可完成,但很多人還是不知從何下手;因此本篇將介紹簡易AT指令解析器給大家,讓有需要的人能夠快速上手。

 

  1. AT指令解析器概念與流程圖

使用過AT指令的人都知道,每個廠商的模塊不一樣,實現的功能也不一樣,因此每個模塊廠商都有自己的一套自訂的AT指令集,所以每個模塊廠商實現的AT指令解析器也不一樣,因此接下來要分享給大家如何創建簡易AT指令解析器。

 何謂AT指令解析器? 簡單的來說,AT指令解析器是透過接收串口(UART)傳來的AT指令,進行解析,依據解析的結果至AT指令表中進行尋找相對應的字串選項,若找到該選項,則執行該選項對應的函式,函式處理完以後回傳其對應的返回數據到串口(UART),可參考(圖3)讓看官們更直觀瞭解簡易AT指令解析器流程。

 

 

 

圖1

圖2

 

 

 

 

圖3 (簡易AT指令解析器流程)

 

 

  1. 簡易AT指令解析器程式碼與說明

由於我只講解簡易AT指令解析器,因此UART部分的資料傳輸需要由看官們依據自己的晶片進行調整。

本節我們將利用基本C語言來創建一個簡易的AT指令解析器。

 

  1. 3.1 首先創建一個at_cmd.h來放AT指令結構體

struct at_cmd_struct{

           char *cmd;                                                          AT指令的名字

           void (*fn)(char *args);                                         AT指令的功能函式

           struct at_cmd_struct *next;                                 指向下一個AT指令

};

程式碼1 (結構體)

 

 

 

 

 

 

 

 

3.2 再創一個aitg_at_cmd.c檔案來放所需的AT指令以及相對應功能函式

void AT_help(char *arg){                                                                    //顯示所有AT指令

    struct at_cmd_struct *cmd = Command_Line.cmds;                     //獲取目前所有AT指令列表

    do {

        AITg_AT_Uart_printf(cmd->cmd);                                              //列印當前的AT指令

        AITg_AT_Uart_printf("\n");                                                  

        cmd = cmd->next;                                                                      //指向下個AT指令

    } while (cmd);                                                                             

}

void AT_ATTEST(char *Args){                                                            //測試AT指令是否正常

        AITg_AT_Uart_printf("OK\n");                                                    //透過UART回傳OK

}

void AT_GetVer(char *Args){                                                             //獲取版本號

           char strTmp[64] = {0};

           sprintf( strTmp, "HW_VER:%s,SW_VER:%s,SDK_VER:%s,TIME:%s %s\r\n", HW_VERSION,                                      SW_VERSION, SDK_VERSION, __DATE__, __TIME__);

           AITg_AT_Uart_printf("%s", strTmp);

           AITg_AT_Uart_printf("OK\n");                                                //透過UART回傳OK

}

void AT_UART_SET_CFG(char *Args){                                            //設定AT指令模組的UART波特率

    uint8_t saveflag = 1;

    int ret = 0;

    ret = chang_uart_baudrate(Args, saveflag);                                 //更改與判斷參數長度是否符合

 

    if (ret == 0){

           AITg_AT_Uart_printf("OK\n");                                                //透過UART回傳OK

      AITg_Uart_Deinit();                                                                     //關閉UART

      AITg_AT_UART_Config(gflash_sys_info.BaudRate);                //重新開啟UART

    }

    else{

            AITg_AT_Uart_printf("ERROR\n");

    }

}

 

void AT_UART_GET_CFG(char *Args){                                                       //獲取 UART 波特率

    int ret = 0;

    if (ret == 0){

      AITg_AT_Uart_printf("BaudRate:%d\r\n", gflash_sys_info.BaudRate);     //將目前UAR吃BaudRate                                                                                                                               //透過 UART回傳

      AITg_AT_Uart_printf("OK\n");                                                                     //透過UART回傳OK

    }

}

 

struct at_cmd_struct AT_Commands[] =  {                                     //利用at_cmd_struct結構創建AT指令表

    {.cmd = "help",                                  .fn = AT_help}

    {.cmd = "AT",                                   .fn = AT_ATTEST},

    {.cmd = "AT+GET_VER",                  .fn = AT_GetVer},

    {.cmd = "AT+RST",                          .fn = AT_RESET},

    {.cmd = "AT+UART_SET_CFG",       .fn = AT_UART_SET_CFG},

    {.cmd = "AT+UART_GET_CFG",       .fn = AT_UART_GET_CFG},

};

 

void at_add_cmd(struct at_cmd_struct *cmd)                                               //進行AT指令表連結

{

    cmd->next = CmdLine.cmds;

    CmdLine.cmds = cmd;

}

 

void at_add_cmd_link(struct at_cmd_struct *cmds, int len)                //AT指令列表的所有AT指令進行連結

{

    int i;

    cmds += (len - 1);

    for (i = 0; i < len; i++)

        at_add_cmd(cmds--);

}

 

void ATCmd_init(void){                                                                       //AT指令表初始或與建立相關連結

    at_add_cmd_link(AT_Commands, sizeof(AT_Commands) / sizeof(AT_Commands[0]));

}

程式碼2( AT指令表與功能函式)

 3.3 接下來創建數據處理函式,經由串口(UART)傳輸進來的數據第一關必須進行數據解析處理。

char * at_CmdLine_GetToken(char ** Args){                                     //尋找相關分割符號進行資料個別擷取

    char *    str;

    char *    line;

    char ch;

    line = *Args;

    ch = line[0];

    while(ch != 0)

    {

        if ( (ch == ' ') || (ch == ',') || (ch == '\t') || (ch == '='))                   //每個字元進行分割符號尋找

        {

            line++;

            ch = line[0];

            continue;

        }

        break;

    }

 

    str = line;

    while(ch != 0)

    {

        if ( (ch == ' ') || (ch == ',') || (ch == '\t') || (ch == '=')) {                 //每個字元進行分割符號尋找

            line[0] = 0;

            line++;

            break;

        }

        line++;

        ch = line[0];

    }

    *Args = line;

    return str;

}

 

int at_process_cmd(char * data){                                                //數據解析

    struct at_cmd_struct *p = CmdLine.cmds;

    char *      Token;

    Token = At_Cmddata_GetToken(&data);                          //尋找接收到的數據是否為有效數據並提取出                                                                                                 //指令部分

    if (Token[0] == 0){

        return -1;

    }

    while (p) {

        if (!strcmp(Token, p->cmd)) {                                      //提取的指令與AT 指令列表進行比對

            data = CmdLine_SkipSpace(data);                        //接收到的數據進行分割並取出資料

            p->fn(data);                                                            //資料傳送給相對應函式進行處理

            return 1;

        }

        p = p->next;

    }

    AITg_AT_Uart_printf( "Unknown cmd: %s\n", Token);   //數據並非AT指令表內的資料將返回Unknown

    return 0;

}

程式碼3(數據解析)

 

 

小叮嚀:如需使用此範例,請將UART得到的數據帶入at_process_cmd函式即可直接判斷出數據內的資料是否為AT指令並,另外如需新增AT指令,請於at_cmd_struct AT_Commands[] 內AT指令表內增加想要新增的指令與功能函式即可。

 

小結:

透過以上的程式教學,任何AT指令數據資料都能夠利用此程式進行簡易的AT指令解析器進行解析,並找到相對應的AT函式應用,有問題的看官們歡迎聯繫我,一同討論。

喜歡我的帖子,請幫我按個”收藏”,我們下回見。

★博文內容均由個人提供,與平台無關,如有違法或侵權,請與網站管理員聯繫。

★文明上網,請理性發言。內容一周內被舉報5次,發文人進小黑屋喔~

評論

花哈哈

花哈哈

2022年6月17日
CmdLine與Command_Line的宣告 typedef struct CMDLINE_LOCAL_S{ unsigned InpCount; char Line[240]; struct cli_cmd_struct *cmds; } CMDLINE_LOCAL; CMDLINE_LOCAL CmdLine = { 0 };
花哈哈

花哈哈

2022年6月17日
CmdLine與Command_Line的宣告 typedef struct CMDLINE_LOCAL_S { unsigned InpCount; char Line[240]; struct cli_cmd_struct *cmds; } CMDLINE_LOCAL; CMDLINE_LOCAL CmdLine = { 0 };
楊庭嘉

楊庭嘉

2022年1月7日
你好,請問CmdLine及Command_Line是宣告在哪?謝謝