1. 程式人生 > >【二代示波器教程】第15章 FreeRTOS操作系統版本二代示波器實現

【二代示波器教程】第15章 FreeRTOS操作系統版本二代示波器實現

per lamp 轉換 length 失去 最大值 ucd 參數 state

第15章 FreeRTOS操作系統版本二代示波器實現

本章教程為大家講解FreeRTOS操作系統版本的二代示波器實現。主要講解RTOS設計框架,即各個任務實現的功能,任務間的通信方案選擇,任務棧,系統棧以及全局變量共享問題。同時,工程調試方法也專門做了說明。

15.1 註意事項(重要必讀)

15.2 任務功能劃分

15.3 用戶任務優先級設置

15.4 全局變量分配,系統堆棧和任務堆棧

15.5 任務間通信和全局變量共享問題

15.6 FreeRTOS系統調試

15.7 MDK優化等級

15.8 總結

15.1 註意事項(重要必讀)

1、學習本章節前,務必保證已經學習完畢前面章節。另外,工程代碼註釋已經比較詳細,了解了框架後,直接看源碼即可。

2、僅支持800*480分辨率顯示屏,如果是電容屏,無需校準。如果是電阻屏,需要校準,按下按鍵K1即可進入校準界面。

3、由於按鍵不夠用,K1按鍵的消息處理做了三個條件編譯,詳情見本章15.6小節。默認K1按鍵執行觸摸校準,也可以選擇執行截圖或者串口打印任務執行情況。另外,不管當前處於任何界面都可以進行觸摸校準,僅電阻屏需要校準,電容屏無需校準。

4、STemWin5.40版本的截圖功能有bug,詳情看此貼:

http://forum.armfly.com/forum.php?mod=viewthread&tid=82445 。

當前用的5.32版本,也是來自STemWin軟件包。

5、FreeRTOS工程的文件系統是采用的FatFS,當前開啟了MDK最高等級優化和時間優化。如果大家要使用FatFS功能,請務必關閉時間優化,因為FatFS在時間優化下會工作異常。詳情見本章15.7小節。

6、工程編譯支持MDK4.7X和MDK5。另外不支持MDK發布的MDK5.24及其以上版本,因為這個版本不支持MDK4創建的工程轉換為MDK5了,所以要使用這個最新的版本,需要給MDK5安裝MDK4的兼容包。

15.2 任務功能劃分

前面第三章已經將任務功能劃分好:

技術分享圖片

根據這個功能劃分,創建所需要的任務。

15.2.1 主函數創建

在main.c文件實現:

/*

*********************************************************************************************************

*    函 數 名: main

*    功能說明: 標準c程序入口。

*    形    參: 無

*    返 回 值: 無

*********************************************************************************************************

*/ int main(void) { /* 在啟動調度前,為了防止初始化STM32外設時有中斷服務程序執行,這裏禁止全局中斷(除了NMI和HardFault)。 這樣做的好處是: 1. 防止執行的中斷服務程序中有FreeRTOS的API函數。 2. 保證系統正常啟動,不受別的中斷影響。 3. 關於是否關閉全局中斷,大家根據自己的實際情況設置即可。 在移植文件port.c中的函數prvStartFirstTask中會重新開啟全局中斷。通過指令cpsie i開啟,__set_PRIMASK(1) 和cpsie i是等效的。 */ __set_PRIMASK(1); /* 硬件初始化 */ bsp_Init(); /* 1. 初始化一個定時器中斷,精度高於滴答定時器中斷,這樣才可以獲得準確的系統信息 僅供調試目的,實際項 目中不要使用,因為這個功能比較影響系統實時性。 2. 為了正確獲取FreeRTOS的調試信息,可以考慮將上面的關閉中斷指令__set_PRIMASK(1); 註釋掉。 */ vSetupSysInfoTest(); /* 創建任務通信機制 */ AppObjCreate(); /* 創建任務 */ AppTaskCreate(); /* 啟動調度,開始執行任務 */ vTaskStartScheduler(); /* 如果系統正常啟動是不會運行到這裏的,運行到這裏極有可能是用於定時器任務或者空閑任務的 heap空間不足造成創建失敗,此要加大FreeRTOSConfig.h文件中定義的heap大小: #define configTOTAL_HEAP_SIZE ( ( size_t ) ( 17 * 1024 ) ) */ while(1); }

主函數中做的工作比較多,下面依次為大家做個說明:

調用函數bsp_Init()做硬件初始化

硬件外設的初始化函數bsp_Init是在 bsp.c 文件實現:

/*

*********************************************************************************************************

*    函 數 名: bsp_Init

*    功能說明: 初始化所有的硬件設備。該函數配置CPU寄存器和外設的寄存器並初始化一些全局變量。只需要調用一次

*    形    參:無

*    返 回 值: 無

*********************************************************************************************************

*/

void bsp_Init(void)

{

     /*

         由於ST固件庫的啟動文件已經執行了CPU系統時鐘的初始化,所以不必再次重復配置系統時鐘。

         啟動文件配置了CPU主時鐘頻率、內部Flash訪問速度和可選的外部SRAM FSMC初始化。

 

         系統時鐘缺省配置為168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件

     */

     /* 使能CRC 因為使用STemWin前必須要使能 */

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_CRC, ENABLE);

    

     /* 優先級分組設置為4,可配置0-15級搶占式優先級,0級子優先級,即不存在子優先級。*/

     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

    

     SystemCoreClockUpdate();    /* 根據PLL配置更新系統時鐘頻率變量 SystemCoreClock */

 

     bsp_InitDWT();     /* 初始DWT */

     bsp_InitUart();    /* 初始化串口 */

     bsp_InitKey();     /* 初始化按鍵變量(必須在 bsp_InitTimer() 之前調用) */

         

     bsp_InitI2C();          /* 配置I2C總線 */

    

     bsp_InitExtSDRAM();

     bsp_DetectLcdType(); /* 檢測觸摸板和LCD面板型號, 結果存在全局變量 g_TouchType, g_LcdType */

    

     TOUCH_InitHard();  /* 初始化配置觸摸芯片 */

     LCD_ConfigLTDC();   /* 初始化配置LTDC */

    

     DSO_ConfigCtrlGPIO();   /* 初始化示波器模塊的引腳配置 */ 

    

     bsp_InitADC();          /* 初始化ADC1,ADC2和ADC3 */

     bsp_InitDAC1();         /* 初始化DAC1 */

    

     g_DAC1.ucDuty = 50;     /* 初始化DAC配置,用於信號發生器 */

     g_DAC1.ucWaveType = 0;

     g_DAC1.ulAMP = 4095;

     g_DAC1.ulFreq = 10000;

     dac1_SetSinWave(g_DAC1.ulAMP, g_DAC1.ulFreq);

 

     MountSD();               /* 掛載SD卡 */

        

     TIM8_MeasureTrigConfig(); /* 初始化TIM8用於記錄一段波形 */

}

調用函數vSetupSysInfoTest()初始化系統信息調試功能

這個函數涉及到的內容比較多,需要大家專門看我們FreeRTOS教程的第8章,有詳細講解:

http://forum.armfly.com/forum.php?mod=viewthread&tid=17658 。

調用函數AppObjCreate()創建任務通信機制

實現代碼如下,其中事件標誌的創建比較重要,emWin任務和數字信號處理任務之間通信要使用。

/*

*********************************************************************************************************

*    函 數 名: AppObjCreate

*    功能說明: 創建任務通信機制

*    形    參: 無

*    返 回 值: 無

*********************************************************************************************************

*/

static void AppObjCreate (void)

{

     /* 創建互斥信號量 */

    xMutex = xSemaphoreCreateMutex();

    

     if(xMutex == NULL)

    {

        /* 沒有創建成功,用戶可以在這裏加入創建失敗的處理機制 */

    }

    

     /* 創建事件標誌組 */

     xCreatedEventGroup = xEventGroupCreate();

    

     if(xCreatedEventGroup == NULL)

    {

        /* 沒有創建成功,用戶可以在這裏加入創建失敗的處理機制 */

    }

    

    /* 動態內存申請部分,省略未貼 */

}

調用函數AppTaskCreate()創建任務

創建了如下五個任務:

/*

*********************************************************************************************************

*    函 數 名: AppTaskCreate

*    功能說明: 創建應用任務

*    形    參: 無

*    返 回 值: 無

*********************************************************************************************************

*/

static void AppTaskCreate (void)

{

     xTaskCreate(  vTaskGUI,         /* 任務函數  */

                  "vTaskGUI",        /* 任務名    */

                  1024,              /* 任務棧大小,單位word,也就是4字節 */

                  NULL,              /* 任務參數  */

                  1,                 /* 任務優先級*/

                  xHandleTaskGUI );   /* 任務句柄  */

    

    xTaskCreate( vTaskTaskUserIF,   /* 任務函數  */

                 "vTaskUserIF",     /* 任務名    */

                 512,               /* 任務棧大小,單位word,也就是4字節 */

                 NULL,              /* 任務參數  */

                 2,                 /* 任務優先級*/

                 &xHandleTaskUserIF );  /* 任務句柄  */

    

     xTaskCreate( vTaskMsgPro,            /* 任務函數  */

                 "vTaskMsgPro",           /* 任務名    */

                 512,                     /* 任務棧大小,單位word,也就是4字節 */

                 NULL,                    /* 任務參數  */

                 3,                       /* 任務優先級*/

                 &xHandleTaskMsgPro );  /* 任務句柄  */

    

     xTaskCreate( vTaskStart,             /* 任務函數  */

                 "vTaskStart",            /* 任務名    */

                 512,                     /* 任務棧大小,單位word,也就是4字節 */

                 NULL,                    /* 任務參數  */

                 4,                       /* 任務優先級*/

                 &xHandleTaskStart );   /* 任務句柄  */

                    

     xTaskCreate( vTaskDSO,               /* 任務函數  */

                  "vTaskDSO",             /* 任務名    */

                   1024,                  /* 任務棧大小,單位word,也就是4字節 */

                   NULL,                  /* 任務參數  */

                   5,                     /* 任務優先級*/

                   &xHandleTaskStart );    /* 任務句柄  */

}

15.2.2 啟動任務(觸摸和按鍵)

啟動任務實現的功能比較簡單,主要是按鍵掃描和觸摸掃描:

/*

*********************************************************************************************************

*    函 數 名: vTaskStart

*    功能說明: 啟動任務,主要實現按鍵檢測和觸摸檢測。

*    形    參: pvParameters 是在創建該任務時傳遞的形參

*    返 回 值: 無

*   優 先 級: 5 

*********************************************************************************************************

*/

static void vTaskStart(void *pvParameters)

{

    uint8_t  ucCount = 0;

 

    

    while(1)

    {

         /* 1ms一次觸摸掃描,電阻觸摸屏 */

         if(g_tTP.Enable == 1)

         {

              TOUCH_Scan();

             

              /* 按鍵掃描 */

              ucCount++;

              if(ucCount == 10)

              {

                   ucCount = 0;

                   bsp_KeyScan();

              }

              vTaskDelay(1);                  

         }

        

         /* 10ms一次觸摸掃描,電容觸摸屏GT811 */

         if(g_GT811.Enable == 1)

         {

              bsp_KeyScan();

              GT811_OnePiontScan();

              vTaskDelay(10);       

         }

        

         /* 10ms一次觸摸掃描,電容觸摸屏FT5X06 */

         if(g_tFT5X06.Enable == 1)

         {

              bsp_KeyScan();

              FT5X06_OnePiontScan();

             vTaskDelay(10);   

         }

     }

}

知識點拓展

新版emWin教程第4章或者第5章,對觸摸的實現做了詳細講解:

http://forum.armfly.com/forum.php?mod=viewthread&tid=19834 。

15.2.3 信號處理任務

信號處理任務的實現如下:

/*

*********************************************************************************************************

*    函 數 名: AppTaskDSO

*    功能說明: 雙通道示波器數據處理任務。

*    形    參: pvParameters 是在創建該任務時傳遞的形參

*    返 回 值: 無

*   優 先 級: 6 

*********************************************************************************************************

*/

static  void vTaskDSO(void *pvParameters)

{

     EventBits_t uxBits;

     uint8_t pcWriteBuffer[512];

    

    

     /* 實數序列FFT長度 */

     fftSize = 2048;

    

     /* 正變換 */

    ifftFlag = 0;

    

     /* 初始化結構體S中的參數 */

     arm_rfft_fast_init_f32(&S, fftSize);

    

    while(1)

    {

         /* 等待所有任務發來事件標誌 */

         uxBits = xEventGroupWaitBits(xCreatedEventGroup, /* 事件標誌組句柄 */

                                   0xFFFF,  /* 等待0xFFFF某一位被設置 */

                                   pdTRUE,  /* 退出前0xFFFF位被清除,這裏是任意0xFFFF位被設置就“退出”*/

                                   pdFALSE, /* 設置為pdTRUE表示等待0xFFFF任意位被設置*/

                                   portMAX_DELAY);  /* 等待延遲時間 */   

         switch (uxBits)

         {

              /* 雙通道波形處理 */

              case DspFFT2048Pro_15:

                   /* 讀取的是ADC3的位置 */

                   g_DSO1->usCurPos = 10240 - DMA2_Stream1->NDTR;

             

                   /* 讀取的是ADC1的位置 */

                   g_DSO2->usCurPos = 10240 - DMA2_Stream0->NDTR;

             

                   DSO2_WaveTrig(g_DSO2->usCurPos);

                   DSO1_WaveTrig(g_DSO1->usCurPos);

                   DSO2_WaveProcess();

                   DSO1_WaveProcess();

                   break;

 

              /* 用於簡單的ADC數據采集 */

              case DspMultiMeterPro_14:

                   g_uiAdcAvgSample = ADC_GetSampleAvgN();

                   break;

             

              /* 僅用於調試目的,打印任務的執行情況,默認不使用 */

              case DspTaskInfo_13:

                   App_Printf("=================================================\r\n");

                   App_Printf("任務名      任務狀態 優先級   剩余棧 任務序號\r\n");

                   vTaskList((char *)&pcWriteBuffer);

                   App_Printf("%s\r\n", pcWriteBuffer);

             

                   App_Printf("\r\n任務名       運行計數         使用率\r\n");

                   vTaskGetRunTimeStats((char *)&pcWriteBuffer);

                   App_Printf("%s\r\n", pcWriteBuffer);

                   App_Printf("當前動態內存剩余大小 = %d字節\r\n", xPortGetFreeHeapSize());

                   break;

             

              /* 其它位暫未使用 */

              default:

                   App_Printf("*ucReceive = %x\r\n", uxBits);

                   break;

                      

         }

    }

}

根據接收到的不同任務消息來處理不同的功能,要處理的消息分為三類:

1、雙通道波形數據處理

主要實現軟件觸發,計算FFT ,FIR ,RMS,最大值,最小值,平均值和峰峰值。兩個通道都進行了處理。具體實現方法已經在前面章節為大家做了講解。

2、簡單電壓測量處理

這個功能比較簡單,就是獲取一組ADC數值,然後求平均。

3、打印任務執行情況

通過串口打印任務棧的使用情況和各個任務的CPU利用率。

15.2.4 GUI任務

emWin任務的實現代碼如下:

/*

*********************************************************************************************************

*    函 數 名: vTaskGUI

*    功能說明: emWin任務

*    形    參: pvParameters 是在創建該任務時傳遞的形參

*    返 回 值: 無

*   優 先 級: 1  (數值越小優先級越低,這個跟uCOS相反)

*********************************************************************************************************

*/

static void vTaskGUI(void *pvParameters)

{

     while (1)

     {

         MainTask();

     }

}

emWin的代碼都是在函數MainTask裏面實現,這樣做是方便在main.c文件裏面統一管理任務。關於GUI部分最重要的界面優化,波形刷新優化,波形瀏覽等,在前面章節已經都做了講解,我們這裏不再贅述。更詳細的實現,需要結合前面章節的講解去看源碼。

15.2.5 用戶接口任務

這個任務暫時未執行任何功能,保留供以後升級使用。代碼如下:

/*

*********************************************************************************************************

*    函 數 名: vTaskTaskUserIF

*    功能說明: 保留,暫時未用到。

*    形    參: pvParameters 是在創建該任務時傳遞的形參

*    返 回 值: 無

*   優 先 級: 2

*********************************************************************************************************

*/

static void vTaskTaskUserIF(void *pvParameters)

{

    while(1)

    {

         vTaskDelay(1000);

     }

}

15.2.6 文件系統處理任務

當前文件系統處理任務主要用來做截圖功能,將GUI界面以BMP格式存儲到SD卡裏面:

/*

*********************************************************************************************************

*    函 數 名: vTaskMsgPro

*    功能說明: 實現截圖功能,將圖片以BMP格式保存到SD卡中

*    形    參: pvParameters 是在創建該任務時傳遞的形參

*    返 回 值: 無

*   優 先 級: 4 

*********************************************************************************************************

*/

static void vTaskMsgPro(void *pvParameters)

{

     uint8_t  Pic_Name = 0;

     uint32_t ulStart, ulEnd;

     char buf[20];

    

    while(1)

    {

         ulTaskNotifyTake( pdTRUE,          /* 此參數設置為pdTRUE,接收到的notification value清零 */

                              portMAX_DELAY ); /* 無限等待 */

        

         sprintf(buf,"0:/PicSave/%d.bmp",Pic_Name);

        

          /* 記錄截圖前起始時間 */

         ulStart = xTaskGetTickCount();

        

         /* 開啟調度鎖 */  

         //vTaskSuspendAll();

        

         /* 如果SD卡中沒有PicSave文件,會進行創建 */

         result = f_mkdir("0:/PicSave");

         /* 創建截圖 */

         result = f_open(&file, buf, FA_WRITE|FA_CREATE_ALWAYS);

         /* 向SD卡繪制BMP圖片 */

         GUI_BMP_Serialize(_WriteByte2File, &file);

        

         /* 創建完成後關閉file */

         result = f_close(&file);

        

         /* 關閉調度鎖 */  

         //xTaskResumeAll ();

        

          /* 記錄截圖後時間並獲取截圖過程耗時 */

         ulEnd = xTaskGetTickCount();

         ulEnd -= ulStart;

        

         App_Printf("截圖完成,耗時 = %dms\r\n", ulEnd);

         Pic_Name++;  

    }

}

後期這個任務將被升級,用於將波形數據以CSV文件格式存儲到SD卡裏面。

15.3 用戶任務優先級設置

當前任務的優先級安排如下(數值越小,優先級越低):

vTaskDSO任務 : 優先級5。

DSP任務一定要是優先級最高的,因為采集的數據要實時處理。

vTaskStart 任務 : 優先級4。

vTaskMsgPro任務 : 優先級3。

啟動任務(觸摸和按鍵掃描)以及MsgPro(文件系統處理)任務的優先級誰高誰低都沒有關系。

vTaskUserIF任務 :優先級2。

保留,未使用任務,暫且安排為這個優先級。

vTaskGUI任務 :優先級1。

emWin任務是除了空閑任務,統計任務以外最低優先級的,因為emWin極其占用系統資源,而且時間長,如果這個任務設置為高優先級,會直接影響低優先級任務的執行。

知識點拓展

關於任務優先級的安排,在我們RTX操作系統教程第8章的8.2小節有些拓展:

http://forum.armfly.com/forum.php?mod=viewthread&tid=14837。

在我們FreeRTOS操作系統教程的第13章的13.2小節有些拓展:

http://forum.armfly.com/forum.php?mod=viewthread&tid=17658。

15.4 全局變量分配,系統堆棧和任務堆棧

示波器的設計需要很多變量進行邏輯管理,從設計之初就需要將變量分類進行結構體封裝,方便以後的維護升級。這一步至關重要,實際中差不多要定義上百個變量,如果不進行分類管理,以後的升級維護將非常麻煩。

這種方式還有一個好處是方便我們將F429的CCM RAM空間分配給這些變量使用。使用CCM RAM的好處是速度比通用RAM要快些,缺點是這部分空間不支持DMA操作。初次使用的用戶比較容易在這個地方犯錯誤。所以在使用局部變量時,切勿將局部變量用於DMA傳輸。

1、任務棧

因為直接將FreeRTOS的動態內存管理文件heap_4.c中的數組重定向到CCM RAM空間了,那麽任務棧以及所有組件需要的內存空間都是來自CCM RAM。

/*

**********************************************************************************************************

                                   FreeRTOS的任務棧空間使用CCM RAM區

**********************************************************************************************************

*/

#if defined ( __CC_ARM )       /* MDK編譯器 */

  uint8_t ucHeap[64*1024] __attribute__((at(0x10000000)));

#elif defined ( __ICCARM__ )   /* IAR編譯器 */

  #pragma location=0x10000000

  uint8_t ucHeap[64*1024];

#endif

知識點拓展1

關於任務棧大小應該分配多大的問題,可以看FreeRTOS教程第11章:

http://forum.armfly.com/forum.php?mod=viewthread&tid=17658 。

知識點拓展2

FreeRTOS教程第28章:動態內存管理。

http://forum.armfly.com/forum.php?mod=viewthread&tid=17658 。

2、全局變量分配

當前需要頻繁調用的變量也通過動態內存管理分配給各個結構體變量,使用的CCM RAM空間。

/*

*********************************************************************************************************

*    函 數 名: AppObjCreate

*    功能說明: 創建任務通信機制

*    形    參: 無

*    返 回 值: 無

*********************************************************************************************************

*/

static void AppObjCreate (void)

{

 

     /* 任務消息機制的創建,省略未寫 */

 

     /* 申請示波器通道1動態內存 */

    g_DSO1 = (DSO_T *)pvPortMalloc(sizeof(DSO_T));

    

     /* 申請示波器通道2動態內存 */

    g_DSO2 = (DSO_T *)pvPortMalloc(sizeof(DSO_T));

    

     /* 申請遊標測量結構體變量動態內存 */

    g_Cursors = (CURSORS_T *)pvPortMalloc(sizeof(CURSORS_T));

 

     /* 申請標誌位結構體變量動態內存 */

    g_Flag = (FLAG_T *)pvPortMalloc(sizeof(FLAG_T));

    

     /* 申請觸發結構體變量動態內存 */

    g_TrigVol = (TRIVOLTAGE_T *)pvPortMalloc(sizeof(TRIVOLTAGE_T));

    

     /* 申請FFT動態內存 */

    testInput_fft_2048 = (float32_t *)pvPortMalloc(sizeof(float32_t)*2048);

    testOutput_fft_2048 = (float32_t *)pvPortMalloc(sizeof(float32_t)*2048);

    

     /* 申請RMS動態內存 */

     g_RMSBUF = (float32_t *)pvPortMalloc(sizeof(float32_t)*600);

    

     /* 申請FIR動態內存 */

    FirDataInput = (float32_t *)pvPortMalloc(sizeof(float32_t)*FIR_LENGTH_SAMPLES);

    FirDataOutput = (float32_t *)pvPortMalloc(sizeof(float32_t)*FIR_LENGTH_SAMPLES);

     firStateF32 = (float32_t *)pvPortMalloc(sizeof(float32_t)*FIR_StateBufSize);

}

3、系統棧分配

系統棧分配的大小如下:

技術分享圖片

15.5 任務間通信機制和全局變量共享問題

二代示波器的雙通道ADC通過DMA方式在實時的采集數據,每個通道的緩沖大小是1024*20字節,采集的數據經過信號處理後送給GUI任務進行波形顯示和測量值顯示。為了實現這個功能,專門測試了兩種方案。

(1)方案一

采用DMA雙緩沖,一路緩沖采集波形的時候,另一路已經采集的波形數據發給數字信號處理任務,信號處理任務再將整理好的波形數據和測量值發給emWin任務做刷新。這種方式的優點是ADC采集的數據可以實時處理。缺點是F429處理不過來,比如我們一個通道的采樣率是2Msps,緩沖大小設置為2048,將緩沖填滿需要1ms左右的時間,而我們僅做一個2048點的實數FFT就需要0.862ms,其它的FIR,RMS等都還沒有做,而且已經沒有時間發消息給emWin任務做界面刷新了。如果我們降低FFT,FIR等信號處理的點數,也就失去了實時處理的意義。也許讀者會說,加大緩沖不就好了,其實不然。如果我們加大了緩沖,我們要處理的數據也增加了,還是處理不過來,而且我們現在要處理的是雙通道。

除了F429的性能問題,這種方式還有一個比較棘手的問題需要解決,就是用戶操作界面的時候,GUI任務基本已經沒有時間去處理數字信號處理任務發來的數據,為了解決這個問題,大大增加了軟件設計的復雜度,特別是波形暫停和運行的切換,窗口的切換以及其它操作時,都要註意這個問題。

如果沒有復雜的界面操作,而且采樣率較低的話,方案一還是比較合適的。由於我們需要滑動操作波形,而且要實現雙通道,每個通道最高采樣率是2.8Msps,所以放棄這種方案。

(2)方案二

與方案一恰恰相反,ADC數據依然是通過DMA方式實時采集,而任務間的通信反過來進行,emWin任務需要波形數據刷新時給數字信號處理任務發消息獲取,這樣就有效地解決了方案一中F429性能不夠的問題,而且方案一中棘手的軟件問題得到了很好的解決,隨時都可以操作界面。

並且這種方式無形中解決了emWin任務和數字信號處理任務之間共同操作全局變量的問題,因為emWin是低優先級任務,而數字信號處理任務在emWin任務發消息後才會執行,這樣就不存在搶占問題了,有效地解決了全局變量共享問題。

但是這種方式也有一個缺陷,無法實時刷新波形和測量值了,不過可以通過普通觸發來解決了,普通觸發方式實時采集了觸發值前後各1024字節的數據,並且可以滑動瀏覽。不過工程中未對這種方式做FFT和FIR的支持。

總結,二代示波器中最終選擇了方案二。

15.6 FreeRTOS系統調試

FreeRTOS的調試比較簡單,采用串口打印,按下按鍵K1即可。不過由於按鍵不夠用,在MainTask.c文件的MainTask函數裏面對按鍵K1的消息處理做了三個條件編譯,大家可以根據需要選擇執行觸摸校準功能,截圖功能還是串口打印功能。#if 0取消執行,#if 1表示執行。

case KEY_1_DOWN:  

     #if 1

         hTouchWin = WM_CreateWindowAsChild(0,

                                                  0,

                                                  800,

                                                  480,

                                                  WM_HBKWIN,

                                                  WM_CF_SHOW,

                                                  _cbTouchCalibration,

                                                  0);

         WM_Exec();

         WM_SelectWindow(hTouchWin);

 

         /* 執行觸摸校準 */

         TOUCH_Calibration();

 

         WM_SelectWindow(0);

 

         WM_DeleteWindow(hTouchWin);

         WM_Exec();

 

         /* 自動觸發暫停狀態 */

         if(g_Flag->hWinRunStop == 1)

         {

              g_Flag->ucWaveRefresh = 1;

         }

    

         /* 普通觸發暫停狀態 */

         if(TriggerFlag == 1)

         {

              TriggerFlag = 2;

         }

     #endif

    

     /* 用於截圖 */

     #if 0

         xTaskNotifyGive(xHandleTaskMsgPro);

     #endif

    

     /* 打印任務的執行情況 */                          

     #if 0

         g_Flag->ucDsoMsg = DspTaskInfo_13;

         xEventGroupSetBits(xCreatedEventGroup, g_Flag->ucDsoMsg);

     #endif  

如果使能了串口打印的條件編譯,串口打印任務執行情況如下:

技術分享圖片

15.7 MDK優化等級

為了發揮STM32F429的最高性能,需要大家開啟最高等級優化和時間優化,即下面兩個選項:

技術分享圖片

FreeRTOS工程的文件系統是采用的FatFS,當前開啟了最高等級的三級優化和時間優化。如果大家要使用FatFS功能,請務必關閉時間優化,即Optimize for Time,取消勾選即可。因為FatFS在時間優化下會工作異常。

知識點拓展

MDK曾經做的專題:如何做MDK編譯器的代碼最小優化和性能最佳優化。

http://forum.armfly.com/forum.php?mod=viewthread&tid=1794 。

15.8 總結

FreeRTOS系統設計二代示波器的關鍵問題在本章節都做了闡釋,建議大家學習完本章節後,直接看源碼做實戰演練,這樣理解的更透徹,而且這時再做改進拓展也容易些。

【二代示波器教程】第15章 FreeRTOS操作系統版本二代示波器實現