一、Systick滴答定时器是什么?
. c9 I! b% F/ ~/ [& t) Z
+ l# A, V: G6 w' C7 T5 | Systick定时器就是系统滴答定时器,一个24 位的倒计数定时器,计到0 时,将从RELOAD 寄存器中自动重装载定时初值。只要不把它在SysTick 控制及状态寄存器中的使能位清除,就永不停息,即使在睡眠模式下也能工作。) `4 D; W y% ]1 S; g. C7 {8 M
* ?; S- y4 h3 {8 n! u' o3 E7 y s 定时器既是在规定时间之后执行某个操作。1 G7 ]0 a* p4 x
% [" t( E( A2 W; o- d Systick定时器常用来做延时,或者实时系统的心跳时钟。这样可以节省MCU资源,不用浪费一个定时器。比如UCOS中,分时复用,需要一个最小的时间戳,一般在STM32+UCOS系统中,都采用Systick做UCOS心跳时钟。 f8 i: ?$ h" p: O9 I
! b/ @$ @2 f) e' R$ c" W4 N二、Systick一般用来做延时,精确延时% e" w1 z5 Y4 O6 h( \6 J
) T2 J9 y) m8 \0 z
一共4个Systick寄存器
" y5 j4 ?$ e" c6 a: U1 D- o! e9 m) @2 O2 t
CTRL SysTick 控制和状态寄存器 . w9 ^, t0 h+ @5 ^: h
( A6 R. u7 a3 b5 m4 V! b) u. }
LOAD SysTick 自动重装载初值寄存器 6 B9 V$ _$ h# f" t t+ P+ O7 _
& q7 k: ^7 x$ y0 d! \) f# I
VAL SysTick 当前值寄存器
/ M! S2 i, L1 w# ]" d; I9 |& L, q [, a9 b; ?5 c
CALIB SysTick 校准值寄存器0 G. ]' ^! h5 D4 ~+ h' S
0 S- M# n$ M0 ?* B
三、systick定时器原理7 W* t1 F$ K9 `* |0 x) ~% d- j
) r- `- f) W7 ?) K6 i这个定时器是设置一个初值,然后这个初值减数到0,就是定时完成,完成之后可以产生中断,也可以不使用中断。( i- r- j C+ M* z) ? E
( T9 f+ X& x9 h
% T0 @) ~4 H3 u: W# N& v/ p
! o$ i; T8 f; u第0位是使能位,可以使能或者使能定时器" ?9 m5 i+ D9 K' d. q! ^
+ U; K" `! g- t, I" n* N2 ?第1位是使能中断位,减数结束之后是否产生中断。/ l$ `* X& i: D4 I* R) W8 U
5 |( b( y" r; b第2位是时钟选择位,可以选择外部的或者内部时钟作为时钟源。
. x& K& h/ w$ d+ j0 q5 l
3 n7 s$ S/ M8 k& V7 N第16位是标志位,减数到0之后该位被置位1,读取过后自动清清零。/ ~. @- N+ f& s0 m- u$ k. i# g/ K
: f0 f$ v% D- u E0 e4 s0 X
四、uSysTick 重装载数值寄存器-LOAD
1 Q, u+ c7 ^! H) h
6 J) C. A/ W! J6 b( ]" i/ p
- d3 S& v6 `+ }7 W
. Z" f$ @# s3 D/ R. H1 q! S当当前值寄存器减数到0,自动会将把这个RELOAD的值赋给当前值寄存器。) |8 s+ K% P2 G0 k; t
6 S1 ~8 `4 T* V2 K- vif(VAL == 0); e; {" |! E* Q; @7 m7 `
+ T2 ~0 P1 `* g j1 K; r VAL = RELOAD0 f5 S3 o2 o4 L9 ^
# \1 N H8 w/ o, o; |6 M$ l
五、SysTick 当前值寄存器- VAL5 b; @/ p! L$ o: s
. g" P/ I0 V5 w, m+ P$ x* r3 v* T
3 Q- m, X3 n1 [7 g- E% P( c
) k0 y" I, H/ B/ a$ @# F$ eVAL就是从初值(RELOAD)一直减一,倒数到0的时候,重新将初值(RELOAD)赋给VAL。* _6 F0 T3 m' r% v( d. ~. w8 k/ S
" a! {4 Y' r& k: z* i" @2 x3 { {% {六、滴答定时器的实现0 `, B! @2 B% b* V( T. A& M4 m& Y
, O2 _, X' N1 z" H: n/ B! E) u. z" z对于STM32,外部时钟源是HCLK(AHB总线时钟)的1/8内核时钟是HCLK时钟
; g6 m0 b' o* X" |# E9 j* g4 [1 a0 e. \ G3 z( [3 L8 V$ e
1、选择时钟源3 L; i6 _! e. Z8 Q8 D2 r
_ h1 F/ j- U% R
SysTick_CLKSourceConfig();//选择时钟源 misc.c文件中
* a O& \; X5 K( ]8 G: e2 L0 O$ W! U9 ?, ?8 S4 ~: d
5 t$ [8 G, Y, S+ _6 X, I% E$ v1 ~# l& m$ E) ?; [
SysTick_Config(uint32_t ticks) //初始化systick,时钟为HCLK,并开启中断 core_cm4.h文件中
2 G1 ?( Q3 h4 \! l- A
" T" A+ [! z Q) \3 Y
- z a* X. v8 P) v' z
f. j) o6 p7 I2、编写延时函数
' [0 N6 t# L2 _# ?: F1 j& f$ \9 @- V K) L3 I- g6 n& F
3、设置中断优先级分组) L& f! ~# e$ T+ T8 e6 F# z
9 D5 j* c! e9 W8 r K5 |; c, Z NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2);3 a$ R, t- E+ ^0 O3 T# |; W
: C7 C" T: M J
一般使用中断都需要在最开始对我们的优先级进行分组,分好之后再进行优先级的设置。
\: r! Z5 E$ k d' R+ G
1 k0 D: Z+ @, K i5 K( R ` 尽量不要在同一个程序里面频繁的改变分组,频繁切换分组可能会使程序出现不可预料错误。. y3 I) T+ ]- Y R' x. F3 S
9 |! |' a& m2 ?2 d; m. Y
七、用中断的方式实现delay延时
2 Z/ \8 o- Y6 U# BSystick中断服务函数:void SysTick_Handler(void);
7 k; {" e3 Y$ A: l9 ^* t3 H+ S7 K0 Q3 E( v3 E8 X# y; F' p
* r$ a5 `9 ^8 A. H# s9 Q/ L% d& l0 X7 t6 I" R: N; ~- v, x
注意:5 \' l( k: {& E6 \4 e5 w- T5 K* p
$ S: Q0 p+ R' @, a% i- \: k
uCortex-M系统中,Systick代码可以通用。6 d) n' p$ j" O! z
g; E: }3 U1 ^ 如果使用中发现延时不一致,问题一般都是因为不同内核时钟不一样而已。修改ticks值即可。
0 g% z1 V6 c8 e* r; ~1 F1 l \' O2 k. a3 f7 j( F$ v
参考代码如下:1 k, G5 ?0 P4 L9 m
/ z+ W) f' U6 O8 ^- x/ u9 W- void init_delay(void)
* Z' T9 I. ]9 b6 Q- I: v1 j0 M5 R - {
7 L. @' R0 h" @- G7 [, M - /*我们外部晶振为8MHz,然后倍频到168M,那么Systick时钟即为21M,也就是Systick的计数器4 a# o/ c6 |$ W: e
- VAL每减1就代表过了1/21us*/
3 y: @9 g% ]* J
& f# E' T. X+ G+ v( |$ p- //1.选择时钟源(选择外部时钟)
3 j# g/ F ~1 n+ p4 X+ \, L3 N - //外部时钟要/8,内部始终不用
/ c b2 G. S/ n- N( }; o - SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
6 v, v1 ]% J7 O/ c - }3 U' d6 Y4 o' P
- O3 O+ J% A8 m8 s* ?4 A( J
- //2.编写延时函数
' v& `- a9 P8 D. L( i2 }. Z. G - //最大延时不超过798.915ms,即(2^24)/21/1000
% g: E: w. F3 D% {, g, o, V$ x- p - void delay_us(u32 nus): x1 i4 m8 q$ E$ g( C
- {
u. H6 m9 G* _! K - u32 temp = 0;% E: M W- g4 V, I2 @
- " l" i2 F7 w( c( c$ Z Y
- //1、实现1us*nus的延时
3 m8 h# X, a" {$ {6 v" ?, h - //21外是外部时钟频率,如果选择内部时钟就是168M/8' ^* \' v/ M; d- Z1 `& a
- SysTick->LOAD = 21*nus;//设置自动装载值为21(1us)) _ `2 _( @3 f3 d' d4 \
- SysTick->VAL = 0x00;//设置当前初值为0! m0 I% a% K6 E
- SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; // 开启(使能)计数:使能SysTick定时器$ p6 m. y. W. S& q# }4 ~2 z1 Z. I# l
- : X) @1 d% ^, R! v2 K
- do3 b6 W- f8 E2 ~& h
- {: b2 A! {# l, h3 O
- //读取控制寄存器
" W0 I! l0 h$ W( U$ m" S- q - temp = SysTick->CTRL;
; H. E3 l4 D, X7 e - }while(!(temp & (1<<16)));//等待计数时间到达(位16)$ a% l7 r1 r3 x5 w' W( n c- g
- ( _. M; C1 A- A! n% B. ?- t4 I
- SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; // 关闭(失能)计数
$ w, t2 X* V6 S& `1 P - SysTick->VAL = 0x00;//再设置当前初值为0,重置VAL ' C4 @+ D9 h8 Q( _; I9 [3 I
- }
' Y/ s9 q5 z% ^8 z
- t9 D. v5 }4 R9 y$ N( `3 p8 ?- void delay_ms(u32 nms): F- D$ |( I6 i! \$ \8 S* L; j
- {+ T c( D3 w6 C& D8 I+ F
- u32 temp = 0;9 j% m# r$ t: l
- ( ~1 B) v( L& x* ]7 r
- //1、实现1us*nus的延时4 N1 C+ d2 P3 f4 G
- SysTick->LOAD = 21000*nms;//设置自动装载值为21(1us)8 F1 `' s/ M% s: o% F/ |1 M* W' P
- SysTick->VAL = 0x00;//设置当前初值为0$ d4 @" l& t/ C7 N4 }* B
- SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; // 开启(使能)计数
- n# X3 Q8 r& v - ) M* c, c: K6 A
- do- J9 Q1 D% m5 X. g
- {) L; a5 [6 q# U7 V0 s% A0 Z
- //读取控制寄存器
0 t/ s6 F7 o: V+ F7 S - temp = SysTick->CTRL;
- e; @7 [' t& P# k - }while(!(temp & (1<<16)));$ K3 a) _$ B& ~0 z& l
- 6 M5 w- X2 j9 F+ P" O
- SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; // 关闭(失能)计数
, X L$ b/ |8 n& N4 V- d - SysTick->VAL = 0x00;//再设置当前初值为0
' I& F/ Q# f. e D - }( b+ [& e( {/ Z% H/ H0 s
- % b4 o$ I) Q" Z# _: M$ Z* l8 Q+ _6 y
- void delay_s(u32 s)
/ J( }6 F: G3 y; Y - {
8 \2 w$ }- ~2 m5 Z1 J h; ` - while(s--)
) C( m3 M3 `# N$ r: i) a - {
0 p8 Q' x; Q/ O) \9 d6 M2 l; v - delay_ms(500);: Z6 L, E, {* V" Q2 j9 E4 p D N
- delay_ms(500);
- P0 F0 F- b9 i- N7 r - }
$ W) [, U. G* c8 e; P - }
2 o3 r( x7 B, [ - ! ~7 \! a9 H" S) t% P3 c" z& r
复制代码 ) ~2 f" {: Q8 J T+ Y) {
/ W: y& g( x) R. }) ^
: m% z! g A, ]! J2 A
|