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