Systick就是一個定時器而已,只是它放在了NVIC中,主要的目的是爲了給操作系統提供一個硬件上的中斷(號稱滴答中斷)。滴答中斷?這裏來簡單地解釋一下。操作系統進行運轉的時候,也會有“心跳”。它會根據“心跳”的節拍來工作,把整個時間段分成很多小小的時間片,每個任務每次只能運行一個“時間片”的時間長度就得退出給別的任務運行,這樣可以確保任何一個任務都不會霸佔整個系統不放。或者把每個定時器週期的某個時間範圍賜予特定的任務等,還有操作系統提供的各種定時功能,都與這個滴答定時器有關。因此,需要一個定時器來產生週期性的中斷,而且最好還讓用戶程序不能隨意訪問它的寄存器,以維持操作系統“心跳”的節律。 只要不把它在SysTick控制及狀態寄存器中的使能位清除,就永不停息。
知道systick在系統中的地位後,我們來了解systick的實現。這裏只是舉例說明systick的使用。它有四個寄存器,筆者把它列出來:
SysTick->CTRL, --控制和狀態寄存器
SysTick->LOAD, --重裝載寄存器
SysTick->VAL, --當前值寄存器
SysTick->CALIB, --校準值寄存器
庫裏SysTick相關的函數我們能找到兩個
一個在msic.h中
void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource)
{
/* Check the parameters */
assert_param(IS_SYSTICK_CLK_SOURCE(SysTick_CLKSource));
if (SysTick_CLKSource == SysTick_CLKSource_HCLK)
{
SysTick->CTRL |= SysTick_CLKSource_HCLK;
}
else
{
SysTick->CTRL &= SysTick_CLKSource_HCLK_Div8;
}
}
一個在core_m3.h中
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{
if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */
SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register */
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);
/* set Priority for Cortex-M0 System Interrupts */
SysTick->VAL = 0; /* Load the SysTick Counter Value */
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
return (0); /* Function successful */
}
我們一般只需要後一個就可以了
需要的操作在SysTick_Handler()(庫版本不同可能是SysTickHandler)中添加就好了,意思每到加載到SysTick中的值減到0時就執行SysTick();
SystemInit();
這個函數可以讓主頻運行到72M。可以把它作爲systick的時鐘源。
接着開始配置systick,實際上配置systick的嚴格過程如下:
1、調用SysTick_CounterCmd() --失能SysTick計數器
2、調用SysTick_ITConfig() --失能SysTick中斷
3、調用SysTick_CLKSourceConfig() --設置SysTick時鐘源。
4、調用SysTick_SetReload() --設置SysTick重裝載值。
5、調用SysTick_ITConfig() --使能SysTick中斷
6、調用SysTick_CounterCmd() --開啓SysTick計數器
這裏大家一定要注意,必須使得當前寄存器的值VAL等於0!
SysTick->VAL = (0x00);只有當VAL值爲0時,計數器自動重載RELOAD。
接下來就可以直接調用Delay();函數進行延遲了。延遲函數的實現中,要注意的是,全局變量TimingDelay必須使用volatile,否則可能會被編譯器優化。(以上的過程 在庫函數SysTick_Config() 裏已經配置稍做了解即可);
時鐘的選擇 在庫文件 system_stm32f10x.c 裏面
static void SetSysClock(void);
#ifdef SYSCLK_FREQ_HSE
static void SetSysClockToHSE(void);
#elif defined SYSCLK_FREQ_24MHz
static void SetSysClockTo24(void);
#elif defined SYSCLK_FREQ_36MHz
static void SetSysClockTo36(void);
#elif defined SYSCLK_FREQ_48MHz
static void SetSysClockTo48(void);
#elif defined SYSCLK_FREQ_56MHz
static void SetSysClockTo56(void);
#elif defined SYSCLK_FREQ_72MHz
static void SetSysClockTo72(void);
#endif
以上 引用參考https://blog.csdn.net/yx_l128125/article/details/7884423
SetSysClock() 函數在SystemInit() 裏面被調用
SystemInit() 在startup_stm32f10x_hd.s 啓動文件中被調用
Systick作爲操作系統的心跳:
UCOS 系統的os_cfg.h文件中 #define OS_TICKS_PER_SEC 100u /* Set the number of ticks in one second */
定義了 每秒心跳(中斷的次數); 100次. 假設是72MH的頻率 , 在這裏ucos給systick 的reload值應該爲720000.
100次中斷耗時1s.
心跳()時間片)原理爲 SysTick_Handler() 中斷函數. 裏面增加 OSIntEnter() (OSIntNesting++),然後調用OSTimeTick() ucos的時鐘服務程序,
最後OSIntExit() 任務調度一次.
OSTimeTick()的功能: 爲OSTime+1 以及爲所有等待任務控制塊的OSTCBDly-1 (如果-1爲0 由後面的OSIntExit啓動調度)
OSTimeDly()與OSTimeDlyHMSM();
OSTimeDly(INT16U ticks)比較簡單就是 任務控制塊 狀態改爲等待 ,OSTCBCur->OSTCBDly賦值延遲心跳次數ticks.並任務調度.
OSTimeDlyHMSM(INT8U hours, INT8U minutes, INT8U seconds, INT16U ms)複雜了一點
小時分鐘秒的延時簡單的計算出對應的ticks 並調用OSTimeDly();毫秒級別的:
注意:最小延時時間爲心跳 (1/OS_TICKS_PER_SEC);
ticks= OS_TICKS_PER_SEC * ((INT32U)ms + 500L / OS_TICKS_PER_SEC) / 1000L;
這裏有個500L的取值.我們暫定爲MINTIME變量吧.
邵貝貝書裏說:這裏的500的取值 目的是爲了(假設OS_TICKS_PER_SEC=100 最小心跳10ms) 延時4ms的時候不延時,5ms以上的時候才延時. 依次14ms延遲一個節拍 15ms兩個節拍.
個人感覺怪怪的....
那麼如果想單純的引發一次任務調度.那麼OSTimeDlyHMSM(0,0,0,5) 至少是5ms才行.
對於單位 10ms以內的精確延時還是要拋棄ucos的函數而自己寫.
爲了實現小於(1/OS_TICKS_PER_SEC)的延時函數.
傳統的做法是定義delay_us()函數.函數功能,對Systicks 定時器的reload 重裝值賦值 需要延時的時間對應的數值.
程序較爲簡單,但是會影響ucos的心跳.不能在操作系統下使用;
爲了能在操作系統下實現us級別的延時.
參考正點原子教程:可以定義一個delay_us()的延時函數
void delay_us(u32 nus)
{
u32 ticks;
u32 told,tnow,tcnt=0;
u32 reload=SysTick->LOAD; //LOAD的值
ticks=nus*fac_us; //需要的節拍數 fac_us=時鐘頻率/1000000(這裏是72)
tcnt=0;
told=SysTick->VAL; //剛進入時的計數器值
while(1)
{
tnow=SysTick->VAL;
if(tnow!=told)
{
if(tnow<told)tcnt+=told-tnow;//這裏注意一下SYSTICK是一個遞減的計數器就可以了.
else tcnt+=reload-tnow+told;
told=tnow;
if(tcnt>=ticks)break;//時間超過/等於要延遲的時間,則退出.
}
};
}
這段函數功能,就是不斷的讀取SysTick->VAL ; Systick定時器的計數值;並計算差值. 如果大於需要的ticks 嘖延時結束
如果需要的ticks大於重裝載值, 用systick遞減的原理.判斷如果取得計數值大於記錄計數值,嘖更新記錄值,並對cnt計數加reload重裝載值. 實現us延時