第一步:官網下載FreeRTOS v10.2.1,解壓後包含的文件如下圖所示
第二步:提取FreeRTOS文件。將…\FreeRTOSv10.2.1\FreeRTOSv10.2.1_191129\FreeRTOS\Source文件夾複製到裸機工程模板的FreeRTOS v10.2.1文件夾,將portable文件夾中除keil、MemMang、RVDS文件夾外的其餘文件夾刪除,如下圖。
並將…\FreeRTOSv10.2.1\FreeRTOSv10.2.1_191129\FreeRTOS\Demo\CORTEX_STM32F103_Keil文件夾中的FreeRTOSConfig.h文件拷貝至工程User文件夾。
第三步:在裸機工程中添加FreeRTOS文件,如下圖所示(需配置include中頭文件的路徑)。選擇…\portable\MemMang文件夾中的heap_4.c以及…\portable\RVDS\ARM_CM3文件夾中的port.c和portmacro.h文件添加至port目錄
第四步:修改啓動文件startup_stm32f10x_hd.s。由於FreeRTOS在port.c中定義了
116:void xPortPendSVHandler( void );
117:void xPortSysTickHandler( void );
118:void vPortSVCHandler( void );
三個中斷供內核使用,所以需要在啓動文件中重新定義中斷的入口函數,以防止調用stm32f10x_it.c中的中斷,彙編代碼修改如下
第五步:編寫main函數,創建點亮LED的任務,代碼如下
/*
******************************************************************************
* 包含的頭文件
******************************************************************************
*/
//FreeRTOS頭文件
#include "FreeRTOS.h"
#include "task.h"
//開發板硬件bsp頭文件
#include "./led/bsp_led.h"
#include "./uart/bsp_usart.h"
/***************************** 任務句柄 **************************************/
/*
* 任務句柄是一個指針,用於指向一個任務。當任務創建好之後,它就具有一個任務句柄,
* 以後我們想操作這個任務都需要用到這個任務句柄,如果是任務操作自身,那麼這個句柄可以爲NULL
*/
//創建任務句柄
static TaskHandle_t AppTaskCreate_Handle;
//LED1任務句柄
static TaskHandle_t LED1_Task_Handle = NULL;
//LED2任務句柄
static TaskHandle_t LED2_Task_Handle = NULL;
/*************************** 內核對象句柄 ************************************/
/*
* 信號量、消息隊列、事件標誌組、軟件定時器都屬於內核的對象,要想使用這些內核對象,
* 必須先創建,創建成功之後會返回相應的句柄。這實際上就是一個指針,後續我們就可以
* 通過句柄操作這些內核對象。
*
*
* 內核對象可以理解爲一種全局的數據結構,通過這些數據結構可以實現任務間的通信、任
* 務間的事件同步等功能。這些功能的實現是通過調用內核對象的函數來完成的。
*
*
*/
/*
******************************************************************************
* 函數聲明
******************************************************************************
*/
static void AppTaskCreate(void); //用於創建任務
static void LED1_Task(void* pvParameters); //LED1_Task任務實現
static void LED2_Task(void* pvParameters); //LED2_Task任務實現
static void BSP_Init(void); //用於初始化板載相關資源
/*
******************************************************************************
* 主函數
******************************************************************************
*/
int main(void)
{
//定義一個創建信息返回值,默認爲pdPASS
BaseType_t xReturn = pdPASS;
//開發板硬件初始化
BSP_Init();
printf("動態創建任務\r\n");
//創建AppTaskCreate任務
xReturn = xTaskCreate((TaskFunction_t)AppTaskCreate,
(const char* )"AppTaskCreate",//任務名稱
(uint16_t )512, //任務棧大小
(void* )NULL, //傳遞給任務函數的參數
(UBaseType_t )1, //任務優先級
(TaskHandle_t* )&AppTaskCreate_Handle);//任務控制塊指針
printf("創建AppTaskCreate任務成功\r\n");
//啓動任務調度
if (pdPASS == xReturn)
vTaskStartScheduler(); //啓動任務,開啓調度
else
return -1;
while(1); //正常情況下不會執行到這裏
}
/*
******************************************************************************
* AppTaskCreate函數
******************************************************************************
*/
static void AppTaskCreate(void)
{
//定義一個創建信息返回值,默認爲pdPASS
BaseType_t xReturn = pdPASS;
taskENTER_CRITICAL();//進入臨界區
//創建LED1_Task任務
xReturn = xTaskCreate((TaskFunction_t)LED1_Task,
(const char* )"LED1_Task",//任務名稱
(uint16_t )512, //任務棧大小
(void* )NULL, //傳遞給任務函數的參數
(UBaseType_t )2, //任務優先級
(TaskHandle_t* )&LED1_Task_Handle); //任務控制塊
if (pdPASS == xReturn)
printf("LED1_Task任務創建成功!\n");
else
printf("LED1_Task任務創建失敗!\n");
//創建LED2_Task任務
xReturn = xTaskCreate((TaskFunction_t)LED2_Task,
(const char* )"LED2_Task",//任務名稱
(uint16_t )512, //任務棧大小
(void* )NULL, //傳遞給任務函數的參數
(UBaseType_t )3, //任務優先級
(TaskHandle_t* )&LED2_Task_Handle); //任務控制塊
if (pdPASS == xReturn)
printf("LED2_Task任務創建成功!\n");
else
printf("LED2_Task任務創建失敗!\n");
vTaskDelete(AppTaskCreate_Handle); //刪除AppTaskCreate任務
taskEXIT_CRITICAL(); //退出臨界區
}
/*
******************************************************************************
* LED1_Task函數
******************************************************************************
*/
static void LED1_Task(void* parameter)
{
while(1)
{
LED1_ON();
vTaskDelay(500);//延時500個tick
printf("led1_task running,LED1_ON\r\n");
LED1_OFF();
vTaskDelay(500);//延時500個tick
printf("led1_task running,LED1_OFF\r\n");
}
}
/*
******************************************************************************
* LED2_Task函數
******************************************************************************
*/
static void LED2_Task(void* parameter)
{
while(1)
{
LED2_ON();
vTaskDelay(1000);//延時500個tick
printf("led2_task running,LED1_ON\r\n");
LED2_OFF();
vTaskDelay(1000);//延時500個tick
printf("led2_task running,LED1_OFF\r\n");
}
}
/*
******************************************************************************
* BSP_Init函數
******************************************************************************
*/
static void BSP_Init(void)
{
/*
* STM32中斷優先級分組爲4,即4位都用來表示搶佔優先級,範圍爲0~15,
* 優先級只需要分組一次即可,以後如果有其他任務需要用到中斷,
* 都統一用這個優先級分組,千萬不要再分組
*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
//LED初始化
LED_Init();
//測試硬件是否正常工作
//LED1_ON();
//串口初始化
USART_Config();
}
第六步:編譯、下載。編譯報“Error: L6218E: Undefined symbol xTaskGetCurrentTaskHandle (referred from stream_buffer.o).”錯誤,需將FreeRTOS.h中的INCLUDE_xTaskGetCurrentTaskHandle定義爲1,如下圖
更改後可以正常運行。