1. 程式人生 > >時間片輪詢法

時間片輪詢法

時間片輪詢法

時間片輪詢法是一種比較簡單易用的系統架構之一,它對於系統中的任務排程演算法是分時處理。核心思路是把CPU的時間分時給各個任務使用。我們常用的定時方法是定時器,把排程器放在定時中,可以簡單的實現時間片輪詢法。

需要注意的是,這種方法的前提是執行的每個任務都是短小精悍的,要不然一個任務執行的時間過長,大於其它任務設定的時間片值,那其它任務就無法保證按它預設的時間片來執行。

尤其需要注意任務中延時的使用,可能會產生不可預料的結果。如果任務內部需要延時的時候,或者說單個任務過長,需要儲存任務執行到一半的狀態,建議使用狀態機切割長任務。

時間片輪詢法架構

一個時間片輪詢應用程式的架構是非常簡單的,包括一個任務結構體,一箇中斷處理函式,一個輪詢執行任務函式。

設計一個結構體:

// 任務結構
typedef struct {
    uint8_t task_id;                  // 任務ID
    uint16_t task_interval;             // 任務執行間隔時間
    void (*task_entry)(void);           // 要執行的任務
    volatile uint16_t task_tick_ms;     // 計時器
}task_info_t;

定時器複用和中斷處理

定時器可以是任意的定時器,這裡採用系統滴答定時器(systick)來定時。systick的配置就不細講,假設定時器的定時中斷為10ms(可以自行設定,中斷過於頻繁效率就低,中斷太長,實時性差)。

timing_task_tick 函式就相當於中斷服務函式,需要在定時器的中斷服務函式中呼叫此函式。

// 為每個任務計時,每次中斷加10ms
void timing_task_tick(void)
{
    uint8_t task_index = 0;

    while (task_index < ARRAY_SIZE(timing_task_array))
    {
        timing_task_array[task_index].task_tick_ms += CONFIG_SYSTEM_TICK_PERIOD_MS;     // 每次加一個systick週期,即10ms
        task_index++;
    }
}

時間片輪詢例項

下面我就說說怎樣應用吧,假設我們有三個任務:時鐘顯示,按鍵掃描,和工作狀態顯示。

定義一個上面定義的那種結構體陣列

// 計算任務個數
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))

// 定義了3個任務
static task_info_t timing_task_array[] =
{
    {0, 100, task_disp_clock_running, 0},    // 顯示時鐘
    {1, 20, task_scan_key_running, 0},      // 按鍵掃描
    {2, 30, task_disp_ws_running, 0},       // 工作狀態顯示
};

在定義變數時,我們已經初始化了值,這些值的初始化,非常重要,跟具體的執行時間優先順序等都有關係,這個需要自己掌握。

  1. 大概意思是,我們有三個任務,每1s執行一下時鐘顯示,因為我們的時鐘最小單位是1s,所以在秒變化後才顯示一次就夠了。

  2. 由於按鍵在按下時會引數抖動,而我們知道一般按鍵的抖動大概是20ms,那麼我們在順序執行的函式中一般是延伸20ms,而這裡我們每20ms掃描一次,是非常不錯的出來,即達到了消抖的目的,也不會漏掉按鍵輸入。

  3. 為了能夠顯示按鍵後的其他提示和工作介面,我們這裡設計每30ms顯示一次,如果你覺得反應慢了,你可以讓這些值小一點。後面的名稱是對應的函式名,你必須在應用程式中編寫這函式名稱和這三個一樣的任務。

編寫任務函式

//Description : 顯示任務
void task_disp_clock_running(void)
{

}

//Description : 掃描任務
void task_scan_key_running(void)
{

}

//Description : 工作狀態顯示
void task_disp_ws_running(void)
{

}

任務處理

//任務計劃表,輪詢執行任務
void timing_task_scheduler(void)
{
    uint8_t task_index = 0;

    while (1)
    {
        for (task_index = 0 ; task_index < ARRAY_SIZE(timing_task_array); task_index++)
        {
            if (timing_task_array[task_index].task_tick_ms >= timing_task_array[task_index].task_interval)
            {
                timing_task_array[task_index].task_tick_ms = 0;
                timing_task_array[task_index].task_entry();
            }
        }
    }
}

程式陷入死迴圈,依次判斷每個任務是否符合執行要求。如果是,則執行相應的任務函式;否則等待計時。