新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > RISC-V單片機快速入門05-玩轉ESP8266 WIFI模塊②

        RISC-V單片機快速入門05-玩轉ESP8266 WIFI模塊②

        作者:一葉孤沙 時間:2020-06-28 來源:知乎 收藏

        前言:

        本文引用地址:http://www.104case.com/article/202006/414785.htm

        上一節,我們使用發送AT指令操作ESP-01S,本節,使用代替傳偶工具完成和的交互過程。

        一、基礎知識

        1.交互流程簡介

        (1)設備上電,先控制8266的復位引腳為低電平,讓模塊復位

        (2)發送指令:ATE0,取消回顯

        (3)發送指令:AT+CWMODE=2,設置ESP01S為AP模式

        (4)發送指令:AT+CIPMUX=1,設置多路連接,AP模式最多支持5個設備連接

        (5)發送指令:AT+CWSAP="ESP01S_test","12345678",1,3,啟動一個WIFI熱點

        (6)發送指令:AT+CIPSERVER=1,8089,啟動TCP Server

        (7)發送指令:AT+CIPSERVER=1,8089,啟動TCP Server

        (8)大循環中檢測是否收到ESP01S數據,收到數據后立刻返回。

        2.程序框架簡介

        程序主要包括如下4個功能模塊:ESP01S初始化、串口處理、Event回調函數、事件處理;串口處理模塊包括串口接收和定時器判斷一幀數據是否接收完成功能,Event回調函數主要用來通知應用層系統的狀態,方便應用層做出相應,比如設備檢測到其他TCP Client客戶端接入模塊,可以控制LED狀態,事件處理模塊主要包含應用程序大循環,大循環中檢測系統事件狀態,根據事件狀態再大循環中做出響應。

        二、系統功能模塊詳述

        1.Event回調函數

        本程序使用了函數指針,應用層將事件處理函數傳到hal_common.c中int hal_sys_contex_init(sys_status_fun fun, void *user_data)函數

        void system_status_callback(int sock, int event)
        {
            system_context->sock_id = sock;
            system_context->event = event;
         switch (event)
            {
         case STA_CONNECTED:
                rt_kprintf("Sock %d connected!rn", sock);
         break;
         case STA_CLOSED:
                rt_kprintf("Sock %d closed!rn", sock);
         break;
         case STA_DATA_ARRIVED:
                rt_kprintf("Sock %d data arrived!rn", sock);
         break;
         default:
         break;
            }
        }
        
        
        typedef enum {
            STA_CONNECTED,
            STA_CLOSED,
            STA_DATA_ARRIVED, // clients send data to 
            STA_EVENT_MAX,
        }sys_event_e;
        
        
        typedef void (*sys_status_fun)(int sock, int event);
        
        
        typedef struct sys_ctx{
         int sock_id;
            sys_event_e event;
         char data_buf[SYS_CTX_UART_RECV_SIZE];
            sys_status_fun sys_status_cb;
         void *user_data;
        }sys_ctx_t;
        
        
        int hal_sys_contex_init(sys_status_fun fun, void *user_data)
        {
            sys_contex.sys_status_cb = fun;
            sys_contex.user_data = user_data;
         return 0;
        }
        
        int main(void)
        {
            hal_sys_contex_init(system_status_callback, RT_NULL);
         
         while(1)
            {
         
            }
        }

        2.串口處理

        串口處理模塊包括串口接收和定時器判斷一幀數據是否接收完成功能,串口接收函數代碼如下:

        #define RX_BUF_MAX_LEN     1024         //最大接收緩存字節數
        
        
        struct STRUCT_USART_Fram_S             //串口數據幀的處理結構體
        {
         char  Data_RX_BUF [ RX_BUF_MAX_LEN ];
         uint16_t FramLength;
         struct {
         uint8_t FramStartFlag;
         uint8_t FramFinishFlag;
            } InfBit;
        } ;
        
        
        struct STRUCT_USART_Fram_S Esp8266_Frame_Record;
        
        
        void USART2_IRQHandler()
        {
         uint8_t ch = -1;
         if(RESET != usart_interrupt_flag_get(EVAL_COM2, USART_INT_FLAG_RBNE))
            {
                ch =  usart_data_receive(EVAL_COM2);
        //      if ( Esp8266_Frame_Record.FramLength < ( RX_BUF_MAX_LEN - 1 ) )                       //預留1個字節寫結束符
        //      {
                    Esp8266_Frame_Record .Data_RX_BUF [ Esp8266_Frame_Record.FramLength ]  = ch;
        //      }
                Esp8266_Frame_Record.FramLength ++;
         if (Esp8266_Frame_Record.FramLength >= 1024)
                {
                    Esp8266_Frame_Record.FramLength = 0;
                }
                cnt = Esp8266_Frame_Record.FramLength;
        //      rt_kprintf(".......uart recv : %c, count is %drn", ch, cnt);
                Esp8266_Frame_Record.InfBit.FramStartFlag = 1;
            }
        }

        中斷處理函數中,將接收的數據放到Esp8266_Frame_Record .Data_RX_BUF中,然后將

        Esp8266_Frame_Record.InfBit.FramStartFlag置1,這個標志位再定時器中會用到,可以用來判斷接收一幀數據是否完成。

        一幀數據接收是否完成的判斷邏輯是:定時器會定期檢測,如果FramStartFlag為1,說明串口正在接收數據,沒接收一個數據,FramLength加1,因此,當進入定時器中斷函數,判斷FramStartFlag為1情況下FrameLength如果不再增加,說明一幀數據接收完成。

        static void timeout1(void *parameter)
        {
         int sock_id = -1;
            char buff[128] = { 0x00 };
         int len = 0;
            sys_event_e event = STA_EVENT_MAX;
         
        //  rt_kprintf("timer's cnt is %d, FrameLength is %drn", cnt, Esp8266_Frame_Record.FramLength);
         if (1 == Esp8266_Frame_Record.InfBit.FramStartFlag)
            {
         if (cnt == Esp8266_Frame_Record.FramLength && cnt != 0)
                {
                    cnt = 0;
                    Esp8266_Frame_Record .Data_RX_BUF [ Esp8266_Frame_Record.FramLength ]  = 0x00;
                    rt_kprintf("timer --------> data %srn", Esp8266_Frame_Record.Data_RX_BUF);
         if (rt_strstr(Esp8266_Frame_Record.Data_RX_BUF, "CONNECT"))
                    {
                        sscanf(Esp8266_Frame_Record.Data_RX_BUF, "%d,%s", &sock_id, buff);
                        event = STA_CONNECTED;
                    }else if (rt_strstr(Esp8266_Frame_Record.Data_RX_BUF, "CLOSED"))
                    {
                        sscanf(Esp8266_Frame_Record.Data_RX_BUF, "%d,%s", &sock_id, buff);
                        event = STA_CLOSED;
                    }else if (rt_strstr(Esp8266_Frame_Record.Data_RX_BUF, "+IPD"))
                    {
                        rt_memset(hal_sys_contex_get()->data_buf, 0x00, SYS_CTX_UART_RECV_SIZE);
                        sscanf(Esp8266_Frame_Record.Data_RX_BUF, "%*[^+]+IPD,%d,%d:%[^r]", &sock_id, &len, hal_sys_contex_get()->data_buf);
                        event = STA_DATA_ARRIVED;
                        rt_kprintf("parsed +IPD :%srn", hal_sys_contex_get()->data_buf);
                    }
                    // call sys_status_cb
         if (hal_sys_contex_get()->sys_status_cb)
                    {
                        hal_sys_contex_get()->sys_status_cb(sock_id, event);
                    }
         
                    Esp8266_Frame_Record.InfBit.FramFinishFlag = 1;
                    Esp8266_Frame_Record.InfBit.FramStartFlag = 0;
                }else
                {
                    cnt = Esp8266_Frame_Record.FramLength;
                }
            }else
            {
                cnt = 0;
                Esp8266_Frame_Record.FramLength = 0;
            }
        }

        注意:事件處理本質上是在此調用hal_sys_contex_get()->sys_status_cb(sock_id, event)映射到應用層的void system_status_callback(int sock, int event)函數。

        3.事件處理

        事件處理的核心再while(1)中,根據系統當前事件狀態做出響應,本節是檢測到事件為數據類型時候,將數據原路返回。

        int main(void)
        {
         /* enable the LED clock */
            rcu_periph_clock_enable(RCU_GPIOA);
         /* configure LED GPIO port */
            gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_1);
            gpio_bit_reset(GPIOA, GPIO_PIN_1);
         // create iwdt_thread
            dynamic_thread = rt_thread_create("led_thread", led_process_thread_entry,
                                                RT_NULL, 512, 2, 10);
            rt_thread_startup(dynamic_thread);
         // init sys_ctx
            hal_sys_contex_init(system_status_callback, RT_NULL);
            system_context = hal_sys_contex_get();
            hal_timer_init();
            ESP8266_Init();
            rt_thread_mdelay(1000);
            ESP8266_Ate0();
            tcp_server_init();
            tcp_server_start();
         
         while(1)
            {
         if (STA_DATA_ARRIVED == system_context->event)
                {
         // send back
                    ESP8266_SendString ( DISABLE, system_context->data_buf, rt_strlen(system_context->data_buf), system_context->sock_id );
                }
                rt_thread_mdelay(10);
            }
         return 0;
        }

        三、運行

        下載程序完畢后,重啟設備,ESP01S啟動一個WIFI熱點,并啟動TCP Server,log如下:

        電腦連接熱點,使用網絡助手連接192.168.4.1:8089

        網絡助手發送數據給ESP01S


        關閉網絡助手,應用程序也可以檢測到,如下Log所示





        評論


        相關推薦

        技術專區

        關閉
        主站蜘蛛池模板: 慈利县| 盐津县| 广德县| 政和县| 社旗县| 福建省| 黄平县| 蓝山县| 焉耆| 白银市| 自贡市| 且末县| 交口县| 金门县| 丹寨县| 昌吉市| 澄城县| 黎城县| 河北区| 满洲里市| 柳河县| 克东县| 安平县| 贺兰县| 德钦县| 平邑县| 塔河县| 黄龙县| 焦作市| 乌兰察布市| 博客| 武隆县| 浮山县| 景洪市| 克东县| 梧州市| 桑日县| 山阴县| 南平市| 光山县| 炉霍县|