代码
https://download.csdn.net/download/ylc0919/13606978
效果
这是一个项目中用到AT指令的部分:
/* 配置lora模块 */
void HandleLoraConfig(void)
{
if(SYS_MAIN_FLAG&1<<5)
{
char cmd_buf1[64]={0};
char cmd_buf2[64]={0};
char cmd_buf3[64]={0};
char cmd_buf4[64]={0};
char cmd_buf5[64]={0};
switch(lora_config_steps)
{
case 0: //复位并打开AT脚(1ms阻塞)
LORA_RESET=0; //开始复位,延时20us以上
clear_at_queue(); //清空AT队列
lora_tick_num=0; //复位计数
timer_enable_flag|=1<<4; //使能lora计时器
if(lora_tick_num)
{
LORA_RESET=1; //复位完成
LORA_AT=1; //进入AT模式
lora_config_steps=1; //进入下一步
}
break;
case 1: //将配置参数送入队列
at_cmd_test();
snprintf(cmd_buf1, 64, "AT+NET=%02x\r\n", lora_config.net);
snprintf(cmd_buf2, 64, "AT+TFREQ=%08x\r\n", lora_config.tfreq);
snprintf(cmd_buf3, 64, "AT+RFREQ=%08x\r\n", lora_config.rfreq);
snprintf(cmd_buf4, 64, "AT+RXW=%02x\r\n", lora_config.rxw);
snprintf(cmd_buf5, 64, "AT+AK=%s\r\n", lora_config.AES);
// atstr len type retrynum otime noAck
write_cmd_to_queue((u8*)cmd_buf1,strlen(cmd_buf1), 3, 5, 100, 0); //设置通信模式
write_cmd_to_queue((u8*)cmd_buf2,strlen(cmd_buf2), 4, 5, 100, 0); //设置发送频点
write_cmd_to_queue((u8*)cmd_buf3,strlen(cmd_buf3), 5, 5, 100, 0); //设置接收频点
write_cmd_to_queue((u8*)"AT+RIQ=00\r\n", 11, 6, 5, 100, 0); //关闭接收反转,暂不支持修改
write_cmd_to_queue((u8*)"AT+TIQ=00\r\n", 11, 7, 5, 100, 0); //关闭发送反转,暂不支持修改
write_cmd_to_queue((u8*)"AT+LCP=FFFF\r\n", 13, 8, 5, 100, 0); //长期休眠,暂不支持修改
write_cmd_to_queue((u8*)cmd_buf4,strlen(cmd_buf4), 9, 5, 100, 0); //接收窗口时间
write_cmd_to_queue((u8*)cmd_buf5,strlen(cmd_buf5), 10, 5, 100, 0); //AES加密
write_cmd_to_queue((u8*)"AT+ID?\r\n", 8, 11, 5, 100, 0); //获取设备ID
lora_config_steps=2; //进入下一步
break;
case 2: //执行读队列发送(一直执行,直到接收处理函数中跳入下一步)
if(lora_tick_num>=10) //每10ms执行一次
{
lora_tick_num=0;
read_cmd_queue_and_send();
}
break;
default: //执行完毕,关闭AT,进入透传模式,清除标记
LORA_AT=0;
lora_config_steps=0;
timer_enable_flag&=~(1<<4);
SYS_MAIN_FLAG&=~(1<<5);
break;
}
}
}
可以看到在步骤1中将需要用到的命令,通过写队列函数写进去,然后在循环中不断执行读队列发送命令函数就行,简单实用。
使用说明
写队列函数
函数原型:
void write_cmd_to_queue(u8 *atstr, //命令缓存地址
u16 len, //命令长度
u8 type, //命令标识
u8 retryNum, //重发次数
u16 otime, //AT指令一次发送超时时间计数
u16 noAckOtime);//一条AT指令总超时时间计数
接像上面示例那样将AT指令填进去就可以,需要注意的是:
1.如果otime为0,则retryNum无意义。
2.如果otime和noAckOtime都为0,则没有超时处理。
3.type需要大于2,且每个指令对应一个type,用于AT指令的回复与超时处理。至于为什么要大于2,下面再说。
读队列与发送函数
函数原型:void read_cmd_queue_and_send(void);
函数比较长,就不贴出来了。
该函数读队列并发送AT命令,包括了超时处理。建议每10ms调用一次,调用时间间隔为超时计数时间间隔,超时时间=调用间隔*超时计数。
其中需要修改的地方有两个发送函数及三个超时处理:
1.两个发送函数,代码中我用了HAL库的串口发送函数
HAL_UART_Transmit(&huart3,
(uint8_t *)at_queue.txbuf,//读到的命令
at_queue.cmd.len, //读到的命令长度
0xffff);//串口,以阻塞模式发送
移植时只需要找到这两个函数,把他们换成自己的发送函数就行。
2.三个超时处理,分别是有重发次数的超时处理,无重发次数的超时处理,总超时处理,不一定全部用到,需要用哪个就改哪个,不需要用的保持原样即可,具体位置代码中都有注释。
测试与模块判断函数
函数如下:
/*测试模块是否正常*/
void at_cmd_test(void)
{ // atstr len type retrynum otime noAck
write_cmd_to_queue("AT\r\n", 4, 1, 30, 100, 0);
write_cmd_to_queue("ATI\r\n", 5, 2, 5, 100, 0);
}
如上面示例一样,在开始先将AT和ATI送入队列,如果连AT都没有回复,那么也没必要继续往下执行了,ATI用于判断是什么模块。
AT接收处理函数
void at_recv_proc(u8 *buf,u16 len)
{
char *p;
p=(char*)buf;
if(at_queue.cmd.type==1) //AT
{
if((strstr(p,"AT\r\n"))||(strstr(p,"\r\nOK\r\n")))
{
at_flag&=~(1<<0);
}
}
else if(at_queue.cmd.type==2) //ATI
{
if(strstr(p,"EC600S")) //可以根据版本信息不同,在此处执行对应初始化函数
{
at_flag&=~(1<<0);
at_queue.modType=MOD_EC600S;
}
else if(strstr(p,"+ATI:"))
{
at_flag&=~(1<<0);
at_queue.modType=MOD_LORA;
}
}
else{ //得到版本信息后,在此处执行对应的处理程序
switch(at_queue.modType)
{
case MOD_EC600S:
// ec600RxProc(buf,len);
break;
case MOD_LORA:
lora_recv_proc(buf,len);
break;
default:break;
}
}
}
该函数处理了at_cmd_test()函数的两个命令,并根据ATI命令收到的回复判断模块类型,调用相应模块的AT接收处理。该函数在串口接收处理中调用。
当然,如果只针对一个模块,完全可以忽略此函数,直接在串口接收处理中调用对应模块的AT处理函数,以下是上面示例对应的接收处理:
/*lora模块接收处理*/
void lora_recv_proc(u8 *buf,u16 len)
{
char *p,*q=NULL;
u8 i=0;
p=(char*)buf;
/*一般情况*/
switch(at_queue.cmd.type)
{
case 3:if(strstr(p,"OK")) //设置通信模式
at_flag&=~(1<<0);
break;
case 4:if(strstr(p,"OK")) //设置发送频点
at_flag&=~(1<<0);
break;
case 5:if(strstr(p,"OK")) //设置接收频点
at_flag&=~(1<<0);
break;
case 6:if(strstr(p,"OK")) //关闭接收反转
at_flag&=~(1<<0);
break;
case 7:if(strstr(p,"OK")) //关闭发送反转
at_flag&=~(1<<0);
break;
case 8:if(strstr(p,"OK")) //长期休眠
at_flag&=~(1<<0);
break;
case 9:if(strstr(p,"OK")) //接收窗口时间
at_flag&=~(1<<0);
break;
case 10:if(strstr(p,"OK")) //AES加密
at_flag&=~(1<<0);
break;
case 11:q=strstr(p,"+ID:"); //ID 16个16进制数字
if(q)
{
at_flag&=~(1<<0);
q=q+4;
for(i=0;i<16;i++)
{
mSystem.loraID[i]=*q;
q++;
}
lora_config_steps=3; //最后一条指令处理完进入下一步
}
break;
default:break;
}
/*特殊情况:无*/
}