一、SysTick系统滴答定时器+ R6 ~5 k, W% D( @3 Y/ O9 @
SysTick是一个24位定时器,属于Cortex-M4内核中的一个外设,类似NVIC。6 w; ]5 }' G- Z% Z
+ s g9 x a& n* k. h
一个周期定时器,用于提供时间基准,多为操作系统所使用,常用于对时间要求严格的情况,意义是很重要的
7 V. l. O" \1 s4 N! a) E$ w: o4 t
SysTick定时器一次最多可以计数2^24(24bit)个时钟脉冲,这个脉冲计数值保存在当前计数值寄存器STK_VAL(Systick current value register)中,只能向下计数,也就是倒计数。" G9 d: h, K1 r9 M1 ]' E( r( l
& @7 P& J$ y+ r
每接收到一个时钟脉冲(CPU主频),STK_VAL的值就会向下减1,当减到0时,硬件会自动将重装载寄存器STK_LOAD(可以设定,跟STK_VAL初始值相等)中保存的数值加载到STK_VAL,使其重新计数,并且,系统滴答定时器就产生一次中断,以此循环往复,只要不把它在SysTick控制及状态寄存器中的使能位清除,就永不停息
& q, f, a4 E% R: l1 E
$ F' V7 M3 ?& c! Z3 Y5 cSystick相关寄存器
+ v+ s; K. B! [" Y9 p+ _5 n6 G# M' `
/ a4 Y3 E0 R3 N8 d* i5 |6 l
# e% [$ P* {1 }4 F4 d, h# l+ C# Z3 k, J" ?1 }$ U3 h( ]
若VAL中的数值为 80,脉冲频率为 80MHz,则VAL由 80 减到 0 所需要的时间为80/80MHz=1us,并且当减到 0 时会触发中断(相当于 1us 定时器),同时 LOAD 的值也会重新载入到VAL中
% B, G, u5 t. s2 D/ x# ^7 c& ^5 l- @
( I: D! n. j8 t+ ]STM32CubeMX系统时钟的配置' c. R! J# N w* u% U( a8 S
3 ^- C6 h+ w0 N
( H% C+ c- h' {( t& i; P) x在 STM32CubeMX 中配置好时钟树后,时钟频率会输入到 Cortex System timer内核时钟 。所以, SysTick 的脉冲频率来自于此0 {* }% {1 V6 v5 O' m3 B
& G+ ?( j9 q/ m; T9 ^- H二、SysTick系统定时器原理
1 M2 ]- E ]; x1 e5 q/ i; c在HAL_Init()(HAL_Init–>HAL_InitTick–>HAL_SYSTICK_Config–>SysTick_Config) 使用SysTick作为时基源,并配置1ms定时(重置后的默认时钟为 HSI)。但是 HSI 为内部时钟,而我们使用的是外部时钟,这里的1ms定时是不精准的& i! Y: v- T" S$ t" ^. F/ D9 z
6 K- o- k* n% \2 @/ m! b$ j& }
- /* Use SysTick as time base source and configure 1ms tick (default clock after Reset is HSI) */
" i+ ~, K; q/ j - if (HAL_InitTick(TICK_INT_PRIORITY) != HAL_OK)
6 z( s) i: M8 N% T; T - {
* Y$ x! X( g% \9 f - status = HAL_ERROR;
* x8 J% O7 _7 J9 g1 d1 S - }
复制代码
- n: }' l u5 v精准的1ms定时在系统定时器配置函数SysTick_Config() 在core_cm4.h头文件里面" O, M0 q1 R: K
; s5 H$ Q- J" q2 k j7 L9 L( a- __STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)5 b/ M( B) W" i1 `1 P
- {; h5 a3 Z: r0 i8 }9 `
- if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)1 c* I1 V. M [7 j0 V& i" \; J
- {2 t/ }6 R" G) v; G8 e$ Y( Z& k8 L p7 q
- return (1UL); /* Reload value impossible */7 j/ M; N7 _9 ~0 s& _
- }
' X c; d& E+ c* p' ?1 t5 S - . S4 m5 l% P6 V3 e
- SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */6 D6 p6 W' ]. k! W5 a
- NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */
" w1 B' E2 c4 w) z: @: u# p - SysTick->VAL = 0UL; /* Load the SysTick Counter Value */
6 ?9 p/ M0 v1 L3 {( a3 B& {+ O2 @ - SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
: ~0 T- ^- V z5 {5 {; J - SysTick_CTRL_TICKINT_Msk |% ]: i5 H$ `, a
- SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */, ^) n& C/ f: J4 P6 L: I6 ~ T
- return (0UL); /* Function successful */0 z; P. ^ z/ _( K
- }
复制代码
& T: \1 v# h, V) m在这个函数里面进行对系统定时器的寄存器进行配置
9 v5 B5 i. S* A7 m/ ?1 R6 c
! m! w9 M0 F; g) d- O, L. Y W系统滴答定时器中断服务函数:每隔1ms进入该服务函数
T1 t/ g1 E5 G7 v5 {3 g, X5 x
8 a+ ?# t+ s" [) M. G6 W8 b注:SysTick系统滴答定时器每1ms产生中断1 R- k7 _1 Q7 V% g2 F( ?- _; C
4 I; c! @) P/ {$ I5 l7 E1 I6 q- /**
' W; J1 z' g+ h) r6 M! t - * @brief This function handles System tick timer.
! J+ E! V2 ~- q& J; o/ N6 Q% @$ | - */0 C K/ G8 O! u+ v
- void SysTick_Handler(void)5 Z; r" S6 _& M! k8 Q& C
- {( Q1 Z. c9 d* J* j4 I, d
- /* USER CODE BEGIN SysTick_IRQn 0 */
: S% f/ J' J( [: ^6 X+ N - / C6 |9 p1 R" \, P, r- y6 q1 A
- /* USER CODE END SysTick_IRQn 0 */! Z4 R; N0 Z. @/ A; q
- HAL_IncTick();
y8 c) k6 P! i! `1 T; M/ I& e! \ - /* USER CODE BEGIN SysTick_IRQn 1 *// ?: q* ~/ Y4 e6 h: b3 s2 D. \
4 _. C7 M: t+ a: x3 H, \1 t- /* USER CODE END SysTick_IRQn 1 */7 g& U5 R' `9 Y, f# c5 @
- }$ S( I! g+ c* s, v
( T1 x7 g1 e# I% b; m6 ]9 e: ]1 z- __weak void HAL_IncTick(void)
/ T+ x) `9 F5 H, i$ H - {
1 k+ w. }3 b( u: g( v8 {" m) A+ h. h - uwTick += uwTickFreq; // uwTickFreq = 1
% V9 `8 n; w' K. l5 E - }8 P" C, l6 H* c
- __IO uint32_t uwTick;& g9 }' S3 |" { {
- uint32_t uwTickFreq = HAL_TICK_FREQ_DEFAULT; /* 1K<span style="background-color: rgb(255, 255, 255);">Hz */</span>
复制代码 0 o. p1 c n* J, h
在中断服务函数中 uwTick 会每1ms加1, 一直循环,直到溢出达到232(大约要一直运行40几天)
7 p8 Y; C5 k a3 @
; C8 \4 G9 i) ~uwTick一般用于计数或者延时函数(HAL_Delay()& B0 i$ H" m! i _0 `( K
; R% s' [$ N% P, e, {- __weak void HAL_Delay(uint32_t Delay)% ?* _' G: G) D x5 h8 O; ^
- {
% v0 d% @: R" H4 S7 a" Q, ] - uint32_t tickstart = HAL_GetTick();$ r. L$ k9 c$ E5 U: N" z
- uint32_t wait = Delay;
' W) X7 k% R1 E E6 Z1 E- |
6 T( E- I/ r' Y* b5 z5 x7 a y- /* Add a freq to guarantee minimum wait */ f4 b/ v+ |7 f Y) O- j
- if (wait < HAL_MAX_DELAY)
9 t5 _7 q6 g3 c5 Y; h - {4 T7 i2 t+ |* }! R2 X
- wait += (uint32_t)(uwTickFreq);
% m! u5 d t- ~9 V u - }
) H/ O4 q+ V' f! q8 O - ( C8 L6 j2 p( L$ R+ V
- while ((HAL_GetTick() - tickstart) < wait)
" y8 a& U( A( C+ \' ~2 O1 x - {
: }. q3 o3 L5 J9 H - }
# B- s6 j" ^& A7 I. L- w# E - }
! c! H" l2 t e9 j# k7 K - * q- r- q$ J, W; Z
- __weak uint32_t HAL_GetTick(void)! Y0 y5 }7 @; k* ?( b
- {
, I; L) W E& l - return uwTick;: d: {! e/ Y5 U: }6 f5 `
- }
复制代码
+ G: ]5 o# n" B; IHAL_GetTick获取当前uwTick,若传入参数500,tickstart 为 HAL_GetTick() 最初的值(即进入延时时uwTick的值),固定不变。
5 {1 X4 `0 a% e6 s: fHAL_GetTick() 与 tickstart 的差值若小于用户定义的500,则继续循环等待;若超过500,则跳出循环(即跳出HAL_Delay() ),延时结束 E/ o: D v/ j4 M7 Q
& _- D7 R4 H# F' Q+ q1 T总结
/ O5 g. g* c1 [2 ?2 Z提示:这里对文章进行总结:: ?# D+ F- g' K2 i9 ?, Z! k# C
& W2 e+ l0 z1 x+ D& j
Systick的两大作用:
: |& ?4 v' W8 M1 b1、可以产生精确的延时(HAL_Delay())
3 K; S. ^7 t* P( W8 y5 l% M: B; M2、可以提供给操作系统一个单独的心跳(时钟)节拍(操作系统时基)
) E( B d4 O5 b' O, M% ]4 g9 k6 r
* {3 J' S' `9 b1 L- O& S
" B! r# w# y: a
* D8 h/ ]. R8 W# m4 F% y+ ^ |