你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】stm32的systick原理与应用

[复制链接]
STMCU小助手 发布时间:2022-1-26 23:35
  1. /* SysTick滴答定时器
    & [/ k2 K/ s: e9 u
  2. 一、功能' h8 \/ N( p7 |- `1 e" G+ t
  3. SysTick定时器是一个简单的定时器,CM3\CM4内核芯片都具备此定时器。SysTick定时器常用来做延时,采用实时系统时则用来做系统时钟。无论用作延时还是用作系统心跳时钟,不需要太复杂的功能,SysTick即可胜任。
    1 H$ Q# v7 K: h7 p& `) i7 V7 ^/ Y* g$ w' X
  4. 二、实现原理# x5 d. D+ S- C! f$ D
  5. SysTick定时器是一个24位的倒计数,当倒计数为0时,将从RELOAD寄存器中取值作为定时器的初始值,同时可以选择在这个时候产生中断(异常号:15)。
    * R9 k; i6 L; S5 f- p, x7 ?
  6. 例如从RELOAD的值为999,那么当倒计数为0时,就会从复位为999继续倒计数。
    5 d% ~5 Q% v7 I, g4 ~0 u
  7.         只要不把它在SysTick控制及状态寄存器中的使能位清楚,就永不停息,即使在睡眠模式下也能继续工作。( Q% P% ^6 e5 f: _
  8. 三、SysTick寄存器(在 core_cm3.h 有定义,凡是 M3 内核的单片机都是一样的) */
    , n# u' B8 \( t
  9. #define SysTick             ((SysTick_Type *)       SysTick_BASE)
    : @+ ^1 D! P, k! O
  10. #define SysTick_BASE        (SCS_BASE +  0x0010)/ i0 F* k7 ^: K! R# n2 B2 D
  11. #define SCS_BASE            (0xE000E000)
    : v3 P0 c4 Y! \; h2 ~. ]2 g
  12. typedef struct  N+ K0 L. ?* N% B8 a
  13. {
    4 J& ]5 n+ j( J* H
  14.     __IO uint32_t CTRL;  // 控制及状态寄存器
    % Y: f/ a1 y/ E' K% A: Z$ R2 F
  15.     __IO uint32_t LOAD;   // 重装载数值寄存器
    3 Q" `9 O! ?4 g$ t. g& y
  16.     __IO uint32_t VAL; // 当前计数数值寄存器
    ' W4 z" l# x+ H; L, Q5 ]3 I- ~
  17.     __I  uint32_t CALIB;  // 校准寄存器4 L. L3 Q% N4 R  W: X$ x
  18. } SysTick_Type;
    ' `) K$ F; j' B, S; A) {6 _1 d% ?
  19. /*
    9 y# |0 ~* ^5 ?$ |. h
  20. SysTick->CTRL: (可通过 SysTick_CLKSourceConfig() 函数设置)
    + F' e0 ]$ M5 @2 W
  21. COUNTFLAG(16)R: 计数标志位
    9 D- P8 c1 q& T- G- J1 G, R' P* S
  22. 当SysTick数到0,则该位被硬件置 1,当读取该位时,将被硬件清零
    : O3 h/ H" w: G. U  q; `' k
  23. / A6 ^0 ?) C4 @2 j
  24. CLKSOURCE(2)R/W: 时钟源设置
    ) M* j/ Y. Z3 o7 [6 }2 J* W
  25. 1 = 外部时钟源(STCLK) (AHB总线时钟的1/8(HCLK/8))
    - t4 i* Y9 z6 ]2 q( S; p
  26. 0 = 内核时钟(FCLK)  (AHB总线时钟的频率(HCLK))
      \' n  r5 r$ e5 h+ \0 ^
  27. ! h0 A7 g" U  Y) `
  28. TICKINT(1)R/W: 中断使能位  x" c  t+ P0 v! O
  29. 1 = SysTick 倒数到0时产生 SysTick 异常请求, V0 y7 c4 r: G
  30. 0 = 数到 0 时无动作
    7 U" D: K6 q7 y1 Y

  31. * y: \$ E0 M0 @6 i3 `; _$ f
  32. ENABLE(0)R/W: SysTick 定时器使能位/ V: ?1 u. u4 J; W: a/ G
  33. (当中断被使能后,需要关注 void SysTick_Handler(void) 函数); ^, I* {' w+ K. V5 b
  34. SysTick_Type->LOAD: (SysTick_Config() 函数会设置该寄存器)% y! h# t( k  Z* W. l

  35. 4 s* X6 y) s5 [+ D% b4 X1 @
  36. RELOAD(23:0)R/W: 重装载数值寄存器- Q4 Z. O3 k: l" B
  37. 当SysTick数到0,将被重装载的值; ^0 k% ~8 F+ ], e: V7 `: r

  38. * o) R( {, Q0 m. M+ p9 P
  39. SysTick_Type->VAL: (SysTick_Config() 函数会设置该寄存器)7 u. c- j! r1 f  f6 y* ~1 H
  40. CURRENT(23:0)R/Wc: 当前计数数值寄存器
    ! q7 T: z7 K% _
  41. 读取时返回当前倒计数的值,写它则使之清零,同时还会清除在 SysTick 控制及状态寄存器中的 COUNTFLAG 标志。( @! h7 ]( s6 N# y; d$ w

  42. : u' v* Q) l) x6 i
  43. 四、库函数分析
    4 I- c' t3 }- O% ]( s9 r* n
  44. misc.c
    / d# ?6 m: P) o- p# C% `4 |
  45. ---------------------------------------------------------------------------------- */) }$ f( x, |, s: z: J+ }
  46. #define SysTick_CLKSource_HCLK_Div8    ((uint32_t)0xFFFFFFFB)
    9 W" Y  n) Y+ d+ F; [  t
  47. #define SysTick_CLKSource_HCLK         ((uint32_t)0x00000004)3 D8 E3 I9 {8 O9 p1 N6 n4 l8 l
  48. #define IS_SYSTICK_CLK_SOURCE(SOURCE) (((SOURCE) == SysTick_CLKSource_HCLK) || \
    . t; v4 z4 B7 t
  49. ((SOURCE) == SysTick_CLKSource_HCLK_Div8))
    0 ^% y( p  i$ Y
  50. void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource)
    ) N7 v% J9 V6 Z1 A+ O1 S
  51. {# s/ E7 H; f% J4 Y; p
  52.     /* Check the parameters */2 t' Y4 O5 d  T" A) y# l) k" z
  53.     assert_param(IS_SYSTICK_CLK_SOURCE(SysTick_CLKSource));
    # R4 B* ^* h: _6 r
  54.     if (SysTick_CLKSource == SysTick_CLKSource_HCLK)
    - B" Z% |! M; e0 F
  55.     {% h* r" l8 M5 P4 D, p3 J2 Q' k
  56.         SysTick->CTRL |= SysTick_CLKSource_HCLK; // 设置 CLKSOURCE 为 1. T# V8 H! |6 ?! M
  57.     }
    7 X7 }2 Y  v* e/ g8 l
  58.     else) ?& x) H) i2 D: m! C
  59.     {! P. S& U- V- @. l! ^! n
  60.         SysTick->CTRL &= SysTick_CLKSource_HCLK_Div8;      // 设置 CLKSOURCE 为 0
    + J# Y1 |" C" ^0 h
  61.     }
    - t7 B6 g9 J# ?* ~
  62. }1 ]3 C: R3 @$ y

  63. + L" n" P! W6 U6 U9 u7 V4 u
  64. core_cm3.c6 p9 j  Z& a8 Z9 }+ m* V; M
  65. ----------------------------------------------------------------------------------
    : b" n* v! ]' |" F! b6 m' _
  66. #define SysTick_LOAD_RELOAD_Pos             0
    4 \& o/ S* E2 Z( ~3 z) `
  67. #define SysTick_LOAD_RELOAD_Msk            (0xFFFFFFul << SysTick_LOAD_RELOAD_Pos)
    1 `% \3 H1 y' Z: W( y
  68. typedef enum IRQn6 [6 ^$ K  i8 p! q% l
  69. {6 U. |2 R. t6 I$ n2 p0 s$ F7 k: n& |7 b
  70. //...0 s/ g+ M, K) R( [3 S. v/ H4 p, d0 K
  71.     SysTick_IRQn                = -1,
    0 u9 H7 }% ?' N4 A8 z/ T
  72. //...
    9 ?! z* S) E7 ?( b( N% N$ [
  73. } IRQn_Type;) j+ M- D2 e7 O* }
  74. #define __NVIC_PRIO_BITS          4
    , R4 D4 Z5 i  R, C8 \% b
  75. #define SysTick_CTRL_CLKSOURCE_Pos          2- ~  d$ k" a( P9 E& ~- W
  76. #define SysTick_CTRL_CLKSOURCE_Msk         (1ul << SysTick_CTRL_CLKSOURCE_Pos)3 x5 n4 \) U8 `; q8 p+ M9 v8 e

  77. 1 W' l" z( R# q  g+ {6 }/ j
  78. #define SysTick_CTRL_TICKINT_Pos            1
    ' I; \' y8 ]% R4 [- ^5 O/ F0 P3 B. B
  79. #define SysTick_CTRL_TICKINT_Msk           (1ul << SysTick_CTRL_TICKINT_Pos)
    / K/ F. O3 H8 y* h

  80. 3 c; u( y8 Z( j( {
  81. #define SysTick_CTRL_ENABLE_Pos             0
    + E6 x" v  ]) c. e. }3 O# }' c
  82. #define SysTick_CTRL_ENABLE_Msk            (1ul << SysTick_CTRL_ENABLE_Pos)
    # }# n9 H: o- [8 n4 R4 v

  83. * r7 S3 l2 e+ j6 p; U' ?1 E" u
  84. static __INLINE uint32_t SysTick_Config(uint32_t ticks)
    % P  ^6 k- _& k! l% ]
  85. {
    ( a' J; o% ~: n- p
  86.     if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);            /* Reload value impossible */
    ; w  ?* b/ Z( X0 J% w( Q+ M7 @
  87.     // 设置计数值为 ticks - 1
    : s* L/ N& c& z: J! u5 c& v) s
  88.     // 原因1:视频说是执行这些代码需要时间,所以减少一个节拍( A8 }. t7 r- n% M7 `
  89.     // 原因2:我认为是因为 SysTick 的倒计数到 0,例如设置 1000 ,那么范围就应该是 999 ~ 0。
    ( H. P& v* f" [! H
  90.     SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;
    * P2 O/ G$ O3 [, U. U: S5 q4 R, T
  91.     // 设置中断优先级
    , N/ I- U) B7 S) H' v& |  r: Y/ A
  92.     NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);4 j4 z& Y# T- K/ P3 r/ Y& u# _
  93.     SysTick->VAL   = 0;
    8 n" G9 y- l" z) T: i
  94.     // 设置时钟源为外部时钟源,同时开启中断、并使能 SysTick 定时器$ G- A' \5 E) p8 C' n) k3 F
  95.     SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |. `) I. ~3 `: N" u! g
  96.                      SysTick_CTRL_TICKINT_Msk   |
    ; B' w! n5 b! S, V5 C+ \
  97.                      SysTick_CTRL_ENABLE_Msk;+ T( _+ v; D' p$ d) F
  98.     return (0);
    7 L8 }8 s3 P; |2 y* W7 f
  99. }; n6 c5 x) b; D' v. {4 V
  100. /* 五、延时应用+ s& E" ^4 e* J
  101. 1、中断方式 */
    5 L0 ~; M. x7 |* e5 C7 \, E+ k
  102. static __IO uint32_t TimingDelay;2 G7 `' h) Z. w. P* o+ ^
  103. void Delay(__IO uint32_t nTime)
    - T, h" ]- q7 G
  104. {3 V1 E5 q* d( m: u# E9 J3 B$ ]4 Q
  105.     TimingDelay = nTime;2 M* g+ ~; _& ?" K) ?
  106.     while(TimingDelay != 0);1 ]% L9 i& k! K1 T0 O4 k6 A( H
  107. }8 J; l9 J! j" a5 ^% i
  108. /* 中断服务函数 */! _' x  t, B; V4 L" p/ g' |2 o; E$ ]
  109. void SysTick_Handler(void): y' [9 {- x& q; E
  110. {
    - U. u; M2 v* V3 _
  111.     if (TimingDelay != 0x00)
    0 N5 j$ m0 C0 m  R
  112.     {1 c/ i% Q) U; x" |. k
  113.         TimingDelay--;( e1 D$ \3 t1 y; B( k+ l
  114.     }: m- Q& r! h4 i$ z
  115. }
    ( J) v" ?  A8 I. s& \' }
  116. int main(void)4 y' _" p7 ^% P, Z* c9 n; S
  117. {
    - h  [7 f& ^& J1 h1 r
  118. // ..." K2 ?8 R% l5 M1 ^
  119.     if (SysTick_Config(SystemCoreClock / 1000)) // 注意,这里systick时钟为HCLK,中断时间间隔1ms/ c; W. _8 x9 \! e* R
  120.     {
    7 ]& y" D: p* O4 A
  121.         while (1);
    4 D+ T( ]" f, D
  122.     }
    3 y% A6 `: e" e
  123.     while(1)8 h6 P$ U# d: ^' @5 r+ ]
  124.     {
    + O) M$ l! Z9 \, Z$ E
  125.         Delay(200);//2ms
    & k% ?6 G, p" A1 B; b% w% G" h0 @
  126. // ...
    ; j! C( V0 L/ g
  127.     }1 u4 T( c/ V4 H- }& y
  128. }
    5 q( ~0 w6 O! b
  129. /* SysTick_Config(SystemCoreClock / 1000): (原代码这里假设是采用时钟源为 HCLK)
    / k& a% J' G1 t: m5 y7 b2 ]3 ?
  130. 这里设置的是 72000000Hz / 1000 = 72000 ticks,也就是说 SysTick 从 (72000-1) 开始倒数。
    3 K. c9 _2 }- `& u
  131. 每倒数完 72000 个节拍就触发一次中断。8 r; }! h( @* m( l
  132. 一个节拍的时间为:72000000 / 72000 = 1000us == 1ms
    ! T- {: o+ M3 A0 w
  133. SysTick_Config((SystemCoreClock / 8000000) * 1000 * 1):+ U# e7 r; D5 ?, ~' k/ T( l
  134. SysTick_Config() 会设置时钟源为 HCLK/8 所以实际应用中不能按照上述代码的参数。4 }$ j: L. ^% A  m0 P: N
  135. SystemCoreClock / 8000000: 1us 的节拍数% ]& h0 s: N5 M/ E7 i: y. `* T" I) c
  136. 1us的节拍数 * 1000: 则为 1ms 的节拍数+ R+ U% y, ?/ t8 i- t
  137. 1ms 的节拍数 * 1: 设置 1ms 一个SysTick中断,即从 ((SystemCoreClock / 8000000) * 1000 * 1) - 1 开始倒数。
      J6 V) N2 j. x+ K
  138. 2、轮询方式 */
    / K% Q/ p/ i# q# c0 }
  139. static u8  fac_us=0; //us延时倍乘数
    $ A" m3 n$ K: l2 k
  140. static u16 fac_ms=0; //ms延时倍乘数
    $ B3 r4 }& W5 X: U' \$ D. m% a
  141. void delay_init()) p, z! B- }: W$ |! ]9 {) |
  142. {( d+ y7 Y( x3 v
  143.     SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //选择外部时钟  HCLK/8# V# |5 P  v- I- ]( A$ c$ }
  144.     fac_us = SystemCoreClock/8000000;         // 为系统时钟的1/8  1us = 72000000 / 8000000 =  9 个节拍/ I% s& [$ l6 h
  145.     fac_ms = (u16)fac_us*1000; // 1ms 需要 9 * 1000 = 9000 个节拍& a$ R9 G1 ~- ^0 k0 I/ a
  146. }: a) p9 X$ L% `: o0 r+ s
  147. //延时 nus 微秒
    % G% t) @" h- o" S+ u
  148. void delay_us(u32 nus)2 Q- q) w2 [- [% i7 z
  149. {- {% m; f6 F/ [( B* d! ~
  150.     u32 temp;
    ! |, s+ \0 `( D# l7 X5 F0 x8 `
  151.     SysTick->LOAD=nus*fac_us;  //时间加载
    8 i' \/ K& }" y, E: g6 z- O) t+ \
  152.     SysTick->VAL=0x00;          //清空计数器
    5 E- a7 v* d4 M! O+ |8 V
  153.     SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;                //开始倒数
    2 [. S7 _. P3 ~5 |. ^- _& x# M
  154.     do' w2 e% b: _) {# H. t/ r( B
  155.     {
    4 K6 d" ?  n4 X6 f7 z6 O( c/ G) p
  156.         temp=SysTick->CTRL;
    / P" p! J; o: N. d8 B
  157.     }
    7 g+ z& N5 N- s6 Z; Z  y6 m
  158.     while((temp&0x01)&&!(temp&(1<<16)));  //等待时间到达
    ) `' ^4 [3 E7 @0 ^! G
  159.     SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;//关闭计数器, w6 x! W. C, E& Q, \% s' E
  160.     SysTick->VAL =0X00;                     //清空计数器1 T0 p' v! O' m" O6 }
  161. }9 V& u6 j( v$ e+ j5 t, a9 z1 E; [
  162. //延时nms. x4 ~' V. m) X  a* q: G4 n
  163. //注意nms的范围
    + W- t4 c& K) A
  164. //SysTick->LOAD为24位寄存器,所以,最大延时为:1 e+ [) t9 O- ~( l: U, [
  165. //nms<=0xffffff*8*1000/SYSCLK- B) U2 H0 ~# U8 a# g
  166. //SYSCLK单位为Hz,nms单位为ms
    $ y: x; m8 `7 X" `8 a
  167. //对72M条件下,nms<=1864
    , N* U( \6 E# j0 ~! T0 o
  168. void delay_ms(u16 nms)$ h& g, m- J1 r
  169. {
    9 K2 _0 ]8 J# @; s- z
  170.     u32 temp;! q/ H% O2 J6 |3 e
  171.     SysTick->LOAD=(u32)nms*fac_ms;   //时间加载(SysTick->LOAD为24bit)" {6 M2 a) [( E5 }2 j
  172.     SysTick->VAL =0x00;                       //清空计数器, ~% s2 k$ ~! H! h' o) W6 x. a2 K
  173.     SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;  //开始倒数
    & v3 v; @, @3 X2 ^9 c: M3 e) w
  174.     do1 P  w3 t5 H1 d4 d
  175.     {% z/ w1 C* G' [+ I- C. C! E
  176.         temp=SysTick->CTRL;7 |  ]% `: t" F7 ^- N
  177.         //等待时间到达,这里使用了一个小技巧,通过(temp&0x01)检查 SysTick 的使能位,避免 Systick 定时器被关闭而导致无限循环% x6 n$ U0 e4 [9 ?7 o  C. S% i
  178.     }
    7 `& D- j  X& `/ b6 @
  179.     while((temp&0x01)&&!(temp&(1<<16)));$ Y8 s& N, ]: h- x
  180.     SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器9 v! ~' v/ Z$ y
  181.     SysTick->VAL =0X00;                      //清空计数器
    4 A. ~+ Y1 F4 |# g" K, O
  182. }
复制代码
0 I1 A: @' @1 @6 x5 y$ B0 X! O

' U" R  J" ?3 N; ?: p6 z% WSYSTick 定时器* r) J6 {$ I; q( w3 p5 ?' B; u
CM3 内核的处理器,内部包含了一个 SysTick 定时器,(SysTick 的时钟源自HCLK的8分频,8个系统时钟周期systick跳一个,即8*1/72M=1/9 us)SysTick是一个24位的倒计数定时器,当计到0时,将从RELOAD寄存器中自动重装载定时初值。只要不把它在SysTick控制及状态寄存器中的使能位清除,就永不停息。0 \1 A( b. d, S+ j
利用 STM32 的内部 SysTick 来实现延时的,这样既不占用中断,也不占用系统定时器。因为在 ucos 下 systic 不能再被随意更改,如果我们还想利用 systick 来做 delay_us 或者delay_ms 的延时,就必须想点办法了,这里我们利用的是时钟摘取法。以 delay_us 为例,比如delay_us (50),在刚进入 delay_us 的时候先计算好这段延时需要等待的 systick 计数次数,这里为 50*9 (假设系统时钟为 72Mhz,那么 systick 每增加 1,就是 1/9us) ,然后我们就一直统计systick的计数变化,直到这个值变化了50*9,一旦检测到变化达到或者超过这个值,就说明延时50us时间到了。———实质上就是不改变systick基本单位时长,以基本单位时长为基本元做多次到达摘取。
$ U0 s) O3 b0 |9 y: i
赞 收藏 评论0 发布时间:2022-1-26 23:35

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版