FreeRTOS基础教程第一章创建任务
1.硬件初始化
本章創(chuàng)建的任務(wù)需要用到開發(fā)板上的 LED,所以先要將 LED 相關(guān)的函數(shù)初始化好, 為了方便以后統(tǒng)一管理板級外設(shè)的初始化,我們在 main.c 文件中創(chuàng)建一個 BSP_Init()函數(shù), 專門用于存放板級外設(shè)初始化函數(shù),
/************************************************************************ @ 函數(shù)名 : BSP_Init* @ 功能說明: 板級外設(shè)初始化,所有板子上的初始化均可放在這個函數(shù)里面* @ 參數(shù) : * @ 返回值 : 無*********************************************************************/ static void BSP_Init(void) {/** STM32中斷優(yōu)先級分組為4,即4bit都用來表示搶占優(yōu)先級,范圍為:0~15* 優(yōu)先級分組只需要分組一次即可,以后如果有其他的任務(wù)需要用到中斷,* 都統(tǒng)一用這個優(yōu)先級分組,千萬不要再分組,切忌。*/NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );/* LED 初始化 */LED_GPIO_Config();/* 串口初始化 */USART_Config();}? ? ?執(zhí)行到 BSP_Init()函數(shù)的時候,操作系統(tǒng)完全都還沒有涉及到,即 BSP_Init()函數(shù)所做 的工作跟我們以前編寫的裸機(jī)工程里面的硬件初始化工作是一模一樣的。運(yùn)行完 BSP_Init () 函數(shù),接下來才慢慢啟動操作系統(tǒng),最后運(yùn)行創(chuàng)建好的任務(wù)。有時候任務(wù)創(chuàng)建好,整個系 統(tǒng)跑起來了,可想要的實(shí)驗現(xiàn)象就是出不來,比如 LED 不會亮,串口沒有輸出,LCD 沒 有顯示等等。如果是初學(xué)者,這個時候就會心急如焚,四處求救,那怎么辦?這個時候如 何排除是硬件的問題還是系統(tǒng)的問題,這里面有個小小的技巧,即在硬件初始化好之后, 順便測試下硬件,測試方法跟裸機(jī)編程一樣,
/************************************************************************ @ 函數(shù)名 : BSP_Init* @ 功能說明: 板級外設(shè)初始化,所有板子上的初始化均可放在這個函數(shù)里面* @ 參數(shù) : * @ 返回值 : 無*********************************************************************/ static void BSP_Init(void) {/** STM32中斷優(yōu)先級分組為4,即4bit都用來表示搶占優(yōu)先級,范圍為:0~15* 優(yōu)先級分組只需要分組一次即可,以后如果有其他的任務(wù)需要用到中斷,* 都統(tǒng)一用這個優(yōu)先級分組,千萬不要再分組,切忌。*/NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );/* LED 初始化 */LED_GPIO_Config();/* 測試硬件是否正常工作 */ LED1_ON;/* 讓程序停在這里,不再繼續(xù)往下執(zhí)行 */while(1);/* 串口初始化 */USART_Config();}2.創(chuàng)建單任務(wù)—SRAM靜態(tài)內(nèi)存
這里,我們創(chuàng)建一個單任務(wù),任務(wù)使用的棧和任務(wù)控制塊都使用靜態(tài)內(nèi)存,即預(yù)先定 義好的全局變量,這些預(yù)先定義好的全局變量都存在內(nèi)部的 SRAM 中。
2.1定義任務(wù)函數(shù)
任務(wù)實(shí)際上就是一個無限循環(huán)且不帶返回值的 C 函數(shù)。目前,我們創(chuàng)建一個這樣的任 務(wù),讓開發(fā)板上面的 LED 燈以 500ms 的頻率閃爍,
/*********************************************************************** @ 函數(shù)名 : LED_Task* @ 功能說明: LED_Task任務(wù)主體* @ 參數(shù) : * @ 返回值 : 無********************************************************************/ static void LED_Task(void* parameter) { while (1){LED1_ON;vTaskDelay(500); /* 延時500個tick */printf("LED_Task Running,LED1_ON\r\n");LED1_OFF; vTaskDelay(500); /* 延時500個tick */ printf("LED_Task Running,LED1_OFF\r\n");} }2.2空閑任務(wù)與定時器任務(wù)堆棧函數(shù)實(shí)現(xiàn)
當(dāng)我們使用了靜態(tài)創(chuàng)建任務(wù)的時候,configSUPPORT_STATIC_ALLOCATION 這個宏 定 義 必 須為 1 (在 FreeRTOSConfig.h 文 件 中 ) , 并且 我 們需 要 實(shí) 現(xiàn)兩 個 函數(shù) : vApplicationGetIdleTaskMemory()與 vApplicationGetTimerTaskMemory(),這兩個函數(shù)是用 戶設(shè)定的空閑(Idle)任務(wù)與定時器(Timer)任務(wù)的堆棧大小,必須由用戶自己分配,而 不能是動態(tài)分配,
/* 空閑任務(wù)任務(wù)堆棧 */ static StackType_t Idle_Task_Stack[configMINIMAL_STACK_SIZE]; /* 定時器任務(wù)堆棧 */ static StackType_t Timer_Task_Stack[configTIMER_TASK_STACK_DEPTH];/* 空閑任務(wù)控制塊 */ static StaticTask_t Idle_Task_TCB; /* 定時器任務(wù)控制塊 */ static StaticTask_t Timer_Task_TCB;/************************************************************************* @brief 獲取空閑任務(wù)的任務(wù)堆棧和任務(wù)控制塊內(nèi)存* ppxTimerTaskTCBBuffer : 任務(wù)控制塊內(nèi)存* ppxTimerTaskStackBuffer : 任務(wù)堆棧內(nèi)存* pulTimerTaskStackSize : 任務(wù)堆棧大小* @date 2018-xx-xx***********************************************************************/ void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize) {*ppxIdleTaskTCBBuffer=&Idle_Task_TCB;/* 任務(wù)控制塊內(nèi)存 */*ppxIdleTaskStackBuffer=Idle_Task_Stack;/* 任務(wù)堆棧內(nèi)存 */*pulIdleTaskStackSize=configMINIMAL_STACK_SIZE;/* 任務(wù)堆棧大小 */ }/************************************************************************ @brief 獲取定時器任務(wù)的任務(wù)堆棧和任務(wù)控制塊內(nèi)存* ppxTimerTaskTCBBuffer : 任務(wù)控制塊內(nèi)存* ppxTimerTaskStackBuffer : 任務(wù)堆棧內(nèi)存* pulTimerTaskStackSize : 任務(wù)堆棧大小***********************************************************************/ void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer, StackType_t **ppxTimerTaskStackBuffer, uint32_t *pulTimerTaskStackSize) {*ppxTimerTaskTCBBuffer=&Timer_Task_TCB;/* 任務(wù)控制塊內(nèi)存 */*ppxTimerTaskStackBuffer=Timer_Task_Stack;/* 任務(wù)堆棧內(nèi)存 */*pulTimerTaskStackSize=configTIMER_TASK_STACK_DEPTH;/* 任務(wù)堆棧大小 */ }2.3 定義任務(wù)棧
目前我們只創(chuàng)建了一個任務(wù),當(dāng)任務(wù)進(jìn)入延時的時候,因為沒有另外就緒的用戶任務(wù), 那么系統(tǒng)就會進(jìn)入空閑任務(wù),空閑任務(wù)是 FreeRTOS 系統(tǒng)自己啟動的一個任務(wù),優(yōu)先級最 低。當(dāng)整個系統(tǒng)都沒有就緒任務(wù)的時候,系統(tǒng)必須保證有一個任務(wù)在運(yùn)行,空閑任務(wù)就是 為這個設(shè)計的。當(dāng)用戶任務(wù)延時到期,又會從空閑任務(wù)切換回用戶任務(wù)。 在 FreeRTOS 系統(tǒng)中,每一個任務(wù)都是獨(dú)立的,他們的運(yùn)行環(huán)境都單獨(dú)的保存在他們 的??臻g當(dāng)中。那么在定義好任務(wù)函數(shù)之后,我們還要為任務(wù)定義一個棧,目前我們使用 的是靜態(tài)內(nèi)存,所以任務(wù)棧是一個獨(dú)立的全局變量,具體見代碼清單 14-5。任務(wù)的棧占用 的是 MCU 內(nèi)部的 RAM,當(dāng)任務(wù)越多的時候,需要使用的??臻g就越大,即需要使用的 RAM 空間就越多。一個 MCU 能夠支持多少任務(wù),就得看你的 RAM 空間有多少。
/* AppTaskCreate任務(wù)任務(wù)堆棧 */ static StackType_t AppTaskCreate_Stack[128]; /* LED任務(wù)堆棧 */ static StackType_t LED_Task_Stack[128];2.4定義任務(wù)控制塊
定義好任務(wù)函數(shù)和任務(wù)棧之后,我們還需要為任務(wù)定義一個任務(wù)控制塊,通常我們稱 這個任務(wù)控制塊為任務(wù)的身份證。在 C代碼上,任務(wù)控制塊就是一個結(jié)構(gòu)體,里面有非常 多的成員,這些成員共同描述了任務(wù)的全部信息,
/* AppTaskCreate 任務(wù)控制塊 */ static StaticTask_t AppTaskCreate_TCB; /* AppTaskCreate 任務(wù)控制塊 */ static StaticTask_t LED_Task_TCB;2.5 靜態(tài)創(chuàng)建任務(wù)
一個任務(wù)的三要素是任務(wù)主體函數(shù),任務(wù)棧,任務(wù)控制塊,那么怎么樣把這三個要素 聯(lián)合在一起?FreeRTOS 里面有一個叫靜態(tài)任務(wù)創(chuàng)建函數(shù) xTaskCreateStatic(),它就是干這 個活的。它將任務(wù)主體函數(shù),任務(wù)棧(靜態(tài)的)和任務(wù)控制塊(靜態(tài)的)這三者聯(lián)系在一 起,讓任務(wù)可以隨時被系統(tǒng)啟動,
/* 創(chuàng)建 AppTaskCreate 任務(wù) */AppTaskCreate_Handle = xTaskCreateStatic((TaskFunction_t )AppTaskCreate, //任務(wù)函數(shù)(const char* )"AppTaskCreate", //任務(wù)名稱(uint32_t )128, //任務(wù)堆棧大小(void* )NULL, //傳遞給任務(wù)函數(shù)的參數(shù)(UBaseType_t )3, //任務(wù)優(yōu)先級(StackType_t* )AppTaskCreate_Stack, //任務(wù)堆棧(StaticTask_t* )&AppTaskCreate_TCB); //任務(wù)控制塊 if(NULL != AppTaskCreate_Handle)/* 創(chuàng)建成功 */vTaskStartScheduler(); /* 啟動任務(wù),開啟調(diào)度 */2.6 啟動任務(wù)
當(dāng)任務(wù)創(chuàng)建好后,是處于任務(wù)就緒(Ready),在就緒態(tài)的任務(wù)可以參與操作系統(tǒng)的調(diào)度。 但是此時任務(wù)僅僅是創(chuàng)建了,還未開啟任務(wù)調(diào)度器,也沒創(chuàng)建空閑任務(wù)與定時器任務(wù)(如果使 能了 configUSE_TIMERS 這個宏定義),那這兩個任務(wù)就是在啟動任務(wù)調(diào)度器中實(shí)現(xiàn),每個操 作系統(tǒng),任務(wù)調(diào)度器只啟動一次,之后就不會再次執(zhí)行了,FreeRTOS 中啟動任務(wù)調(diào)度器的函 數(shù)是 vTaskStartScheduler(),并且啟動任務(wù)調(diào)度器的時候就不會返回,從此任務(wù)管理都由 FreeRTOS管理,此時才是真正進(jìn)入實(shí)時操作系統(tǒng)中的第一步,
? vTaskStartScheduler(); ? /* 啟動任務(wù),開啟調(diào)度 */
2.7 main.c文件內(nèi)容全貌
現(xiàn)在我們把任務(wù)主體,任務(wù)棧,任務(wù)控制塊這三部分代碼統(tǒng)一放到 main.c 中,我們在 main.c 文件中創(chuàng)建一個 AppTaskCreate 任務(wù),這個任務(wù)是用于創(chuàng)建用戶任務(wù),為了方便管 理,我們的所有的任務(wù)創(chuàng)建都統(tǒng)一放在這個函數(shù)中,在這個函數(shù)中創(chuàng)建成功的任務(wù)就可以 直接參與任務(wù)調(diào)度了,
/* FreeRTOS頭文件 */ #include "FreeRTOS.h" #include "task.h" /* 開發(fā)板硬件bsp頭文件 */ #include "bsp_led.h" #include "bsp_usart.h" static void AppTaskCreate(void);/* 用于創(chuàng)建任務(wù) */static void LED_Task(void* pvParameters);/* LED_Task任務(wù)實(shí)現(xiàn) */static void BSP_Init(void);/* 用于初始化板載相關(guān)資源 */ /**************************** 任務(wù)句柄 ********************************/ /* * 任務(wù)句柄是一個指針,用于指向一個任務(wù),當(dāng)任務(wù)創(chuàng)建好之后,它就具有了一個任務(wù)句柄* 以后我們要想操作這個任務(wù)都需要通過這個任務(wù)句柄,如果是自身的任務(wù)操作自己,那么* 這個句柄可以為NULL。*//* 創(chuàng)建任務(wù)句柄 */ static TaskHandle_t AppTaskCreate_Handle; /* LED任務(wù)句柄 */ static TaskHandle_t LED_Task_Handle; /********************************** 內(nèi)核對象句柄 *********************************/ /** 信號量,消息隊列,事件標(biāo)志組,軟件定時器這些都屬于內(nèi)核的對象,要想使用這些內(nèi)核* 對象,必須先創(chuàng)建,創(chuàng)建成功之后會返回一個相應(yīng)的句柄。實(shí)際上就是一個指針,后續(xù)我* 們就可以通過這個句柄操作這些內(nèi)核對象。** 內(nèi)核對象說白了就是一種全局的數(shù)據(jù)結(jié)構(gòu),通過這些數(shù)據(jù)結(jié)構(gòu)我們可以實(shí)現(xiàn)任務(wù)間的通信,* 任務(wù)間的事件同步等各種功能。至于這些功能的實(shí)現(xiàn)我們是通過調(diào)用這些內(nèi)核對象的函數(shù)* 來完成的* *//******************************* 全局變量聲明 ************************************/ /** 當(dāng)我們在寫應(yīng)用程序的時候,可能需要用到一些全局變量。*/ /* AppTaskCreate任務(wù)任務(wù)堆棧 */ static StackType_t AppTaskCreate_Stack[128]; /* LED任務(wù)堆棧 */ static StackType_t LED_Task_Stack[128];/* AppTaskCreate 任務(wù)控制塊 */ static StaticTask_t AppTaskCreate_TCB; /* AppTaskCreate 任務(wù)控制塊 */ static StaticTask_t LED_Task_TCB;/* 空閑任務(wù)任務(wù)堆棧 */ static StackType_t Idle_Task_Stack[configMINIMAL_STACK_SIZE]; /* 定時器任務(wù)堆棧 */ static StackType_t Timer_Task_Stack[configTIMER_TASK_STACK_DEPTH];/* 空閑任務(wù)控制塊 */ static StaticTask_t Idle_Task_TCB; /* 定時器任務(wù)控制塊 */ static StaticTask_t Timer_Task_TCB;/*** 使用了靜態(tài)分配內(nèi)存,以下這兩個函數(shù)是由用戶實(shí)現(xiàn),函數(shù)在task.c文件中有引用* 當(dāng)且僅當(dāng) configSUPPORT_STATIC_ALLOCATION 這個宏定義為 1 的時候才有效*/ void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer, StackType_t **ppxTimerTaskStackBuffer, uint32_t *pulTimerTaskStackSize);void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize);/****************************************************************** @brief 主函數(shù)* @param 無* @retval 無* @note 第一步:開發(fā)板硬件初始化 第二步:創(chuàng)建APP應(yīng)用任務(wù)第三步:啟動FreeRTOS,開始多任務(wù)調(diào)度****************************************************************/ int main(void) { /* 開發(fā)板硬件初始化 */BSP_Init();printf("這是一個[野火]-STM32全系列開發(fā)板-FreeRTOS-靜態(tài)創(chuàng)建單任務(wù)!\r\n");/* 創(chuàng)建 AppTaskCreate 任務(wù) */AppTaskCreate_Handle = xTaskCreateStatic((TaskFunction_t )AppTaskCreate, //任務(wù)函數(shù)(const char* )"AppTaskCreate", //任務(wù)名稱(uint32_t )128, //任務(wù)堆棧大小(void* )NULL, //傳遞給任務(wù)函數(shù)的參數(shù)(UBaseType_t )3, //任務(wù)優(yōu)先級(StackType_t* )AppTaskCreate_Stack, //任務(wù)堆棧(StaticTask_t* )&AppTaskCreate_TCB); //任務(wù)控制塊 if(NULL != AppTaskCreate_Handle)/* 創(chuàng)建成功 */vTaskStartScheduler(); /* 啟動任務(wù),開啟調(diào)度 */while(1); /* 正常不會執(zhí)行到這里 */ }/************************************************************************ @ 函數(shù)名 : AppTaskCreate* @ 功能說明: 為了方便管理,所有的任務(wù)創(chuàng)建函數(shù)都放在這個函數(shù)里面* @ 參數(shù) : 無 * @ 返回值 : 無**********************************************************************/ static void AppTaskCreate(void) {taskENTER_CRITICAL(); //進(jìn)入臨界區(qū)/* 創(chuàng)建LED_Task任務(wù) */LED_Task_Handle = xTaskCreateStatic((TaskFunction_t )LED_Task, //任務(wù)函數(shù)(const char* )"LED_Task", //任務(wù)名稱(uint32_t )128, //任務(wù)堆棧大小(void* )NULL, //傳遞給任務(wù)函數(shù)的參數(shù)(UBaseType_t )4, //任務(wù)優(yōu)先級(StackType_t* )LED_Task_Stack, //任務(wù)堆棧(StaticTask_t* )&LED_Task_TCB); //任務(wù)控制塊 if(NULL != LED_Task_Handle)/* 創(chuàng)建成功 */printf("LED_Task任務(wù)創(chuàng)建成功!\n");elseprintf("LED_Task任務(wù)創(chuàng)建失敗!\n");vTaskDelete(AppTaskCreate_Handle); //刪除AppTaskCreate任務(wù)taskEXIT_CRITICAL(); //退出臨界區(qū) }/*********************************************************************** @ 函數(shù)名 : LED_Task* @ 功能說明: LED_Task任務(wù)主體* @ 參數(shù) : * @ 返回值 : 無********************************************************************/ static void LED_Task(void* parameter) { while (1){LED1_ON;vTaskDelay(500); /* 延時500個tick */printf("LED_Task Running,LED1_ON\r\n");LED1_OFF; vTaskDelay(500); /* 延時500個tick */ printf("LED_Task Running,LED1_OFF\r\n");} }/************************************************************************ @ 函數(shù)名 : BSP_Init* @ 功能說明: 板級外設(shè)初始化,所有板子上的初始化均可放在這個函數(shù)里面* @ 參數(shù) : * @ 返回值 : 無*********************************************************************/ static void BSP_Init(void) {/** STM32中斷優(yōu)先級分組為4,即4bit都用來表示搶占優(yōu)先級,范圍為:0~15* 優(yōu)先級分組只需要分組一次即可,以后如果有其他的任務(wù)需要用到中斷,* 都統(tǒng)一用這個優(yōu)先級分組,千萬不要再分組,切忌。*/NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );/* LED 初始化 */LED_GPIO_Config();/* 測試硬件是否正常工作 */ LED1_ON;/* 讓程序停在這里,不再繼續(xù)往下執(zhí)行 */while(1);/* 串口初始化 */USART_Config();}/************************************************************************* @brief 獲取空閑任務(wù)的任務(wù)堆棧和任務(wù)控制塊內(nèi)存* ppxTimerTaskTCBBuffer : 任務(wù)控制塊內(nèi)存* ppxTimerTaskStackBuffer : 任務(wù)堆棧內(nèi)存* pulTimerTaskStackSize : 任務(wù)堆棧大小* @date 2018-xx-xx***********************************************************************/ void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize) {*ppxIdleTaskTCBBuffer=&Idle_Task_TCB;/* 任務(wù)控制塊內(nèi)存 */*ppxIdleTaskStackBuffer=Idle_Task_Stack;/* 任務(wù)堆棧內(nèi)存 */*pulIdleTaskStackSize=configMINIMAL_STACK_SIZE;/* 任務(wù)堆棧大小 */ }/************************************************************************ @brief 獲取定時器任務(wù)的任務(wù)堆棧和任務(wù)控制塊內(nèi)存* ppxTimerTaskTCBBuffer : 任務(wù)控制塊內(nèi)存* ppxTimerTaskStackBuffer : 任務(wù)堆棧內(nèi)存* pulTimerTaskStackSize : 任務(wù)堆棧大小***********************************************************************/ void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer, StackType_t **ppxTimerTaskStackBuffer, uint32_t *pulTimerTaskStackSize) {*ppxTimerTaskTCBBuffer=&Timer_Task_TCB;/* 任務(wù)控制塊內(nèi)存 */*ppxTimerTaskStackBuffer=Timer_Task_Stack;/* 任務(wù)堆棧內(nèi)存 */*pulTimerTaskStackSize=configTIMER_TASK_STACK_DEPTH;/* 任務(wù)堆棧大小 */ }/********************************END OF FILE****************************/注意:在使用靜態(tài)創(chuàng)建任務(wù)的時候必須要將 FreeRTOSConfig.h 中 的 configSUPPORT_STATIC_ALLOCATION 宏配置為 1。
下載后,可以看到板子上面的 LED 燈已經(jīng)在 閃爍,說明我們創(chuàng)建的單任務(wù)(使用靜態(tài)內(nèi)存)已經(jīng)跑起來了。
本篇內(nèi)容摘自野火《FreeRTOS內(nèi)核實(shí)現(xiàn)與應(yīng)用開發(fā)》
總結(jié)
以上是生活随笔為你收集整理的FreeRTOS基础教程第一章创建任务的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 牛人自述模拟电路学习历程
- 下一篇: Express实现路由分发控制、REST