AT命令簡易解析

AT命令的應用場合很多,也是應用最爲通用的命令。而AT命令並不像通信協議特定的數據幀格式,解析AT命令需要匹配每個字符。AT命令一般有三種狀態:查詢、執行、設置,根據這三種狀態,本文將使用一種簡易的方法解析AT命令。
宏定義AT命令的三種狀態:

#define QUERY_CMD		0x01 	/* 查詢命令 */
#define EXECUTE_CMD		0x02	/* 執行命令 */
#define SET_CMD			0x03	/* 設置命令 */

定義命令結構體,列出需要的AT命令,以及處理的指針函數。

tyepdef struct {
	char *cmd;	/* AT指令 */
	int (*deal_func)(int opt, int argc, char *argv[]);	
}at_cmd_t;

int deal_uart_func(int argc, char *argv[]);
at_cmd_t at_table[] = {
	{"AT+UART?",  deal_uart_func},
};

#define AT_TABLE_SIZE	(sizeof(at_table) / sizeof(at_cmd_t))

對於AT命令中的多個參數進行字符串參數拆分處理,比如AT+UART=9600,0,8,1 ,最後拆分爲9600、0、8、1字符串存入argv[]中。

/*
 * @brief 字符串拆分解析處理
 * @return 檢測歸類的參數個數
 **/
int string_split(char *strp, uint32_t strsize, char ch, char *argv[], uint32_t argcM )
{
	int ch_index = 0;
	int argc_index = 0;
	uint8_t spliflag = 0;
	
	if ((!strsize) || (!argcM)) return 0;

	argv[argc_index++] = &strp[ch_index];
	for (ch_index = 0; ch_index < strsize; ch_index++) {
		if (strp[ch_index] == 'ch') {
			strp[ch_index] = '\0';
#if 0	/* 處理連續出現ch情況 */
			if (1 == splitflag) {
				argv[argc_index++] = &strp[ch_index];
			}
#endif			
			splitflag = 1;
		} else if (splitflag == 1) {
			splitflag = 0;
			argv[argc_index++] = &strp[ch_index];
			if (argc_index >= argcM) break;
		} else {
			splitflag = 0;
		}
	}
	
	return argc_index;
}

將接收到AT命令跟列表中的AT指令進行匹對解析,歸類劃分爲查詢類型、執行類型、設置類型。
查詢類型:命令後綴爲‘?’+‘\r’+’\n’,例如:AT+UART?\r\n.
執行類型:命令後綴只爲’\r’+’\n’,例如:AT+UART\r\n.
設置類型:命令‘=’後有多個參數,並以’\r’+’\n’結尾,例如:AT+UART=9600,0,8,1\r\n

#define respond_error()	printf("ERROR\r\n")
#define respond_ok()	printf("OK\r\n");

int at_cmd_parse(uint8_t *pdata, uint16_t size)
{
	int ret  = -1;
	char *ptr = NULL;
	int argc = ARGC_LIMIT;
	uint16_t offset = 0;
	int index = 0;
	char *argv[ARGC_LIMIT] = { (char *)0 };

	if (strstr((const char *)pdata, "AT") == NULL) goto at_end;
	for (index = 0; index < AT_TABLE_SIZE; index++) {
		ptr = strstr((const char *)pdata, at_table[index].cmd);
		if (ptr != NULL) {
			ptr += strlen(at_table[index].cmd);
			offset = ptr - (char *)pdata;
			break;
		}
	}
	if (index >= AT_TABLE_SIZE) goto at_end;

	/* 解析查詢命令 */
	if ((ptr[0] == '?') && (ptr[1] == '\r') && (ptr[2] == '\n')) {
		if (NULL != at_table[index].deal_func) {
			ret = at_table[index].deal_func(QUERY_CMD, argc, argv);
		}
	} else if ((ptr[0] == '\r') && (ptr[1] == '\n')) { /* 解析執行命令 */
		if (NULL != at_table[index].deal_func) {
			ret = at_table[index].deal_func(EXECUTE_CMD, argc, argv);
		}
	} else if (ptr[0] == '=') { /* 解析設置命令 */
		ptr += 1;
		argc = string_split((char*)ptr, size - offset, ',', argv, argc);
		if (NULL != at_table[index].deal_func) {
			ret = at_table[index].deal_func(SET_CMD, argc, argv);
		}
	} else {
		ret = -1;
	}

at_end:
	if (-1 == ret) respond_error();
	else respond_ok();

	return ret;
}

在應用層定義一個接收AT命令的緩衝區,接收處理每一個字節數據,當出現’\r’+’\n’,則爲一條AT命令,並設置超時處理,設置時間內沒接收到下一個字節則超時重新接收。

#define AT_RX_TIMEOUT	200	/* ms */
#define AT_RX_BUF_SIZE	512	/* bytes */

static uint8_t cmdbuf[AT_RX_BUF_SIZE];

int at_cmd_recv(uint8_t data)
{
	int ret = -1;
	static uint16_t index = 0;
	static uint32_t tick = 0;
	static uint8_t flag = 0;

	if (((HAL_GetTick() - tick) > AT_RX_TIMEOUT) || (index >= AT_RX_BUF_SIZE	)) {
		index = 0;
		flag = 0;
	}
	tick = HAL_GetTick();
	cmdbuf[index++] = data;
	if ((data == '\n') && flag) {
		ret = at_cmd_parse(cmdbuf, index);
		flag = 0;
		index = 0;
	} else if (data == '\r') {
		flag = 1;
	} else {
		flag = 0;
	}

	return ret;
 }

void at_task(void)
{
	uint8_t data;

	while (uart_recv(&data, 1) > 0) {
		at_cmd_recv(data);
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章