一、Systick滴答定时器是什么?9 U h0 W2 m: L2 n
% I/ u2 l5 [$ \5 \! d Systick定时器就是系统滴答定时器,一个24 位的倒计数定时器,计到0 时,将从RELOAD 寄存器中自动重装载定时初值。只要不把它在SysTick 控制及状态寄存器中的使能位清除,就永不停息,即使在睡眠模式下也能工作。) g$ b5 w; x: x' @- ]8 p( ~
/ o# [/ G" X0 C! R, ? 定时器既是在规定时间之后执行某个操作。
- C( K1 A* `" r* F* _3 `1 v) U# w4 }
Systick定时器常用来做延时,或者实时系统的心跳时钟。这样可以节省MCU资源,不用浪费一个定时器。比如UCOS中,分时复用,需要一个最小的时间戳,一般在STM32+UCOS系统中,都采用Systick做UCOS心跳时钟。% G9 Y2 F# A, \/ m5 x. R
$ N R l* W4 j+ s! p# L6 k
二、Systick一般用来做延时,精确延时
; @) C8 R6 K6 b+ E8 F
* E% Z+ D3 {4 z- G/ z; E8 M一共4个Systick寄存器
' F) p( R1 \+ x1 J9 [
4 L T% P0 r1 ~- h: h1 j CTRL SysTick 控制和状态寄存器 ' b. l, M* W) s# m
" u1 B2 d9 B. u# I LOAD SysTick 自动重装载初值寄存器
) y2 K* G. `# r2 L. ^( a/ A/ B2 ]* {7 Z8 D4 h/ {0 b
VAL SysTick 当前值寄存器 0 ?% O2 _& b/ b; q0 A' v
, M! ~, }) }' }
CALIB SysTick 校准值寄存器7 N( y3 P1 ^7 G' g2 F
! R9 V2 t1 R4 N: L& ^0 W
三、systick定时器原理6 T0 N) g5 H# v9 T4 j1 M
8 ]7 C$ G# e% y. {这个定时器是设置一个初值,然后这个初值减数到0,就是定时完成,完成之后可以产生中断,也可以不使用中断。) E- t* |% ^$ }( g9 `- p5 B
5 W/ u* F: M7 `- R
~- R* c+ ]1 u. U" A- A& W# C ]( |
第0位是使能位,可以使能或者使能定时器7 g5 a( I' {, Q9 q" c' m+ g5 j
; s/ \4 u6 ]2 T7 ~; [) _1 z+ M
第1位是使能中断位,减数结束之后是否产生中断。
: x: Q* H6 U) W8 ~. ~( o5 Y0 N1 @; n- i
第2位是时钟选择位,可以选择外部的或者内部时钟作为时钟源。4 R# X) O( Z s, _$ {3 U% v
% j6 p, L& J* r7 w0 n6 K第16位是标志位,减数到0之后该位被置位1,读取过后自动清清零。
+ s# |) z6 y6 T9 D( g
- |9 `6 Y$ N# F/ r1 I四、uSysTick 重装载数值寄存器-LOAD
& j2 K$ \1 Q, u( Z
6 A" t1 E9 W* N1 x/ B( O
" t( Z/ p& q0 v% T8 f/ A
& u( o4 s- b: \0 h! y; D* o, v当当前值寄存器减数到0,自动会将把这个RELOAD的值赋给当前值寄存器。
1 q. d- b+ L! Y2 O- @# W j
5 ?2 O& [; i$ nif(VAL == 0)
0 Y! L5 Z7 l& r) B5 E' Q4 p" h0 v
$ M; ?$ j: {) ]- Y VAL = RELOAD/ W* E1 P: k" | B) ]
. y: H& J$ @% p$ A$ K五、SysTick 当前值寄存器- VAL, z: {( }4 t1 B( ]! [
- N1 Y4 |- X# m' O
0 I8 }! w1 k6 Y' q5 o _: D2 J/ s4 z; O: n4 {- {
VAL就是从初值(RELOAD)一直减一,倒数到0的时候,重新将初值(RELOAD)赋给VAL。) ]& T- H- d ~3 f
$ L# J& o: p7 |" o' v* u9 \; u- q( V
六、滴答定时器的实现# _8 @1 L5 T2 X5 M: Y2 H# K
7 O% r' P' f) P4 q f7 ~# L/ U对于STM32,外部时钟源是HCLK(AHB总线时钟)的1/8内核时钟是HCLK时钟
5 P& u, |1 C0 C1 s& T, [8 v3 T+ e" o
: a r2 {+ {2 V! x) ^; F1、选择时钟源
' {/ H( P9 t& C2 Z
: \, J" a# `5 K! s8 U* ~8 F SysTick_CLKSourceConfig();//选择时钟源 misc.c文件中8 u3 c! o: Q3 C& p& g( U1 z
$ ^' z" P: f! |
0 n3 a3 J( u& h& _: I0 b; b* k8 L. q( k$ s9 u$ \( M( ^4 W; Y: N8 c
SysTick_Config(uint32_t ticks) //初始化systick,时钟为HCLK,并开启中断 core_cm4.h文件中
2 g4 _2 |: r t% N3 J; h
) n" ?$ L: i/ F7 j, V6 e
2 b: Z" i' @9 y9 W ^7 p+ J; W# l: q2 ]
: ^- {: P Y6 I. G9 ]2、编写延时函数: ?3 q& X6 @: g: D Y
, u0 K; q4 F/ Q" Y- b7 V9 [+ n
3、设置中断优先级分组( @9 g! }' N9 r% t" r. Z
. F. D" ~: V8 b, d* Q# J6 W NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2);
2 j/ R m2 K/ E* k. Z
3 J- {0 {0 U* \7 M5 ]% T' X 一般使用中断都需要在最开始对我们的优先级进行分组,分好之后再进行优先级的设置。2 R1 {4 J7 v q$ `
) y, d7 E! i( c 尽量不要在同一个程序里面频繁的改变分组,频繁切换分组可能会使程序出现不可预料错误。8 {6 f7 n- V. y$ p0 i/ C
; b0 p& f* _; M/ h& Z8 T七、用中断的方式实现delay延时) v( [! D }5 d
Systick中断服务函数:void SysTick_Handler(void);
' @$ F( V; \, Q" A
0 ^. X! H4 u3 F1 x/ y
0 E9 a( W+ A. Z7 w# _8 D& G7 W
) @5 q2 }7 ]$ U! O- R注意:
& n. U; {4 B P& {6 Q% I
/ I+ e7 r+ z; D/ J3 T% q1 G$ p uCortex-M系统中,Systick代码可以通用。3 L- S0 A1 H' B: ?3 T) R0 D
8 x# o! [6 x! ^8 p; |1 p
如果使用中发现延时不一致,问题一般都是因为不同内核时钟不一样而已。修改ticks值即可。, ~1 K4 Y" d7 l# D0 `3 `
& P' x( u2 r! g9 B: q( j" F
参考代码如下:. i$ @; O) f& V( s# Q( E) V
2 c4 ^5 E5 U1 K& ?- void init_delay(void)# s: }' V' _ i) n ~* W, v
- {8 J# u* r, o+ g9 K* _
- /*我们外部晶振为8MHz,然后倍频到168M,那么Systick时钟即为21M,也就是Systick的计数器2 o+ _' L) |1 r; ]: w; R
- VAL每减1就代表过了1/21us*/2 o: a6 j3 D! P+ t' Z, I" G
5 ^% h1 V1 W( A- Y- //1.选择时钟源(选择外部时钟)3 L0 y2 Y5 W- R
- //外部时钟要/8,内部始终不用
6 W+ `& {- p' P7 ^ L - SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); , r% l6 W8 Y8 a+ a
- }4 ~7 }/ [6 f+ F( q3 C' S0 @
- 2 R. n# R0 `8 z) y4 `% f$ [ x7 n
- //2.编写延时函数
2 a; o9 ?8 V1 x8 J, X7 p4 L' P - //最大延时不超过798.915ms,即(2^24)/21/1000
" _. q% q2 J/ e, N" ~ - void delay_us(u32 nus)
" a$ _$ {0 V' E% E8 \; F - {
/ d* y6 ?& _7 Z% w - u32 temp = 0;$ e. K: Y2 _$ k% y
. F; \6 @2 w, h y* g u; @- //1、实现1us*nus的延时- V# t j) h; \4 k
- //21外是外部时钟频率,如果选择内部时钟就是168M/80 M( J3 V6 j- X6 J4 I, f( X
- SysTick->LOAD = 21*nus;//设置自动装载值为21(1us)9 k, Q- b) C2 a
- SysTick->VAL = 0x00;//设置当前初值为0
" a. X# e6 g3 s% c# r! N - SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; // 开启(使能)计数:使能SysTick定时器+ E+ ]8 {5 U6 S5 d
' d+ w0 L1 L5 o/ a/ v- U7 M- do @" W- Z, s. t; n2 j! S
- {7 A! I9 y" e0 m1 j
- //读取控制寄存器
/ h$ c6 {2 w. t# [4 H+ G+ ` - temp = SysTick->CTRL;
# ~# Y3 M; C, } - }while(!(temp & (1<<16)));//等待计数时间到达(位16)
}, n. a) @6 G8 |( ?
" m5 L! _: J" A# `" j# j" z- SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; // 关闭(失能)计数
. J/ @2 J& }0 V. ^: t* | - SysTick->VAL = 0x00;//再设置当前初值为0,重置VAL : q( k9 Q( [: a7 H7 y
- }
. [/ |' g! U3 f0 x9 J2 [. }( i - & l _6 I; \9 l6 b9 V5 Q
- void delay_ms(u32 nms)
* R8 ?8 d2 ^% \+ r - {
& s2 |. ?, k& Q, L5 {/ \ - u32 temp = 0;7 U& m" h F- d( M) w" w
, z. h. p; g# `* O7 Z- //1、实现1us*nus的延时
% \1 ]2 q# z1 t ~) @" @ - SysTick->LOAD = 21000*nms;//设置自动装载值为21(1us)8 c7 Q7 X$ }' ^9 C6 m- C: u
- SysTick->VAL = 0x00;//设置当前初值为0# q/ _# n @$ h, s
- SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; // 开启(使能)计数
5 z) E* ~4 l% j" P5 q4 G - , {: W7 i4 F- W9 `2 d6 J N$ o+ i
- do1 v4 S, w: T& U+ m
- {
9 j6 _ E; K- N# @ [ - //读取控制寄存器
- D4 x& U3 X# @* i _1 `! C; S% l - temp = SysTick->CTRL;
+ A3 F5 s9 z" J' [: k' J* V - }while(!(temp & (1<<16)));
# T B1 y& u8 J8 r9 ]2 H
# K6 h( @ J& l* {& a* `7 ~- SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; // 关闭(失能)计数
0 W6 X5 ?+ Q# B" I - SysTick->VAL = 0x00;//再设置当前初值为0 2 V5 g# v+ N5 B6 |" @8 I8 W
- }0 |' p& ~/ x1 U2 f2 e7 C1 S. Q
& |, a8 L) R6 q# M- X, Q) o/ o- void delay_s(u32 s)6 X1 s1 j5 ^4 r$ I
- {. Y0 R+ Q" O8 {# T( Q! U* _% c' n
- while(s--)
* U L# C3 W8 |8 W- N9 L" d - {
5 t* B: A& J2 ]1 j- e) r% r5 c& H8 ? - delay_ms(500);
* W e K& e' ^/ t - delay_ms(500);4 n. G I+ |4 r& e o- L: |
- }
2 E: S' ?' t3 Z) A: n8 _" Q7 v# `& c - }
6 L6 ~- o' x+ L# O* U# k - + p1 K, e7 w: d) Y
复制代码
: l5 ^/ R0 A( k! r$ P/ n$ `* ?% @3 O
- h/ Q$ H: d7 G/ |. Q |