CubeMX使用FreeRTOS編程指南(cubemx生成freertos)
文章目錄
CubeMX使用FreeRTOS編程指南
一、開發前言
1.1 軟件準備
1.2 開啟FreeRTOS
二、配置界面
三、系統設置
2.1 調度內核設置
2.2 內存管理設置
2.3 鉤子函數配置
2.5 任務運行追蹤配置
2.6 協程配置
2.7 軟件定時器配置
2.8 中斷優先級配置
三、內核裁剪
四、創建任務與隊列
4.1 CubeMX 下任務創建與配置
4.2 CubeMX 下隊列的創建與配置
五、創建定時器和信號量
5.1 CubeMX下定時器的創建和配置
5.2 CubeMX下信號量的創建和配置
六、創建互斥量
6.1 CubeMX下互斥量的創建和配置
七、創建事件標志組
7.1 CubeMX下事件的創建和配置
八、用戶常量
九、任務通知
十、系統內核配置
CubeMX使用FreeRTOS編程指南
一、開發前言
1.1 軟件準備
STM32CubeMX 代碼生成軟件
MDK 集成代碼開發環境
1.2 開啟FreeRTOS
新建一個 CubeMX 工程,在配置好時鐘后,點擊 Middleware -> 選擇 FreeRTOS -> 下拉框選擇 V2 版本 CMSIS
到此在 CubeMX 中就已經開啟 FreeRTOS 系統了,下面分享 FreeRTOS 的配置:
二、配置界面
開啟 FreeRTOS 之后,可以看到配置項主要分為以下幾個部分
這幾個部分的主要功能如下表:
以上各個功能分的很清晰,我們需要配置什么功能就去對應的選項下進行配置,下面根據各個配置項進行詳細配置介紹
三、系統設置
首先我們先了解一下 Config Parameters,他的配置參數如下
參數功能表:
API 和 Version 不過多解釋,顯示版本信息
2.1 調度內核設置
Kernel Setting 是 FreeRTOS 的調度內核配置,展開后有下面的配置項,使用時一般保持默認,也可以根據需要修改
USE_PREEMPTION
USE_PREEMPTION 是 RTOS 的調度方式選擇,為 1 時使用搶占式調度器,為 0 時使用協程,如果使用搶占式調度器的話內核會在每個時鐘節拍中斷中進行任務切換,當使用協程的話會在如下地方進行任務切換
一個任務調用了函數 taskYIELD()。
一個任務調用了可以使任務進入阻塞態的 API 函數。
應用程序明確定義了在中斷中執行上下文切換。
CPU_CLOCK_HZ
CPU_CLOCK_HZ 是 CPU 系統時鐘頻率,默認使用的是晶振通過時鐘樹后獲得的時鐘頻率
TICK_RATE_HZ
TICK_RATE_HZ 是 RTOS 的心跳時鐘頻率,默認為最大值 1000 ,即心跳時鐘 1ms 跳動一次
MAX_PRIORITIES
MAX_PRIORITIES 是 RTOS 任務的最高優先級設置,默認56級,一般來說一個優先級表是32位,這里用了兩個,對應64位,其中8位用于系統任務的優先級處理
MINIMAL_STACK_SIZE
MINIMAL_STACK_SIZE 設置分配給空閑任務的堆棧大小,該值是用字(32位)指定的,而不是字節,默認為128個字,如果修改過空閑任務,則根據實際情況修改
MAX_TASK_NAME_LEN
MAX_TASK_NAME_LEN 設置任務名稱的最大字符數,默認16位足夠
USE_16_BIT_TICKS
USE_16_BIT_TICKS 存放 Tick 周期的計數器的數字位寬,默認為 Disable 即 16 位
IDLE_SHOULD_YIELD
如果IDLE_SHOULD_YIELD 設置為0,則空閑任務永遠不會讓位于另一個任務,只在被搶占時才會離開運行狀態。如果 IDLE_SHOULD_YIELD 設置為1,那么當有另一個空閑優先級任務處于Ready狀態時,空閑任務將不會執行它定義的功能的不止一次迭代,而不會讓位于另一個任務,這確保當應用程序任務處于空閑狀態時,在空閑任務中花費的時間最少,即同在空閑優先級下,空閑任務優先級更高,不會被搶占,不會以時間片運行
USE_MUTEXES、USE_RECURSIVE_MUTEXES、USE_COUNTING_SEMAPHORES
為 1 則開啟系統構建過程中的互斥量、遞歸互斥量和信號量,該值強制為1(ENABLE)
QUEUE_REGISTRY_SIZE
隊列注冊表的大小,可以用于管理隊列名稱和隊列實體,方便運行中進行查看與管理,默認為8
USE_APPLICATION_TASK_TAG
使能時會給任務一個 TAG 標簽,便于用戶進行使用
ENABLE_BACKWARD_COMPATIBILITY
一個兼容性使能,使能后, FreeRTOS 8.0.0 之后的版本可以通過宏定義使用 8.0.0 版本之前的函數接口,默認使能
USE_PORT_OPTIMISED_TASK_SELECTION
查找下一個任務方式的選擇,查找下一個就緒任務就是查找優先級表,對優先級表進行導0算法,分為通用切換或者針對性切換,一般默認不使能,使用通用切換,通用切換使用C編寫,執行效率低,兼容性高;針對性切換使用處理器自帶的導0指令,使用匯編編寫,切換效率高,但兼容性差
USE_TICKLESS_IDLE
使能后會生成的兩個空函數PreSleepProcessing和PostSleepProcessing,用戶可以編寫代碼進入低功耗模式,生成函數如下圖
USE_TASK_NOTIFICATIONS
任務通知使能,每個RTOS任務都有一個32位的通知值,RTOS任務通知是一個直接發送給任務的事件,它可以解除接收任務的阻塞,并可選地更新接收任務的通知值,為1開啟,為0關閉,關閉可以為每個任務節省8個字節的內存空間
RECORD_STACK_HIGH_ADDRESS
記錄任務的堆棧入口地址到TCB,為1使能,為0關閉
2.2 內存管理設置
內存管理可以看到3個配置參數
Memory Allocation
內存分配方式,此處默認動態和靜態都可以
TOTAL_HEAP_SIZE
內存堆的分配大小,堆本質上就是一個數組,此處是設置堆數組的大小,設置時要考慮最小要滿足所有任務的使用要求,最大不要超過系統的分配上限
Memory Management scheme
內存分配方式,有heap_1.c, heap_2.c, heap_3.c, heap_4.c and heap5.c 5種,其中1、2、4、5都是先建立一個堆數組,從數組中申請,用完再釋放,與C語言中molloc和free使用鏈表的方式不同,該方式在 MCU 中更安全穩定,此處默認使用的方式4,具體申請釋放方式可以在heap4.c中閱讀到
關于堆和棧的區別,可以閱讀我的另外一篇文章進行了解:C語言:內存四區
2.3 鉤子函數配置
鉤子函數是一種回調函數,用于在任務執行一次之后或者某些事件發生后執行的函數,該配置項里面有五個選項,控制5種不同功能的鉤子函數開啟,當然用戶也可以在代碼中自己定義
USE_IDLE_HOOK
使能后,系統生成一個空回調函數,由用戶編寫函數主體
void vApplicationIdleHook(void)
1
每當空閑任務執行一次,鉤子函數都會被執行一次
USE_TICK_HOOK
使能后,系統生成一個空回調函數,由用戶編寫函數主體
void vApplicationTickHook(void)
1
每個TICK周期,鉤子函數都會執行一次
USE_MALLOC_FAILED_HOOK
使能后,系統生成一個空回調函數,由用戶編寫函數主體
void vApplicationMallocFailedHook(void)
1
當申請動態內存失敗時,鉤子函數會執行一次
USE_DAEMON_TASK_STARTUP_HOOK
使能后,系統生成一個空回調函數,由用戶編寫函數主體
void vApplicationDaemonTaskStartupHook(void).
1
任務剛啟動時,鉤子函數會執行一次
CHECK_FOR_STACK_OVERFLOW
使能后,系統生成一個空回調函數,由用戶編寫函數主體
void vApplicationStackOverflowHook( xTaskHandle xTask, signed char *pcTaskName );
1
任務棧溢出時,鉤子函數會執行一次,傳入任務 TCB 和任務名稱
當我們在 CubeMX 里面開啟對應鉤子函數,生成代碼之后,在FreeRTOS就可以看到自動生成的鉤子函數,我們在里面編寫相應的功能就行
2.5 任務運行追蹤配置
功能配置項如下:
GENERATE_RUN_TIME_STATS
開啟時間統計功能,在調用 vTaskGetRunTimeStats() 函數時,將任務運行時間信息保存到可讀列表中
USE_TRACE_FACILITY
使能后會包含額外的結構成員和函數以幫助執行可視化和跟蹤,默認開啟,方便 MDK 軟件工具調試使用
USE_STATS_FORMATTING_FUNCTIONS
使能后會生成 vTaskList() 和 vTaskGetRunTimeStats() 函數用于獲取任務運行狀態
2.6 協程配置
Co-routine related definitions 是協程的配置項,兩個選項用來配置協程是否開啟,以及協程的優先級,開啟后,需要用戶手動創建協程,在協程幾乎很少用到了,是 FreeRTOS目前還沒有把協程移除的計劃,但 FreeRTOS是不會再更新和維護協程了,因此大家解一下就行
協程特點:
堆棧使用
所有的協程使用同一個堆棧(如果是任務的話每個任務都有自己的堆棧),這樣就比使用任務消耗更少的 RAM
調度器和優先級
協程使用合作式的調度器,但是可以在使用搶占式的調度器中使用協程
宏實現
協程是通過宏定義來實現的
使用限制
為了降低對 RAM 的消耗做了很多的限制
具體 API 接口和調度原理可以參考這篇文章 : FreeRTOS協程
2.7 軟件定時器配置
軟件定時器配置的一些相關項如下:
這四個配置項主要與軟件定時器處理任務有關,軟件定時器任務屬于系統任務(守護線程),開啟軟件定時器后用于維護軟件定時器
USE_TIMERS
默認開啟軟件定時器任務
TIMER_TASK_PRIORITY
軟件定時器任務優先級
TIMER_QUEUE_LENGTH
定時器任務隊列長度,FreeRTOS 是通過隊列來發送控制命令給定時器任務,叫做定時器命令隊列,此處設置隊列長度
TIMER_TASK_STACK_DEPTH
軟件定時器任務堆棧大小
2.8 中斷優先級配置
LIBRARY_LOWEST_INTERRUPT_PRIORITY
此宏是用來設置最低優先級,FreeRTOS 使用的4位優先級,對應16位優先級,對應的最低優先級為15
LIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY
設置FreeRTOS 系統可管理的最大優先級,也就是設置閾值優先級,這個大家可以自由設置,這里設置為5,也就是高于5 的優先級(優先級數小于5)不歸 FreeRTOS 管理
三、內核裁剪
Include Parameters 下的選項應用于內核裁剪,裁剪不必要的功能,精簡系統功能,減少資源占用,主要有以下幾個選項:
配置項可裁剪的函數功能如下:
四、創建任務與隊列
4.1 CubeMX 下任務創建與配置
任務(線程)是操作系統運行的基本單元,也是資源分配的基本單元, CubeMX 任務的創建基本以圖形化進行,配置方式如下
進入Tashs and Queues 配置,點擊 Add 添加新任務
任務配置參數介紹
設置完成后點擊OK,配置就完成了,之后生成代碼,使用 MDK 進一步配置任務的具體信息
在生成的代碼中,我們打開 freertos.c 文件可以在代碼中看到任務的配置信息
在 freertos.c 文件的末尾部分,我們可以看到生成的任務實體
任務實體本身就是一個死循環函數,循環執行程序代碼,但循環體代碼里面必須要有延時函數,釋放當前任務對 MCU 的控制權,使其他低優先級可以執行,此外,關于任務,CubeMX 提供了一系列的用戶調用接口函數,具體如下
4.2 CubeMX 下隊列的創建與配置
隊列,又稱為消息隊列,用于任務間的數據通信,傳輸數據,在操作系統里面,直接使用全局變量傳輸數據十分危險,看似正常運行,但不知道啥時候就會因為寄存器或者內存等等原因引起崩潰,所以引入消息,隊列的概念,任務發送數據到隊列,需要接受消息的任務掛起在隊列的掛起列表,等待消息的到來,CubeMX 創建隊列的步驟如下:
先點擊 Add 添加隊列
隊列配置參數介紹
配置需要的參數后,點擊OK,然后生成代碼
生成代碼后,我們可以在 freertos.c 中系統初始話函數中看到隊列的初始化
初始化函數會在一開始被調用,對 FreeRTOS 系統和內核對象進行初始化,初始化后系統就可以進行調度和使用內核對象,CubeMX 生成的代碼自動將創建的內核對象放到初始化函數內,所以我們在任務和中斷中直接使用就可以,隊列的 FreeRTOS API 接口在CubeMX 內再次進行了封裝,使用更加簡單,使用方式如下:
我們使用的 CMSIS 2.0 版本,所以在任務文件中包含調用聲明頭文件
#include "cmsis_os2.h"
1
在隊列頭文件內我們可以在 600 多行的位置找到有關隊列的 API 函數聲明:
下面介紹一下隊列有關接口的函數接口:
以上的API接口有其對應的傳入參數,具體使用方式需要在翻源碼的注釋,這里我選常用的來介紹一下:
消息隊列常用的是插入與獲取消息,初始化系統已經幫助我們完成,在初始化的時候會獲取一個隊列的句柄,之后對隊列的操作都是圍繞這個句柄展開,比如上面的代碼中,句柄就是 myQueue01Handle ,我們發送一個消息到這個隊列,就是調用發送函數,對句柄進行操作,先看一下發送消息的函數原型
osStatus_t osMessageQueuePut (osMessageQueueId_t mq_id, const void *msg_ptr, uint8_t msg_prio, uint32_t timeout);
1
參數的功能
返回值的可能
所以我們發送一個消息到隊列,函數用法如下:
void StartTask02(void *argument) { /* USER CODE BEGIN StartTask02 */ osStatus_t result; uint8_t dat[]="666\r\n"; /* Infinite loop */ for(;;) { result= osMessageQueuePut(myQueue01Handle,dat,1,0); if(result == osOK) { //發送成功 }else { //發送失敗 } osDelay(1); } /* USER CODE END StartTask02 */ }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
發送消息的優先級暫時無用,CubeMX 對 FreeRTOS 的支持還不完善,發送消息里面的優先級未使用到,并且入隊方式使用的是發送到隊列尾部,沒有從頭部插入的方式,有需求可以 通過包含 queue.h 文件,調用 FreeRTOS 的官方代碼,或者自己修改 生成代碼的 API 接口結合優先級使用隊列的向前插入和向后插入,豐富系統功能!
除了發送消息到隊列,接受隊列的消息 API 接口也經常用到,函數原型如下
osStatus_t osMessageQueueGet (osMessageQueueId_t mq_id, void *msg_ptr, uint8_t *msg_prio, uint32_t timeout);
1
參數的功能
函數用法
void StartTask02(void *argument) { /* USER CODE BEGIN StartTask02 */ osStatus_t result; uint8_t dat[10]={}; uint8_t *pro; /* Infinite loop */ for(;;) { result= osMessageQueueGet(myQueue01Handle,dat,pro,10); if(result == osOK) { //接受成功 }else { //接受失敗 } osDelay(1); } /* USER CODE END StartTask02 */ }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
注意:FreeRTOS 中獲取和發送消息的 API 接口函數分為任務中調用和中斷中調用,CubeMX 代碼接口將兩者整合了,調用時自動判斷調用環境是在 ISR 還是正常運行環境中
五、創建定時器和信號量
5.1 CubeMX下定時器的創建和配置
軟件定時器本質上就是設置一段時間,當設置的時間到達之后就執行指定的功能函數,調用的這個函數叫做回調函數。回調函數的兩次執行間隔叫做定時器的定時周期,簡而言之,當定時器的定時周期到了以后就會執行回調函數,下面介紹一下 CubeMX 中開啟定時器的方法:
在 CubeMX 里面按下面步驟添加定時器
然后配置具體參數,參數的功能如下:
參數配置完成后,生成代碼,我們可以在 freertos.c 文件里面看到定時器創建后獲得的句柄,以及生成的回調函數:
有了句柄,我們就可以調用 cmsis_os2.c 里面的定時器接口函數對定時器進行操作,先看一下 CubeMX 提供的定時器接口函數及其功能
其中常用的接口是定時器的啟動和停止
定時器啟動: osTimerStart,函數原型
osStatus_t osTimerStart (osTimerId_t timer_id, uint32_t ticks);
1
參數介紹:
此處的 ticks 設定的數字是定時器兩次調用回調函數的周期數目,每個 tick 是一個心跳時鐘的長度
使用例程:
void StartTask02(void *argument) { /* USER CODE BEGIN StartTask02 */ osStatus_t result; uint8_t dat[10]={0}; uint8_t *pro; result= osTimerStart(myTimer01Handle,10); if(result == osOK) { //啟動成功 }else { //啟動失敗 } /* Infinite loop */ for(;;) { osDelay(10); } /* USER CODE END StartTask02 */ }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
按照例程啟動定時器,定時器會以 10個tick 的周期,調用回調函數
回調函數不要放阻塞函數,程序盡可能短
定時器啟動: osTimerStop,函數原型
osStatus_t osTimerStop (osTimerId_t timer_id);
1
參數只有一個,就是定時器的控制句柄,傳入即可停止定時器,例程如下
void StartTask02(void *argument) { /* USER CODE BEGIN StartTask02 */ osStatus_t result; uint8_t dat[10]={0}; uint8_t *pro; result= osTimerStop(myTimer01Handle); if(result == osOK) { //停止成功 }else { //停止失敗 } /* Infinite loop */ for(;;) { osDelay(10); } /* USER CODE END StartTask02 */ }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
軟件定時器是由軟件定時器維護任務進行維護,檢測各個定時器的狀態,進行處理,回調回調函數,軟件定時器維護任務的參數配置在前面的 Config 就已經提到過
5.2 CubeMX下信號量的創建和配置
信號量是 RTOS 的一個內核對象,該對象有一個隊列表示該信號量擁有的信號數目,任何任務都可以對這個信號數目進行獲取和釋放,獲取時信號-1,釋放時信號+1,為0時不能繼續獲取,此時有任務想要繼續獲取信號量的話,任務會掛起在該內核對象的掛起列表,等到信號可以獲取時進行恢復,根據這個特性,信號量常用于控制對共享資源的訪問和任務同步,下面介紹一下 CubeMX 下信號量的配置:
點開配置頁面,可以看到有兩個信號量添加頁面,其中 Binary Semaphores 是二值信號量,Counting Semaphores 是計數信號量,二進制信號量,僅有一個隊列或者說 token,用于同步一個操作;計數信號量則擁有多個 tokens,可用于同步多個操作,或者管理有限資源
二值信號量創建:
點擊 Add,配置參數
參數介紹
計數信號量:
點擊 Add,配置參數
參數介紹
配置完成后我們生成代碼,在 freertos.c 的初始化代碼中可以看到信號量被創建,并且返回了信號量的控制句柄
下面介紹一下 CubeMX 提供的信號量操作函數接口:
其中常用的函數有獲取和釋放信號量,下面介紹一下這兩個函數的參數和使用方式
獲取信號量 osSemaphoreAcquire
函數原型
osStatus_t osSemaphoreAcquire (osSemaphoreId_t semaphore_id, uint32_t timeout);
1
參數介紹
使用例程
void StartDefaultTask(void *argument) { /* USER CODE BEGIN StartDefaultTask */ osStatus_t result; /* Infinite loop */ for(;;) { result = osSemaphoreAcquire(myBinarySem01Handle,10); if(result == osOK) { //獲取成功 }else { //獲取失敗 } osDelay(1); } /* USER CODE END StartDefaultTask */ }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
釋放信號量 osSemaphoreRelease
函數原型
osStatus_t osSemaphoreRelease (osSemaphoreId_t semaphore_id);
1
使用例程
void StartDefaultTask(void *argument) { /* USER CODE BEGIN StartDefaultTask */ osStatus_t result; /* Infinite loop */ for(;;) { result = osSemaphoreRelease(myBinarySem01Handle); if(result == osOK) { //釋放成功 }else { //釋放失敗 } osDelay(1); } /* USER CODE END StartDefaultTask */ }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
二值信號量和計數信號量的操作基本一致,沒用區別,只是用有的信號隊列最大數目不同而已
同時注意信號量在使用過程中會出現優先級反轉的Bug,使用時需要注意
六、創建互斥量
6.1 CubeMX下互斥量的創建和配置
互斥量其實就是一個擁有優先級繼承的二值信號量,互斥信號量適合用于那些需要互斥訪問的應用中,在互斥訪問中互斥信號量相當于一個鑰匙,當任務想要使用資源的時候就必須先獲得這個鑰匙,當使用完資源以后就必須歸還這個鑰匙,這樣其他的任務就可以拿著這個鑰匙去使用資源,與信號量不同的是,互斥量的釋放必須由獲取他的任務進行釋放,如果不釋放,可能會造成死鎖
死鎖就是兩個任務獲取對方擁有的鎖,各自進入掛起列表,無法釋放互斥鎖
下面介紹一下 CubeMX 下互斥量的配置,在配置界面我們可用看到兩個互斥量配置界面,上面的是普通互斥量,其獲取只能獲取一次,重復獲取是無效的,而第二個則是遞歸互斥量,遞歸互斥信號量可以獲取多次,但對應的也要釋放多次才能讓出使用權,比如我獲取3次,任務要釋放3次才能釋放該互斥量的使用權
使用互斥量,需要點擊 Add 然后配置參數
參數介紹:
遞歸互斥信號量的配置方式與其相同,包括配置參數也相同,兩者只是在用法上有些許區別,添加方式如下:
添加配置完成后,點擊生成代碼,在 freertos.c 文件中我們可以看到互斥量初始化完成,并且生成了對應的控制句柄
CubeMX 提供的 API 接口函數如下
主要使用到的還是互斥量的獲取與釋放,下面分析一下這兩個函數:
獲取互斥量 osMutexAcquire
函數原型
osStatus_t osMutexAcquire (osMutexId_t mutex_id, uint32_t timeout);
1
參數介紹:
使用方式
void StartTask02(void *argument) { /* USER CODE BEGIN StartTask02 */ osStatus_t result; result= osMutexAcquire(myMutex01Handle,10); if(result == osOK) { //獲取成功 }else { //獲取失敗 } /* Infinite loop */ for(;;) { osDelay(10); } /* USER CODE END StartTask02 */ }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
釋放互斥量 osMutexRelease
函數原型
osStatus_t osMutexRelease (osMutexId_t mutex_id);
1
參數介紹:
使用方式
void StartTask02(void *argument) { /* USER CODE BEGIN StartTask02 */ osStatus_t result; result= osMutexRelease(myMutex01Handle); if(result == osOK) { //釋放成功 }else { //釋放失敗 } /* Infinite loop */ for(;;) { osDelay(10); } /* USER CODE END StartTask02 */ }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
使用方式和信號量基本相同,因為互斥量本質上就是信號量的一種
七、創建事件標志組
7.1 CubeMX下事件的創建和配置
任務間的同步除了信號量還有時間標志組,信號的同步通常是一對一的同步,有的時候系統需要多對一的同步,比如同時滿足5個按鍵按下時,任務啟動,如果使用信號會很占據資源,所以 RTOS 引入了事件標志組來滿足這一需求,下面我們看一下 CubeMX 內事件標志組的配置方法:
點擊 Add 創建事件標志組
配置介紹
配置完成后,生成代碼,在系統初始化內,看有沒有生成事件標志組控制句柄,可以看到句柄創建完成
CubeMX 提供的配置事件標志組的接口 API 如下:
常用的 API 接口是設置事件標志組以及等待事件標志組的觸發,下面我們分析一下這兩個 API
在了解 API 前我們需要簡單了解一下事件的觸發原理:首先事件標志組的數據類型為 EventGroupHandle_t,事件標志組中的所有事件位都存儲在一個無符號的 EventBits_t 類型的變量中,當 configUSE_16_BIT_TICKS 為 1 的時候事件標志組可以存儲 8 個事件位,當 configUSE_16_BIT_TICKS 為 0 的時候事件標志組存儲 24個事件位,每個事件位其實就是一個0或者1數字,就像下面的24位組成一個事件標志組
我們在使用事件API接口函數前需要先定義我們需要的觸發事件位,比如添加如下的代碼
#define event1 1<<1 //事件1 #define event2 1<<2 //事件2
1
2
編寫好觸發事件后,我們在看如何使用 API 接口
設置事件標志 osEventFlagsSet
函數原型
uint32_t osEventFlagsSet (osEventFlagsId_t ef_id, uint32_t flags);
1
參數介紹:
使用方式:設置事件1和事件2
void StartDefaultTask(void *argument) { /* USER CODE BEGIN StartDefaultTask */ osStatus_t result; /* Infinite loop */ for(;;) { result = osEventFlagsSet(myEvent01Handle,event1); if(result == osOK) { //事件1設置成功 }else { //事件1設置失敗 } result = osEventFlagsSet(myEvent01Handle,event2); if(result == osOK) { //事件2設置成功 }else { //事件2設置失敗 } osDelay(1); } /* USER CODE END StartDefaultTask */ }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
等待事件標志 osEventFlagsWait
函數原型
uint32_t osEventFlagsWait (osEventFlagsId_t ef_id, uint32_t flags, uint32_t options, uint32_t timeout);
1
參數介紹:
使用例子:同時等待事件1和事件2,且等待到不清除
void StartDefaultTask(void *argument) { /* USER CODE BEGIN StartDefaultTask */ osStatus_t result; /* Infinite loop */ for(;;) { result = osEventFlagsWait(myEvent01Handle,event1|event2,osFlagsWaitAll|osFlagsNoClear,10); if(result == osOK) { //等待成功 }else { //等待失敗 } osDelay(1); } /* USER CODE END StartDefaultTask */ }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
八、用戶常量
User Constants 用于添加用戶常量,將不變的量轉化為常量保存,可以節省 RAM 資源空間,因為常量和變量的保存位置不同,詳細了解可以參考這篇文章:C語言:內存四區
九、任務通知
FreeRTOS 的每個任務都有一個 32 位的通知值,任務控制塊中的成員變量 ulNotifiedValue 就是這個通知值。任務通知是一個事件,假如某個任務通知的接收任務因為等待任務通知而阻塞的話,向這個接收任務發送任務通知以后就會解除這個任務的阻塞狀態,CubeMX內沒有提供相關的配置項,但在其生成的 FreeRTOS 接口里面有相關函數進行配置,函數位置如下:
接口函數功能:
常用的兩個 API 就是設置任務通知和等待任務通知函數
設置通知 osThreadFlagsSet
函數原型
uint32_t osThreadFlagsSet (osThreadId_t thread_id, uint32_t flags);
1
參數介紹:
使用方式
先定義一個事件標志
#define event1 1<<1 //事件1
1
然后調用 API 通知對應任務事件發生
void StartDefaultTask(void *argument) { /* USER CODE BEGIN StartDefaultTask */ osStatus_t result; /* Infinite loop */ for(;;) { result = osThreadFlagsSet(myTask02Handle,event1); if(result == osOK) { //設置成功 }else { //設置失敗 } osDelay(1); } /* USER CODE END StartDefaultTask */ }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
等待通知 osThreadFlagsWait
函數原型
uint32_t osThreadFlagsWait (uint32_t flags, uint32_t options, uint32_t timeout);
1
參數介紹:
options參數
使用方式
調用 API 等待對應的任務通知就緒,當其他任務設置到對應的通知后,任務恢復運行
void StartDefaultTask(void *argument) { /* USER CODE BEGIN StartDefaultTask */ osStatus_t result; /* Infinite loop */ for(;;) { result = osThreadFlagsWait(myTask02Handle,osFlagsWaitAll,event1); if(result == osOK) { //等待成功 }else { //等待失敗 } osDelay(1); } /* USER CODE END StartDefaultTask */ }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
任務通知其實個任務事件標志組使用上沒有多大的區別,但他們兩個的實現原理不同,同時任務通知對資源的占用更少
根據 FreeRTOS 官方的統計,使用任務通知替代二值信號量的時候任務解除阻 塞的時間要快 45%,并且需要的 RAM 也更少
十、系統內核配置
CubeMX 生成的代碼中封裝了一系列內核配置函數,有些函數也經常使用到,比如獲取時間戳和調度器管理的函數,這里不做過多解釋,簡單的介紹一下函數的功能
API 嵌入式
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。