01. SysTick定时器概述
5 ?3 Z" j3 ?" Q2 c& H/ fSysTick定时器被捆绑在NVIC中,用于产生SYSTICK异常(异常号:15)。在以前,大多操作系统需要一个硬件定时器来产生操作系统需要的滴答中断,作为整个系统的时基。例如,为多个任务许以不同数目的时间片,确保没有一个任务能霸占系统;或者把每个定时器周期的某个时间范围赐予特定的任务等,还有操作系统提供的各种定时功能,都与这个滴答定时器有关。因此,需要一个定时器来产生周期性的中断,而且最好还让用户程序不能随意访问它的寄存器,以维持操作系统“心跳”的节律。
% b9 f8 y5 Z" P& Y6 k9 @; u( \# B) w
Systick定时器就是系统滴答定时器,一个24 位的倒计数定时器,计到0 时,将从RELOAD 寄存器中自动重装载定时初值。只要不把它在SysTick 控制及状态寄存器中的使能位清除,就永不停息,即使在睡眠模式下也能工作。. U. t# |$ D, [$ n9 h4 I& U
. E) ^/ N+ P* a" T. }. @/ Q+ n! _02. SysTick定时器寄存器描述0 l& O7 K! Y- {' K6 c* y! d; Z
SysTick控制及状态寄存器(地址:0xE000_E010)5 X; j/ m" w9 w
6 u( ~! p7 L1 Z, z" {: b) n
5 u5 N* y: f( x3 k6 A
: R; L$ _! r6 ?SysTick重装载数值寄存器(地址:0xE000_E014)4 {2 N/ ] ]" a! d; k; a
9 A9 P& j, z% q6 ]1 I
: J2 ^4 a3 {) U0 a: c" g
3 v% z9 L$ Z8 X) I3 d, F: a5 Y5 hSysTick当前数值寄存器(地址:0xE000_E018)
( ?1 Z9 \- p6 v" ` \1 |
9 z7 d/ q. w' `8 V3 E9 f* W; L' q
" [$ M0 z* D+ A- q0 J1 m5 p, }
3 Q1 g; j+ U `% }- _
SysTick校准数值寄存器(地址:0xE000_E01C)
/ g2 W( D* `+ B# j3 a* C8 `2 y! |8 @7 }( T
7 g* |$ L4 s- A# d8 m; j
6 V" P6 a! h3 c: o, ~+ L# j6 v- c2 `
03. 相关函数实现
. `" t: ~4 l- f. F5 j. Jcore_cm4.h文件5 M6 l% j3 W3 Q5 o- O
7 G8 g2 \0 C2 I9 u Y
- /** \brief System Tick Configuration/ N. { k( w( A
$ l- x3 g1 N1 Y) B- The function initializes the System Timer and its interrupt, and starts the System Tick Timer.
& E# p5 ]8 V# A2 r, I1 d. A - Counter is in free running mode to generate periodic interrupts.
& n j( V, @* ^7 G
1 ]7 W2 Q; t# I! K( }- \param [in] ticks Number of ticks between two interrupts./ y4 c- r. O% ~; Z
) Q. o, g; ]0 q ]; |" s- \return 0 Function succeeded.9 b8 y" Z X: X5 w
- \return 1 Function failed.
/ m) V+ I0 i& f7 p9 u
Z' _: q$ g# _4 _. S- \note When the variable <b>__Vendor_SysTickConfig</b> is set to 1, then the
- d5 P" T- x' l! k - function <b>SysTick_Config</b> is not included. In this case, the file <b><i>device</i>.h</b>+ T# N# E- c9 a. {5 L# C* v
- must contain a vendor-specific implementation of this function.) w5 ~, O9 k9 p. B: c) j6 S& G
: u. H4 s" H( O" k# E- */
6 n% ?. o- E1 \! E% Z - __STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)) e3 }5 R$ Y5 M, q% R
- {
1 A' f% Q! m8 A0 [% i - if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) { return (1UL); } /* Reload value impossible */
( d) A, o2 [0 _2 }; K+ y7 e" F2 g - & b' |- N( Q" A
- SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */
, U3 Y: K# l: M" \8 m& s! g7 t9 L - NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */
, y) \3 l7 Y% _4 Z - SysTick->VAL = 0UL; /* Load the SysTick Counter Value */
5 M0 ?( u* v, q% L( [' _ - SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | t. r' R) L, T# h" Y
- SysTick_CTRL_TICKINT_Msk |. ?$ N/ y- J/ f6 I+ A
- SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */& ~& N9 \* i5 Z% F5 b
- return (0UL); /* Function successful */
! R9 j9 _, A) a- m7 p: f; P - }
复制代码 4 P8 [$ b& W9 Q3 G
misc.c文件' @5 U; B% u% L% n2 H4 b$ u) [
- /**
- X. T0 a+ r6 y6 j - * @brief Configures the SysTick clock source.
% y' s1 I3 e1 c' g' n - * @param SysTick_CLKSource: specifies the SysTick clock source.; }/ s, V* x4 A
- * This parameter can be one of the following values:
/ x' ]' w" x+ x# C; ]5 N- T - * @arg SysTick_CLKSource_HCLK_Div8: AHB clock divided by 8 selected as SysTick clock source.: q, A: ?. ~. T# J5 H
- * @arg SysTick_CLKSource_HCLK: AHB clock selected as SysTick clock source.# v2 ]6 n0 w( b7 P
- * @retval None
% e' `; z! l- E/ O0 c6 y - */
C z( b( h, l/ ?0 W/ s - void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource)
2 B+ A a) a1 L2 l7 ?, Q K) L - {9 ]9 T3 y: c' @
- /* Check the parameters */7 s/ M( O, q( w) y7 {. `. }; F1 g" E
- assert_param(IS_SYSTICK_CLK_SOURCE(SysTick_CLKSource));
" s8 I' F3 l0 Y7 z8 A+ y5 {5 h, X - if (SysTick_CLKSource == SysTick_CLKSource_HCLK)
* @8 D' z& g c - {
4 J; M% \! ]0 X1 R8 O7 t4 P2 a3 A - SysTick->CTRL |= SysTick_CLKSource_HCLK;
, x4 p6 ^+ b! `; Y6 \ - }/ U/ Q+ j; g; t8 _$ U$ p. B+ X
- else
% w- e7 P" Y$ E - {
9 P9 k$ u3 |, |6 |- b+ J - SysTick->CTRL &= SysTick_CLKSource_HCLK_Div8;
4 A- c+ R& F, R2 u5 a3 A - }" W" _. i& x- s7 |- Q: I4 }$ G
- }5 o5 C( c( S. o1 \/ I2 }% l/ k
复制代码
- y5 R+ e: j" R @7 K9 h04. 延时函数实现 o! u! P4 a8 P$ d' s
delay.h文件' q7 T2 E' G% q4 i4 [6 h
( N, y) z' e$ A+ n! I
- #ifndef __DELAY_H
, A1 d" n. i5 _+ Q2 c2 N* t ` - #define __DELAY_H : o: u6 I( H0 J. b; j* t" m
- #include <sys.h> % e, O, f3 s: O0 m4 P* U
, j) C0 Y+ ?6 C9 Y: I- void delay_init(u8 SYSCLK);
; h* t8 W2 W, X; m/ w - void delay_ms(u16 nms);8 i) R( d# P& t7 w7 K6 n, X
- void delay_us(u32 nus);
( e8 T1 z/ P- M
" \: K! } ~( Y s0 P8 m' z, @2 s- #endif
复制代码
" T3 q7 A- U- j; w h: Fdelay.c文件
+ ^ m F, }5 V, p$ s
8 \, P0 o( E S) r9 _# C1 k& ~- static u8 fac_us=0; //us延时倍乘数
! A0 r+ D; D! W - static u16 fac_ms=0; //ms延时倍乘数,在os下,代表每个节拍的ms数
I$ p! t* \7 z) L0 R& X" s% R( t - 2 T1 _ m$ ~ |% D3 L. @, u
- //初始化延迟函数
; ^6 b/ z& I2 N8 B3 } - //当使用OS的时候,此函数会初始化OS的时钟节拍
3 j6 }% v, b! x0 | - //SYSTICK的时钟固定为AHB时钟的1/8
" @- m1 {/ ~: I8 M2 v - //SYSCLK:系统时钟频率
8 J# v% D; A2 E8 W( X& x - void delay_init(u8 SYSCLK)
" H* T0 I" s( Q2 B/ C5 _ - {
m! e! k) O" D+ q$ g: W - $ p! v+ ?; h' B0 ]7 a
- SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
1 |6 I, [+ `; c5 V$ s - fac_us=SYSCLK/8; //不论是否使用OS,fac_us都需要使用
4 d9 |4 D0 a. u& L6 v$ l - ! m- n. W' h9 B! Z5 A% [( o
- fac_ms=(u16)fac_us*1000; //非OS下,代表每个ms需要的systick时钟数
4 a7 j( V- X; S - } " Y0 x% H7 ]5 c) _$ ~% o. ?
- 8 Y! d/ I- l" \6 }% ?" @
- //延时nus
* B! Q: q& L5 Z" n - //nus为要延时的us数.
) o9 [' f% v3 k* V1 J - //注意:nus的值,不要大于798915us(最大值即2^24/fac_us@fac_us=21)1 J8 o" X) i* @0 c6 z
- void delay_us(u32 nus), @: S! X* g0 B, j) U6 ?
- {
# y2 H2 U/ K, \ - u32 temp; ; M" L5 N" k; F% v
- SysTick->LOAD=nus*fac_us; //时间加载
y7 Y5 y! I* b; S - SysTick->VAL=0x00; //清空计数器# z* ]. } y. u5 H1 t6 X1 l
- SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数 ) a; W6 X4 e) m# S
- do
+ \' n' A1 f1 m# J$ M A. S i7 E - {, O6 c% L! G- v2 ?$ |( Z% u
- temp=SysTick->CTRL;
0 X. \! @; m9 M6 y& u5 g - }while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达
& p# a+ u/ b! }5 Y5 X# r - SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器
/ i5 h: }$ q e% i- ] - SysTick->VAL =0X00; //清空计数器 9 \" R' t: f, p- d5 B" l
- }7 y" g6 @ o$ ^, _4 g7 L4 \3 c
/ ? A C' i, L9 {4 i: v* X- //延时nms; K" k, v2 ~! Z. J9 z8 t4 F
- //注意nms的范围9 L# F9 l, H1 B' X7 ^. Q1 O
- //SysTick->LOAD为24位寄存器,所以,最大延时为: m( t; ~, C* U8 a8 u+ {
- //nms<=0xffffff*8*1000/SYSCLK$ z1 U) f4 n9 @; [9 R. m% ~, m. _
- //SYSCLK单位为Hz,nms单位为ms) Q, b: h4 Y" r/ [
- //对168M条件下,nms<=798ms 0 O, O+ a" E' @' E; `
- void delay_xms(u16 nms); A1 S; x& z& _
- { , j z# @( E" W K- D
- u32 temp;
1 x( j. `; |- y! W: Q" r" S - SysTick->LOAD=(u32)nms*fac_ms; //时间加载(SysTick->LOAD为24bit)8 z, v2 d: E+ W% l& a
- SysTick->VAL =0x00; //清空计数器
( x% z* f' k& E1 J* { - SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数
2 Y, u0 y5 } q' v4 U( H - do* ~ b* {) d* b! ]6 \
- {
- v4 K" d: P3 z$ y# S' L E - temp=SysTick->CTRL;' a0 |9 V5 |2 P9 T( X/ C- G
- }while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达
' f& g& Q- h, k6 v5 `9 q - SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器5 ?% h; w- Z3 w0 A! Z1 j1 d* a
- SysTick->VAL =0X00; //清空计数器
; B9 D$ x$ d4 f+ e w7 b, k, f& P - } 9 j3 H3 k6 N& v0 p$ F- t6 I; m& v
- ) Z* i- h/ q; Y8 b; q/ I J. K
- //延时nms / X# \- s1 |' g; I" b; f, A
- //nms:0~65535
) u3 a' L/ l/ ]9 J7 v5 ` - void delay_ms(u16 nms)/ U4 |1 X6 ?$ l0 ~/ N
- {
" R) |- b" {1 Z' q0 e - u8 repeat=nms/540; //这里用540,是考虑到某些客户可能超频使用,- c ?* ~! ^+ I2 f& b0 b( X# [* ?" @
- //比如超频到248M的时候,delay_xms最大只能延时541ms左右了- A! d& k) d- H1 q
- u16 remain=nms%540;
. _; Y7 U9 m* ~! B' O0 W - while(repeat)
3 p% X6 f1 x0 a: O! V - {
2 E. n6 F; j& A& h5 t! x% t' }9 M - delay_xms(540);
9 i3 A A2 ` o! k - repeat--;
1 g } H- G$ H9 J - }
% N, d9 P( T1 O; I- } - if(remain)delay_xms(remain);* N! J) j% h) w" i/ C3 p
- } ) J" [ P: f' y
复制代码 - ]! @' ]/ K( t3 q) _
05. 应用示例
4 F9 k1 k* K# C3 `4 K" @用中断的方式实现delay延时. t1 X6 {, ?8 r Z* K- [
5 T' n3 p, Y! {; y
- static __IO uint32_t TimingDelay;3 [* E% b2 r9 Z6 B L0 ~
- void Delay(__IO uint32_t nTime)
0 \( J! w: r- t3 k L6 P - {
; A7 c% r ?7 ?; a9 \; r - TimingDelay = nTime;
4 Q$ J9 p$ U5 K" }8 J - while(TimingDelay != 0);
( u. Z7 J, q9 ]2 \ - }. z+ Y& m( R; q& D
- void SysTick_Handler(void)
! P B$ ]; ]$ M! i) Y' N8 o - {: P6 w/ U' \- r* O2 |* q* l
- if (TimingDelay != 0x00)
( c; I2 q1 P J; G' U: k( K. M1 L - { ! c( E7 d7 M B8 A
- TimingDelay--;
; W7 P+ ^, R: p5 O7 Y - }
: F. J; G5 f! l# L9 j - }, L" H h' H! Z% `3 d# V
- int main(void)
' \' x+ l# k1 S/ h - { …8 t2 A& I) i" T; j8 G$ _% N
- if (SysTick_Config(SystemCoreClock / 1000)) //systick时钟为HCLK,中断时间间隔1ms3 V0 C) ^, a9 ]8 }+ z; I' b H
- {
* E* ?8 w; [2 H9 d a' s - while (1);
# _; r% g( u0 m$ Z8 b1 X, A - }
6 \+ ^, a5 T& @" G! l* w( Y0 } - while(1)) D4 Z1 k, G0 e( a+ s( i( h
- { Delay(200);//2ms) Y r7 N; z* ]. \% t, \
- … 0 X2 j9 S# ?. g- e
- }
0 t' N; T1 A( N3 J ~, r - }
复制代码 4 f5 B9 j! `; {" R& s# v* \
|