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);
}
}