第13章 RTX操作系統版本二代示波器實現 本章教程為大家講解RTX操作系統版本的二代示波器實現。主要講解RTOS設計框架,即各個任務實現的功能,任務間的通信方案選擇,任務棧,系統棧以及全局變數共用問題。同時,工程調試方法也專門做了說明。 13.1 註意事項(重要必讀) 13.2 任務功能劃分 13 ...
第13章 RTX操作系統版本二代示波器實現
本章教程為大家講解RTX操作系統版本的二代示波器實現。主要講解RTOS設計框架,即各個任務實現的功能,任務間的通信方案選擇,任務棧,系統棧以及全局變數共用問題。同時,工程調試方法也專門做了說明。
13.1 註意事項(重要必讀)
13.2 任務功能劃分
13.3 用戶任務優先順序設置
13.4 全局變數分配,系統堆棧和任務堆棧
13.5 任務間通信和全局變數共用問題
13.6 RTX配置嚮導
13.7 RTX系統調試
13.8 MDK優化等級
13.9 總結
13.1 註意事項(重要必讀)
1、學習本章節前,務必保證已經學習完畢前面章節。另外,工程代碼註釋已經比較詳細,瞭解了框架後,直接看源碼即可。
2、RTX操作系統版本的限制使用MDK4.74,其它MDK版本不支持。詳情看我們RTX教程即可:
http://forum.armfly.com/forum.php?mod=viewthread&tid=14837 。
3、僅支持800*480解析度顯示屏,如果是電容屏,無需校準。如果是電阻屏,需要校準,按下按鍵K1即可進入校準界面。
4、由於按鍵不夠用,在MainTask.c文件的MainTask函數裡面對按鍵K1的消息處理做了一個條件編譯,大家可以根據需要選擇執行觸摸校準功能還是截圖功能。#if 1表示執行觸摸校準,#if 0表示執行截圖功能。
case KEY_1_DOWN: /************由於按鍵不夠用,將截圖功能取消***********/ #if 0 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; } #else os_sem_send (&semaphore); #endif break;
5、文件系統是用的RL-FlashFS,如果大家想學習RL-FlashFS的使用,學習KEIL給的手冊即可:
http://forum.armfly.com/forum.php?mod=viewthread&tid=2988 。
6、MDK安裝目錄裡面帶的emWin5.4x版本的截圖功能有bug,詳情看此貼:
http://forum.armfly.com/forum.php?mod=viewthread&tid=82445 。
當前用的5.36版本,也是來自MDK。
13.2 任務功能劃分
前面第三章已經將任務功能劃分好:
根據這個功能劃分,創建所需要的任務。另外,RTX本身是不支持CPU利用率統計的,所以專門創建了一個任務實現CPU利用率統計。
13.2.1 主函數創建
在main.c文件實現:
/* ********************************************************************************************************* * 函 數 名: main * 功能說明: 標準c程式入口。 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ int main (void) { /* 初始化外設 */ bsp_Init(); /* 創建啟動任務 */ os_sys_init_user (AppTaskStart, /* 任務函數 */ 6, /* 任務優先順序 */ &AppTaskStartStk, /* 任務棧 */ sizeof(AppTaskStartStk)); /* 任務棧大小,單位位元組數 */ while(1); }
硬體外設的初始化函數bsp_Init是在 bsp.c 文件實現:
/* ********************************************************************************************************* * 函 數 名: bsp_Init * 功能說明: 初始化所有的硬體設備。該函數配置CPU寄存器和外設的寄存器並初始化一些全局變數。只需要調用一次 * 形 參:無 * 返 回 值: 無 ********************************************************************************************************* */ void bsp_Init(void) { /* 由於ST固件庫的啟動文件已經執行了CPU系統時鐘的初始化,所以不必再次重覆配置系統時鐘。 啟動文件配置了CPU主時鐘頻率、內部Flash訪問速度和可選的外部SRAM FSMC初始化。 系統時鐘預設配置為168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件 */ /* 優先順序分組設置為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用於記錄一段波形 */ }
創建任務的主要功能是硬體外設初始化和啟動任務的創建,相對比較簡單。
13.2.2 啟動任務(信號處理)
啟動任務用於二代示波器的信號處理:
/* ********************************************************************************************************* * 函 數 名: AppTaskStart * 功能說明: 啟動任務,也是最高優先順序任務,用於信號處理。 * 形 參: 無 * 返 回 值: 無 * 優 先 級: 6 ********************************************************************************************************* */ __task void AppTaskStart(void) { OS_RESULT xResult; /* 優先創建統計任務-----------------*/ HandleTaskStat = os_tsk_create_user(AppTaskStatistic, /* 任務函數 */ 1, /* 任務優先順序 */ &AppTaskStatStk, /* 任務棧 */ sizeof(AppTaskStatStk)); /* 任務棧大小,單位位元組數 */ OSStatInit(); /* 創建任務間通信機制和動態記憶體分配,此函數要優先調用 */ AppObjCreate(); /* 創建任務 */ AppTaskCreate(); /* 實數序列FFT長度 */ fftSize = 2048; /* 正變換 */ ifftFlag = 0; /* 初始化結構體S中的參數 */ arm_rfft_fast_init_f32(&S, fftSize); HandleTaskStart = os_tsk_self(); while(1) { if(os_evt_wait_or(StartTaskWaitFlag, 0xFFFF) == OS_R_EVT) { xResult = os_evt_get (); switch (xResult) { 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; case DspMultiMeterPro_0: g_uiAdcAvgSample = ADC_GetSampleAvgN(); break; /* 其它位暫未使用 */ default: printf_taskdbg("xResult = %x\r\n", xResult); break; } } } }
除了信號處理,還有一個重要的功能要在啟動任務裡面優先實現,就是統計任務的創建和執行,用於統計CPU利用率,實現步驟如下:
- 進入到啟動任務後,其它任何任務都不要創建,先創建一個統計任務,不讓執行。
- 啟動任務延遲100ms,延遲的這100ms時間基本都是空閑任務在執行,在空閑任務裡面做32位變數加1計算。我們就以這100ms,變數計數的最大值作為CPU利用率的分母。
- 然後開啟統計任務的執行,每100ms執行一次,統計即可。空閑任務此時的計數值作為分子。通過這種方式就實現了CPU利用率的統計。
統計任務執行後就是任務間通信機制函數AppObjCreate(動態記憶體分配也是在這個函數裡面實現,在本章13.4小節有說明)和任務創建函數AppTaskCreate,代碼比較簡單,我們這裡就不貼出來了。
說完了前面這些,最重要的還是信號處理。根據不同的事件標誌處理不同的功能,任務裡面主要是分為了兩類:
1、雙通道波形數據處理
主要實現軟體觸發,計算FFT ,FIR ,RMS,最大值,最小值,平均值和峰峰值。兩個通道都進行了處理。具體實現方法已經在前面章節為大家做了講解。
2、另一個是簡單電壓測量處理
這個功能比較簡單,就是獲取一組ADC數值,然後求平均。
13.2.3 統計任務
統計任務的實現代碼如下:
/* ********************************************************************************************************* * 函 數 名: AppTaskStatistic * 功能說明: 統計任務,用於實現CPU利用率的統計。為了測試更加準確,可以開啟註釋調用的全局中斷開關 * 形 參: 無 * 返 回 值: 無 * 優 先 級: 1 (數值越小優先順序越低,這個跟uCOS相反) ********************************************************************************************************* */ void OSStatInit (void) { OSStatRdy = FALSE; os_dly_wait(2u); /* 時鐘同步 */ //__disable_irq(); OSIdleCtr = 0uL; /* 清空閑計數 */ //__enable_irq(); os_dly_wait(100); /* 統計100ms內,最大空閑計數 */ //__disable_irq(); OSIdleCtrMax = OSIdleCtr; /* 保存最大空閑計數 */ OSStatRdy = TRUE; //__enable_irq(); } __task void AppTaskStatistic (void) { while (OSStatRdy == FALSE) { os_dly_wait(200); /* 等待統計任務就緒 */ } OSIdleCtrMax /= 100uL; if (OSIdleCtrMax == 0uL) { OSCPUUsage = 0u; } //__disable_irq(); OSIdleCtr = OSIdleCtrMax * 100uL; /* 設置初始CPU利用率 0% */ //__enable_irq(); for (;;) { //__disable_irq(); OSIdleCtrRun = OSIdleCtr; /* 獲得100ms內空閑計數 */ OSIdleCtr = 0uL; /* 複位空閑計數 */ //__enable_irq(); /* 計算100ms內的CPU利用率 */ OSCPUUsage = (100uL - (float)OSIdleCtrRun / OSIdleCtrMax); os_dly_wait(100); /* 每100ms統計一次 */ } }
統計任務的實現思路就是前面13.2.2小節中介紹的方法。這個統計任務的實現思路是由uCOS-II修改而來的,如果大家研究過uCOS-II的源碼,這裡的代碼還是比較好理解的。沒有研究過也沒有關係,直接根據13.2.2小節裡面介紹的思路看統計任務的實現代碼即可。
13.2.4 GUI任務
emWin任務的實現代碼如下:
/* ********************************************************************************************************* * 函 數 名: AppTaskGUI * 功能說明: GUI任務。 * 形 參: 無 * 返 回 值: 無 * 優 先 級: 2 ********************************************************************************************************* */ __task void AppTaskGUI(void) { while(1) { MainTask(); } }
emWin的代碼都是在函數MainTask裡面實現,這樣做是方便在main.c文件裡面統一管理任務。關於GUI部分最重要的界面優化,波形刷新優化,波形瀏覽等,在前面章節已經都做了講解,我們這裡不再贅述。更詳細的實現,需要結合前面章節的講解去看源碼。
13.2.5 用戶介面任務
這個任務暫時未執行任何功能,保留供以後升級使用。代碼如下:
/* ********************************************************************************************************* * 函 數 名: AppTaskUserIF * 功能說明: 保留,暫未使用。 * 形 參: 無 * 返 回 值: 無 * 優 先 級: 3 ********************************************************************************************************* */ __task void AppTaskUserIF(void) { while(1) { os_dly_wait(2000); } }
13.2.6 文件系統處理任務
當前文件系統處理任務主要用來做截圖功能,將GUI界面以BMP格式存儲到SD卡裡面:
/* ********************************************************************************************************* * 函 數 名: AppTaskFsPro * 功能說明: 文件系統處理任務。 * 形 參: 無 * 返 回 值: 無 * 優 先 級: 4 ********************************************************************************************************* */ __task void AppTaskFsPro(void) { OS_RESULT xResult; const uint16_t usMaxBlockTime = 0xFFFF; uint8_t Pic_Name = 0; char buf[40]; while(1) { xResult = os_sem_wait (&semaphore, usMaxBlockTime); switch (xResult) { /* 無需等待接受到信號量同步信號 */ case OS_R_OK: /* 信號量不可用,usMaxBlockTime等待時間內收到信號量同步信號 */ case OS_R_SEM: sprintf((char *)buf,"M0:\\PicSave\\%d.bmp",Pic_Name); foutbmp = fopen (buf, "w"); if (foutbmp != NULL) { /* 向SD卡繪製BMP圖片 */ GUI_BMP_Serialize(_WriteByte2File, foutbmp); /* 關閉文件 */ fclose(foutbmp); } printf_taskdbg("截圖完成\r\n"); Pic_Name++; break; /* 超時 */ case OS_R_TMO: break; /* 其他值不處理 */ default: break; } } }
後期這個任務將被升級,用於將波形數據以CSV文件格式存儲到SD卡裡面。
13.2.7 觸摸和按鍵任務
觸摸和按鍵任務實現的功能比較簡單,主要是按鍵掃描和觸摸掃描:
/* ********************************************************************************************************* * 函 數 名: AppTaskMsgPro * 功能說明: 按鍵和觸摸檢測 * 形 參: 無 * 返 回 值: 無 * 優 先 級: 5 ********************************************************************************************************* */ __task void AppTaskMsgPro(void) { uint8_t ucCount = 0; while(1) { /* 1ms一次觸摸掃描,電阻觸摸屏 */ if(g_tTP.Enable == 1) { TOUCH_Scan(); /* 按鍵掃描 */ ucCount++; if(ucCount == 10) { ucCount = 0; bsp_KeyScan(); } os_dly_wait(1); } /* 10ms一次觸摸掃描,電容觸摸屏GT811 */ if(g_GT811.Enable == 1) { bsp_KeyScan(); GT811_OnePiontScan(); os_dly_wait(10); } /* 10ms一次觸摸掃描,電容觸摸屏FT5X06 */ if(g_tFT5X06.Enable == 1) { bsp_KeyScan(); FT5X06_OnePiontScan(); os_dly_wait(10); } } }
知識點拓展:
新版emWin教程第4章或者第5章,對觸摸的實現做了詳細講解:
http://forum.armfly.com/forum.php?mod=viewthread&tid=19834 。
13.3 用戶任務優先順序設置
當前任務的優先順序安排如下(數值越小,優先順序越低):
AppTaskStatistic任務 : 優先順序1。
統計任務的優先順序最低,這個毫無爭議,因為它要統計CPU利用率。
AppTaskGUI任務 : 優先順序2。
emWin任務是除了空閑任務,統計任務以外最低優先順序的,因為emWin極其占用系統資源,而且時間長,如果這個任務設置為高優先順序,會直接影響低優先順序任務的執行。
AppTaskUserIF任務 : 優先順序3。
保留,未使用任務,暫且安排為這個優先順序。
AppTaskFsPro任務 : 優先順序4。
AppTaskMsgPro任務 : 優先順序5。
觸摸和按鍵任務,以及文件系統任務的優先順序誰高誰低都沒有關係。
AppTaskStart任務 : 優先順序6。
DSP任務一定要是優先順序最高的,因為採集的數據要實時處理。
知識點拓展:
關於任務優先順序的安排,在我們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。
13.4 全局變數分配,系統堆棧和任務堆棧
1、全局變數分配
示波器的設計需要很多變數進行邏輯管理,從設計之初就需要將變數分類進行結構體封裝,方便以後的維護升級。這一步至關重要,實際中差不多要定義上百個變數,如果不進行分類管理,以後的升級維護將非常麻煩。
這種方式還有一個好處是方便我們將F429的CCM RAM空間分配給這些變數使用。使用CCM RAM的好處是速度比通用RAM要快些,缺點是這部分空間不支持DMA操作。初次使用的用戶比較容易在這個地方犯錯誤。所以在使用局部變數時,切勿將局部變數用於DMA傳輸。
當前需要頻繁調用的變數已經通過動態記憶體管理分配給各個結構體變數,使用的CCM RAM空間。
uint64_t AppMallocCCM[40*1024/8] __attribute__((at(0x10000000 + 1024*24))); /* 數字信號處理 */ /* ********************************************************************************************************* * 函 數 名: AppObjCreate * 功能說明: 創建任務通信機制和動態記憶體分配 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ static void AppObjCreate (void) { /* 創建信號量計數值是0, 用於任務同步 */ os_sem_init (&semaphore, 0); /* 將內部CCM SRAM的40KB全部供動態記憶體使用 */ os_init_mem(AppMallocCCM, 1024*40); /* 申請示波器通道1動態記憶體 */ g_DSO1 = (DSO_T *)os_alloc_mem(AppMallocCCM, sizeof(DSO_T)); /* 申請示波器通道2動態記憶體 */ g_DSO2 = (DSO_T *)os_alloc_mem(AppMallocCCM, sizeof(DSO_T)); /* 申請游標測量結構體變數動態記憶體 */ g_Cursors = (CURSORS_T *)os_alloc_mem(AppMallocCCM, sizeof(CURSORS_T)); /* 申請標誌位結構體變數動態記憶體 */ g_Flag = (FLAG_T *)os_alloc_mem(AppMallocCCM, sizeof(FLAG_T)); /* 申請觸髮結構體變數動態記憶體 */ g_TrigVol = (TRIVOLTAGE_T *)os_alloc_mem(AppMallocCCM, sizeof(TRIVOLTAGE_T)); /* 申請FFT動態記憶體 */ testInput_fft_2048 = (float32_t *)os_alloc_mem(AppMallocCCM, sizeof(float32_t)*2048); testOutput_fft_2048 = (float32_t *)os_alloc_mem(AppMallocCCM, sizeof(float32_t)*2048); /* 申請RMS動態記憶體 */ g_RMSBUF = (float32_t *)os_alloc_mem(AppMallocCCM, sizeof(float32_t)*600); /* 申請FIR動態記憶體 */ FirDataInput = (float32_t *)os_alloc_mem(AppMallocCCM, sizeof(float32_t)*FIR_LENGTH_SAMPLES); FirDataOutput = (float32_t *)os_alloc_mem(AppMallocCCM, sizeof(float32_t)*FIR_LENGTH_SAMPLES); firStateF32 = (float32_t *)os_alloc_mem(AppMallocCCM, sizeof(float32_t)*FIR_StateBufSize); }
2、任務棧分配
任務棧也是用的CCM RAM空間,具體分配如下: /* ********************************************************************************************************** 任務棧和任務句柄 ********************************************************************************************************** */ static uint64_t AppTaskStatStk[1024/8] __attribute__((at(0x10000000))); /* 任務棧 */ static uint64_t AppTaskGUIStk[4096/8] __attribute__((at(0x10000000+1024))); /* 任務棧 */ static uint64_t AppTaskUserIFStk[1024/8] __attribute__((at(0x10000000+1024*5))); /* 任務棧 */ static uint64_t AppTaskFsProStk[1024/8] __attribute__((at(0x10000000+1024*6))); /* 任務棧 */ static uint64_t AppTaskMsgProStk[1024/8] __attribute__((at(0x10000000+1024*7))); /* 任務棧 */ static uint64_t AppTaskStartStk[4096/8] __attribute__((at(0x10000000+1024*8))); /* 任務棧 */
將任務棧定義成uint64_t類型可以保證任務棧是8位元組對齊的,8位元組對齊的含義就是數組的首地址對8求餘等於0。如果不做8位元組對齊的話,部分C語言庫函數、浮點運算和uint64_t類型數據運算會出問題。
知識點拓展:
關於任務棧大小應該分配多大的問題,可以看FreeRTOS教程第11章,對於RTX系統也是適用的。
http://forum.armfly.com/forum.php?mod=viewthread&tid=17658 。
3、系統棧分配
系統棧分配的大小如下:
13.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的支持。
總結,二代示波器中最終選擇了方案二。
13.6 RTX配置嚮導
RTX配置嚮導詳情如下:
Task Configuration
(1)Number of concurrent running tasks
允許創建9個任務,實際創建瞭如下6個任務:
AppTaskStatistic任務 : 統計任務,獲取CPU利用率。
AppTaskGUI任務 : emWin任務。
AppTaskUserIF任務 : 保留,暫未使用。
AppTaskFsPro任務 : 文件系統任務。
AppTaskMsgPro任務 : 按鍵和觸摸檢測。
AppTaskStart任務 : 啟動任務,也是最高優先順序任務,用於信號處理。
(2)Number of tasks with user-provided stack
8個任務可以採用自定義堆棧方式。
(3)Run in privileged mode
設置任務運行在非特權級模式。
13.7 RTX系統調試
MDK自帶RTX調試組件,展示系統信息非常方便,本工程的展示效果如下:
調試組件的使用方法請看F429的RTX教程第3章3.4小節,有詳細說明:
http://forum.armfly.com/forum.php?mod=viewthread&tid=14837 。
13.8 MDK優化等級
為了發揮STM32F429的最高性能,需要大家開啟最高等級優化和時間優化,即下麵兩個選項:
知識點拓展:
MDK曾經做的專題:如何實現MDK編譯器的代碼最小優化和性能最佳優化。
http://forum.armfly.com/forum.php?mod=viewthread&tid=1794 。
13.9 總結
RTX系統設計二代示波器的關鍵問題在本章節都做了闡釋,建議大家學習完本章節後,直接看源碼做實戰演練,這樣理解的更透徹,而且這時再做改進拓展也容易些。