FreeRTOS 事件標志組 ——提高篇


假設你已經看過FreeRTOS 事件標志組這篇隨筆了。

之前的基礎篇,真的就只是簡單了解一下,相當於大學實驗室的實驗,但是,我們實際公司項目中,需要更多地思考,就算我們之前只是學習了基礎概念以及基礎語法,只要我們勤加思考,就能靈活的運用基礎知識了,基礎是內功,基礎打好了,功力自然上升。

事件標志組的概念就不再解釋了,直接來正題。

你一定和我剛開始接觸FreeRTOS一樣,知道了事件標志組,也調用過API函數,並且也實現了開發板上歷程的功能,不過開發板歷程僅僅是介紹了某些API函數的用法,僅僅使用法而已。是否有和我當初一樣的想法,到底事件標志組用來干什么,在我的程序設計中,我到底什么時候需要使用事件標志組?

在基礎篇說到,事件標志組,相當於我們裸機開發中最常用的標志位flag。由於在os上跑,全局變量要謹慎使用。

事件標志組有自己的超時等待,也有同步線程的作用。

現在舉例說明:

網絡通信模塊,SIM868,這個我已經使用裸機編寫了第一代代碼,切實體會到超時等待的優勢,如果你沒有足夠的裸機編程經驗,確實很難從教程中體會到超時等待,比如這個SIM868,我發送AT指令,有些指令1s就返回,有些指令幾十秒甚至上分鍾才返回,裸機開發中,要么一個阻塞延時等待(第一代代碼采用的這種方式),要么定時器中斷不斷查詢,這就出現一個問題,比如,我采取發送一個AT指令,延時3s之后,再去讀模塊返回的數據,在網絡好的時候,可能只需要1s就可以讀到模塊返回的數據,這樣我們功能正常,只是多花了2s,但是在網絡不好的時候,可能需要5s模塊才返回,這樣的話,我們的裸機程序就會因為超時沒有接收到數據需要重新發送或者其他什么處理,這就有很大弊端。所以,現在采取操作系統的方式。

其優勢在於,創建兩個任務(線程),一個發送,一個接收。發送任務只管發送,接收任務接收到模塊返回之后立即和發送任務通信,實現消息同步。我們的網絡通信模塊SIM868,就是發送一條AT指令,收到返回數據之后,解析數據,再發送下一條。

好的,那么問題就來了,這,恰恰就是我們事件標志組的用途了啊。

首先說裸機,接收到SIM868返回數據的時候,我們可以立即解析,也可以設置標志,通過這個標志,在其他函數中做處理;

os,通過事件標志組,在接收任務中解析數據,解析完成之后,設置一個事件標志,發送任務等待這個事件的觸發,然后周期性執行。

eg:

聲明和定義:

 

static void AppTaskCreate (void);
static TaskHandle_t xHandleTaskSIM868send = NULL;
static TaskHandle_t xHandleTaskSIM868recive = NULL;
static EventGroupHandle_t xCreatedEventGroup = NULL; 

#define BIT_0    (1 << 0)
#define BIT_1    (1 << 1)
#define BIT_ALL (BIT_0 | BIT_1)

 

main函數:

    AppTaskCreate();
    
        /* 創建任務通信機制 */
    AppObjCreate();
    
  /* 啟動調度,開始執行任務 */
  vTaskStartScheduler();

其他函數:

static void AppObjCreate (void)
{
    /* 創建事件標志組 */
    xCreatedEventGroup = xEventGroupCreate(); 
    
    if(xCreatedEventGroup == NULL)
    {
        /* 沒有創建成功,用戶可以在這里加入創建失敗的處理機制 */
            printf("creak event failure!\r\n");
    }
}

 

static void vTaskSIM868send(void *pvParameters)
{
        
    EventBits_t uxBits;
    const TickType_t xTicksToWait =10000; /* 最大延遲10s */
    SIM868_PowerReset();
    SIM868_instruction();
    while(1)
    {
        comSendBuf(COM4,(uint8_t *)(SendCommand_Init.Echo_Off),strlen(SendCommand_Init.Echo_Off));


        uxBits = xEventGroupWaitBits(xCreatedEventGroup, /* 事件標志組句柄 */
                                     BIT_0,            /* 等待bit0被設置 */
                                     pdTRUE,             /* 退出前bit0被清除,這里是bit0被設置才表示“退出”*/
                                     pdTRUE,             /* 設置為pdTRUE表示等待bit0被設置*/
                                     xTicksToWait);      /* 等待延遲時間 */
        
                if((uxBits & BIT_ALL) == BIT_0)
                {
                    /* 接收到bit0都被設置的消息 */
                    printf("接收到bit0被設置的消息\r\n");
                }
                else
                {
                    
                    printf("沒接收到bit0被設置的消息\r\n");
                }
    }
    
}

 

static void vTaskSIM868recive(void *pvParameters)
{
    
uint8_t read;
        int count =0;
 char buf[100]={0};
 char buf1[1];
EventBits_t uxBits;
    while(1)
    {
            for(int i=0;i<100;i++)
                 {
                    while(comGetChar(COM4, &read))
                    {
                        sprintf(buf1, "%c", read);
                            if((read!='\r')&&( read !='\n'))//不存放這兩個特殊字符
                            {
                                buf[count++]=read;
                            }
         
                        vTaskDelay(1);
                    }
                    count=0;
            switch(buf[0])
            {
                case 'A':
                    if(!strncmp(buf,"ATE0OK",6))
                                    {
                                        comClearRxFifo(COM4);//清除緩
                                        printf("去除回顯\r\n");
                                        memset(buf,0,6);
                                        uxBits = xEventGroupSetBits(xCreatedEventGroup, BIT_0);
                                                    if((uxBits & BIT_0) != 0)
                                                    {
                                                        printf("收到ok返回並且事件標志置位");
                                                    }
                                                    else
                                                    {
                                                        printf("noready\r\n");
                                                    }
                                                    break;
                                    }
                                    break;
                }
            }
                    
        }
}

 

static void AppTaskCreate (void)
{
    xTaskCreate( vTaskSIM868send,       /* 任務函數  */
                 "vTaskSIM868send",         /* 任務名    */
                 512,                   /* 任務棧大小,單位word,也就是4字節 */
                 NULL,                  /* 任務參數  */
                 2,                     /* 任務優先級*/
                 &xHandleTaskSIM868send );  /* 任務句柄  */
    
    
    xTaskCreate( vTaskSIM868recive,            /* 任務函數  */
                "vTaskSIM868recive",          /* 任務名    */
                 512,                 /* 任務棧大小,單位word,也就是4字節 */
                 NULL,                /* 任務參數  */
                 1,                   /* 任務優先級*/
                 &xHandleTaskSIM868recive ); /* 任務句柄  */

}

在啟動調度之前,我們先創建了事件標志組,采用24bit的方式,這個在基礎篇已有說明。

現在,創建兩個任務,一個發送,一個接收。

發送函數,首先,通過串口給SIM868發送去回顯指令,然后就進入阻塞態,因為wait函數會讓其阻塞,這里設置的最大等待時間是10s,一般的指令,10s內都返回了,特殊的關系到網絡問題的指令,時間再根據需要更改。

然后,接收任務中,就在串口里面讀取SIM868返回的數據,解析數據之后,調用set函數,此時高優先級的發送任務立即退出阻塞態,打斷低優先級的接收任務,執行后面的指令,當然這里僅僅舉例,因為我這里就一條指令,以后有機會分享不涉及公司的demo。

這樣的好處在於,第一,通過os的方式,獨立發送和接收,發送只管發,接收到了就把相應的事件標志位置位,通知wait的任務,為了立即響應,wait任務優先級設置比set任務的高,這樣在10s超時等待時間內,如果網絡好,我1s就可返回,網絡不好,等待5s也無所謂,特殊需要長時間等待的指令再特殊處理。這樣,就會讓系統運行更加高效,尤其是低功耗類產品。其次,在裸機開發中,我之前使用阻塞延時方式,當多個指令都返回的是ok的時候,我不能很方便的打印出到底是哪個指令返回的ok,而os使用事件標志組之后,我一定能知道是哪個觸發的。最后,一個任務通知另外一個,標志着某個動作完成或者異常時,就是我們使用事件標志組的時候。

在這里,因為當初很是困惑,開發板廠商的代碼大多換湯不換葯,一個調調:

這里,由於接收任務優先級低於發送,所以if條件不會滿足,為什么呢?因為事件標志置位,讓wait的高優先級任務返回了,會清除這個標志,所以,打印else的內容。在最初開開發板教程的時候,我只覺得if滿足的條件才證明是事件被置位了,可是它依托於wait函數,這個在基礎教程中也說到了。

那么,我們實際項目中,其實就應該只判斷else的內容,滿足else分支,證明執行wait函數的任務已經返回(當然這是建立在執行wait函數的任務優先級高於執行set函數的任務的前提下,這個也是比較推薦的方式),這說明了什么?說明我們不能死板地學習教程,開發板教程僅僅是熟悉,需要自己思考,自己去官網,論壇,Google查詢資料,並且一定要有自己獨立思考地過程,這樣,基礎有了,進階就會容易得多。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM