李百仪 发表于 2021-12-21 18:20

【安信可NB-IoT开发板EC-01F-Kit测评】05.基于STM32+EC-01F Socket通信

<div class='showpostmsg'> 本帖最后由 李百仪 于 2021-12-22 21:11 编辑

<p>1.通过PC串口助手发送指令形式大概了解EC-01F建立Socket通信所需的必要条件和步骤,剩下就是码起来&hellip;&hellip;</p>

<p>2.AT指令通信基于串口收发,用到串口收发少不了串口超时+FIFO机制。串口接收存放到FIFO,每接收一个字节设置超时10ms,10ms无下一字节,认为一帧数据结束,设置数据帧接收完成标志。待主循环轮询到该标志位,从FIFO读取数据并解析。</p>

<p></p>

<p>3.AT指令发送后往往需要等待应答,这里使用轮询+状态机方式实现非阻塞。</p>

<p>以初始化NB为例使用两个状态机,A状态机:NB初始化状态,B状态机:AT指令状态.</p>

<p>A状态机(NB初始化):查询初始化状态需要发送的指令-&gt;发送指令-&gt;等待应答结果-&gt;根据结果切换到下一个状态</p>

<p>B状态机(处理AT指令):开始-&gt;发送-&gt;查询串口接收完成标志位-&gt;解析数据-&gt;返回结果</p>

<p>&nbsp;</p>

<p>B状态机实现:</p>

<pre>
<code class="language-cpp">/*******************************************************************************
* 函 数 名      : 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-&gt;%s", cmd);
      USART_SendDataByIT(cmd, strlen((const char*)cmd));
      break;
    case ATCMD_WAIT_REV:
      if(AT_cmd_tick &lt; timeout)
      {
            if(NB_Hand((char*)res,&amp;temp,0))//
            {
                atcmd_status = ATCMD_REVOK;
            }
      }
      else
      {
            if(cnt &gt; 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;
}
</code></pre>

<pre>
<code class="language-cpp">/*******************************************************************************
* 函 数 名      : 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;
       
}
</code></pre>

<pre>
<code class="language-cpp">/*******************************************************************************
* 函 数 名      : 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 &lt; Size; i++)
        {
                if(str1<i> == 0)
                {
                        break;
                }
                if(str1<i> == *str2)
                {
                        flag = 0;
                        p    = str2;
                        while(*p)
                        {
                                if(str1<i> == *p)
                                {
                                        *p++;
                                        i++;
                                }
                                else
                                {
                                        flag = 1;
                                        break;
                                }
                        }
                        if(flag == 0)
                        {
                                break;
                        }
                }
        }
        return flag;
}
</i></i></i></code></pre>

<p><i><i><i>A状态机实现:&nbsp;</i></i></i></p>

<pre>
<i><i><i>
<code class="language-cpp">U8 NB_Config(void)
{
    static ATCMD_StatusTypeDef res;
       
    switch(NB.CON)
    {
            case 0x00:
                res = NB_AT_CMD((U8*)&quot;ATE0\r\n&quot;, 2000, &quot;OK&quot;, 3);
                if(res == ATCMD_REVOK)
                {
                        if(NB_parseDefaultMsg(&amp;EC01RxCache, EC01_handle.rcv_size)&amp;0x08)
                                {
                                        uprintf(&quot;\r\n NB.RegStatus %d&quot;, NB.RegStatus);
                                }
                    NB.CON = 1;
                    uprintf(&quot;\r\n ATE0 OK&quot;);
                }
                else if(res == ATCMD_TIMEOUT)
                {
                    NB.CON = 0;
                    uprintf(&quot;\r\n ATE0_TIMEOUT&quot;);
                    return 1;
                }
       break;
      
       case 0x01:
                res = NB_AT_CMD(&quot;AT+CSCON=0\r\n&quot;, 2000, &quot;OK&quot;, 1);//信号连接状态 关闭连接状态主动通知
                if(res == ATCMD_REVOK)
                {
                                uprintf(&quot;\r\n CSCON OK&quot;);
                    NB.CON = 2;
                    if(NB_parseDefaultMsg(&amp;EC01RxCache, EC01_handle.rcv_size)&amp;0x08)
                                {
                                        uprintf(&quot;\r\n NB.RegStatus %d&quot;, NB.RegStatus);
                                }
                }
                else if(res == ATCMD_TIMEOUT)
                {
                                uprintf(&quot;\r\n CSCON_TIMEOUT&quot;);
                    NB.CON = 1;
                    return 1;
                }
          break;
          
      case 0x02:
                res = NB_AT_CMD((U8*)&quot;AT+CEREG=1\r\n&quot;, 2000, &quot;OK&quot;, 3);//设置网络注册状态上报信息
                //设置成功后,当网络注册状态信息有变化,会主动上报给用户终端,如:
                       if(res == ATCMD_REVOK)
                {
                                NB.CON = 3;
                    uprintf(&quot;\r\n CEREG OK&quot;);
                    if(NB_parseDefaultMsg(&amp;EC01RxCache, EC01_handle.rcv_size)&amp;0x08)
                                {
                                        uprintf(&quot;\r\n NB.RegStatus %d&quot;, NB.RegStatus);
                                }
                }
                else if(res == ATCMD_TIMEOUT)
                {
                    NB.CON = 2;
                    uprintf(&quot;\r\n CEREG_TIMEOUT&quot;);
                    return 1;
                }
      break;
      case 0x03:
      break;
      default:
      break;
    }
}</code></i></i></i></pre>

<p><font face="monospace"><i>中断处理:</i></font></p>

<pre>
<code class="language-cpp">/*串口超时*/
void UartTimeout(uart_process_t *up)
{
        if(up-&gt;rcv_timeout != 0)
        {
                up-&gt;rcv_timeout = up-&gt;rcv_timeout - 1;
                if(up-&gt;rcv_timeout == 0)
                {
                        up-&gt;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 = SET;}        //5ms

                if(uiCount%2 == 0){TimeSlice = SET;}        //10ms

                if(uiCount%4 == 0){TimeSlice = SET;}        //20ms
               
                if(uiCount%20 == 0) {TimeSlice = SET;}        //100ms

                if(uiCount%40 == 0){TimeSlice = SET;}        //200ms

                if(uiCount%200 == 0) {TimeSlice = SET; uiCount = 0;}//1S

                NB_Tick();

                UartTimeout(&amp;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(&amp;EC01Q,Res);
                EC01_handle.rcv_timeout = 2;
        }
}
</code></pre>

<p><i><i><i>FIFO复制到待解析缓存:</i></i></i></p>

<pre>
<code class="language-cpp">U16 NB_CopyFIFOToParseBuf(U8 *uP)
{
        U16 rc = 0;
       
        if(EC01_handle.rcv_status == SET)//串口接收超时
        {
                for(rc = 0; rc &lt; USART_BUFFER_SIZE; rc++)//取出所有数据到待读取缓存
                {
                        if(IsEmpty(&amp;EC01Q) == 0)//有数据
                        {
                                OutQueue(&amp;EC01Q, &amp;uP);
                        }
                        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)
        &amp;&amp; (NB.CON == 0xFF))//初始化完成允许轮询消息,(未初始化完成轮询消息可能无意义)
        {
                if(NB_parseDefaultMsg(&amp;EC01RxCache, EC01_handle.rcv_size))
                {
                       
                }
        }       
}</code></pre>

<p><i><i><i>&nbsp;</i></i></i></p>

<p><i><i><i>程序调用:</i></i></i></p>

<pre>
<i><i><i>
<code class="language-cpp">
void Task_RealTime(void)
{
        NB_Polling_Msg();//轮询NB消息
       
        NB_CycleCheck();//周期性查询NB状态:信号、注册状态、附着状态、Socket连接状态
       
        NB_TCP_ReportProcess();//处理TCP任务
       
        NB_Config();//NB初始化
}


voidTask_100ms(void)
{   
    if(NB.CON != 0xFF)//上电开始闪烁
        {
                LedFlash();
        }
}


voidTask_200ms(void)
{   
    if(NB.CON == 0xFF &amp;&amp; NB.isConnect != 1)//NB初始化完成,Socket未建立
        {
                LedFlash();
        }
}

void Task_1000ms(void)
{
        static U8 Times = 0;

       
        if(NB.CON == 0xFF &amp;&amp; NB.isConnect == 1)//NB初始化完成,Socket建立
        {
                LedFlash();
        }       

        if(Times%10 == 0)
        {
                if(NB.CON == 0xFF)
                {
                        uprintf(&quot;\r\n NB.CON %d CSQ %d RegStatus %d attach %dCGACT_state %d CycleCheckStatus %d ReportStatus %d&quot;,
                        NB.CON, NB.csq, NB.RegStatus,NB.attach, NB.CGACT_state, NB.CycleCheckStatus, NB.ReportStatus);
                        uprintf(&quot; MQTT isConnect %d state %d ret_code %d conn_result %d&quot;, 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(&amp;EC01Q);
        Tim3_Config(49,7199);//5ms
        Delay_init(72);
        USART1_Config(115200);
        USART2_Config(9600);
        LED_Config();

        memset(&amp;EC01_handle, 0, sizeof(uart_process_t));
        memset(EC01RxCache, 0, sizeof(EC01RxCache));
        memset(&amp;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();        }
        }
}</code></i></i></i></pre>

<p><i><i><i>调试运行效果:&nbsp;</i></i></i></p>

<p><i><i><i><a href="https://training.eeworld.com.cn/video/32079" target="_blank">https://training.eeworld.com.cn/video/32079</a></i></i></i></p>

<p><iframe allowfullscreen="true" frameborder="0" height="510" src="https://training.eeworld.com.cn/shareOpenCourseAPI?isauto=true&amp;lessonid=32079" style="background:#eee;margin-bottom:10px;" width="550"></iframe></p>

<p>&nbsp;</p>
</div><script>                                        var loginstr = '<div class="locked">查看本帖全部内容,请<a href="javascript:;"   style="color:#e60000" class="loginf">登录</a>或者<a href="https://bbs.eeworld.com.cn/member.php?mod=register_eeworld.php&action=wechat" style="color:#e60000" target="_blank">注册</a></div>';
                                       
                                        if(parseInt(discuz_uid)==0){
                                                                                                (function($){
                                                        var postHeight = getTextHeight(400);
                                                        $(".showpostmsg").html($(".showpostmsg").html());
                                                        $(".showpostmsg").after(loginstr);
                                                        $(".showpostmsg").css({height:postHeight,overflow:"hidden"});
                                                })(jQuery);
                                        }                </script><script type="text/javascript">(function(d,c){var a=d.createElement("script"),m=d.getElementsByTagName("script"),eewurl="//counter.eeworld.com.cn/pv/count/";a.src=eewurl+c;m.parentNode.insertBefore(a,m)})(document,523)</script>
页: [1]
查看完整版本: 【安信可NB-IoT开发板EC-01F-Kit测评】05.基于STM32+EC-01F Socket通信