01. SysTick定时器概述
% ^ F8 U4 R4 V7 l9 ySysTick定时器被捆绑在NVIC中,用于产生SYSTICK异常(异常号:15)。在以前,大多操作系统需要一个硬件定时器来产生操作系统需要的滴答中断,作为整个系统的时基。例如,为多个任务许以不同数目的时间片,确保没有一个任务能霸占系统;或者把每个定时器周期的某个时间范围赐予特定的任务等,还有操作系统提供的各种定时功能,都与这个滴答定时器有关。因此,需要一个定时器来产生周期性的中断,而且最好还让用户程序不能随意访问它的寄存器,以维持操作系统“心跳”的节律。
" T [$ w# T7 Y$ D
% ~7 x O/ h! L0 j) w2 j4 \) vSystick定时器就是系统滴答定时器,一个24 位的倒计数定时器,计到0 时,将从RELOAD 寄存器中自动重装载定时初值。只要不把它在SysTick 控制及状态寄存器中的使能位清除,就永不停息,即使在睡眠模式下也能工作。
6 E& z. s7 a. e% q
6 i% t4 l+ D* h: n, h. y- C& k3 U4 ^02. SysTick定时器寄存器描述
@' X7 N* E4 R$ fSysTick控制及状态寄存器(地址:0xE000_E010) k* f6 G1 o: H& }' c9 s
8 _6 F+ l( Q3 m/ R
2 Q \ G; C S; s, K, Q; j+ b8 G0 b0 D, m0 m0 Q
SysTick重装载数值寄存器(地址:0xE000_E014)* s$ s% _/ s1 ~$ f4 j
3 H" [* X8 x" Z
a* c6 L/ L" _! y( N. k9 M% ]0 j3 [) R2 I1 G
SysTick当前数值寄存器(地址:0xE000_E018)
; F, `! r+ f$ |/ n1 s. u8 j$ K& _5 a- \3 E
5 D# G2 f% X) G9 L( X& ]
' N- I4 c a' m
SysTick校准数值寄存器(地址:0xE000_E01C). B, z) ]' a& S5 m( B& b: y
; B8 [" t, J: A% F/ V; }5 _/ F
; l1 G# t. p, N, p1 y1 {# F& G
7 k/ k; q* f) Y) }9 M: Z. C- v03. 相关函数实现+ }8 B" M( @1 Y# S5 p' z
core_cm4.h文件
) N- X6 U* f9 n% A( z$ ^8 X- r0 h5 m* \. o
- /** \brief System Tick Configuration
) m( \3 y J6 r: a- G, L) {' f
: r- Q5 h4 K9 Q- u' C- The function initializes the System Timer and its interrupt, and starts the System Tick Timer.
9 Z* G- y. X" S( Q) K - Counter is in free running mode to generate periodic interrupts.( `8 y$ o' P8 w! I0 a# {8 h
# [1 a" S/ y% E4 N7 W0 q! h( u- \param [in] ticks Number of ticks between two interrupts.$ u, W% E2 K% s) C
- 4 M. v* F3 K+ s' |9 H/ o
- \return 0 Function succeeded., X0 n- L8 e) ~; H: b/ v
- \return 1 Function failed.! m$ j" o5 ]" t% e5 |+ O7 q
- - s9 s- p1 k! C; j
- \note When the variable <b>__Vendor_SysTickConfig</b> is set to 1, then the
& m6 e7 U1 A9 f& S - function <b>SysTick_Config</b> is not included. In this case, the file <b><i>device</i>.h</b> l) s6 X9 J4 [
- must contain a vendor-specific implementation of this function.
% ]. }& `- d3 q7 h) `% K1 B) X - 4 Q T% M+ v9 f k
- */4 f: I( C! v4 p$ n
- __STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)8 o/ `( c1 w$ ?- U
- {
2 S U4 ] p2 r+ @2 L' u - if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) { return (1UL); } /* Reload value impossible */
) ]. K, ]/ D; D1 K
% c% U. Y' j8 u3 N- SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */$ r+ \' }$ w4 |% ?
- NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */5 i3 K! W' x% }7 m
- SysTick->VAL = 0UL; /* Load the SysTick Counter Value */9 f% \& e7 q0 p5 ~
- SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |# W8 K+ L7 B F) Z' P( @9 p
- SysTick_CTRL_TICKINT_Msk |4 h) g+ r3 U7 t0 G6 O9 w
- SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
; o- {# m+ A7 ^ - return (0UL); /* Function successful */5 v% V) j. _$ P3 K; E5 I+ P M
- }
复制代码 ' M" F% n# V5 e" N6 _ f# w
misc.c文件 C$ w3 O' h6 b' b0 L
- /**
7 H- F7 C! z/ Q - * @brief Configures the SysTick clock source.3 L3 e" l. i1 i% H8 V, w4 O" e5 X
- * @param SysTick_CLKSource: specifies the SysTick clock source.
p& t' R: r! L$ ~ - * This parameter can be one of the following values: r5 E; _# @3 c/ A: u
- * @arg SysTick_CLKSource_HCLK_Div8: AHB clock divided by 8 selected as SysTick clock source." G, O) I3 Y9 j6 C8 _; E
- * @arg SysTick_CLKSource_HCLK: AHB clock selected as SysTick clock source./ w# n c6 @/ ^
- * @retval None
6 C4 X6 S5 t& q, w5 [ - */
5 k7 i! X3 u. j W& x3 f% X - void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource)
3 Q( A% c' o8 [% U - {' Z w( Y( O/ D' j
- /* Check the parameters */
' Y; s4 c0 E9 @+ w6 A - assert_param(IS_SYSTICK_CLK_SOURCE(SysTick_CLKSource));1 H: D0 H" Z4 I7 n
- if (SysTick_CLKSource == SysTick_CLKSource_HCLK)# C: s$ |5 H7 }/ n0 x' M
- {& u' {) t* a5 }! n7 x
- SysTick->CTRL |= SysTick_CLKSource_HCLK;
8 D9 d$ \: m' \" v8 ?$ v- j3 P - }
& Q/ [, R c( f C - else
0 S9 `/ b2 b" e( y% {* W f; A - {
2 n$ p0 x' q6 o& P5 S) [ - SysTick->CTRL &= SysTick_CLKSource_HCLK_Div8;
2 @$ u: _2 Q P- \: c1 M# G - }3 {5 x- J. z4 y+ m" z* D
- }2 H4 ?6 F1 }3 |& m1 L" ]+ P
复制代码 ( k2 ^) B5 f2 s
04. 延时函数实现
: {5 H# ]/ P% G7 |( Z( Tdelay.h文件
9 |. `4 m6 j9 O$ l$ p
4 ~. Y0 x3 W6 u5 ^1 r! `* A# L- #ifndef __DELAY_H
3 x" `, J& D/ _4 Q& U! R- {/ a' R - #define __DELAY_H
. y& E H$ t: G1 [4 q& y" S - #include <sys.h>
3 g# ^ U& s! g) O$ [) @3 } - 8 @5 Y/ [& W6 x( e/ m6 p* R8 }6 N( s
- void delay_init(u8 SYSCLK);! P, _( i, q2 i4 H. K
- void delay_ms(u16 nms);
* j: P3 C' C! W5 x' x/ | - void delay_us(u32 nus);! Z% ~+ B, p! _9 k c4 V
- + _; [: l% z6 m# ?: K9 D' X
- #endif
复制代码
: I( n* A/ J- pdelay.c文件0 S1 H3 A: Z# ~7 m) r
I% f1 s8 E- V( c" y: p7 F- static u8 fac_us=0; //us延时倍乘数
( \/ Q! ]$ {5 k, Y* R8 n6 i3 l - static u16 fac_ms=0; //ms延时倍乘数,在os下,代表每个节拍的ms数+ q# Q7 O- v5 ^- P
8 |8 t, O" W8 Q: ~- //初始化延迟函数
/ B. r) u) y: [- y# s - //当使用OS的时候,此函数会初始化OS的时钟节拍
$ K8 ^( z/ u+ M+ h6 H - //SYSTICK的时钟固定为AHB时钟的1/8
6 L- c$ |" k6 p" D - //SYSCLK:系统时钟频率- {1 x9 y& s! J, B. T" \
- void delay_init(u8 SYSCLK) P' ]; s$ K; c+ x& z8 o
- {% r4 m3 B! X; }0 W
, ~9 ~" a2 I8 T# u4 L- SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
6 J7 S$ O l1 A4 H$ b5 Q+ y4 k0 R5 a - fac_us=SYSCLK/8; //不论是否使用OS,fac_us都需要使用
+ Q# F! K( X# J& m5 s3 I& m* m - 3 p9 j$ A: z% p4 Z- h2 w4 Y3 F+ W1 H1 q
- fac_ms=(u16)fac_us*1000; //非OS下,代表每个ms需要的systick时钟数
1 Q5 C8 \/ r& M3 v2 t7 d! e# ` - } / Q. V( x6 Z E4 x8 H4 l. J
- 1 C7 k# t* R2 ]6 Y
- //延时nus
* n4 ]' C+ L Y! | - //nus为要延时的us数.
# t% B! V- q( @ d - //注意:nus的值,不要大于798915us(最大值即2^24/fac_us@fac_us=21)! ~3 V0 H) ^0 b9 n, O! |8 c
- void delay_us(u32 nus) |, k% E I. m
- {
$ h8 [3 n" ]+ J - u32 temp;
& o: d# C1 {/ f2 O- f2 C* T0 z7 h& X - SysTick->LOAD=nus*fac_us; //时间加载 $ Q' S4 k5 h: `+ A1 H e+ i
- SysTick->VAL=0x00; //清空计数器+ x- F, X8 D7 e, O
- SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数 ( v; m$ w: l6 m" _3 q! `. C9 }3 n: u% T
- do6 d5 c3 g/ T; x# V/ n
- {
7 H2 t% ]5 F" A" t, m - temp=SysTick->CTRL;# V! i0 Y7 W( u6 ]& j0 e4 q, j
- }while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达 $ ~% C! `, X4 x% [7 Z3 i* B
- SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器7 G# C: \6 q$ ^, u
- SysTick->VAL =0X00; //清空计数器
+ M( U! P8 f8 }: G& [. q - }% c( }7 D4 {) Y' l: q
- * s1 z9 H8 L4 X* S. G
- //延时nms
& }, j" w- p0 h7 ~6 [ - //注意nms的范围
; E4 Q0 n$ z4 K7 z; O - //SysTick->LOAD为24位寄存器,所以,最大延时为:9 m% g# M5 q# ]2 F
- //nms<=0xffffff*8*1000/SYSCLK$ R9 W& b' `9 t, |! c
- //SYSCLK单位为Hz,nms单位为ms
% d- r) _3 z! b - //对168M条件下,nms<=798ms , h9 U0 h! {2 u4 i& S1 M8 L
- void delay_xms(u16 nms), n& L7 B% u6 O! B/ X6 M! \& ?
- { 0 x/ C0 H. W) R& d. n4 W4 ^
- u32 temp; - S6 ?' K! H2 v) \
- SysTick->LOAD=(u32)nms*fac_ms; //时间加载(SysTick->LOAD为24bit)
+ b$ x1 d! j; W: X& C2 W - SysTick->VAL =0x00; //清空计数器) k& U3 f: c& @6 \0 L
- SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数 + c8 w7 @; s- \0 z9 V0 `0 ~
- do9 @4 p3 R4 m8 _
- {% z% P- h! p+ m1 G6 O
- temp=SysTick->CTRL;
& F$ t; y4 T4 _/ Z0 _ - }while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达
, [' \% ~+ w/ g: S6 R; `/ ^- h - SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器) |5 N: D7 C ^1 W9 W$ o. T) Z
- SysTick->VAL =0X00; //清空计数器 4 o L' G- D3 I2 B' x% }+ q
- }
( G* |. r2 {+ t5 L: @ - 0 D, u6 c0 l6 L( a$ b) [# [: [
- //延时nms 3 H4 l1 J( I5 y/ Y1 r- r
- //nms:0~65535! T% }- e# \' D
- void delay_ms(u16 nms)
. _- S5 C: i1 v2 g8 M* h6 J - {
% Q. K I' T9 j - u8 repeat=nms/540; //这里用540,是考虑到某些客户可能超频使用,
3 h8 \0 z- X* A" {5 A2 q8 { - //比如超频到248M的时候,delay_xms最大只能延时541ms左右了# L) T+ u: f h7 w3 N
- u16 remain=nms%540;
" Y& V3 M8 o7 z0 _0 h/ R - while(repeat)
- j" ]) |+ g. v+ E - {
! K' y/ O; X" [8 e - delay_xms(540);0 O' i$ i: G7 k+ P% Q( }4 Z( M5 R
- repeat--;
) e0 g( ^' c7 B% Z! {* n - }
; X: |$ x0 Q* z/ _& X - if(remain)delay_xms(remain);
1 ?; \& y; t; J - }
. V6 s; @ ^, U: g: D) W
复制代码 7 k( h5 l& \; f' J
05. 应用示例
/ Q# q5 S) b* I用中断的方式实现delay延时
6 M% r( |2 U1 d/ Q" P! ]5 ?, R8 r1 D
- static __IO uint32_t TimingDelay;
/ b: c, [; F: o$ c - void Delay(__IO uint32_t nTime)4 m4 U7 k) ]# Y4 D
- {
; q1 U& B: s" l7 R - TimingDelay = nTime;
1 K$ r0 J5 ^/ w& a0 ] - while(TimingDelay != 0);
- h2 }/ j0 B4 ^% n& z( M6 G- f! R - }+ x1 m+ W4 f- k4 h
- void SysTick_Handler(void)* [3 V/ g' f) F ]' s( d! G) C4 O
- { e1 G$ V+ T- L) J$ Y0 z! `
- if (TimingDelay != 0x00)
! A h( p2 P; y6 k1 |5 U0 N$ i - { ( Y/ F2 r; u4 S- y, J3 X
- TimingDelay--;
" I/ I+ J1 m7 ~ }+ w/ p, S - }3 i5 g. f/ o4 u" o( N/ b' ^' D! J
- }" f% `/ Q: w- o7 G, G( m! |, Q3 H
- int main(void)5 P# `, C% } z' q0 T2 p3 _2 N0 |
- { …7 d# D7 Y* x+ V9 w+ H! s# x
- if (SysTick_Config(SystemCoreClock / 1000)) //systick时钟为HCLK,中断时间间隔1ms
4 w5 b. y5 b# z - {
2 {1 y0 W9 T. K- J1 I+ M% F1 V - while (1);
* ^8 v" u' T4 |9 y9 v - }0 \8 A& N7 `1 h6 z
- while(1)
" P. F' p7 q. l7 F4 g' j+ P - { Delay(200);//2ms8 u S! G6 x9 u! k
- … $ l c% L( \1 G" h' _
- }
" }1 {" ]9 u$ R* c; K - }
复制代码
; _5 K, `; s, a4 O, D |