This post was last edited by Li Baiyi on 2021-12-22 21:11
1. Use the PC serial port assistant to send commands to roughly understand the necessary conditions and steps for EC-01F to establish socket communication. The rest is to code...
2. AT command communication is based on serial port transmission and reception, and serial port transmission and reception are indispensable for serial port timeout + FIFO mechanism. The serial port receives and stores it in FIFO, and sets a timeout of 10ms for each byte received. If there is no next byte within 10ms, it is considered that a frame of data has ended, and the data frame reception completion flag is set. When the main loop polls the flag bit, read the data from FIFO and parse it.
3. After sending an AT command, it is often necessary to wait for a response. Here, the polling + state machine method is used to achieve non-blocking.
Taking NB initialization as an example, two state machines are used: state machine A: NB initialization state, state machine B: AT command state.
A state machine (NB initialization): query the instructions that need to be sent in the initialization state -> send instructions -> wait for the response result -> switch to the next state according to the result
B state machine (processing AT commands): start->send->query serial port reception completion flag->parse data->return result
B state machine implementation:
/*******************************************************************************
* 函 数 名 : 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 state machine implementation:
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;
}
}
Interrupt handling:
/*串口超时*/
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 is copied to the buffer to be parsed:
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))
{
}
}
}
Program call:
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(); }
}
}
Debugging and running effect:
https://training.eeworld.com.cn/video/32079