時間片輪詢法
時間片輪詢法
時間片輪詢法是一種比較簡單易用的系統架構之一,它對於系統中的任務排程演算法是分時處理。核心思路是把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}, // 工作狀態顯示
};
在定義變數時,我們已經初始化了值,這些值的初始化,非常重要,跟具體的執行時間優先順序等都有關係,這個需要自己掌握。
大概意思是,我們有三個任務,每1s執行一下時鐘顯示,因為我們的時鐘最小單位是1s,所以在秒變化後才顯示一次就夠了。
由於按鍵在按下時會引數抖動,而我們知道一般按鍵的抖動大概是20ms,那麼我們在順序執行的函式中一般是延伸20ms,而這裡我們每20ms掃描一次,是非常不錯的出來,即達到了消抖的目的,也不會漏掉按鍵輸入。
為了能夠顯示按鍵後的其他提示和工作介面,我們這裡設計每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();
}
}
}
}
程式陷入死迴圈,依次判斷每個任務是否符合執行要求。如果是,則執行相應的任務函式;否則等待計時。