本文主要介紹了如何創(chuàng)建FreeRTOS的任務(wù),基于STM32F767 Nucleo-144平臺(tái),在CubeIDE下進(jìn)行開發(fā),結(jié)合官方的HAL庫。如果覺得不錯(cuò),歡迎關(guān)注、分享、收藏、點(diǎn)贊。希望能幫助到大家,如有錯(cuò)誤敬請指出,謝謝!
文章目錄
- FreeRTOS
- 獲取源碼
- 源碼結(jié)構(gòu)
- CubeMX 整合 RTOS
- 新建RTOS任務(wù)
- 總結(jié)
FreeRTOS是免費(fèi)的嵌入式實(shí)時(shí)系統(tǒng),可以訪問官網(wǎng),至少目前是免費(fèi),并且社區(qū)相當(dāng)活躍,但是以后會(huì)不會(huì)被割韭菜就不清楚了,所以感覺還是支持國產(chǎn)比較好,大家可以參考一下RT-Thread;
- FreeRTOS-Kernel:這個(gè)倉庫是RTOS最核心的東西,很純凈,包括調(diào)度算法,信號(hào)量,內(nèi)存管理等等,它同時(shí)作為一個(gè)子模塊存在于FreeRTOS倉庫中;
- FreeRTOS:這個(gè)倉庫是比較全的源碼,除了核心的部分,還包括對各個(gè)芯片平臺(tái)的支持以及各個(gè)主流IDE的demo,因此如果要移植的話,主要還是下載這個(gè)源碼;
但是本文暫不會(huì)介紹如何移植RTOS,在遠(yuǎn)古時(shí)期,因?yàn)榈谌降闹С至Χ炔粔?因此有一些平臺(tái)只能自己移植,現(xiàn)在第三方的支持相當(dāng)于給力,直接拿來用即可.
自動(dòng)生成的代碼中找到FreeRTOS的源碼如下;
對于相應(yīng)的文件夾和源碼文件做一下介紹;
CMSIS_RTOS_V2
:這是API的版本,對應(yīng)的還有CMSIS_RTOS_V1;portable
:這里主要是移植的時(shí)候需要修改的一些文件,針對不同的MCU以及不同的編譯器,需要對這塊進(jìn)行修改;portmacro.h
:定義編譯器相關(guān)的數(shù)據(jù)類型和中斷處理的宏定義;port.c
:實(shí)現(xiàn)任務(wù)的堆棧初始化、systick
和任務(wù)上下文切換;
MemMang
:內(nèi)存管理的文件,目前主要用heap_1.c
,heap_2.c
,heap_3.c
,heap_4.c
,heap_5.c
;list.c
:雙向鏈表的實(shí)現(xiàn),主要供給內(nèi)核調(diào)度器使用;queue.c
: 隊(duì)列的實(shí)現(xiàn),主要支持中斷環(huán)境和信號(hào)量控制;croutine.c
:任務(wù)使用同一個(gè)堆棧,這樣使得減小RAM的使用,但是在使用上會(huì)受到相當(dāng)大的限制;task.c
:每個(gè)任務(wù)都有各自的獨(dú)立堆棧,支持完全的搶占式調(diào)度;
- 在配置工程的時(shí)候或者打開后綴名為
.ioc
的cubemx
配置文件; - 點(diǎn)擊菜單欄
Pinout&Configuration
; - 點(diǎn)擊
Middleware
選擇FREERTOS
;
具體如下圖所示;
最終生成的文件結(jié)構(gòu)如下圖所示;
這時(shí)候已經(jīng)將FreeRTOS集成到工程中了,*****,簡直不能再方便了;
直接生成的工程中,系統(tǒng)已經(jīng)創(chuàng)建好了一個(gè)任務(wù)StartDefaultTask
;整體的main.c
代碼如下;
#include "main.h"
#include "cmsis_os.h"
/* Definitions for defaultTask */
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
.name = "defaultTask",
.priority = (osPriority_t) osPriorityNormal,
.stack_size = 128 * 4
};
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
void StartDefaultTask(void *argument);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
osKernelInitialize();
defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
/* Start scheduler */
osKernelStart();
/* We should never get here as control is now taken by the scheduler */
while (1)
{
}
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE3);
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
Error_Handler();
}
}
static void MX_GPIO_Init(void)
{
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOA_CLK_ENABLE();
}
void StartDefaultTask(void *argument)
{
for(;;)
{
osDelay(1);
}
}
void Error_Handler(void)
{
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
}
#endif /* USE_FULL_ASSERT */
這里可以看到整體的流程為兩部分HAL層和OS層;
HAL:
- 硬件初始化
HAL_Init
; - 時(shí)鐘配置
SystemClock_Config
; - BSP初始化
MX_GPIO_Init
;
OS:
- 內(nèi)核初始化
osKernelInitialize
; - 創(chuàng)建任務(wù)/線程
osThreadNew
; - 任務(wù)調(diào)度
osKernelStart
整體流程圖如下所示;
osThreadId_t defaultTaskHandle;
定義一個(gè)變量,后面創(chuàng)建任務(wù)會(huì)分配一個(gè)id
作為該任務(wù)的唯一標(biāo)識(shí)符/身份證;osThreadAttr_t defaultTask_attributes;
作為定義一個(gè)任務(wù)時(shí)的參數(shù),包括任務(wù)的優(yōu)先級(jí),所需要分配的棧空間大小.以及任務(wù)的名字等等;具體結(jié)構(gòu)體osThreadAttr_t
如下所示;
typedef struct {
const char *name; ///< name of the thread
uint32_t attr_bits; ///< attribute bits
void *cb_mem; ///< memory for control block
uint32_t cb_size; ///< size of provided memory for control block
void *stack_mem; ///< memory for stack
uint32_t stack_size; ///< size of stack
osPriority_t priority; ///< initial thread priority (default: osPriorityNormal)
TZ_ModuleId_t tz_module; ///< TrustZone module identifier
uint32_t reserved; ///< reserved (must be 0)
} osThreadAttr_t;
- 創(chuàng)建任務(wù)回調(diào)函數(shù),這里有一個(gè)生命周期的問題,通常在任務(wù)中放一個(gè)死循環(huán),以便于任務(wù)保持存活,可以被一直調(diào)度;
void StartDefaultTask(void *argument)
{
for(;;)
{
osDelay(1);
}
}
最后,調(diào)用osThreadNew
創(chuàng)建任務(wù);該函數(shù)聲明如下所示;
osThreadId_t osThreadNew ( osThreadFunc_t func,
void *argument,
const osThreadAttr_t *attr);
通過cube自動(dòng)生成了一個(gè)FREERTOS工程,簡單分析了一下如何創(chuàng)建一個(gè)任務(wù),后續(xù)需要在實(shí)踐中深入到源碼中學(xué)習(xí)FreeRTOS
;
筆者能力和水平有限,文中難免有錯(cuò)誤和紕漏之處,請大佬們不吝賜教。