一、SysTick系统滴答定时器
/ d- t( l& T% b( U }' ]+ HSysTick是一个24位定时器,属于Cortex-M4内核中的一个外设,类似NVIC。3 T* | B0 ^- W9 d6 h1 b! x# v
/ ^- b/ o% S( P6 a一个周期定时器,用于提供时间基准,多为操作系统所使用,常用于对时间要求严格的情况,意义是很重要的 i. V: g! W4 z: C
# _( G4 f$ D4 p2 n9 }; m7 m
SysTick定时器一次最多可以计数2^24(24bit)个时钟脉冲,这个脉冲计数值保存在当前计数值寄存器STK_VAL(Systick current value register)中,只能向下计数,也就是倒计数。
[, ?6 V7 D4 b$ {# d
1 S3 E) \3 d# y) ^; h每接收到一个时钟脉冲(CPU主频),STK_VAL的值就会向下减1,当减到0时,硬件会自动将重装载寄存器STK_LOAD(可以设定,跟STK_VAL初始值相等)中保存的数值加载到STK_VAL,使其重新计数,并且,系统滴答定时器就产生一次中断,以此循环往复,只要不把它在SysTick控制及状态寄存器中的使能位清除,就永不停息
6 p O. g" \: U; f. J* o5 P. X8 q& T& r
Systick相关寄存器4 \1 k& h2 W. J( U2 F9 H! |
$ r3 p; L# T; l5 }# k
, I, K. D! ^+ ]6 e5 d+ ]- b
: i% W% f' W, ]+ y7 F
; H1 Y2 Q6 z) [6 f+ b8 N" r若VAL中的数值为 80,脉冲频率为 80MHz,则VAL由 80 减到 0 所需要的时间为80/80MHz=1us,并且当减到 0 时会触发中断(相当于 1us 定时器),同时 LOAD 的值也会重新载入到VAL中- H+ E- R' ^( B1 @
! E. e( x+ E# N7 e8 W
STM32CubeMX系统时钟的配置
3 E1 S" C9 I* z* F$ q
* a8 y. c9 @( m5 K4 d5 b" u& M$ p8 I
2 b7 x. K) \* _8 w1 f+ s# ^
在 STM32CubeMX 中配置好时钟树后,时钟频率会输入到 Cortex System timer内核时钟 。所以, SysTick 的脉冲频率来自于此# V h- U" d1 t8 T
- y- a2 U5 z# F2 ?
二、SysTick系统定时器原理! j$ ?8 T, x# w- Y% } F1 K6 J
在HAL_Init()(HAL_Init–>HAL_InitTick–>HAL_SYSTICK_Config–>SysTick_Config) 使用SysTick作为时基源,并配置1ms定时(重置后的默认时钟为 HSI)。但是 HSI 为内部时钟,而我们使用的是外部时钟,这里的1ms定时是不精准的
2 s/ v) h$ u; b" ~9 _
2 w1 p. r. o1 X" ?4 v& ?- /* Use SysTick as time base source and configure 1ms tick (default clock after Reset is HSI) */
, v G, c" M: f, m, ^' g - if (HAL_InitTick(TICK_INT_PRIORITY) != HAL_OK)
9 {. l, r( b, e7 t* j - {2 E3 V6 T8 W1 p. [* J0 ]8 V& p
- status = HAL_ERROR; E% ~- O7 m* A: e4 l5 J
- }
复制代码 & f$ l) p" t% E$ s; g
精准的1ms定时在系统定时器配置函数SysTick_Config() 在core_cm4.h头文件里面' L. ` o: s6 G
' M. l g' v* V' O- __STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
9 z8 ^. v7 t( K i! ~% f - {
9 M, T7 j+ f' x! g8 y& V c; b - if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) [/ J6 [* O2 K9 F
- {! T1 Z5 E( L: [) x1 F- q# M+ M1 E
- return (1UL); /* Reload value impossible */
/ I0 W+ j) n, [5 o2 W) y' @ - }
) D: Q' B" H7 h. ~ - 6 V& i$ r7 `9 x: y/ R+ H
- SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */& x- M( o3 `$ W& E& x1 Q
- NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */. v9 S; L, z/ V: Q o% S
- SysTick->VAL = 0UL; /* Load the SysTick Counter Value */3 _. K8 L; v5 ~6 H% S
- SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |8 E+ \( P3 a& z8 i- o1 z( D
- SysTick_CTRL_TICKINT_Msk |
/ G+ j ^ W9 }9 m - SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */, K+ {! V( F% |5 B$ k
- return (0UL); /* Function successful */" ^" y2 C2 h" G5 U5 U3 d
- }
复制代码
6 s+ k5 d# o" t7 a# E$ m在这个函数里面进行对系统定时器的寄存器进行配置# d# D. T6 Q; G
0 { a) ?: o. Q8 h! M0 W
系统滴答定时器中断服务函数:每隔1ms进入该服务函数
: J4 T5 s- L7 I0 {$ E* b a0 Y% b3 \% C N1 G o
注:SysTick系统滴答定时器每1ms产生中断' S. `( L, _% w) ]9 y
5 b( S- B; s$ e/ [- /**& [! W4 Z0 u4 ~5 l3 ?8 E T
- * @brief This function handles System tick timer.
5 b: K1 f9 T3 r - */' s% s: b9 f4 @3 I4 q( R9 G! ?5 g3 U
- void SysTick_Handler(void)& E0 ^6 g! u# b" v" T8 b
- {" ?& ]6 P5 R9 X. p1 a
- /* USER CODE BEGIN SysTick_IRQn 0 */0 |0 }* _# ?2 {: y* Z( u
- 1 C* Q* m1 k+ ^% d' |9 W$ x; ]! i' r# ?
- /* USER CODE END SysTick_IRQn 0 */
" ^% c# m5 M$ s% ~/ L. [. x - HAL_IncTick();
' W5 w1 S$ Y6 D# E3 | - /* USER CODE BEGIN SysTick_IRQn 1 */
( @5 s0 h9 {1 u$ Y# E
, d- q+ e, k, L5 }* q- /* USER CODE END SysTick_IRQn 1 */. D# c+ y9 f! `* r1 ]
- }* `/ |/ p* M+ X; I" }8 Q' N
! U2 x) Z; ?. X% l- __weak void HAL_IncTick(void)
9 `, F- \+ v( ?- r; f3 `& L& ~ - {4 ~) }% A" g$ i2 @8 j' D: z2 V
- uwTick += uwTickFreq; // uwTickFreq = 1* O$ N/ o. o! \1 S# v
- }
9 x5 \, a9 C2 X5 X, k - __IO uint32_t uwTick;9 l) }, e4 |4 q P' w) V" W
- uint32_t uwTickFreq = HAL_TICK_FREQ_DEFAULT; /* 1K<span style="background-color: rgb(255, 255, 255);">Hz */</span>
复制代码 1 i! M% q7 m- ? l
在中断服务函数中 uwTick 会每1ms加1, 一直循环,直到溢出达到232(大约要一直运行40几天)
( s6 c9 W$ S4 B5 C/ i% t' T9 M6 Z. Y) i2 \( V( w
uwTick一般用于计数或者延时函数(HAL_Delay()
) I |- l( @$ e; s2 u- [8 M6 r8 e |# v
- __weak void HAL_Delay(uint32_t Delay)
- W* C# K/ N0 ^7 q# i - {: t7 O+ u& b. h6 Y4 Y c" x
- uint32_t tickstart = HAL_GetTick();$ ?2 {' E* O5 z Y
- uint32_t wait = Delay;
( l0 C. u$ j+ ]9 Q: A2 k - , |2 `; a9 n1 Z; `) f
- /* Add a freq to guarantee minimum wait */
' s, x: }! M1 p9 `' L - if (wait < HAL_MAX_DELAY)
4 ]) l+ Q4 q0 |, w& X& `/ ] - {
# v+ }5 m$ D) y$ y2 s0 M+ z - wait += (uint32_t)(uwTickFreq);
' G, y! p5 R4 ?6 {/ W - }
2 t! i! e8 R0 f - 2 X3 c3 G; H$ A+ t# q9 d7 b: ]. l
- while ((HAL_GetTick() - tickstart) < wait)8 c5 _/ ^$ V4 z* }
- {+ e$ x6 ^$ S8 I$ }. Q
- }
. L+ P2 I7 s8 R- T$ X - }( q( \0 B* O" U- Z; U; K
) s* m! D( h2 w B- __weak uint32_t HAL_GetTick(void)/ `3 t) O! t* z% `$ l0 q
- {6 l" `& X+ f% Z& N y0 c
- return uwTick;
# T( K9 _$ {& W% b1 t2 U - }
复制代码
4 m9 x. \. ~3 D5 E% CHAL_GetTick获取当前uwTick,若传入参数500,tickstart 为 HAL_GetTick() 最初的值(即进入延时时uwTick的值),固定不变。$ {' Q) e" X1 J& t Z [: Z
HAL_GetTick() 与 tickstart 的差值若小于用户定义的500,则继续循环等待;若超过500,则跳出循环(即跳出HAL_Delay() ),延时结束 {9 v# W0 y( e) S- E, f; F' B! H
9 A% E5 }5 i8 c总结
! Y& H- C3 N+ l4 F2 h. M提示:这里对文章进行总结:+ X0 b' _+ g! [$ r/ b) E1 w
& g4 W! ]3 m% O0 T4 W0 BSystick的两大作用:
3 V$ ^+ n' q( H5 t3 O1、可以产生精确的延时(HAL_Delay())# P+ p& P- g1 t1 `
2、可以提供给操作系统一个单独的心跳(时钟)节拍(操作系统时基)$ e& y( C0 t5 A& {6 k7 ]+ u7 I
. u, @ [% C) u( p6 R7 s
6 S' M% J1 F* s' l. }3 x
) c i7 S# h' w$ L$ I |