使用STM32編寫一個簡單的RTOS:4.時鐘管理(一)SysTick

參考資料:RTT官網文檔、《cortex-M3權威指南》
關鍵字:分析RT-Thread源碼、stm32、RTOS、時鐘管理。

簡介

完成了調度器,對象管理,線程管理後,我們就可以多任務並行執行了,但是還是有很多問題,例如我們同優先級的線程,在不掛起的情況下,只能通過手動yield放棄CPU,另一個線程纔有機會運行。低優先級的線程也得不到獲取CPU資源的機會,這樣我們的多任務功能就大打折扣。同時我們的線程也無法做一些延時操作,不能做一些定時任務。所以,爲了確保沒有任務能霸佔系統,以及爲系統提供各種定時功能,我們需要一個時鐘管理。

首先,時鐘管理需要哪些功能:
1,要有一個定期性的中斷或者異常來作爲系統的時基 --時鐘節拍
2,通過時鐘節拍來推動我們的任務管理(調度) --管理時間片
3,通過時鐘節拍來實現延時功能 --延時功能
4,通過時鐘節拍來實現定時功能 --定時功能

可以看出,時鐘節拍是我們實現這些功能的核心。

時鐘節拍
任何操作系統都需要提供一個時鐘節拍,以供系統處理所有和時間有關的事件,如線程的延時、線程的時間片輪轉調度以及定時器超時等。時鐘節拍是特定的週期性中斷,這個中斷可以看做是系統心跳,中斷之間的時間間隔取決於不同的應用,一般是 1ms–100ms,時鐘節拍率越快,系統的額外開銷就越大,從系統啓動開始計數的時鐘節拍數稱爲系統時間。

RT-Thread 中,時鐘節拍的長度可以根據 RT_TICK_PER_SECOND 的定義來調整,等於 1/RT_TICK_PER_SECOND 秒。RT_TICK_PER_SECOND默認是100,即一個時鐘節拍爲10ms。

SysTick定時器
在以前,大多操作系統需要一個硬件定時器來產生操作系統需要的滴答中斷,作爲整個系統的時基。現在,cortex-M3處理器內部就包含了一個專門用來處理系統滴答的定時器–SysTick。
CM3爲它專門開出一個異常類型,SYSTICK異常(異常號:15)。在上下文切換的時候,CM3也專門提供了一個PendSV異常,是不是很貼心。下面介紹一下SysTick這個定時器。
SysTick 是一個24 位的倒計數定時器,當計到0 時,將從RELOAD 寄存器中自動重裝載定時初值。只要不把它在SysTick 控制及狀態寄存器中的使能位清除,就永不停息。

Alt

詳細請看一下CM3權威指南第8章。

源碼分析

下面我們來看SysTick在RTT中的具體操作。

在 rt_hw_board_init 初始化了SysTick。

void rt_hw_board_init(void)
{
   ...
    /* Configure the SysTick */
    SysTick_Config( SystemCoreClock / RT_TICK_PER_SECOND );
   ...
}

SystemCoreClock = 72000 000,因爲我的是103的,頻率是72M。RT_TICK_PER_SECOND默認是100,所以這裏將觸發異常改爲了10ms觸發。

異常處理函數:

void SysTick_Handler(void)
{
    rt_tick_increase();
}

void rt_tick_increase(void)
{
    struct rt_thread *thread;

    /* increase the global tick */
    ++ rt_tick;

    /* check time slice */
    thread = rt_thread_self();

    -- thread->remaining_tick;
    if (thread->remaining_tick == 0)
    {
        /* change to initialized tick */
        thread->remaining_tick = thread->init_tick;

        /* yield */
        rt_thread_yield();
    }

    /* check timer */
    rt_timer_check();
}

在時鐘節拍SysTick的的異常處理函數中調用了rt_tick_increase,rt_tick_increase裏維護了一個系統節拍數的變量rt_tick,然後檢查當前線程是否已經達到規定的時間片,如果是則調用rt_thread_yield讓出cpu控制權。接着是定時器檢測,這個我們後面再詳細講,暫時先不管。

測試

接下來我們就將系統時間節拍和時間片加入我們的RTT-Mini中。

void clock_systick_handler(void)
{
    ++systick;
    
    --current_thread->remaining_tick;
    
    if (current_thread->remaining_tick == 0) {
        current_thread->remaining_tick = current_thread->tick;
        thread_yield();
    }
    
}
                             
void thread_1_entry(void *param)
{
    uint32_t i = 0;
    
    while (1) {
        printf("hello. i : %ld\r\n", i++);
//        thread_yield();
    }
}


void thread_2_entry(void *param)
{
    uint32_t i = 0;
    
    while (1) {
        printf("world. i : %ld\r\n", i++);
//        thread_yield();
    }
}

在這裏插入圖片描述
現在我們線程中去掉yield,兩個任務也能輪流運行了。

測試源碼:https://download.csdn.net/download/u012220052/11236203

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