第14章 uCOS-III操作系統版本二代示波器實現 本章教程為大家講解uCOS-III操作系統版本的二代示波器實現。主要講解RTOS設計框架,即各個任務實現的功能,任務間的通信方案選擇,任務棧,系統棧以及全局變數共用問題。同時,工程調試方法也專門做了說明。 14.1 註意事項(重要必讀) 14.2 ...
第14章 uCOS-III操作系統版本二代示波器實現
本章教程為大家講解uCOS-III操作系統版本的二代示波器實現。主要講解RTOS設計框架,即各個任務實現的功能,任務間的通信方案選擇,任務棧,系統棧以及全局變數共用問題。同時,工程調試方法也專門做了說明。
14.1 註意事項(重要必讀)
14.2 任務功能劃分
14.3 用戶任務優先順序設置
14.4 全局變數分配,系統堆棧和任務堆棧
14.5 任務間通信和全局變數共用問題
14.6 uCOS-III系統調試
14.7 MDK優化等級
14.8 總結
14.1 註意事項(重要必讀)
1、學習本章節前,務必保證已經學習完畢前面章節。另外,工程代碼註釋已經比較詳細,瞭解了框架後,直接看源碼即可。
2、僅支持800*480解析度顯示屏,如果是電容屏,無需校準。如果是電阻屏,需要校準,按下按鍵K1即可進入校準界面。
3、由於按鍵不夠用,K1按鍵的消息處理做了三個條件編譯,詳情見本章14.6小節。預設K1按鍵執行觸摸校準,也可以選擇執行截圖或者串口列印任務執行情況。另外,不管當前處於任何界面都可以進行觸摸校準,僅電阻屏需要校準,電容屏無需校準。
4、STemWin5.40版本的截圖功能有bug,詳情看此貼:
http://forum.armfly.com/forum.php?mod=viewthread&tid=82445 。
當前用的5.32版本,也是來自STemWin軟體包。
5、Micrium官方曾經發佈過一個非常棒的文檔,如何發揮uCOS-III最高性能之重要提示和項目應用建議,推薦大家看看:http://forum.armfly.com/forum.php?mod=viewthread&tid=31634 。
6、uCOS-III工程的文件系統是採用的FatFS,當前開啟了MDK最高等級優化和時間優化。如果大家要使用FatFS功能,請務必關閉時間優化,因為FatFS在時間優化下會工作異常。詳情見本章14.7小節。
7、工程編譯支持MDK4.7X和MDK5。另外不支持MDK發佈的MDK5.24及其以上版本,因為這個版本不支持MDK4創建的工程轉換為MDK5了,所以要使用這個最新的版本,需要給MDK5安裝MDK4的相容包。
14.2 任務功能劃分
前面第三章已經將任務功能劃分好:
根據這個功能劃分,創建所需要的任務。
14.2.1 主函數創建
在main.c文件實現:
/* ********************************************************************************************************* * 函 數 名: main * 功能說明: 標準c程式入口。 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ int main(void) { OS_ERR err; /* 動態記憶體分配 */ MallocInit(); /* 初始化uC/OS-III 內核 */ OSInit(&err); /* 創建一個啟動任務(也就是主任務)。啟動任務會創建所有的應用程式任務 */ OSTaskCreate((OS_TCB *)AppTaskStartTCB, /* 任務控制塊地址 */ (CPU_CHAR *)"App Task Start", /* 任務名 */ (OS_TASK_PTR )AppTaskStart, /* 啟動任務函數地址 */ (void *)0, /* 傳遞給任務的參數 */ (OS_PRIO )APP_CFG_TASK_START_PRIO, /* 任務優先順序 */ (CPU_STK *)&AppTaskStartStk[0], /* 堆棧基地址 */ (CPU_STK_SIZE )APP_CFG_TASK_START_STK_SIZE / 10, /* 堆棧監測區,這裡表示後10%作為監測區 */ (CPU_STK_SIZE )APP_CFG_TASK_START_STK_SIZE, /* 堆棧空間大小 */ (OS_MSG_QTY )0, /* 本任務支持接受的最大消息數 */ (OS_TICK )0, /* 設置時間片 */ (void *)0, /* 堆棧空間大小 */ (OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR), /* 定義如下: OS_OPT_TASK_STK_CHK 使能檢測任務棧,統計任務棧已用的和未用的 OS_OPT_TASK_STK_CLR 在創建任務時,清零任務棧 OS_OPT_TASK_SAVE_FP 如果CPU有浮點寄存器,則在任務切換時保存浮點寄存器的內容 */ (OS_ERR *)&err); /* 啟動多任務系統,控制權交給uC/OS-III */ OSStart(&err); (void)&err; return (0); }
在主函數中,首先是初始化任務所需的棧空間以及部分全局變數所需的空間,通過函數MallocInit實現(詳情看本章節14.4小節)。然後就是創建一個啟動任務,其它外設初始化,任務創建,任務消息創建等,都是在啟動任務裡面實現。
14.2.2 啟動任務(觸摸和按鍵)
啟動任務實現的功能比較簡單,主要是按鍵掃描和觸摸掃描:
/* ********************************************************************************************************* * 函 數 名: AppTaskStart * 功能說明: 這是一個啟動任務,在多任務系統啟動後,必須初始化滴答計數器。本任務主要實現按鍵和觸摸檢測。 * 形 參: p_arg 是在創建該任務時傳遞的形參 * 返 回 值: 無 優 先 級: 3 ********************************************************************************************************* */ static void AppTaskStart (void *p_arg) { OS_ERR err; uint8_t ucCount = 0; /* 僅用於避免編譯器告警,編譯器不會產生任何目標代碼 */ (void)p_arg; /* BSP 初始化。 BSP = Board Support Package 板級支持包,可以理解為底層驅動。*/ CPU_Init(); /* 此函數要優先調用,因為外設驅動中使用的us和ms延遲是基於此函數的 */ bsp_Init(); BSP_Tick_Init(); #if OS_CFG_STAT_TASK_EN > 0u OSStatTaskCPUUsageInit(&err); #endif #ifdef CPU_CFG_INT_DIS_MEAS_EN CPU_IntDisMeasMaxCurReset(); #endif /* 創建任務通信 */ AppObjCreate(); /* 創建應用程式的任務 */ AppTaskCreate(); while(1) { /* 1ms一次觸摸掃描,電阻觸摸屏 */ if(g_tTP.Enable == 1) { TOUCH_Scan(); /* 按鍵掃描 */ ucCount++; if(ucCount == 10) { ucCount = 0; bsp_KeyScan(); } OSTimeDly(1, OS_OPT_TIME_DLY, &err); } /* 10ms一次觸摸掃描,電容觸摸屏GT811 */ if(g_GT811.Enable == 1) { bsp_KeyScan(); GT811_OnePiontScan(); OSTimeDly(10, OS_OPT_TIME_DLY, &err); } /* 10ms一次觸摸掃描,電容觸摸屏FT5X06 */ if(g_tFT5X06.Enable == 1) { bsp_KeyScan(); FT5X06_OnePiontScan(); OSTimeDly(10, OS_OPT_TIME_DLY, &err); } } }
在啟動任務里,首先做了硬體外設初始化,任務間通信創建和任務創建。然後是觸摸和按鍵掃描。
知識點拓展
新版emWin教程第4章或者第5章,對觸摸的實現做了詳細講解:
http://forum.armfly.com/forum.php?mod=viewthread&tid=19834 。
硬體外設的初始化函數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_InitUart(); /* 初始化串口 */ bsp_InitKey(); /* 初始化按鍵變數(必須在 bsp_InitTimer() 之前調用) */ bsp_InitI2C(); /* 配置I2C匯流排 */ bsp_InitExtSDRAM(); /* 初始化SDRAM */ 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用於記錄一段波形 */ }
任務間的通信創建如下:
/* ********************************************************************************************************* * 函 數 名: AppObjCreate * 功能說明: 創建任務通訊 * 形 參: p_arg 是在創建該任務時傳遞的形參 * 返 回 值: 無 ********************************************************************************************************* */ static void AppObjCreate (void) { OS_ERR err; /* 創建信號量數值為1的時候可以實現互斥功能,也就是只有一個資源可以使用 本常式是將串口1的列印函數作為保護的資源。防止串口列印的時候被其它任務搶占 造成串口列印錯亂。 */ OSSemCreate((OS_SEM *)&AppPrintfSemp, (CPU_CHAR *)"AppPrintfSemp", (OS_SEM_CTR )1, (OS_ERR *)&err); /* 創建計數值為0,用於實現任務同步功能 */ OSSemCreate((OS_SEM *)&SEM_SYNCH, (CPU_CHAR *)"SEM_SYNCH", (OS_SEM_CTR )0, (OS_ERR *)&err); }
任務創建如下:
/* ********************************************************************************************************* * 函 數 名: AppTaskCreate * 功能說明: 創建應用任務 * 形 參: p_arg 是在創建該任務時傳遞的形參 * 返 回 值: 無 ********************************************************************************************************* */ static void AppTaskCreate (void) { OS_ERR err; /**************創建MsgPro任務*********************/ OSTaskCreate((OS_TCB *)AppTaskMsgProTCB, (CPU_CHAR *)"App Task MsgPro", (OS_TASK_PTR )AppTaskMsgPro, (void *)0, (OS_PRIO )APP_CFG_TASK_MsgPro_PRIO, (CPU_STK *)&AppTaskMsgProStk[0], (CPU_STK_SIZE )APP_CFG_TASK_MsgPro_STK_SIZE / 10, (CPU_STK_SIZE )APP_CFG_TASK_MsgPro_STK_SIZE, (OS_MSG_QTY )0, (OS_TICK )0, (void *)0, (OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR | OS_OPT_TASK_SAVE_FP), (OS_ERR *)&err); /**************創建USER IF任務*********************/ OSTaskCreate((OS_TCB *)AppTaskUserIFTCB, (CPU_CHAR *)"App Task UserIF", (OS_TASK_PTR )AppTaskUserIF, (void *)0, (OS_PRIO )APP_CFG_TASK_USER_IF_PRIO, (CPU_STK *)&AppTaskUserIFStk[0], (CPU_STK_SIZE )APP_CFG_TASK_USER_IF_STK_SIZE / 10, (CPU_STK_SIZE )APP_CFG_TASK_USER_IF_STK_SIZE, (OS_MSG_QTY )0, (OS_TICK )0, (void *)0, (OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR), (OS_ERR *)&err); /**************創建GUI任務*********************/ OSTaskCreate((OS_TCB *)AppTaskGUITCB, (CPU_CHAR *)"App Task GUI", (OS_TASK_PTR )AppTaskGUI, (void *)0, (OS_PRIO )APP_CFG_TASK_GUI_PRIO, (CPU_STK *)&AppTaskGUIStk[0], (CPU_STK_SIZE )APP_CFG_TASK_GUI_STK_SIZE / 10, (CPU_STK_SIZE )APP_CFG_TASK_GUI_STK_SIZE, (OS_MSG_QTY )0, (OS_TICK )0, (void *)0, (OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR | OS_OPT_TASK_SAVE_FP), (OS_ERR *)&err); /**************創建DSO任務*********************/ OSTaskCreate((OS_TCB *)AppTaskDsoTCB, (CPU_CHAR *)"App Task DSO", (OS_TASK_PTR )AppTaskDSO, (void *)0, (OS_PRIO )APP_CFG_TASK_DSO_PRIO, (CPU_STK *)&AppTaskDsoStk[0], (CPU_STK_SIZE )APP_CFG_TASK_DSO_STK_SIZE / 10, (CPU_STK_SIZE )APP_CFG_TASK_DSO_STK_SIZE, (OS_MSG_QTY )5, (OS_TICK )0, (void *)0, (OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR | OS_OPT_TASK_SAVE_FP), (OS_ERR *)&err); }
14.2.3 信號處理任務
信號處理任務的實現如下:
/* ********************************************************************************************************* * 函 數 名: AppTaskDSO * 功能說明: 雙通道示波器數據處理任務。 * 形 參: 無 * 返 回 值: 無 * 優 先 級: 2 ********************************************************************************************************* */ static void AppTaskDSO (void *p_arg) { OS_ERR err; CPU_TS ts; void *p_msg; OS_MSG_SIZE msg_size; uint32_t *ucReceive, /* 實數序列FFT長度 */ fftSize = 2048; /* 正變換 */ ifftFlag = 0; /* 初始化結構體S中的參數 */ arm_rfft_fast_init_f32(&S, fftSize); while(1) { /* 接受數據 */ p_msg = OSTaskQPend(0, OS_OPT_PEND_BLOCKING, &msg_size, /* 此參數是接收到數據個數 */ &ts, &err); if(err == OS_ERR_NONE) { ucReceive = (uint32_t *)p_msg; switch (*ucReceive) { /* 雙通道波形處理 */ 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: DispTaskInfo(); break; /* 其它位暫未使用 */ default: App_Printf("*ucReceive = %x\r\n", *ucReceive); break; } } } }
根據接收到的不同任務消息來處理不同的功能,要處理的消息分為三類:
1、雙通道波形數據處理
主要實現軟體觸發,計算FFT ,FIR ,RMS,最大值,最小值,平均值和峰峰值。兩個通道都進行了處理。具體實現方法已經在前面章節為大家做了講解。
2、簡單電壓測量處理
這個功能比較簡單,就是獲取一組ADC數值,然後求平均。
3、列印任務執行情況
通過串口列印任務棧的使用情況和各個任務的CPU利用率。
14.2.4 GUI任務
emWin任務的實現代碼如下:
/* ********************************************************************************************************* * 函 數 名: AppTaskGUI * 功能說明: GUI任務,最低優先順序 * 形 參: p_arg 是在創建該任務時傳遞的形參 * 返 回 值: 無 * 優 先 級: OS_CFG_PRIO_MAX - 4u ********************************************************************************************************* */ static void AppTaskGUI(void *p_arg) { (void)p_arg; /* 避免編譯器告警 */ while (1) { MainTask(); } }
emWin的代碼都是在函數MainTask裡面實現,這樣做是方便在main.c文件裡面統一管理任務。關於GUI部分最重要的界面優化,波形刷新優化,波形瀏覽等,在前面章節已經都做了講解,我們這裡不再贅述。更詳細的實現,需要結合前面章節的講解去看源碼。
14.2.5 用戶介面任務
這個任務暫時未執行任何功能,保留供以後升級使用。代碼如下:
/* ********************************************************************************************************* * 函 數 名: AppTaskUserIF * 功能說明: 保留,暫未使用。 * 形 參: p_arg 是在創建該任務時傳遞的形參 * 返 回 值: 無 優 先 級: 5 ********************************************************************************************************* */ static void AppTaskUserIF(void *p_arg) { OS_ERR err; (void)p_arg; /* 避免編譯器報警 */ while (1) { OSTimeDly(1000, OS_OPT_TIME_DLY, &err); } }
14.2.6 文件系統處理任務
當前文件系統處理任務主要用來做截圖功能,將GUI界面以BMP格式存儲到SD卡裡面:
/* ********************************************************************************************************* * 函 數 名: AppTaskMsgPro * 功能說明: 實現截圖功能,將圖片以BMP格式保存到SD卡中 * 形 參: p_arg 是在創建該任務時傳遞的形參 * 返 回 值: 無 優 先 級: 4 ********************************************************************************************************* */ static void AppTaskMsgPro(void *p_arg) { uint32_t ulStart, ulEnd; OS_ERR err; uint8_t Pic_Name = 0; char buf[20]; (void)p_arg; while(1) { /* 等待獲取信號量同步消息,接收到後實現截圖功能,將圖片以BMP格式保存到SD卡中 */ OSSemPend((OS_SEM *)&SEM_SYNCH, (OS_TICK )0, (OS_OPT )OS_OPT_PEND_BLOCKING, (CPU_TS )0, (OS_ERR *)&err); if(err == OS_ERR_NONE) { sprintf(buf,"0:/PicSave/%d.bmp",Pic_Name); /* 記錄截圖前起始時間 */ ulStart = OSTimeGet(&err); /* 開啟調度鎖 */ //OSSchedLock(&err); /* 如果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); /* 開啟調度鎖 */ //OSSchedUnlock(&err); /* 記錄截圖後時間並獲取截圖過程耗時 */ ulEnd = OSTimeGet(&err); ulEnd -= ulStart; App_Printf("截圖完成,耗時 = %dms\r\n", ulEnd); Pic_Name++; } } }
後期這個任務將被升級,用於將波形數據以CSV文件格式存儲到SD卡裡面。
14.3 用戶任務優先順序設置
當前任務的優先順序安排如下(數值越小,優先順序越高):
App Task DSO任務 : 優先順序2。
DSP任務一定要是優先順序最高的,因為採集的數據要實時處理。
App Task Start任務 : 優先順序3。
App Task MsgPro任務 : 優先順序4。
啟動任務(觸摸和按鍵掃描)以及MsgPro(文件系統處理)任務的優先順序誰高誰低都沒有關係。
App Task UserIF任務 :優先順序5。
保留,未使用任務,暫且安排為這個優先順序。
App Task GUI任務 :優先順序OS_CFG_PRIO_MAX - 4u,即32 – 4 = 28
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。
14.4 全局變數分配,系統堆棧和任務堆棧
1、全局變數分配
示波器的設計需要很多變數進行邏輯管理,從設計之初就需要將變數分類進行結構體封裝,方便以後的維護升級。這一步至關重要,實際中差不多要定義上百個變數,如果不進行分類管理,以後的升級維護將非常麻煩。
這種方式還有一個好處是方便我們將F429的CCM RAM空間分配給這些變數使用。使用CCM RAM的好處是速度比通用RAM要快些,缺點是這部分空間不支持DMA操作。初次使用的用戶比較容易在這個地方犯錯誤。所以在使用局部變數時,切勿將局部變數用於DMA傳輸。
當前需要頻繁調用的變數已經通過動態記憶體管理分配給各個結構體變數,使用的CCM RAM空間。
uint64_t AppMallocCCM[64*1024/8] __attribute__((at(0x10000000))); /* CCM RAM動態記憶體池 */ /* ********************************************************************************************************* * 函 數 名: MallocInit * 功能說明: 用戶任務棧,用戶TCB(任務控制塊)和示波器結構體變數使用CCM RAM * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ static void MallocInit(void) { /* 將內部CCM SRAM的64KB全部供動態記憶體使用 */ rt_init_mem(AppMallocCCM, 1024*64); /* 任務棧和任務控制塊TCB的初始化,省略未寫 */ /* 申請示波器通道1動態記憶體 */ g_DSO1 = (DSO_T *)rt_alloc_mem(AppMallocCCM, sizeof(DSO_T)); /* 申請示波器通道2動態記憶體 */ g_DSO2 = (DSO_T *)rt_alloc_mem(AppMallocCCM, sizeof(DSO_T)); /* 申請游標測量結構體變數動態記憶體 */ g_Cursors = (CURSORS_T *)rt_alloc_mem(AppMallocCCM, sizeof(CURSORS_T)); /* 申請標誌位結構體變數動態記憶體 */ g_Flag = (FLAG_T *)rt_alloc_mem(AppMallocCCM, sizeof(FLAG_T)); /* 申請觸髮結構體變數動態記憶體 */ g_TrigVol = (TRIVOLTAGE_T *)rt_alloc_mem(AppMallocCCM, sizeof(TRIVOLTAGE_T)); /* 申請FFT動態記憶體 */ testInput_fft_2048 = (float32_t *)rt_alloc_mem(AppMallocCCM, sizeof(float32_t)*2048); testOutput_fft_2048 = (float32_t *)rt_alloc_mem(AppMallocCCM, sizeof(float32_t)*2048); /* 申請RMS動態記憶體 */ g_RMSBUF = (float32_t *)rt_alloc_mem(AppMallocCCM, sizeof(float32_t)*600); /* 申請FIR動態記憶體 */ FirDataInput = (float32_t *)rt_alloc_mem(AppMallocCCM, sizeof(float32_t)*FIR_LENGTH_SAMPLES); FirDataOutput = (float32_t *)rt_alloc_mem(AppMallocCCM, sizeof(float32_t)*FIR_LENGTH_SAMPLES); firStateF32 = (float32_t *)rt_alloc_mem(AppMallocCCM, sizeof(float32_t)*FIR_StateBufSize); }
2、任務棧和任務控制塊TCB
任務棧和任務控制塊TCB也是用的CCM RAM空間,具體分配如下:
uint64_t AppMallocCCM[64*1024/8] __attribute__((at(0x10000000))); /* CCM RAM動態記憶體池 */ /* ********************************************************************************************************* * 函 數 名: MallocInit * 功能說明: 用戶任務棧,用戶TCB(任務控制塊)和示波器結構體變數使用CCM RAM * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ static void MallocInit(void) { /* 將內部CCM SRAM的64KB全部供動態記憶體使用 */ rt_init_mem(AppMallocCCM, 1024*64); /* 任務堆棧和任務控制塊是使用CCM RAM */ AppTaskDsoTCB = (OS_TCB *)rt_alloc_mem(AppMallocCCM, sizeof(OS_TCB)); AppTaskDsoStk = (CPU_STK *)rt_alloc_mem(AppMallocCCM, sizeof(CPU_STK)*APP_CFG_TASK_DSO_STK_SIZE); AppTaskStartTCB = (OS_TCB *)rt_alloc_mem(AppMallocCCM, sizeof(OS_TCB)); AppTaskStartStk = (CPU_STK *)rt_alloc_mem(AppMallocCCM, sizeof(CPU_STK)*APP_CFG_TASK_START_STK_SIZE); AppTaskMsgProTCB = (OS_TCB *)rt_alloc_mem(AppMallocCCM, sizeof(OS_TCB)); AppTaskMsgProStk = (CPU_STK *)rt_alloc_mem(AppMallocCCM, sizeof(CPU_STK)*APP_CFG_TASK_MsgPro_STK_SIZE); AppTaskUserIFTCB = (OS_TCB *)rt_alloc_mem(AppMallocCCM, sizeof(OS_TCB)); AppTaskUserIFStk = (CPU_STK *)rt_alloc_mem(AppMallocCCM, sizeof(CPU_STK)*APP_CFG_TASK_USER_IF_STK_SIZE); AppTaskGUITCB = (OS_TCB *)rt_alloc_mem(AppMallocCCM, sizeof(OS_TCB)); AppTaskGUIStk = (CPU_STK *)rt_alloc_mem(AppMallocCCM, sizeof(CPU_STK)*APP_CFG_TASK_GUI_STK_SIZE); /* 部分全局變數的初始化,省略未寫 */ }
知識點拓展
關於任務棧大小應該分配多大的問題,可以看FreeRTOS教程第11章,對於uCOS-III系統也是適用的:http://forum.armfly.com/forum.php?mod=viewthread&tid=17658 。
3、系統棧分配
系統棧的大小不是在啟動文件裡面配置,因為系統啟動過程中做了重新配置,所以啟動文件裡面配置的系統棧只在uCOS-III開啟多任務之前使用:
重新配置的系統棧需要在如下所示位置設置,單位4位元組,比如這裡是配置的512u,也就是2048位元組。
14.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的支持。
總結,二代示波器中最終選擇了方案二。
14.6 uCOS-III系統調試
調試uCOS-III有兩種方法,一種是uC/Probe,還有一種是串口列印。
1、uC/Probe調試
uCOS-III的調試推薦使用uC/Probe,性能強勁。針對uC/Probe的調試,我們專門做過一個專題教程,如果沒有用過的話,務必先學習教程。
知識點拓展
【專題教程第2期】uC/Probe簡易使用說明,含MDK和IAR,支持F103,F407和F429開發板
http://forum.armfly.com/forum.php?mod=viewthread&tid=50280 。
另外特別註意一個問題,二代示波器的uCOS-III做了最高等級的優化和時間優化,使用uC/Probe調試需要關閉所有優化,否則無法使用,因為調試信息都被優化掉了。