制作一个简单的AT指令解析
最近在开发nrf51822工作上需要搭建一个简单的AT指令库,突然灵机一动想到了这个样一个简单的AT解析函数,虽然不是特别完美但还需各位大神多多指教。
一般,我们的AT命令组成为:
1、查询命令 AT+instruction\r\n
2、设置命令AT+instruction= <param>\r\n
举个栗子:我要通过AT查询波特率以及设置波特率
1、查询 AT+BAUD\r\n
2、设置 AT+BAUD= 115200\r\n
好知道这种格式之后我们发现这个命令都有些规律。
第一:指令头都是固定的为"AT+"
第二:尾部都是固定的为"\r\n"
第三:如果要接参数的话都是"= "(注意等于号后面接了空格)
第四:我们的可以通过命令的首字母来建立一个AT索引表,正是这样我 们的可以建立一个长度为26的索引表(A~Z),在通过相应的索引表存放相应的指令,例如BAUD,我们就可以存在指令表的第二个位置(B在字母表中排第二)。
找到这种规律我们就知道该怎么去拆分AT指令是作为查询命令还是设置命令,接下来就开始析构代码。
第一、创建两个结构体
//第一个用来存放AT的指令
typedef struct node{
uint8_t position; //位置根据命令首字母来进行位置区分 B = 2
uint8_t *at_str; //存放AT命令
void (*function)(uint8_t *str); //存放命令所用的函数
struct node *next; //链接下一个同位置的命令
}AT_DEAL;
//第二个用来构建一个索引表
typedef struct{
AT_DEAL *next;
}INDEX_NODE
第二、开始构建索引表以及AT库
#define MAX_AT_INSTRUCTION 32 //AT指令数量
#define LAST_INST MAX_AT_INSTRUCTION - 1 //最后条语句表示AT指令输入错误
#define MAX_RECEIVE_LENGTH 28 //接送数据的最大长度
#define CONNECT_DATA(str1,str2,str3) (str1##str2##str3) //字符串连接
#define MAX_RECEIVE_LENGTH 28 //接送数据的最大长度
//此下这个结构体必须创建为一个全局变量、
static AT_DEAL at_deal[MAX_AT_INSTRUCTION] = {
{ 1, (uint8_t*)"ADVI", get_advi, NULL },
{ 2, (uint8_t*)"BAUD", get_baud, NULL },
{ 3, (uint8_t*)"CONA", set_cona, NULL },
{ 3, (uint8_t*)"CONN", set_conn, NULL },
{ 3, (uint8_t*)"CHAR", get_char, NULL },
{ 4, (uint8_t*)"DEFAULT", set_default, NULL },
{ 7, (uint8_t*)"HELP", get_help, NULL }, //获取指令集
{ 8, (uint8_t*)"IMME", get_imme, NULL },
{ 8, (uint8_t*)"IBE0", get_ibe0, NULL },
{ 8, (uint8_t*)"IBE1", get_ibe1, NULL },
{ 8, (uint8_t*)"IBE2", get_ibe2, NULL },
{ 8, (uint8_t*)"IBE3", get_ibe3, NULL },
{ 8, (uint8_t*)"IBEA", get_ibea, NULL },
{ 12, (uint8_t*)"LADDR", get_laddr, NULL },
{ 13, (uint8_t*)"MARJ", get_marj, NULL },
{ 13, (uint8_t*)"MEA", get_mea, NULL },
{ 13, (uint8_t*)"MINO", get_mino, NULL },
{ 14, (uint8_t*)"NAME", get_name, NULL }, //获取蓝牙设备名称
( 14, (uint8_t*)"NOTI", get_noti, NULL ),
( 14, (uint8_t*)"NOTP", get_notp, NULL ),
( 16, (uint8_t*)"PWRM", set_pwrm, NULL ),
{ 16, (uint8_t*)"POWE", get_powe, NULL },
{ 16, (uint8_t*)"PIN", get_pin, NULL },
{ 16, (uint8_t*)"PARI", get_pari, NULL },
{ 18, (uint8_t*)"ROLE", set_role, NULL },
{ 18, (uint8_t*)"RESET", reset, NULL },
{ 19, (uint8_t*)"SLEEP", set_sleep, NULL },
{ 19, (uint8_t*)"STOP", get_stop, NULL },
{ 19, (uint8_t*)"START", get_start, NULL },
{ 21, (uint8_t*)"UUID", get_uuid, NULL },
{ 22, (uint8_t*)"VERSION", version, NULL }, //获取版本号
{ 0, (uint8_t*)"AT\r\n", test_inst, NULL }, //测试指令
{ 0, (uint8_t*)NULL, inst_error, NULL } //指令错误
};
//接着就开始注册 索引表
void enroll_at_instruction_index(void) //注册AT指令索引表
{
unsigned char i;
AT_DEAL *deal = NULL;
for(i=0;i<INDEX_TABLE_NUMBER;i++) //先初始化索引表
index_node[i].next = NULL;
//开始构建索引表
for(i=0;i<MAX_AT_INSTRUCTION-2;i++) { //此处减二是因为AT库最后两个不加入索引表
if(at_deal[i].position != 0) {
at_deal[i].next = index_node[at_deal[i].position].next;
index_node[at_deal[i].position].next = &at_deal[i];
}
}
//测试链表链接情况
/*
printf("**********************\r\n");
for(i=0;i<INDEX_TABLE_NUMBER;i++) {
deal = index_node[i+1].next;
if(deal != NULL) {
printf("%c\r\n",'A'+i);
while(deal != NULL) {
printf("%5d %s\r\n",deal->position,deal->at_str);
deal = deal->next;
}
}
}
printf("**********************\r\n");
*/
}
第三、开始构建解析AT以及跳转相应指令函数
void chansfer_char(unsigned char*data_array,unsigned char index)
{
uint8_t i;
for(i=0;i<index;i++) { //大小写强制转换
if(data_array[i] == ' ' || data_array[i] == '\\') //遇到空格
break;
if(data_array[i] >= 'a' && data_array[i] <= 'z')
data_array[i] = data_array[i]-32;
else
data_array[i] = data_array[i];
}
}
//此处的str最长为28
void at_info_deal(unsigned char*str,uint8_t len) //AT指令处理
{
AT_DEAL *node = NULL;
unsigned char position = 0;
chansfer_char(str,len);
if(len == 4) { //判断是否为指令测试"AT\r\n"
if(memcmp(str,at_deal[LAST_INST-1].at_str,strlen((char *)at_deal[LAST_INST-1].at_str)) == 0)
at_deal[LAST_INST-1].function(NULL);
else
at_deal[LAST_INST].function(NULL);
}
else if(len > 4){ //指令验证
if(memcmp(str,"AT+",3) == 0) { //命令头是否为"AT+"
switch(str[3])
{
case 'A':
node = index_node[1].next;
flag = 1;
break;
case 'B':
node = index_node[2].next;
flag = 1;
break;
case 'C':
node = index_node[3].next;
flag = 1;
break;
case 'D':
node = index_node[4].next;
flag = 1;
break;
case 'H':
node = index_node[7].next;
flag = 1;
break;
case 'I':
node = index_node[8].next;
flag = 1;
break;
case 'L':
node = index_node[12].next;
flag = 1;
break;
case 'M':
node = index_node[13].next;
flag = 1;
break;
case 'N':
node = index_node[14].next;
flag = 1;
break;
case 'P':
node = index_node[16].next;
flag = 1;
break;
case 'R':
node = index_node[18].next;
flag = 1;
break;
case 'S':
node = index_node[19].next;
flag = 1;
break;
case 'U':
node = index_node[21].next;
flag = 1;
break;
case 'V':
node = index_node[22].next;
flag = 1;
break;
}
}
else //错误指令
at_deal[LAST_INST].function(NULL);
}
else //错误指令
at_deal[LAST_INST].function(NULL);
if(flag == 1) {
flag = 0;
while(node != NULL) {
if(memcmp(str+3,node->at_str,strlen((char *)node->at_str)) == 0) { //找到相应指令
str = str+3;
position = strlen((char *)node->at_str) + 1;
if(str[position] == '\r' && str[position+1] == '\n') //指令后无参数
node->function(NULL);
else if(str[position] == '=' && str[len-1] == '\r' && str[len] == '\n') //指令后有参数
node->function(str);
break;
}
node = node->next;
}
node = NULL;
}
}
第四、创建指令函数(所有指令函数大同小异,就以BAUD和LADDR指令为例)
//LADDR
//将16进制数转换成字符串
uint8_t hex_to_char(unsigned char dat)
{
if(dat > 9)
return (dat+55);
else
return (dat+0x30);
}
void get_laddr(unsigned char*str) //获取蓝牙地址
{
unsigned char temp,i;
ble_gap_addr_t get_addr;
if(str == NULL) { //此指令是无参数的
sd_ble_gap_address_get(&get_addr);
for(i=0;i<6;i++) {
temp = get_addr.addr[BLE_GAP_ADDR_LEN-i] & 0xf0;
chansfer[2*i] = hex_to_char(temp >> 4);
temp = get_addr.addr[BLE_GAP_ADDR_LEN-i] & 0x0f;
chansfer[2*i+1] = hex_to_char(temp);
}
printf("+LADDR=%s\r\n",chansfer);
}
else { //否则命令错误
at_deal[LAST_INST].function(NULL);
}
}
//BAUD
void get_baud(unsigned char *str)
{
unsigned char *baud=NULL;
if(str == NULL) { //无参数输出当前的波特率
//从内存中读取
baud = flash_read(addr);
printf("+BUAD=%s\r\n",baud);
}
else { //有参数则更新
/*
此处要做一个波特率处理。判断是否传入的参数是否是真的波特率
*/
flash_write(addr,str,strlen(str));
printf("+BAUD=%s OK\r\n",str);
}
}
第五、开始执行main
static uint8_t index = 0; //接收字符
static uint8_t data_array[MAX_RECEIVE_LENGTH]; //存储接收的字符
static void timers_init(void)
{
uint32_t err_code = 0;
...
err_code = app_timer_create(&uart_receive_timeout,
APP_TIMER_MODE_SINGLE_SHOT,
receive_timeout);
APP_ERROR_CHECK(err_code);
}
static void receive_timeout(void * p_context)
{
app_timer_stop(uart_receive_timeout);
at_info_deal(data_array,index);
index = 0;
memset(data_array,'\0',MAX_RECEIVE_LENGTH);
}
void uart_event_handle(app_uart_evt_t * p_event)
{
uint32_t err_code;
switch (p_event->evt_type)
{
case APP_UART_DATA_READY:
UNUSED_VARIABLE(app_uart_get(&data_array[index]));
index++;
app_timer_stop(uart_receive_timeout);
if(index>=MAX_RECEIVE_LENGTH) {
receive_timeout(NULL);
}
else { //判断是否接收完成
//SEGGER_RTT_TRC("--%d--",index);
err_code = app_timer_start(uart_receive_timeout, APP_TIMER_TICKS(9/*ms*/, APP_TIMER_PRESCALER), NULL);
PP_ERROR_CHECK(err_code);
}
break;
case APP_UART_COMMUNICATION_ERROR:
APP_ERROR_HANDLER(p_event->data.error_communication);
break;
case APP_UART_FIFO_ERROR:
APP_ERROR_HANDLER(p_event->data.error_code);
break;
case APP_UART_TX_EMPTY:
index = 0;
break;
default:
break;
}
}
int main(void)
{
timers_init();
uart_init();
...
enroll_at_instruction_index();
for (;;)
{
power_manage();
}
}
最后便可以执行成功了。