-
動機:
為什麼會想分享簡易AT指令解析器(AT Command Parser)? 本人大多都是使用AT指令進行功能控制,從沒自訂過AT指令,因此遇到需要創建自家AT指令時,卻不知從何創起,因此經過一番研究後發現,創建自家AT指令時必須要有一套AT指令解析器;雖然知道AT指令解析器只需簡單的字串字元進行比較即可完成,但很多人還是不知從何下手;因此本篇將介紹簡易AT指令解析器給大家,讓有需要的人能夠快速上手。
-
AT指令解析器概念與流程圖
使用過AT指令的人都知道,每個廠商的模塊不一樣,實現的功能也不一樣,因此每個模塊廠商都有自己的一套自訂的AT指令集,所以每個模塊廠商實現的AT指令解析器也不一樣,因此接下來要分享給大家如何創建簡易AT指令解析器。
何謂AT指令解析器? 簡單的來說,AT指令解析器是透過接收串口(UART)傳來的AT指令,進行解析,依據解析的結果至AT指令表中進行尋找相對應的字串選項,若找到該選項,則執行該選項對應的函式,函式處理完以後回傳其對應的返回數據到串口(UART),可參考(圖3)讓看官們更直觀瞭解簡易AT指令解析器流程。
|
|
圖1 |
圖2 |
|
圖3 (簡易AT指令解析器流程) |
-
簡易AT指令解析器程式碼與說明
由於我只講解簡易AT指令解析器,因此UART部分的資料傳輸需要由看官們依據自己的晶片進行調整。
本節我們將利用基本C語言來創建一個簡易的AT指令解析器。
- 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函式應用,有問題的看官們歡迎聯繫我,一同討論。
喜歡我的帖子,請幫我按個”收藏”,我們下回見。
評論
花哈哈
2022年6月17日
花哈哈
2022年6月17日
楊庭嘉
2022年1月7日