本帖最后由 李百仪 于 2021-12-22 21:11 编辑
1.通过PC串口助手发送指令形式大概了解EC-01F建立Socket通信所需的必要条件和步骤,剩下就是码起来……
2.AT指令通信基于串口收发,用到串口收发少不了串口超时+FIFO机制。串口接收存放到FIFO,每接收一个字节设置超时10ms,10ms无下一字节,认为一帧数据结束,设置数据帧接收完成标志。待主循环轮询到该标志位,从FIFO读取数据并解析。
3.AT指令发送后往往需要等待应答,这里使用轮询+状态机方式实现非阻塞。
以初始化NB为例使用两个状态机,A状态机:NB初始化状态,B状态机:AT指令状态.
A状态机(NB初始化):查询初始化状态需要发送的指令->发送指令->等待应答结果->根据结果切换到下一个状态
B状态机(处理AT指令):开始->发送->查询串口接收完成标志位->解析数据->返回结果
B状态机实现:
/*******************************************************************************
* 函 数 名 : NB_AT_CMD
* 函数功能 : 发送AT指令?
* 输 入 :
参数1:cmd AT指令
参数2:timeout 单次发送的超时时间
参数3:res 要判断的返回结果
参数4:count 尝试次数
* 输 出 : ATCMD_REVOK 发送且收到回复 ATCMD_TIMEOUT 超时
*******************************************************************************/
ATCMD_StatusTypeDef NB_AT_CMD(U8* cmd,U16 timeout,const char* res, U8 count)
{
static ATCMD_StatusTypeDef atcmd_status = ATCMD_START;
static U8 cnt = 1;
U16 temp;
switch(atcmd_status)
{
case ATCMD_START:
cnt = count;
atcmd_status = ATCMD_SEND;
break;
case ATCMD_SEND:
atcmd_status = ATCMD_WAIT_REV;
AT_cmd_tick = 0;
memset(EC01RxCache, 0, sizeof(EC01RxCache));
EC01_handle.rcv_size = 0;
printf("\r\n AT->%s", cmd);
USART_SendDataByIT(cmd, strlen((const char*)cmd));
break;
case ATCMD_WAIT_REV:
if(AT_cmd_tick < timeout)
{
if(NB_Hand((char*)res,&temp,0))//
{
atcmd_status = ATCMD_REVOK;
}
}
else
{
if(cnt > 0)//再次发送
{
atcmd_status = ATCMD_SEND;
}
else
{
atcmd_status = ATCMD_TIMEOUT;
}
cnt--;
}
break;
case ATCMD_REVOK:
case ATCMD_TIMEOUT:
atcmd_status = ATCMD_START;
default:
break;
}
return atcmd_status;
}
/*******************************************************************************
* 函 数 名 : NB_Hand
* 函数功能 : 判断串口接收缓存中是否包含substr,
* 输 入 : substr:比较字符串指针,index:字符串比较完成结束位置指针,start_index:串口接收缓存起始位置
* 输 出 : 包含返回1 不包含返回0
*******************************************************************************/
U8 NB_Hand(char* substr,U16 *index,U16 start_index)
{
U8 flag = 0;
if(EC01_handle.rcv_size != 0)
{
if(!NB_strcmp(( char*)EC01RxCache, ( char*)substr, start_index, EC01_handle.rcv_size))
{
flag = 1;
}
else
{
flag = 0;
}
}
return flag;
}
/*******************************************************************************
* 函 数 名 : NB_strcmp
* 函数功能 : 判断str1中是否包含str2
* 输 入 : 待比较字符串指针,起始位置,长度
* 输 出 : 包含返回1 不包含返回0
*******************************************************************************/
U8 NB_strcmp(char *str1, char *str2, U16 start_index, U16 Size)
{
U16 i, flag = 1;
char *p;
for(i = start_index; i < Size; i++)
{
if(str1 == 0)
{
break;
}
if(str1 == *str2)
{
flag = 0;
p = str2;
while(*p)
{
if(str1 == *p)
{
*p++;
i++;
}
else
{
flag = 1;
break;
}
}
if(flag == 0)
{
break;
}
}
}
return flag;
}
A状态机实现:
U8 NB_Config(void)
{
static ATCMD_StatusTypeDef res;
switch(NB.CON)
{
case 0x00:
res = NB_AT_CMD((U8*)"ATE0\r\n", 2000, "OK", 3);
if(res == ATCMD_REVOK)
{
if(NB_parseDefaultMsg(&EC01RxCache[0], EC01_handle.rcv_size)&0x08)
{
uprintf("\r\n NB.RegStatus %d", NB.RegStatus);
}
NB.CON = 1;
uprintf("\r\n ATE0 OK");
}
else if(res == ATCMD_TIMEOUT)
{
NB.CON = 0;
uprintf("\r\n ATE0_TIMEOUT");
return 1;
}
break;
case 0x01:
res = NB_AT_CMD("AT+CSCON=0\r\n", 2000, "OK", 1);//信号连接状态 关闭连接状态主动通知
if(res == ATCMD_REVOK)
{
uprintf("\r\n CSCON OK");
NB.CON = 2;
if(NB_parseDefaultMsg(&EC01RxCache[0], EC01_handle.rcv_size)&0x08)
{
uprintf("\r\n NB.RegStatus %d", NB.RegStatus);
}
}
else if(res == ATCMD_TIMEOUT)
{
uprintf("\r\n CSCON_TIMEOUT");
NB.CON = 1;
return 1;
}
break;
case 0x02:
res = NB_AT_CMD((U8*)"AT+CEREG=1\r\n", 2000, "OK", 3);//设置网络注册状态上报信息
//设置成功后,当网络注册状态信息有变化,会主动上报给用户终端,如:
if(res == ATCMD_REVOK)
{
NB.CON = 3;
uprintf("\r\n CEREG OK");
if(NB_parseDefaultMsg(&EC01RxCache[0], EC01_handle.rcv_size)&0x08)
{
uprintf("\r\n NB.RegStatus %d", NB.RegStatus);
}
}
else if(res == ATCMD_TIMEOUT)
{
NB.CON = 2;
uprintf("\r\n CEREG_TIMEOUT");
return 1;
}
break;
case 0x03:
break;
default:
break;
}
}
中断处理:
/*串口超时*/
void UartTimeout(uart_process_t *up)
{
if(up->rcv_timeout != 0)
{
up->rcv_timeout = up->rcv_timeout - 1;
if(up->rcv_timeout == 0)
{
up->rcv_status = SET;
}
}
}
void TIM3_IRQHandler(void)
{
static U16 uiCount = 0;
if(TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
uiCount++;
if(uiCount%1 == 0) {TimeSlice[0] = SET;} //5ms
if(uiCount%2 == 0) {TimeSlice[1] = SET;} //10ms
if(uiCount%4 == 0) {TimeSlice[2] = SET;} //20ms
if(uiCount%20 == 0) {TimeSlice[3] = SET;} //100ms
if(uiCount%40 == 0) {TimeSlice[4] = SET;} //200ms
if(uiCount%200 == 0) {TimeSlice[5] = SET; uiCount = 0;}//1S
NB_Tick();
UartTimeout(&EC01_handle);
}
}
void USART2_IRQHandler(void)
{
u8 Res;
if(USART_GetITStatus(USART2, USART_IT_TXE) == SET)//发送完成中断
{
if(SendIdx != SendLength)
{
USART_SendData(USART2, *(pDataByte + SendIdx) );
SendIdx++;
}
else
{
SendLength = 0;
SendIdx = 0;
USART_ITConfig(USART2, USART_IT_TXE, DISABLE);
}
}
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)//接收中断
{
Res = USART_ReceiveData(USART2);
InQueue(&EC01Q,Res);
EC01_handle.rcv_timeout = 2;
}
}
FIFO复制到待解析缓存:
U16 NB_CopyFIFOToParseBuf(U8 *uP)
{
U16 rc = 0;
if(EC01_handle.rcv_status == SET)//串口接收超时
{
for(rc = 0; rc < USART_BUFFER_SIZE; rc++)//取出所有数据到待读取缓存
{
if(IsEmpty(&EC01Q) == 0)//有数据
{
OutQueue(&EC01Q, &uP[rc]);
}
else
{
EC01_handle.rcv_status = RESET;
break;
}
}
}
return rc;
}
void NB_Polling_Msg(void)
{
EC01_handle.rcv_size = NB_CopyFIFOToParseBuf(EC01RxCache);
if((EC01_handle.rcv_size != 0)
&& (NB.CON == 0xFF))//初始化完成允许轮询消息,(未初始化完成轮询消息可能无意义)
{
if(NB_parseDefaultMsg(&EC01RxCache[0], EC01_handle.rcv_size))
{
}
}
}
程序调用:
void Task_RealTime(void)
{
NB_Polling_Msg();//轮询NB消息
NB_CycleCheck();//周期性查询NB状态:信号、注册状态、附着状态、Socket连接状态
NB_TCP_ReportProcess();//处理TCP任务
NB_Config();//NB初始化
}
void Task_100ms(void)
{
if(NB.CON != 0xFF)//上电开始闪烁
{
LedFlash();
}
}
void Task_200ms(void)
{
if(NB.CON == 0xFF && NB.isConnect != 1)//NB初始化完成,Socket未建立
{
LedFlash();
}
}
void Task_1000ms(void)
{
static U8 Times = 0;
if(NB.CON == 0xFF && NB.isConnect == 1)//NB初始化完成,Socket建立
{
LedFlash();
}
if(Times%10 == 0)
{
if(NB.CON == 0xFF)
{
uprintf("\r\n NB.CON %d CSQ %d RegStatus %d attach %d CGACT_state %d CycleCheckStatus %d ReportStatus %d",
NB.CON, NB.csq, NB.RegStatus, NB.attach, NB.CGACT_state, NB.CycleCheckStatus, NB.ReportStatus);
uprintf(" MQTT isConnect %d state %d ret_code %d conn_result %d", NB.isConnect, NB.mqtt.state,
NB.mqtt.ret_code, NB.mqtt.conn_result);
}
}
}
int main(void)
{
RCC_Config();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable , ENABLE);
InitQueue(&EC01Q);
Tim3_Config(49,7199);//5ms
Delay_init(72);
USART1_Config(115200);
USART2_Config(9600);
LED_Config();
memset(&EC01_handle, 0, sizeof(uart_process_t));
memset(EC01RxCache, 0, sizeof(EC01RxCache));
memset(&NB, 0, sizeof(NBStatus));
while (1)
{
Task_RealTime();
if(GetSysClkFlg(3) == SET) {Task_100ms(); }
if(GetSysClkFlg(4) == SET) {Task_200ms(); }
if(GetSysClkFlg(5) == SET) {Task_1000ms(); }
}
}
调试运行效果:
https://training.eeworld.com.cn/video/32079