1.systick介绍& \6 i! Z( `6 o: a1 V6 u
3 a# P/ @, q7 B6 M8 A: G! [5 K( @ Systick就是一个定时器而已,只是它放在了NVIC中,主要的目的是为了给操作系统提供一个硬件上的中断(号称滴答中断)。滴答中断?这里来简单地解释一下。操作系统进行运转的时候,也会有“心跳”。它会根据“心跳”的节拍来工作,把整个时间段分成很多小小的时间片,每个任务每次只能运行一个“时间片”的时间长度就得退出给别的任务运行,这样可以确保任何一个任务都不会霸占整个系统不放。或者把每个定时器周期的某个时间范围赐予特定的任务等,还有操作系统提供的各种定时功能,都与这个滴答定时器有关。因此,需要一个定时器来产生周期性的中断,而且最好还让用户程序不能随意访问它的寄存器,以维持操作系统“心跳”的节律。 只要不把它在SysTick控制及状态寄存器中的使能位清除,就永不停息。
- p$ o( I9 |& a$ G) G7 E2 h8 E S1 f' w
知道systick在系统中的地位后,我们来了解systick的实现。这里只是举例说明systick的使用。它有四个寄存器,笔者把它列出来:
1 K6 F+ A* _4 S& a/ e0 U0 X: M
2 f$ O& f. e. v: a m8 q: J7 g SysTick->CTRL, --控制和状态寄存器
- s! ^% s. b, N& y# Q4 z: Y
' L/ b- p$ C4 x SysTick->LOAD, --重装载寄存器# B& B* F7 l5 B. P4 S: N0 ]( K
, w3 k9 ]" b. T" s" E
SysTick->VAL, --当前值寄存器
1 D c9 r4 d1 q1 W6 b: x; I! x: p1 K; `0 x
SysTick->CALIB, --校准值寄存器
# L! } U. C/ c, E7 R# d* f& G# E' i3 ~- g( L
% U+ ~; p/ ^- z* S: O
/ q+ O9 f8 J( P$ Z* R/ E+ Q$ N; v
0 s" V; `2 p- k; Q: S; F1 M/ [& s2 w3 O ^( g7 W. P2 a3 v
, K' i: Y) m0 u; ^! c# c. V0 [1 l
" k4 W( j) I' v! g! J
& @; ]( p' I: t8 z4 g; n( R
3 o. R7 {! l# H8 J+ H+ M7 c2.systick编程/ T+ p5 V8 H, V# N0 [- h& o7 s
: ?7 M# x* ^% W# _, l 现在我们想通过Systick定时器做一个精确的延迟函数,比如让LED精确延迟1秒钟闪亮一次。
' e' o i! \ b2 s Q' B' t- V
: }( Q% h t2 }0 g6 c% q$ G 思路:利用systick定时器为递减计数器,设定初值并使能它后,它会每个1系统时钟周期计数器减,计数到 0时,SysTick计数器自动重装初值并继续计数,同时触发中断。
; U8 |2 Y9 y' z) e4 h1 N% C6 ]1 I1 s, u% U
那么每次计数器减到0,时间经过了:系统时钟周期 *计数器初值。我们使用72M作为系统时钟,那么每次计数器减1所用的时间是1/72M,计数器的初值如果是72000,那么每次计数器减到0,时间经过(1/72M)*72000= 0.001,即1ms。(简单理解:用72M的时钟频率,即1s计数72M=72000000次,那1ms计数72000次,所以计数值为72000)
+ r6 o- C9 x" j) ?; F' k4 l5 ~5 ~7 n% e% e% f4 o
- W4 i$ W R" C6 @5 e. Z# {7 t. d8 @ d6 Y( Y
首先,我们需要有一个72M的systick系统时钟,那么,使用下面这个时钟OK就 !9 s5 H3 `# U, v; D. A5 Z
9 O5 R, C9 t' t' N( O
SystemInit();+ J7 B3 ~$ I, h- o
) I3 ~0 a/ S0 \2 |: A 这个函数可以让主频运行到72M。可以把它作为systick的时钟源。9 \/ N# `1 x* L) v
' r! Y! g5 M3 m. B; f7 f% n7 T3 g
接着开始配置systick,实际上配置systick的严格过程如下:
" k0 V, V* C5 h' }8 R1 J. X" ? T$ D) h2 x/ Z- I- I4 _+ G
1、调用SysTick_CounterCmd() --失能SysTick计数器
/ V$ _* E0 Z4 L/ V5 p: o( T( Y& I4 G
2、调用SysTick_ITConfig() --失能SysTick中断
8 r7 \3 {& B) W: B
+ }8 K( O; Q: k+ c) f- w/ x 3、调用SysTick_CLKSourceConfig() --设置SysTick时钟源。
. m- o3 O7 `5 n A# {7 w' c5 a* O! a0 e d. B) l* s+ ]8 v1 |
4、调用SysTick_SetReload() --设置SysTick重装载值。
^0 r% t8 \0 p# E2 k% V3 Q" J; g4 X9 E, a& x0 L5 \
5、调用SysTick_ITConfig() --使能SysTick中断7 s. r7 z+ l$ A: e
* k w6 C2 m1 @1 L* A
6、调用SysTick_CounterCmd() --开启SysTick计数器
0 X( s; v7 r8 \5 A- ^0 G6 r$ p; s
6 P1 A: K" _- x) n" I# V 这里大家一定要注意,必须使得当前寄存器的值VAL等于0!
" c; u* Q3 @: i% j1 s1 J! |) X# p8 I
SysTick->VAL = (0x00);只有当VAL值为0时,计数器自动重载RELOAD。
2 i0 D# N0 {) q
) ~5 e" S: T. Z( B7 w接下来就可以直接调用Delay();函数进行延迟了。延迟函数的实现中,要注意的是,全局变量TimingDelay必须使用volatile,否则可能会被编译器优化。( |9 \1 Y$ x, `6 s
; }& b+ D( Y5 {8 L+ H9 {8 y" M下面我们来做一下程序分析:" T% y0 r7 j. c2 {
$ \, h5 K6 {. Y7 _9 N7 C% q9 W
(1)系统时钟进配置
0 J% J+ Y, U" R/ V( r, ?0 w& `/ G2 v3 p5 J# }. b* p% ^
首先我们对系统时钟进行了配置并且SetSysClock(void)函数使用72M作为系统时钟;
8 @1 g9 E" }: _2 r8 g* B, ~/ g ]2 F9 ^9 Z* A* O
为了方面看清代码我选择截图:
A: q2 ]) F$ i
5 X( q: m! I P5 Z
; Q6 B0 _* g7 X4 t+ `4 b% Q
X$ ?# n! w. x1 W0 `8 f
: O# [/ q- J# H$ ?9 Z3 F6 @
8 d8 v1 u! W5 r(2)先来看看主函数8 w- b! {: p7 ]' Q
* E6 ]8 X1 Z% k. E: N" J- int main(void)
5 b+ O9 z7 k4 F2 A, u1 D$ E5 D
+ M0 [9 G, @+ C( e5 r- { unsigned char i=0;
9 M k1 ~2 n) T5 ~: L. a. @7 d. g - % z0 j! r8 g% o: n
- unsigned char a[] = "abncdee";7 ]& p! S% i3 P$ t
- 5 W; d- a. X; {# q4 _
2 }5 k) M/ F- I7 a1 Q- + G7 Z2 [( I) I6 @
- SystemInit1();//系统初始化' U# V) w8 s3 K' d* S. z
1 f" K4 r) @+ a# J# G
/ K5 p3 {' |8 q+ D0 n1 L- Y4 f
3 A$ }1 m4 f& ?5 a8 S- U, P3 u- if (SysTick_Config(72000)) //1ms响应一次中断
+ s* a: \7 X, S/ n1 c% i) x6 R
& |4 S+ V* N% Q" ~: j- { ( w% I: n: S) Q; d
/ J# c3 e8 _* s. b8 |- /* Capture error */) ~# x/ |( K) p2 ], M3 r
- 7 z+ j& ~# I$ G- d; `" h
- while (1);8 S4 `# s0 c4 I; r; V+ G, |: N- {
- : w N8 P$ @: b7 Y3 F2 j' o# a1 d7 z
- }
2 |4 P2 b2 m- g% r* d' {" @ - * S2 S9 c: B9 U: l% S7 t6 X8 G
- /*解析:因为要求是每500ms往中位机发数据一件事,所以放在while语句中,8 ]4 r& ?( R/ ?* q1 M9 G
- $ L% z1 r8 a( ]9 y2 Q
- *送据+延时可以完成相当于中断的效果;
/ S8 A/ K8 v5 l
/ k4 k4 H9 `! I9 w9 g4 l- *若是多任务中,其中一个任务需要中断,这把这个任务放在中断函数中调用;6 F# d& {% L/ c. X
& ]5 N B- @1 q& q- */
# \0 A' l- v/ i3 F - & Q0 x$ t$ l5 A4 j
- while (1)% i3 @. @7 C5 |; Q3 o
- ; Y6 Q2 m# Y/ l+ @0 q+ L
- {
0 c+ N- g: N. B0 _, I - % G6 Z& s) Z+ Y9 C
- //测试代码:测试定时器功能,通过延时来测试8 J. j4 l' A6 a! M5 Q7 X
- : F# o( k T M$ ^2 J
" X3 l2 r9 [0 a: X/ z9 z' H$ V
% q( G, n n4 ^/ k2 o- GPIO_SetBits(GPIOC, GPIO_Pin_6); //V6
- \) k+ C" E5 m - 1 v$ c/ x+ C. R: a" `& B
- Delay(50);& P6 @5 u+ Z2 j2 @
3 x& P1 v/ X$ P* j% M7 L- GPIO_ResetBits(GPIOC, GPIO_Pin_6); //V6
1 Y( ~" P! ]- }0 J4 @: M% ~
) Z2 {! Q4 [5 H+ ?+ [0 X( N a" V- Delay(50);! X1 G$ F8 B4 h/ C/ p& A
/ I. \" b. f5 x l6 y4 h) F
: G N h. V2 f4 V, e- 1 b" v% A& C* p1 ~
- //功能1代码:每500ms发送数据
! L- ]+ {0 p' g - " t; G9 O, J0 ?
- /*
% a+ ]; A& r- |2 h5 `- r - ! a& E! H' Q3 C. L, j
- UART2_TX485_Puts("123450");
) V* `8 y$ n6 [# I! v5 x
! H# M! V: n$ ?/ n) H- Delay(500);
# M% h* w! o( c( @/ U, l: f - 9 u6 _5 S! j$ _- `8 x- m/ M
- */
" n! }& m. G! r) z+ A$ l - 0 y' b- J& l( g) y W0 {
- //功能2代码:上位发特定指令,中位机执行相应操作& a' S: ]% Z1 [3 \
- 0 M7 L9 I: g" i6 E) F" c, m2 y
- // RS485_Test();: K' O+ l w- N5 j
' [- h [- Q% A) V8 X: ^- } . L& R9 r6 G/ ?! R5 g! X' R8 q
" T2 y% A4 Y, @6 K- }
复制代码
) U) u5 F& H- ]! v, s8 k$ r2 x7 P+ I9 h
(3)系统滴答定时器的配置--主角登场:1 e+ K7 y% A& O4 Z1 F0 p
+ `/ o. H0 W4 m4 {' g! i3 c$ m4 j2 F主函数中: SysTick_Config(72000) ;滴答定时器的参数是72000即计数72000
# L, J# i: ~ n9 f& P1 y q
W/ |* H* m* P$ u# ~$ l1 C( F) B(因为我们使用72M的时钟频率,即1s计数72M=72000000次,那1ms计数72000次,所以计数值为72000) ) E/ g/ R6 G5 Z. k, G
6 U% n5 ?5 M3 a0 X6 f
在文件Core_cm3.h中
/ C/ e# U) \5 U0 B( ], }2 U; B
7 S% R1 p1 z& E& B' ASysTick_Config函数的具体实现如下:, M& ?. ], p! W5 ~: T$ j% R% z
6 P9 N4 a8 N4 ?. m# Y0 \, t# ~
- static __INLINE uint32_t SysTick_Config(uint32_t ticks)% }) w. c3 K- D# ^- e2 P' u
, w( }! u& Z' P0 R! i8 Z% K- { ) n4 s! w& J1 f) O) u
- 4 l1 g* E7 e" y2 a
- if (ticks>SYSTICK_MAXCOUNT) 6 s" F Y$ ~3 e
) [, q9 R3 b) @0 ?2 @* u' F1 U& q- return (1); /* Reload value impossible */+ m- n) C2 m. D% q/ T! n" |/ k
- 2 C7 g8 b F0 E4 @4 t
- SysTick->LOAD = (ticks & SYSTICK_MAXCOUNT) - 1;//systick重装载值寄存器 /* set reload register */
3 A' h/ [' f! `# b' d) A) ~ - / v2 W0 d% ~1 o3 P
- NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Cortex-M0 System Interrupts */
3 L' f! v. H z
4 i& ^) Q, g8 ]0 K0 S& k9 N4 P) ]- SysTick->VAL = (0x00); //systick当前值寄存器 . t& L! r" x- a2 r) G: x! G
- 6 e; r8 T2 K$ H' f4 v
- /* Load the SysTick Counter Value */
: N e: J8 r" s4 a0 W' K; h - SysTick->CTRL = (1 << SYSTICK_CLKSOURCE) | (1<<SYSTICK_ENABLE) | (1<<SYSTICK_TICKINT);//使能IRQ(普通中断)和系器 return(0); /* Function successful */5 ~) n) Q3 N+ N& g* P
- 6 J, v2 I) L: y- Z
- }
复制代码 + S8 c' z6 w5 O: @
+ H% J" p) x" x" i j我们来看一下这句代码:SysTick->CTRL = (1 << SYSTICK_CLKSOURCE) | (1<<SYSTICK_ENABLE) | (1<<SYSTICK_TICKINT); 这是使能IRQ(普通中断)和系统定时器,为什么要使能中断和系统定时器呢?" ]( E3 Y3 [7 p
0 Z. @0 _0 u* W( I下面我们来看一下stm32f10x_it.h文件中:
9 r$ ^6 E& z0 H; \6 ?: D
4 m& d7 m6 h( [0 _$ g8 u5 K R找到滴答定时器中断函数:SysTickHandler()4 ]& B% E9 p2 T/ d. k/ t
. |4 i! m; t8 z4 l0 o7 Y, Vvoid SysTickHandler(void)
4 v6 V4 C. z0 T9 y- f7 I2 Q6 B. D$ _% T/ h
{3 s8 e2 E/ p5 S8 P# J9 l
. R1 k6 u; i7 I7 A TimingDelay_Decrement();; C X. g3 T3 i( Z$ I
6 w2 H: s+ l& F; G% D5 a/ \7 F
}
( ^+ h1 b7 F! U/ V
4 }1 K8 M# e- X* z2 h从上文我们通过装载的计数值72000知道每1ms发生一次中断,在中断函数中调用一个函数TimingDelay_Decrement();-----即每1ms发生中断时就调用到此函数;: T V# h4 `' B0 f G' b
" `5 Y' w5 o: S8 Z
下面我们来看看TimingDelay_Decrement();在干些什么?
/ z7 V7 z% N8 H B) I3 T! c
3 s5 b2 C" A7 c4 \/ o# L- /*****************************************************************
4 ^& D4 T1 E. a% _
: I7 s; [0 y* S& j" K. ]- *函数名称:TimingDelay_Decrement
8 Z" o' S8 F: B - 9 V! @5 {3 H& ?5 P% g% n
- *功能描述:中断里调用此函数,即没发生一次中断,此函数被调用,此函数里 ( o. r: z+ l0 s- n c5 o
; q. @9 x% E4 l- * 的变量TimingDelay 相当于减法计数器
1 u" W( F4 H( Y, v
0 Z( L: ^- b5 d6 C. x7 `" z- *
9 ~2 b0 P4 H) I' S3 |/ x- G
. X" A: U' x8 K1 [3 a5 ]- Z- *输入参数:无7 X+ x' _1 N: ^# U8 B$ h
7 _0 P# y4 }4 W- G2 g1 C- *返回值:无. y9 I: ?' X% E" J& E4 o
: q' _ v4 G2 \; q- *其他说明:无
# Z- y4 e/ S0 D - ' d! Q* A5 b5 c
- *当前版本:v1.0
+ _& C- ?* ?' `/ t% |3 c( p - ( p) S0 x3 O$ O6 L; ?
- *作 者: 梁尹宣 U4 M9 w. ~" e- G1 r9 A
; ~" C( q, f4 w7 E! S- *完成日期:2012年8月3日
6 L U' h- w2 [+ @" E - % T3 _, z2 c; O
- *修改日期 版本号 修改人 修改内容
& t$ O/ ^1 Q1 \0 M" K
& M& H0 h3 O4 N- *-----------------------------------------------------------------
/ I! R' P1 j; A; n5 h5 f- u
' T _( i0 w+ ~# C- ]+ n& i- * c1 X3 U' R( Y; s, ^! W
- ! }0 H: @0 z$ O2 Q6 N. ^) s
- ******************************************************************/
+ C2 g# M5 d+ r1 J4 F; W8 V
5 m7 U; g% O% ~4 R9 @2 q: s+ i- 1 n3 \2 U. N3 e
: `: H3 K4 X$ _' m- void TimingDelay_Decrement(void) * U; T/ ~8 F ^4 C8 ~
9 f# k g0 E+ D- { 5 |2 H- f2 t( b3 X' ]
- 0 g& W7 q: y6 a! C; A- a/ x% \ D* e
* u; W" S4 r6 a* \$ M
8 e% @& i8 H# U% O3 w9 q) Z: E; Q5 j- if (TimingDelay != 0x00) 7 x8 A3 n+ p; Z/ A" f5 L
- 9 Y, d2 W! g t* f/ ]4 m
- {
, \# H( _: P( |
0 I/ S: b0 {3 _0 D- `+ ]- TimingDelay--;
: h6 y2 y' s/ k2 {, A
: } S0 u/ i% m( q- }
3 F/ w @0 b& D9 O/ G& e - 9 g9 Q3 I) ]4 a# s
- } 6 v6 C( B3 H4 n% B6 \/ r
u' S0 t3 ^1 D2 t- 我们看了TimingDelay的定义,又看了还有哪些函数调用到这个变量,如下:
" \% w3 N# b4 J
8 Z; ?/ O1 Z7 J9 s* O6 @- /*****************************************************************
0 ] a$ o/ n- H& b# R# Y
" N6 c0 c1 s0 p, ^4 u2 b- F! K2 Q- * 全局变量
5 y6 D+ N; G9 z% ~: ~ - : M8 H2 Y: O1 H) @
- ******************************************************************/
" M3 u& `) D' n) D0 g2 M
u. V3 [- E C+ B& u0 s3 ~- R7 @1 K3 G# }! \
# O/ f2 q8 T2 o1 ?+ {- static __IO uint32_t TimingDelay=0;& d& u* K* y. p& A9 J$ j
7 c3 W/ G, s0 R/ u* }# b- 4 y3 R4 n. l/ x9 N3 U$ {& ?
4 }# m; O) t+ x( ?# Y! T9 W- /*****************************************************************
0 {& l8 A! @3 H2 ?, _+ y: d - - y _" b: Z$ _* Q
- *函数名称: Delay
: \% }8 l! ~4 @ ^! _7 j( }' k
: d" D+ P' ]/ E- z( O& m8 d7 }4 k- *功能描述: 利用系统时钟计数器递减达到延时功能
; _8 |+ T( ]# ]7 L5 j$ R o - 5 ^: a, `$ j' T7 `$ ^8 ?! I i
- * . D+ F7 O. |) m
- / P$ E2 ?$ {5 K( o" U: u
- *输入参数:nTime :需要延的时毫秒数
2 w8 q, ?- G/ | r7 `2 ^2 X" p - 7 `, X( B9 q9 w* f$ \6 Y0 _
- *返回值:无1 S# K8 S1 g/ d8 G7 `! p- X$ ~( q1 }+ `
. z8 L0 X- G+ p- r. f) _- *其他说明:无
+ k- [' F% q9 @; P c - & }+ Y; s0 r; f" N- i9 x, u
- *当前版本:v1.0
1 `( c+ T) ?# ?; H" G0 {. P( @
g4 O' t* q8 w- u$ ^$ w- *作 者: 梁尹宣
9 p+ @0 N! M( k' R* C
+ l; K/ ~& h/ o: L* Q" U7 u- W- *完成日期:2012年8月3日& U- c" L% g( r1 \1 b2 c# U: t E
- * N$ u% I- Q* R( X# q% M
- *修改日期 版本号 修改人 修改内容; s; b2 @" m% M3 O
- ) t1 Y1 {5 [8 z; ^8 ~
- *-----------------------------------------------------------------+ |5 D$ k6 f! S
& U( F3 V$ ?8 d, V- *7 `- N5 [% B6 J- S. x
0 t6 `" e2 ?$ S, w8 A1 [6 A- ******************************************************************/% @0 u1 e2 ~$ G& B( W
$ F, ^ N$ B. k1 k" i" d) G5 G
* G& c9 @7 a8 a) Q o8 ]8 E
( _/ V6 C- c" a2 l- void Delay(__IO uint32_t nTime)//delay被调用时,nTime=500
9 ` O8 N& f7 i# e. Q - : q6 k% b7 @+ ?& |# f
- { 2 d7 A( o+ h; J( h, f% ]
?7 m) ]9 h/ X: l. H) g6 _- TimingDelay = nTime;
( X3 X5 z* y" i% D: ~1 K9 K( w5 V - & n+ y4 s; G5 S4 h- I
- " [0 X1 ?& b- ?
- 1 f# E0 p G8 I. F, k3 ]
- while(TimingDelay != 0);
+ k: }# Q% Y' \ - 1 ~, ?& Q& U- U/ T& A, b
- }" w b& I5 H7 y, Q
复制代码
( K2 x E7 u, J8 f通过上面几个函数我们知道了,在调用Delay(500)即nTime=500;在后在Delay()函数中TimingDelay =nTime;(即TimingDelay=500是它的初始值),再TimingDelay_Decrement(void)函数的作用就是把TimingDelay- -;每毫秒进行递减直到减到0为止;这样就起到一个延时的作用;7 I7 f+ `5 F. X/ p9 e" g" d
; x& V- l. n" Y+ {现在我们做出来的Delay(1),就是1毫秒延迟。Delay(1000)就是1秒。
: u. c# U; P1 i1 x2 b4 Q- q; V
$ x* h. ~2 N) ^5 | 我们来画个图,方便这几个函数间关系的理解:
2 |9 |8 h' r# V$ u7 B. t! X z q) ]3 `7 I1 m8 r
. {# V/ ]6 K( J4 J: [( o, U; B) w& {* S, \ N7 F
我们在返回到主函数main()中看这几条语句:红色标注de
* H2 y! a8 t% _3 t7 m J" @
# t0 F$ T; M8 z* E- while (1)0 ~6 S& l! l7 A' g
2 o& f5 w1 ` G* i& h1 u7 v" U0 z- {
0 \- t+ q. A2 e) k# Q) L7 [" e
) `4 e$ F; P: y# W9 L3 _* [- //测试代码:测试定时器功能,通过延时来测试6 \. R5 A9 Z, ~7 y/ ? w9 p( G
* M' S2 C6 B7 J/ h D- GPIO_SetBits(GPIOC, GPIO_Pin_6); //V6 4 ~3 J, n/ k- g D! A0 D
# c2 x; H! u7 o2 t5 {- Delay(500);- d+ b1 R {$ w/ @& D( [( }
- # L2 P4 Q- r( k. m) M4 Z2 X
- GPIO_ResetBits(GPIOC, GPIO_Pin_6); //V6 . w2 T* L+ |5 M
& p/ i5 i; S& v) e- Delay(500);
3 O* F/ M' ^4 H) x8 N- M - 3 I* i, X1 X& r. m
" d+ u* _' ^# }8 N; P2 }- ' k& B' l) M7 I* }4 ]% S
- //功能1代码:每500ms发送数据
% C! U7 e6 T2 `; [$ M - * C" F, u+ `6 }2 h' m% e) \3 _/ J; _
- /*/ y0 O, n* ^$ @1 z/ v$ L3 a6 [
- + `, {8 _1 O3 F$ e' ]
- UART2_TX485_Puts("123450");
6 Z1 Y6 F/ |/ ]) E* L - $ _' `% A9 \# o6 `
- Delay(500);
0 @, {) b& v$ [+ J1 ^3 R
- u; v& I* h$ k- */# C+ l2 Y" C) f4 f" P+ ^0 b9 |
+ R* D1 O+ Y W7 S- //功能2代码:上位发特定指令,中位机执行相应操作
# `/ p- w* X& P& R+ ~ - / V ^3 X. Q$ v/ P& E) ^
- // RS485_Test();4 i# ?8 G$ @7 P. B/ e+ O# J
- 1 {! U! r0 _6 D5 z8 A, a
- }
复制代码 / A k5 m7 x6 x. n7 c$ @ X8 }
经过上面系统定时器的分析我们知道Delay(500);是延时500ms ;那么LED就是每隔500ms闪烁一次;5 a! Q5 ^& y4 j* c0 [- t
0 L5 B7 w( l9 ^ W1 P3 S
上面有关系统滴答定时器的应用讲解基本完毕!
: J. d5 u( \& k* Y
, t, e. [/ o7 u( y 有关SysTick编译后的源代码包,(其实客官细心的话一经发现上面代码含有485通讯代码,! M8 o/ @& ]& f2 Q( |/ p1 J' H
( H8 B& _) e9 }2 Z2 g. b6 ^( a D! N
下面我们来看看一下参考资料的问题,一边对上面我写的博客有更深入的理解:
6 y0 s. t2 R2 L E% n2 |& H3 g
) o5 ]9 t, @! }, t' f《Cortex-M3权威指南》
1 U/ P4 f/ |. ?' U& N; J
* I. A; r# Y2 e. K5 |; `7 C《Cortex-M3 Technical Reference Manual》+ T/ K6 z0 G7 e) K3 G+ n8 s" l# ~
' a+ L. q+ U3 L/ [2 v2 n+ l+ tQ:什么是SYSTick定时器?
- u: T' |9 g0 i
4 P6 E9 x3 S- P0 Z9 fSysTick 是一个24位的倒计数定时器,当计到0时,将从RELOAD寄存器中自动重装载定时初值。只要不把它在SysTick控制及状态寄存器中的使能位清除,就永不停息。
. \; c' h( D* o" Y$ ^( @% {2 X; @5 C, X2 n; w( D
Q:为什么要设置SysTick定时器?
, G5 y. o! e- }6 D9 D8 ^( x$ M$ J
, p/ E9 B, U. Y. \9 X& T( e(1)产生操作系统的时钟节拍
6 @' ?, l7 g, T2 P; A, q3 d+ E2 @6 M8 L9 Y1 g- O" Z; V4 G! j8 y
SysTick定时器被捆绑在NVIC中,用于产生SYSTICK异常(异常号:15)。在以前,大多操作系统需要一个硬件定时器来产生操作系统需要的滴答中断,作为整个系统的时基。因此,需要一个定时器来产生周期性的中断,而且最好还让用户程序不能随意访问它的寄存器,以维持操作系统“心跳”的节律。
- Q3 Q9 R. H& e. X! d+ i; J% c- c
% z; a7 f' c4 ~5 Q; s, E(2)便于不同处理器之间程序移植。
8 d! F* i* |( [0 l) _ o: U
+ e. l+ t- A7 h2 a4 a' qCortex‐M3处理器内部包含了一个简单的定时器。因为所有的CM3芯片都带有这个定时器,软件在不同 CM3器件间的移植工作得以化简。该定时器的时钟源可以是内部时钟(FCLK,CM3上的自由运行时钟),或者是外部时钟( CM3处理器上的STCLK信号)。/ S" C$ ]% q; u0 l; J
i( L7 |! d* _4 c
不过,STCLK的具体来源则由芯片设计者决定,因此不同产品之间的时钟频率可能会大不相同,你需要检视芯片的器件手册来决定选择什么作为时钟源。SysTick定时器能产生中断,CM3为它专门开出一个异常类型,并且在向量表中有它的一席之地。它使操作系统和其它系统软件在CM3器件间的移植变得简单多了,因为在所有CM3产品间对其处理都是相同的。
% m( `2 w: g' f0 r7 W; S8 o$ n& R, s' [. j% s
(3)作为一个闹铃测量时间。
8 a' {+ h/ W7 f7 Y7 R- r" C* ^1 ]3 Z7 u4 u4 j
SysTick定时器除了能服务于操作系统之外,还能用于其它目的:如作为一个闹铃,用于测量时间等。要注意的是,当处理器在调试期间被喊停(halt)时,则SysTick定时器亦将暂停运作。# L7 i& \+ ]1 `7 x2 Y! _
* |% y, n& w5 b4 b' N
Q:Systick如何运行? l) ~: G0 A- e7 F' {, ~ o
; w5 N2 \& X8 q5 f( Z
首先设置计数器时钟源,CTRL->CLKSOURCE(控制寄存器)。设置重载值(RELOAD寄存器),清空计数寄存器VAL(就是下图的CURRENT)。置CTRL->ENABLE位开始计时。7 A0 r4 d: n8 N% Q f
4 S5 ], A# c) F' j) }如果是中断则允许Systick中断,在中断例程中处理。如采用查询模式则不断读取控制寄存器的COUNTFLAG标志位,判断是否计时至零。或者采取下列一种方法
; F, T4 U( @. m w" B Z
0 J T" F" y* P/ _: N* D* u z当SysTick定时器从1计到0时,它将把COUNTFLAG位置位;而下述方法可以清零之:+ e; Z1 j. x% z( \' r K6 ~* t7 r7 E
: O, @- g' }1 f4 D# h) T# I
1. 读取SysTick控制及状态寄存器(STCSR)
5 w+ l. @6 U7 a
% l0 {9 ~. h7 L+ m2 \8 @! z( r2. 往SysTick当前值寄存器(STCVR)中写任何数据
. r' t9 C, ]% @9 p; }/ w, a& V/ Y! c* D
只有当VAL值为0时,计数器自动重载RELOAD。
) i" H4 l, E: Y/ y0 K) S
! ?5 p+ ~/ ]5 a* ?Q:如何使用SysTicks作为系统时钟?
' U) O2 k9 T# \
: N- c% K$ W' h+ G( PSysTick 的最大使命,就是定期地产生异常请求,作为系统的时基。OS都需要这种“滴答”来推动任务和时间的管理。如欲使能SysTick异常,则把STCSR.TICKINT置位。另外,如果向量表被重定位到SRAM中,还需要为SysTick异常建立向量,提供其服务例程的入口地址。: `) N: @6 E% |
8 ]7 M7 T8 T* L$ E6 L6 s: x/ q
V" c5 A. U7 @# x
|