一、SysTick系统滴答定时器3 `3 j, T; Y* x+ h- I1 l9 W: J$ O7 |
SysTick是一个24位定时器,属于Cortex-M4内核中的一个外设,类似NVIC。
% y: Z2 K2 G1 r7 h" Q0 h0 r o1 g/ X
8 P. b- B, E3 R" i3 P, {一个周期定时器,用于提供时间基准,多为操作系统所使用,常用于对时间要求严格的情况,意义是很重要的
0 e( B8 }& N) U/ V; g4 x
5 ~6 ^2 A3 g* @7 G" ]* lSysTick定时器一次最多可以计数2^24(24bit)个时钟脉冲,这个脉冲计数值保存在当前计数值寄存器STK_VAL(Systick current value register)中,只能向下计数,也就是倒计数。
+ b: q6 J1 ~1 d# I; F" [1 ?( f2 }2 g0 v) j. O8 q) S
每接收到一个时钟脉冲(CPU主频),STK_VAL的值就会向下减1,当减到0时,硬件会自动将重装载寄存器STK_LOAD(可以设定,跟STK_VAL初始值相等)中保存的数值加载到STK_VAL,使其重新计数,并且,系统滴答定时器就产生一次中断,以此循环往复,只要不把它在SysTick控制及状态寄存器中的使能位清除,就永不停息/ o% X6 S! W h$ n8 n1 K
+ R9 l; p1 q- x' w h+ z
Systick相关寄存器7 X3 ]# \3 A x3 W
% B0 [6 z# \4 I! i0 r$ \9 Y' p) `
7 U2 \! b7 L8 j j/ Z4 b
- U! @7 n: P. ]) \/ j9 A* T/ n1 G' E: A9 `" [
若VAL中的数值为 80,脉冲频率为 80MHz,则VAL由 80 减到 0 所需要的时间为80/80MHz=1us,并且当减到 0 时会触发中断(相当于 1us 定时器),同时 LOAD 的值也会重新载入到VAL中
! ]7 |% H0 D# H, v! l3 c, r/ ^0 r7 n6 j: A' w5 i
STM32CubeMX系统时钟的配置/ r" H; E5 V5 Z+ h, i5 [8 n
4 |) Q |" q6 Y
. y" q9 |/ o/ s$ q$ R
在 STM32CubeMX 中配置好时钟树后,时钟频率会输入到 Cortex System timer内核时钟 。所以, SysTick 的脉冲频率来自于此- c, s0 m5 G1 S5 M; c7 t' B2 `1 [
3 k' Q9 l- M" ~3 n3 b二、SysTick系统定时器原理" \. z# e0 Q. o2 \
在HAL_Init()(HAL_Init–>HAL_InitTick–>HAL_SYSTICK_Config–>SysTick_Config) 使用SysTick作为时基源,并配置1ms定时(重置后的默认时钟为 HSI)。但是 HSI 为内部时钟,而我们使用的是外部时钟,这里的1ms定时是不精准的5 J! n& t& I! R. }$ O& z+ H* B+ Q
: R9 R& M# J1 j" D. ^* i0 P% b k- /* Use SysTick as time base source and configure 1ms tick (default clock after Reset is HSI) */
) v( l" z0 @/ W7 x& c, V4 n - if (HAL_InitTick(TICK_INT_PRIORITY) != HAL_OK)
& N( j2 a& C" p) | - {
, ~7 t- ]1 P8 s$ ~* \ - status = HAL_ERROR;. c! O0 C S4 S5 E# J8 ]
- }
复制代码
- V; u; @$ x/ k精准的1ms定时在系统定时器配置函数SysTick_Config() 在core_cm4.h头文件里面) a' ^- ^5 A: k: }# k$ i) y. ?
' m: D8 n8 ^, ^# Y4 w' M/ Q
- __STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
$ z' ]* J- b" D& P - {
6 G! {0 d. Y v6 Q - if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)
1 P6 c5 S' q- w0 B0 R$ `. r% | - {, Q8 N4 g, Y" N
- return (1UL); /* Reload value impossible */5 |* N; V& K1 u0 b3 s7 h
- }
E+ h4 n1 O! {6 C" R" b - + w6 g9 e0 z" f
- SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */
: q/ h5 ?! t S4 K- n+ V, f+ y - NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */. a u9 E4 |, y5 B' h# p# G2 M
- SysTick->VAL = 0UL; /* Load the SysTick Counter Value */% L5 Z; k: p3 \: Q) r5 C
- SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |3 I4 \% P( K+ g# a- b; a( p$ S2 `
- SysTick_CTRL_TICKINT_Msk |
0 x3 f) g, @7 S% y6 P. R - SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */1 S8 j- l% h$ h* X* ~
- return (0UL); /* Function successful */
q3 ? y/ p, v* P( w7 h - }
复制代码
4 |" D- e1 Q M, L! P$ \6 o在这个函数里面进行对系统定时器的寄存器进行配置' R9 |6 j$ O* p. n
! P ~! U% v7 u
系统滴答定时器中断服务函数:每隔1ms进入该服务函数
6 b( h; E" ?( d2 t0 n6 c7 P
; Y3 ^" y9 h- n0 H" Y3 [注:SysTick系统滴答定时器每1ms产生中断
# n- R; W5 S2 j) ~. t/ @5 `: D9 Q! b! Z9 w! E1 n
- /**
@- ^4 J! B) k2 [) j+ l7 q - * @brief This function handles System tick timer.0 y3 s. D" |( O3 g/ p* P
- */9 J" Z1 s) M. {/ c% g
- void SysTick_Handler(void)5 s6 x- t$ K) r4 ~" F
- {9 f: _ P! v" N( }! @! I
- /* USER CODE BEGIN SysTick_IRQn 0 */
9 E6 I$ _( i4 Z - ' S6 c: y* g" V! \% y+ P( g" E$ F/ \3 E
- /* USER CODE END SysTick_IRQn 0 */. w7 ^) z8 V" ?8 }8 C( u
- HAL_IncTick();
' y6 a, o8 i! {) E/ @$ k+ Q ]/ D - /* USER CODE BEGIN SysTick_IRQn 1 */0 j9 u+ G8 s' w7 g0 m( V
) _3 y8 R/ i) M4 o, t# O- /* USER CODE END SysTick_IRQn 1 */% m- i5 I/ O" p7 ]% l4 O
- }
5 b# f1 w$ ~# V5 Z8 s7 ~
) b: n( V0 Z. ?: @2 {2 h( q- __weak void HAL_IncTick(void)
5 ~" l% A: @5 Z, Z8 F/ y - {1 J0 n4 l3 N4 J, i
- uwTick += uwTickFreq; // uwTickFreq = 1
- c- ~& h6 f, z h - }/ o8 w$ g- a# J W: Q
- __IO uint32_t uwTick;
- Z2 D! k2 M' Q1 I$ T - uint32_t uwTickFreq = HAL_TICK_FREQ_DEFAULT; /* 1K<span style="background-color: rgb(255, 255, 255);">Hz */</span>
复制代码
8 U h( K- c' @5 ~在中断服务函数中 uwTick 会每1ms加1, 一直循环,直到溢出达到232(大约要一直运行40几天)* i6 L6 ]5 u% y8 P* K0 g+ i! {
( O$ k5 y8 w+ z2 D1 g& ^. \uwTick一般用于计数或者延时函数(HAL_Delay()2 o' b+ ] Z" J7 {! h' r
`' r- i5 L+ F! q! w! R0 A- __weak void HAL_Delay(uint32_t Delay)4 \, B) x, b6 {* S
- {
5 a$ u2 Z+ Q; Q6 [7 \9 N$ D' d: r - uint32_t tickstart = HAL_GetTick();, A+ X+ m1 P9 }6 z/ W
- uint32_t wait = Delay;
0 o0 H& k4 h# S8 j1 Z
- j1 f# g. g' L. I! p2 _, M0 @8 o- /* Add a freq to guarantee minimum wait */6 J- g1 G, E7 d% C' Q# L
- if (wait < HAL_MAX_DELAY)
4 E: B9 ~+ e7 a# a' \, J" E - {- l4 l( q$ S$ M
- wait += (uint32_t)(uwTickFreq);! b0 G2 X8 {/ O) L2 u* ?( H
- }
5 d! u! }+ ^1 J* j6 k# j( u1 ]
, I7 h0 Q: z( {+ X- while ((HAL_GetTick() - tickstart) < wait)* E i) M' A0 Y# k; [, L3 v
- {; k# g1 Q( Q) B' c% j* K8 v ^
- }
9 @1 V$ f. V8 L' K* u - }
3 t O' l* d* H4 Q8 ^: ^ p - 9 A6 k6 P: o" H) R0 [9 @' V3 a: `
- __weak uint32_t HAL_GetTick(void)
0 i4 h! { O' L g! | - {
: C( J4 Y* S, A( ~+ T2 Z - return uwTick;
0 f/ }- N, X6 n7 D, {) N C - }
复制代码
6 z7 ~' |' ~5 v( }( u% A! `5 |6 nHAL_GetTick获取当前uwTick,若传入参数500,tickstart 为 HAL_GetTick() 最初的值(即进入延时时uwTick的值),固定不变。 x* b+ [) ^& F! W* z
HAL_GetTick() 与 tickstart 的差值若小于用户定义的500,则继续循环等待;若超过500,则跳出循环(即跳出HAL_Delay() ),延时结束
# q1 H& k) u$ q. o7 y
4 l6 k4 v. I+ R8 l总结' b, L ^7 B2 z* n4 p, c+ ]
提示:这里对文章进行总结:; _4 P- T1 Q' F: T! b3 ^" P
; e) r/ G3 v" f8 X5 G
Systick的两大作用:4 Y) S* Z \- k6 z6 Z
1、可以产生精确的延时(HAL_Delay())
- U. A8 v! i: g' a) ?2、可以提供给操作系统一个单独的心跳(时钟)节拍(操作系统时基)
; z$ Q S( E9 t9 y, G7 a6 J
0 E3 \/ I, C2 I6 p6 l. E
, `5 z* E9 I, Y1 E8 q8 x# m. G
7 L. O) q% z% A) B7 q# {* J) C8 B |