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

STM32心得:Systick滴答定时器和延时函数解读

[复制链接]
STMCU小助手 发布时间:2022-11-20 16:46
主要内容:
7 V( ~- A) m# A* U: ~1) Systick定时器及相关寄存器;
5 p! F2 k. V* f0 c, _  `2) Systick相关函数及延迟函数代码解读。: g/ O& z8 `. y! y6 n
6 H& h* i: y, R  i
1. Systick定时器是什么?# }0 a; d4 i% M( C( i* i; }
1.1 Systick定时器,是一个简单的定时器,对于CM3,CM4内核芯片,都有Systick定时器;$ t$ {1 W) b2 z9 S  p  [% w
1.2 Systick定时器常用来做延时,或者实时系统的心跳时钟。这样可以节省MCU资源,不用浪费一个定时器;
3 A: ?/ ~: ?4 b% d, o/ U/ X1.3 Systick定时器就是系统滴答定时器,一个24 位的倒计数定时器,计到0 时,将从RELOAD 寄存器中自动重装载定时初值。只要不把它在SysTick 控制及状态寄存器中的使能位清除,就永不停息,在睡眠模式下也能工作;
* F7 N6 W5 {( I5 |; l5 ~1.4 SysTick定时器被捆绑在NVIC中,用于产生SYSTICK异常(异常号:15);' J1 F$ W* q) z& c8 H
1.5 Systick中断的优先级也可以设置。6 A8 m( m3 N" ]% C7 V
2. Systick相关寄存器
2 x+ W* b' [, f2.1 CTRL(SysTick控制和状态寄存器)
7 z- X+ _3 Z7 R4 _  u
1 q4 e  q! a  N5 q* X# L" k 20200402163231341.png # p# V9 ]$ s. D' x# }

7 _/ v) ]3 I; Q. k( `' B1.1.1 对于STM32,外部时钟源是HCLK(AHB总线时钟)的1/8,内核时钟是HCLK时钟;6 J+ {2 ^0 e: K' w1 B6 Z& `, D
1.1.2 配置函数:SysTick_CLKSourceConfig();
9 ^* m( I; b, q7 L2.2 LOAD(SysTick自动重装载除值寄存器): ?1 [; E) _6 m. ?2 p) z

  U. ~5 U+ E  K, t3 {2 ` 2020040216335691.png
+ r; i) d9 |* C5 H4 v
- q3 y- F" n: e9 }/ Q2.3 VAL(SysTick 当前值寄存器)
+ ^9 i) }7 N; @: ^* P3 c+ e: g8 v4 j$ n$ Y9 Y$ x1 H5 K' x
20200402163505454.png 4 q7 U2 G1 A4 k
, ^% e& d* z3 H) c
2.4 CALIB(SysTick 校准值寄存器), X4 V3 D6 ?4 l, {7 [7 }1 T5 P
* U0 q& H1 [: z. G
2020040216354576.png 8 b* J2 [9 g' U9 G! i
) g  a9 C% C# p* b$ u: ?/ B( P, v
2.4.1 校准值寄存器使系统即使在不同的CM3产品上运行,也能产生恒定的SysTick中断频率。最简单的作法就是:直接把TENMS的值写入重装载寄存器,这样一来,只要没突破系统极限,就能做到每10ms来一次 SysTick异常。如果需要其它的SysTick异常周期,则可以根据TENMS的值加以比例计算。只不过,在少数情况下,CM3芯片可能无法准确地提供TENMS的值(如,CM3的校准输入信号被拉低),所以为保险起见,最好在使用TENMS前检查器件的参考手册。
# O* P; T; M, K; a" H5 V9 m7 P; ^6 ^0 {/ U- o7 u0 N
3. Systick库函数7 D5 U! i  W- J2 ?; r, Z1 m
3.1 固件库中的Systick相关函数:& G* J3 P* f, ?# a  D; I
3.1.1; ?# X( u( ?7 |* P5 I/ |( r
  1. SysTick_CLKSourceConfig()        //Systick时钟源选择(在misc.c文件中)//
复制代码
/ z6 B9 E# `7 r8 L: G
3.1.2
9 Q1 c# P! V# j& |1 O2 `8 o4 W6 r! c& i" ]& Y
  1. SysTick_Config(uint32_t ticks)      //初始化systick,时钟为HCLK,并开启中断(在core_cm3.h/core_cm4.h文件中)
复制代码
/ {  i3 _5 X( B) X! K7 d' M
3.2 Systick中断服务函数:; K% o; f% v& N+ s7 z, |
3.2.13 w) Z6 Q# C7 h8 e9 F
2 t  Y7 k; T3 D  r2 R9 \
  1. void SysTick_Handler(void);
复制代码
1 C9 k! h4 U) S# t/ O
3.3 SysTick_CLKSourceConfig函数:
3 g+ J' S7 g, q8 G( T
$ N$ M" M* T% z2 t+ `
  1. void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource)    //该函数在misc.c文件中//4 G" J* r+ p7 ], N) x
  2. {
    7 s& b, w+ K: f  r5 S& i
  3. /*Check the parameters */
    : ]1 a" @6 X+ S- p# d. m; x
  4. assert_param(IS_SYSTICK_CLK_SOURCE(SysTick_CLKSource));: V- I( M5 f: u. i4 \7 B( F
  5. if
    1 j3 X( o3 E# c2 |7 A
  6. (SysTick_CLKSource == SysTick_CLKSource_HCLK)
    8 x/ P1 x$ e* [
  7. {
    8 o. V. |3 U5 Y
  8. SysTick->CTRL |= SysTick_CLKSource_HCLK;                   /*SysTick_CLKSource_HCLK 为0x00000004*/
    / s* a) n" @5 y5 `
  9. }
    / o5 m; o% X8 d8 {. ^
  10. Else; F! L7 T8 [( T7 R1 M
  11. {/ d0 y; ?( ^- X3 @( v- g* K8 z
  12. SysTick->CTRL &=
    " G( K$ f- u  ^% v4 o, H2 q
  13. SysTick_CLKSource_HCLK_Div8;                               /* SysTick_CLKSource_HCLK_Div8为0xFFFFFFFB)*// a, v0 K8 {" n  Y4 V" L! `- H
  14. }
    8 X$ B! T1 e3 F( S
  15. }
复制代码

0 ~8 h. w( A6 |; p" n! ~; j) R3.4 SysTick_Config函数:
4 V& O; h, h: i' a& c5 A
7 R  {% K2 {" D, L, ^
  1. /* @brief  Initialize and start the SysTick counter and its interrupt.*/
    ( z8 h+ T; _' W6 f: y' z
  2. /* @param  ticks   number of ticks between two interrupts*/, X( N3 H7 i" Y0 z: x' O
  3. /* @return  1 = failed, 0 = successful*/, j! o3 c5 V3 {
  4. /* Initialise the system tick timer and its interrupt and start the system tick timer / counter in free running mode to generate periodical interrupts.*/3 X+ D, G0 C! T
  5. static __INLINE uint32_t SysTick_Config(uint32_t ticks)  //该文件位于core_cm3.h中,内核级别//
    - ~0 D! J9 y6 L- g2 Z* A7 Q1 z
  6. { ) o: Z2 D9 H4 n6 `0 ?- {+ M
  7. if (ticks >SysTick_LOAD_RELOAD_Msk)  return(1);               /* Reload value impossible */
    / G; U+ f% s6 N& {
  8. /*SysTick_LOAD_RELOAD_Pos    0;*/  
    : `7 B$ ?! F5 L& e
  9. /*SysTick_LOAD_RELOAD_Msk (0xFFFFFFul<<SysTick_LOAD_RELOAD_Pos);*/9 X, M, W: P" Y3 Q, n" e
  10. /*将SysTick_LOAD_RELOAD_Msk的值设为0xFFFFFF*// v. k# F+ A! N5 ^
  11. /* set reload register */                                                            & i( C) g3 U! N- L+ K, d& ?
  12. SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; ; |' O- F9 S2 A6 E' S/ g; Z
  13. /*set Priority for Cortex-M0 System Interrupts */
    1 u0 _2 I0 U% x  t5 `
  14. NVIC_SetPriority (SysTick_IRQn,(1<<__NVIC_PRIO_BITS) - 1);    /*设置优先级,暂时不细讲*/8 ?; R2 z# E$ ^) l
  15. SysTick->VAL   = 0;                                           /* Load the SysTick Counter Value */6 a% e' t, @" D' ~1 u
  16. SysTick->CTRL= SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk |SysTick_CTRL_ENABLE_Msk;  9 C; v' I( j  }. n) u' i
  17. /* Enable SysTick IRQ and SysTick Timer */
    & l9 W1 C. p8 _5 Q
  18. /*SysTick_CTRL_CLKSOURCE_Pos          2;                                      */
    8 @3 s& i) e% O5 ]  u$ w, Z% U
  19. /*SysTick_CTRL_CLKSOURCE_Msk         (1ul <<SysTick_CTRL_CLKSOURCE_Pos);      */
    # J! n+ z- X" ?. [5 F' u# x9 n& a' ?; c
  20. /*SysTick_CTRL_TICKINT_Pos            1;                                      */
    9 O, `6 y& X' n$ F: [, A( h  s
  21. /*SysTick_CTRL_TICKINT_Msk           (1ul<< SysTick_CTRL_TICKINT_Pos);        */
    # \% c6 I4 x5 p2 r0 ^
  22. /*SysTick_CTRL_ENABLE_Pos             0;                                      */
      y4 \5 @% K9 [7 F. C
  23. /*SysTick_CTRL_ENABLE_Msk            (1ul<< SysTick_CTRL_ENABLE_Pos);         */
    - P' G3 h  n2 b& i
  24. return (0);                                            /* Function successful */# i: }. q$ G/ x* Z
  25. }
复制代码
/ a' v+ [5 o  T% i, `! N8 M! m
3.5 用中断的方式实现delay延时6 Q" r- a% p; ~3 P8 y( Q

( `8 X2 |. F6 X
  1. static __IO uint32_t TimingDelay;
    0 L7 s" s; Y" F( W8 }( I
  2. void Delay(__IO uint32_t nTime)              /*申明Delay()函数*/
    1 @. j$ L7 i; N
  3. {   
    2 g* ^: l/ ~: p: u+ `8 r5 e4 `
  4. TimingDelay = nTime;  0 x! C) h! x; b4 z' }, b
  5. while(TimingDelay != 0);
      t6 |7 i! B9 a! K
  6. }9 A$ _1 u. T" F, {# ]! u- u
  7. void SysTick_Handler(void)                   /*申明SysTick_Handler()函数,作用每隔1ms运行该函数*/
    9 j7 B* N3 _6 h" T* f0 S/ D
  8. {/ @+ L' z8 Q8 D4 R7 Z
  9.     if
    3 S3 \9 b, |1 d
  10. (TimingDelay != 0x00)                        /*如果TimingDelay不等于0*/
    2 d( `6 ]6 u% G2 i6 S' L
  11.      {      
    + U/ G2 G6 P/ K, u' r
  12. TimingDelay--;                               /*则TimingDelay减1*/
    5 n3 a9 g* y  Z, R9 O3 O9 ~
  13.      }
      O8 i3 X9 g% ~
  14. }3 E3 }7 O7 H" o! ]& T4 ^. h
  15. int main(void)* N6 a6 T3 [3 L# r0 ?
  16. {  …
    6 H. B' a: K6 ^, h
  17. if (SysTick_Config(SystemCoreClock/ 1000))    //systick时钟为HCLK,中断时间间隔1ms//
    ! V% a9 M/ S9 f$ n7 d, i
  18. /*SysTick_Config()函数位于core_cm3.h中,请参考3.4小节,内核级别,若systick时钟选择为HCLK,不分频,则SystemCoreClock时钟频率为72MHz,因此SysTick_Config(72000),意思是以72MHz频率计数72000个ticks需要1ms*/( h3 f9 T, m" H
  19.      {   
    4 T7 z3 V( ?0 k% k2 Q& L9 g* `+ f3 C
  20. while (1);
    2 q& d1 y7 t% N: T9 q0 ^
  21.      }   3 h  |1 R1 d  _* F4 t& z
  22. while(1)6 b& c& g" W" R/ {
  23.      {
    5 H7 y! ^& A9 ^5 x1 ?
  24. Delay(200);                                 //延时200ms//! i7 M4 g% ?. l1 r
  25.      …
    4 }# k/ c; ~4 `# k+ Y, ^
  26.      }1 Z( q9 t3 j1 M
  27. }
复制代码

5 L. @+ [% T; o, O5 X3.6 delay_init()函数解读:, E; X: S' F4 G) Q

" @6 l/ l, i" d2 l
  1. void delay_init(), s! \- h! x+ C  e' ~
  2. {" l& S& @0 X# A% f% U( B
  3. //**#if SYSTEM_SUPPORT_OS**//                                            //如果需要支持OS.//: A5 a" k$ M- k$ @+ P0 w; t) C1 E( g
  4. //**u32 reload;**//                ) w0 c% q* ]# y: J
  5. //**#endif**//                  
    6 J$ G8 W0 D% g- X, Y4 x
  6.   SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);                 //选择外部时钟  HCLK/8//! b; D% z0 D# R8 E
  7. //**SysTick_CLKSource_HCLK_Div8为0xFFFFFFFB,选择外部时钟源的1/8,请参考3.3小节**//8 Q! S2 n8 S. ~1 N* q- |6 C) @7 m
  8.   fac_us=SystemCoreClock/8000000;                                       //为系统时钟的1/8//  $ O! D3 j$ v0 `9 f3 A8 M
  9. //**SystemCoreClock时钟频率=HCLK=72MHz,SysTick时钟频率为9MHz,则1us时间内SysTick计数9次**//
      X& q* u/ P  r  q$ W6 ~! t& _
  10. //**#if SYSTEM_SUPPORT_OS                                               //如果需要支持OS.//
    2 o# Y% v& G  n4 I5 m0 O7 g2 }
  11. //**     reload=SystemCoreClock/8000000;                                //每秒钟的计数次数 单位为M//  
    & C* L. T2 M: s- K
  12. //**     reload*=1000000/delay_ostickspersec;                           //根据delay_ostickspersec设定溢出时间//% L! P, o3 }' I' T
  13. //reload为24位寄存器,最大值:16777216,在72M下,约合1.86s左右//
    # x6 L8 l6 _7 I
  14. //**     fac_ms=1000/delay_ostickspersec;                               //代表OS可以延时的最少单位//          3 p- D9 n; {3 I3 j6 _
  15. //**     SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;                       //开启SYSTICK中断//
    ; m  {; k- ?9 b) \5 }5 e/ d
  16. //**     SysTick->LOAD=reload;                                          //每1/delay_ostickspersec秒中断一次//      2 n: v& W  }1 U& y; U0 W
  17. //**     SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk;                        //开启SYSTICK//   
    $ ]( e1 r: f/ i  _2 M& p& d
  18. #else
    ) n) k& i) J! f$ i9 I3 M1 d
  19.   fac_ms=(u16)fac_us*1000;                                              //非OS下,代表每个ms需要的systick时钟数//   
    : d) u( b- H! U$ O6 T6 A: a
  20. #endif* _3 I: m" T" e
  21. }      
复制代码
- _/ j; }- \4 r% C; P
3.7 void delay_us()函数解读:
! B1 e) r1 V# [; R& `2 L5 R7 B& Q$ i; `5 q$ c
  1. //延时nus,nus为要延时的us数.  //   
    - p) }1 t: i% u  A( E& N: o
  2. void delay_us(u32 nus)
    3 R' A% R' W& o% L$ l
  3. {              
    , A6 x" o$ _2 n$ t0 Z; |- d6 ~% U
  4.   u32 temp;                  
    # [, C3 h# h/ y& B9 O
  5.   SysTick->LOAD=nus*fac_us;                                      //时间加载//                    4 z+ `+ ]: w# j7 ]* h8 |' w. s
  6.   SysTick->VAL=0x00;                                             //清空计数器//6 d8 X0 {" R  X# T, `. Z
  7.   SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;                       //使能SysTick定时器//         & Q( |; u2 y! L. j5 g% {" D
  8. /*SysTick_CTRL_ENABLE_Pos              0;                                      */% I1 w, K  `& {* X
  9. /*SysTick_CTRL_ENABLE_Msk             (1ul<< SysTick_CTRL_ENABLE_Pos);         */  
    7 D7 `/ Y& I/ r7 k3 l: J
  10.   do
    ' ^1 }4 g: C- I& k* \
  11.   {! z) D8 F0 d- x* N! _3 M5 ]
  12.          temp=SysTick->CTRL;& f( ]# O6 K: C" z6 L/ I) W( Z- J
  13.   }while((temp&0x01)&&!(temp&(1<<16)));                          //等待时间到达//  5 z# Z. o$ Q! b: `  ?
  14. //**temp为xxxx xxxx xxxx xxxx | xxxx xxxx xxxx xxx1,因此跟0x01与运算后,其值肯定不是0,而跟(1<<16)与运算后,不确定,若不为0,则说明COUNTFLAG值为1,经逻辑非运算符!后,值为0,则while(0),跳出循环。否则,while(1)**//8 ?# S+ U, O% y3 j8 Y) [
  15.   SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;                       //关闭计数器/// ]1 Y- u2 c  r' O- F- W
  16.   SysTick->VAL =0X00;                                            //清空计数器//       ) b, R$ G. ?- W/ J9 h' ^4 ]
  17. }
    ! u' V2 P9 J2 B" g
复制代码

" ^/ P: Q+ k3 R; b4 z3.8 void delay_ms()函数解读:: u& t* l% |1 |! Q4 z: @/ M

9 ~4 \1 ^+ j. o: I
  1. //延时nms//# A  w  }$ h7 p, c
  2. //SysTick->LOAD为24位寄存器,所以,最大延时为: nms<=0xffffff*8*1000/SYSCLK//
    5 }6 L* ^5 Z) X3 S$ U7 f
  3. //SYSCLK单位为Hz,nms单位为ms,对72M条件下,nms<=1864 //
    9 W2 U; Y, l  \* K6 ]6 ?5 j
  4. void delay_ms(u16 nms): V  M! K6 m( |! B
  5. {                           
    7 f& }- u9 ~& T& o
  6.   u32 temp;                 # u& Q8 b1 P& k+ j9 Q' `
  7.   SysTick->LOAD=(u32)nms*fac_ms;                                     //时间加载(SysTick->LOAD为24bit)//
    4 b! p. O1 I& D8 M6 r( N, u% N
  8.   SysTick->VAL =0x00;                                                //清空计数器//3 }- {; A% i9 ^8 w4 C, O- W
  9.   SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;                           //使能SysTick定时器//
    % i0 M) R0 f; q! w) |5 B
  10. /*SysTick_CTRL_ENABLE_Pos              0;                                   */
    3 Q1 H, U7 \$ E0 z5 }/ G6 O
  11. /*SysTick_CTRL_ENABLE_Msk             (1ul<< SysTick_CTRL_ENABLE_Pos);      */* z- J, p0 z6 z% P6 N1 K$ l
  12.   do
    . S9 S. H$ a3 B- a7 @; a
  13.   {
    3 [3 J) [/ Y5 _& T: J1 U2 k# [; b
  14.          temp=SysTick->CTRL;2 p! w3 j5 z$ K/ v) `& _- ~. W6 Q
  15.   }while((temp&0x01)&&!(temp&(1<<16)));                              //等待时间到达//   5 s# v- s4 d( P' P
  16. //**temp为xxxx xxxx xxxx xxxx | xxxx xxxx xxxx xxx1,因此跟0x01与运算后,其值肯定不是0,而跟(1<<16)与运算后,不确定,若不为0,则说明COUNTFLAG值为1,经逻辑非运算符!后,值为0,则while(0),跳出循环。否则,while(1)**//
    ) n8 I% R* p) D" ~5 @+ q5 x
  17.   SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;                           //关闭计数器//
    # ~' v2 V# w1 f/ K+ t
  18.   SysTick->VAL =0X00;                                                //清空计数器//               
    ! }& l& n8 `) O% v# e' P. r4 }
  19. }
    # P. @7 V# W# v7 B! ^0 m" x
  20. #endif
复制代码
) Y& C3 E: h9 L0 {
————————————————4 `0 g3 e) N2 a. f
版权声明:天亮继续睡
1 R$ K/ G8 D. N+ c' }# n  s% w/ `- T5 ?

( v! B& k" f( a- ^7 y) s, ]
赞 收藏 评论0 发布时间:2022-11-20 16:46

举报

0个回答

所属标签

相似技术帖

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