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