系統定時器(systick)
一.框架圖
STK_CLK 時鍾
STK_LOAD 重裝載寄存器
STK_VAL 遞減計數器
遞減計數器(時鍾驅動)→重裝載寄存器的初始值開始往下遞減計數到0(遞減過程中 在STK_value寄存器中實時值)→產生中斷(COUNTFLAG標志為1)→若計數器沒有關掉,重新計數在產生中斷。
COUNTFLAG 計時標志
CLKSOURCE 進行時鍾源的選擇
0:AHB/8 = 9MZH 1:AHB→72MHZ
TICKINT:中斷使能 1:進入中斷服務程序 執行中斷程序 0:不進入中斷服務函數
RELOAD:可以讀取到正在計數的數值(value)
二.systick定時器時間的計算
1-t(定時時間):一個計數的循環時間 與reload clk相關
一個循環:reload的值計數降到0
2-CLK:可以為72m或9m 可以通過CTRL寄存器進行配置
3-RELAOD:24位值 用戶自己配置
計算公式
t = reload*(1/clk) 配置的次數與遞減計數器計數一次的時間
比如
CLK=72M , relaod = 72 t = 72*(1/72M) = 1us
CLK=72M , relaod = 72000 t = 72000*(1/72M) = 1ms
時間單位的換算:
1s = 1000ms = 1000 000us = 1000 000 000ns
三.systick寄存器
寄存器參數
typedef struct
{
__IO uint32_t CTRL; /*!< Offset: 0x00 SysTick Control and Status Register */
__IO uint32_t LOAD; /*!< Offset: 0x04 SysTick Reload Value Register */
__IO uint32_t VAL; /*!< Offset: 0x08 SysTick Current Value Register */
__I uint32_t CALIB; /*!< Offset: 0x0C SysTick Calibration Register */
} SysTick_Type;
系統時鍾配置函數
/**
* @brief Initialize and start the SysTick counter and its interrupt.
*
* @param ticks number of ticks between two interrupts
* @return 1 = failed, 0 = successful
*
* Initialise the system tick timer and its interrupt and start the
* system tick timer / counter in free running mode to generate
* periodical interrupts.
*/
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{
//判斷tick的值 是否大於2^24 大於的話 則不符合規則
if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */
//初始化寄存器的值
SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register */
//配置中斷優先級 配置為15 默認為最低的優先級
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 */
}
中斷優先級配置函數
static __INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
{
if(IRQn < 0) {
SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for Cortex-M3 System Interrupts */
else {
NVIC->IP[(uint32_t)(IRQn)] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for device specific Interrupts */
}
問題
systick為內核外設 優先級與外設中斷優先級相比 那個高
答:外設的中斷優先級會更高
systick中斷優先級配 scb-> shprx寄存器
外設的中斷優先級配置的是nvic-/ipex 有優先級分組 有搶占優先級和子優先級的說法
四.systick中斷優先級
1.STM32里面無論是內核還是外設都是使用4個二進制位來表示中斷優先級
2.中斷優先級的分組對內核與外設同樣適合使用。當比較的時候,只需要把內核外設的中斷優先級的四個為按照外設的中斷優先級來分組來解析即可 即人為的分出搶占優先級與子優先級
優先級分組配置 中斷優先級越大 優先級越低
五.編寫微秒/毫秒延時函數
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{
//判斷tick的值 是否大於2^24 大於的話 則不符合規則
if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */
//初始化寄存器的值
SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register */
//配置中斷優先級 配置為15 默認為最低的優先級
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Cortex-M0 System Interrupts */
//配置counter 計數器的值
SysTick->VAL = 0; /* Load the SysTick Counter Value */
//配置systick的時鍾為72m
//使能中斷過
//使能systick
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
return (0); /* Function successful */
}
六.代碼實現
原理廢話都看了那么多了 最后還得看怎么進行實現的 下面就是具體的實現 配置好后 可進行准確的1us、1ms等系統中斷延時
微妙延時寫法
#include "systick.h"
void Systick_Delay_us(uint32_t us)
{
//系統時鍾
SysTick_Config(72);
//
uint32_t i;
for(i=0;i<us;i++)
{
//即為計數結束后 SysTick中CTRL寄存器第十六位就會置1
//等待其讀數 達到后又會置0 從而實現延時
while(!((SysTick->CTRL) & (1<<16)));
}
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
}
毫秒延時寫法
void Systick_Delay_ms(uint32_t ms)
{
//系統時鍾
SysTick_Config(72000);
//
uint32_t i;
for(i=0;i<ms;i++)
{
//即為計數結束后 SysTick中CTRL寄存器第十六位就會置1
//等待其讀數 達到后又會置0 從而實現延時
while(!((SysTick->CTRL) & (1<<16)));
}
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
}