一、新建線程,並完成一個LED閃爍,介紹我們完成一個最簡單的程序需要做些什麼。rtt提供自己的GPIO驅動架構,IO的初始化(rt_pin_mode())、寫入(rt_pin_write())、讀入(rt_pin_read())三個函數。
- 我們以LED閃爍爲訴求,新建test.c文件,我們先寫個要調用的函數led_entry(),然後寫創建線程分配資源的函數led_test()
static void led_entry(void *parameter) { rt_pin_mode(GPIOF9,PIN_MODE_OUTPUT); while (1) { rt_pin_write(GPIOF9, PIN_HIGH); rt_thread_mdelay(500); rt_pin_write(GPIOF9, PIN_LOW); rt_thread_mdelay(500); } } void led_test(void) { rt_thread_t tid1; /* 創建線程1,名稱是thread1,入口是thread1_entry*/ tid1 = rt_thread_create("thread1", led_entry, RT_NULL, THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE); /* 如果獲得線程控制塊,啓動這個線程 */ if (tid1 != RT_NULL) rt_thread_startup(tid1); }
- 填上頭文件和必要的宏定義
#include <rtthread.h> #include <rtdevice.h> #define THREAD_PRIORITY 10 //優先級 #define THREAD_STACK_SIZE 512 //資源 #define THREAD_TIMESLICE 10 //時間片 #define GPIOF9 89 //drv_gpio.c裏面查得到
-
找到main.c,添加全局函數,並且調用,完成。
extern void led_test(void); int main(void) { led_test(); return RT_EOK; }
二、主要介紹創建線程的創建函數,下面是官方給出的函數說明。
* This function will create a thread object and allocate thread object memory
* and stack.
*
* @param name the name of thread, which shall be unique
* @param entry the entry function of thread
* @param parameter the parameter of thread enter function
* @param stack_size the size of thread stack
* @param priority the priority of thread
* @param tick the time slice if there are same priority thread
*
* @return the created thread object
*/
rt_thread_t rt_thread_create(const char *name,
void (*entry)(void *parameter),
void *parameter,
rt_uint32_t stack_size,
rt_uint8_t priority,
rt_uint32_t tick)
{
- priority參數代表優先級,描述線程競爭資源的能力。
- 用戶可以通過rt_config.h中的RT_THREAD_PRIORITY_MAX宏來修改最大支持的優先級。
- stm32默認支持32個優先級,最低優先級預留給空閒線程。
- 任務A搶佔調度當前任務的發生條件:
- 有任務A的優先級大於當前任務的優先級。
- 任務A處於就緒狀態。
- 將程序中實時性要求較高的任務優先級設置成高優先級。
- tick參數代表時間片,對相同的優先級的就緒線程採用時間片輪轉調度。時間片起到約束線程單次運行時長作用,單位是一個時間節拍。
- 時間片輪詢,保證兩個線程輪流佔用處理器。
- parameter參數可以將需要的變量傳入調用的函數裏
// 線程調用的時候向函數裏傳入了(void*)類型的1 tid = rt_thread_create("thread1", thread_entry, (void*)1, THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE) //這是函數裏面同樣需要void*類型的參數 static void thread_entry(void* parameter)
三、空閒線程鉤子函數
- 空閒線程鉤子函數可以讓系統在空閒的時候執行一些非緊急事務,例如系統運行指示燈閃爍,CPU使用率統計等等。
- rt_err_t rt_thread_idle_sethook(void(*hook)(void))設置鉤子函數,放置在創建線程的函數中。
- rt_err_t rt_thread_idle_delhook(void(*hook)(void))刪除鉤子函數,放置在線程調用的函數中。
- 空閒線程是一個線程狀態永遠爲就緒態的線程,所以鉤子函數中不得包含使線程掛起的阻塞類函數,例如:rt_thread_delay()、rt_sem_take()。
- 如果需要打印系統線程的轉換關係:可以在創建線程的函數中調用系統調度鉤子函數rt_scheduler_sethook(),要注意的是對應的調度函數爲:static void hook_of_scheduler(struct rt_thread* from, struct rt_thread* to){},裏面的參數不能變。別的可以參考官方給的例程。
int scheduler_hook(void) { /* 設置調度器鉤子 */ rt_scheduler_sethook(hook_of_scheduler); /* 創建線程1 */ tid1 = rt_thread_create("thread1", thread_entry, (void*)1, THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE); if (tid1 != RT_NULL) rt_thread_startup(tid1); /* 創建線程2 */ tid2 = rt_thread_create("thread2", thread_entry, (void*)2, THREAD_STACK_SIZE, THREAD_PRIORITY,THREAD_TIMESLICE - 5); if (tid2 != RT_NULL) rt_thread_startup(tid2); return 0; }
四、對調度的函數進行臨界區保護
-
臨界資源:一次僅允許一個線程訪問的共享資源,可以是一個具體的硬件設備(比如打印機),也可以是一個變量,一個緩衝區。不論是硬件或軟件的資源,多個線程必須互斥的對他們進行訪問。一段時間裏只允許一個線程進行訪問。
-
每個線程訪問臨界資源的代碼稱爲臨界區,每次只允許一個線程進入臨界區。
-
兩個方法保護臨界區:
-
禁止調度:給任務調度器加鎖,不讓進行線程切換
-
上鎖:rt_enter_critical()
-
解鎖:rt_exit_critical()
-
放在需要調度的線程裏。
-
-
關閉中斷:因爲所有的線程調度是建立在中斷的基礎上。但是此時所有的中斷都不能用了,所有不建議用。
void thread_entry(void *parameter) { rt_uint32_t level; while (1) { /* 關閉中斷 */ level = rt_hw_interrupt_disable(); { //寫自己需要的代碼 } rt_hw_interrupt_enable(level); } }
-
參考:https://www.rt-thread.org/document/site/tutorial/kernel/kernel-video/