RT_Thread學習筆記_1、針對單個線程、新建線程、鉤子函數、臨界區保護

一、新建線程,並完成一個LED閃爍,介紹我們完成一個最簡單的程序需要做些什麼。rtt提供自己的GPIO驅動架構,IO的初始化(rt_pin_mode())、寫入(rt_pin_write())、讀入(rt_pin_read())三個函數。

  1. 我們以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);
    }
  2. 填上頭文件和必要的宏定義
    #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裏面查得到

     

  3. 找到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)
{
  1. priority參數代表優先級,描述線程競爭資源的能力。
    1. 用戶可以通過rt_config.h中的RT_THREAD_PRIORITY_MAX宏來修改最大支持的優先級。
    2. stm32默認支持32個優先級,最低優先級預留給空閒線程。
    3. 任務A搶佔調度當前任務的發生條件:
      1. 有任務A的優先級大於當前任務的優先級。
      2. 任務A處於就緒狀態。
    4. 將程序中實時性要求較高的任務優先級設置成高優先級。
  2. tick參數代表時間片,對相同的優先級的就緒線程採用時間片輪轉調度。時間片起到約束線程單次運行時長作用,單位是一個時間節拍。
    1. 時間片輪詢,保證兩個線程輪流佔用處理器。
  3. 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)

     

 

三、空閒線程鉤子函數

  1. 空閒線程鉤子函數可以讓系統在空閒的時候執行一些非緊急事務,例如系統運行指示燈閃爍,CPU使用率統計等等。
  2. rt_err_t rt_thread_idle_sethook(void(*hook)(void))設置鉤子函數,放置在創建線程的函數中。
  3. rt_err_t rt_thread_idle_delhook(void(*hook)(void))刪除鉤子函數,放置在線程調用的函數中。
  4. 空閒線程是一個線程狀態永遠爲就緒態的線程,所以鉤子函數中不得包含使線程掛起的阻塞類函數,例如:rt_thread_delay()、rt_sem_take()。
  5. 如果需要打印系統線程的轉換關係:可以在創建線程的函數中調用系統調度鉤子函數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;
    }

        

四、對調度的函數進行臨界區保護

  1. 臨界資源:一次僅允許一個線程訪問的共享資源,可以是一個具體的硬件設備(比如打印機),也可以是一個變量,一個緩衝區。不論是硬件或軟件的資源,多個線程必須互斥的對他們進行訪問。一段時間裏只允許一個線程進行訪問。

  2. 每個線程訪問臨界資源的代碼稱爲臨界區,每次只允許一個線程進入臨界區。

  3. 兩個方法保護臨界區:

    1. 禁止調度:給任務調度器加鎖,不讓進行線程切換

      1. 上鎖:rt_enter_critical()

      2. 解鎖:rt_exit_critical()

      3. 放在需要調度的線程裏。

    2. 關閉中斷:因爲所有的線程調度是建立在中斷的基礎上。但是此時所有的中斷都不能用了,所有不建議用。

      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/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章