请选择 进入手机版 | 继续访问电脑版

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

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

[复制链接]
STMCU小助手 发布时间:2022-11-20 16:46
主要内容:! J  F1 C/ y3 {% s& m% z
1) Systick定时器及相关寄存器;* u; X6 r! M8 D/ v  J: {
2) Systick相关函数及延迟函数代码解读。: W4 v6 j0 Q* l0 m7 u' K8 E
3 A1 a! a2 v: f. }
1. Systick定时器是什么?
$ ~. W" A* I; R6 T# i6 N: ~1.1 Systick定时器,是一个简单的定时器,对于CM3,CM4内核芯片,都有Systick定时器;
6 V% Z8 F$ G, W8 a1.2 Systick定时器常用来做延时,或者实时系统的心跳时钟。这样可以节省MCU资源,不用浪费一个定时器;1 Q6 N) B; N" j/ G
1.3 Systick定时器就是系统滴答定时器,一个24 位的倒计数定时器,计到0 时,将从RELOAD 寄存器中自动重装载定时初值。只要不把它在SysTick 控制及状态寄存器中的使能位清除,就永不停息,在睡眠模式下也能工作;
3 u6 O$ z+ ^4 K0 c; K! b1.4 SysTick定时器被捆绑在NVIC中,用于产生SYSTICK异常(异常号:15);
* \5 g2 X& C9 o0 U1.5 Systick中断的优先级也可以设置。
+ I4 e0 C5 g7 ?# T0 Y5 Y. ~6 Z2. Systick相关寄存器
6 Z' @* O: F' Q( H2.1 CTRL(SysTick控制和状态寄存器)
: G* r# n; y' I4 y( V+ `
% ?7 {3 A% q3 x/ |" c 20200402163231341.png 3 P) C6 C0 D8 y
! e% N, g. n4 p' x2 V! V8 U
1.1.1 对于STM32,外部时钟源是HCLK(AHB总线时钟)的1/8,内核时钟是HCLK时钟;
1 z3 C% ]; V) M6 v- Y1.1.2 配置函数:SysTick_CLKSourceConfig();3 v0 h' @7 c2 d- w4 [1 ?
2.2 LOAD(SysTick自动重装载除值寄存器)" p, g- e' I, r2 j5 J

& x# K  w8 S1 w3 L 2020040216335691.png
- p& v' c  ~+ N. {! u
* Q, G9 O$ E/ M7 D2.3 VAL(SysTick 当前值寄存器)& w: S6 D/ J" t8 Z+ x

0 C. C; {& ]- _, _ 20200402163505454.png ) T& G$ O3 O( t( n

' d3 `8 |' l, \/ u, F/ r: J) s" d6 p/ {2.4 CALIB(SysTick 校准值寄存器)& Q8 ]) z, x; T

6 d5 C* M  V1 L' R  P: @ 2020040216354576.png ; ?7 X: X! O) X% r" T
& N# n0 y4 b! H( q: j+ _9 n1 w
2.4.1 校准值寄存器使系统即使在不同的CM3产品上运行,也能产生恒定的SysTick中断频率。最简单的作法就是:直接把TENMS的值写入重装载寄存器,这样一来,只要没突破系统极限,就能做到每10ms来一次 SysTick异常。如果需要其它的SysTick异常周期,则可以根据TENMS的值加以比例计算。只不过,在少数情况下,CM3芯片可能无法准确地提供TENMS的值(如,CM3的校准输入信号被拉低),所以为保险起见,最好在使用TENMS前检查器件的参考手册。
! k' M! u# \% _2 H% l  [. l
4 S' E. z( h6 c& ?8 V" u8 y3. Systick库函数
# m3 Z# V2 [1 o$ {7 i' `$ M# z3.1 固件库中的Systick相关函数:
0 g* X6 i" b6 k: g2 T: J+ d" `3.1.19 E$ G1 `' R: K
  1. SysTick_CLKSourceConfig()        //Systick时钟源选择(在misc.c文件中)//
复制代码

- i# O' J# F( g  a( M7 d6 ]( w3.1.2  e% m( @. [8 U( W% H2 b) @* ^; ]

7 O3 f" D5 H2 d) R( T. h
  1. SysTick_Config(uint32_t ticks)      //初始化systick,时钟为HCLK,并开启中断(在core_cm3.h/core_cm4.h文件中)
复制代码
4 ]/ T5 J0 `2 Y' B% o, I
3.2 Systick中断服务函数:$ j3 U( p. @+ H  v: k
3.2.1) K3 ?4 I; f  F7 r  G
- m& x9 e8 Y2 P( K  Z7 b0 F
  1. void SysTick_Handler(void);
复制代码

/ Y3 l9 X8 C' O+ e; d* P6 D3.3 SysTick_CLKSourceConfig函数:' n# U8 b* x) b. `6 o
( d# {% D. i! o; p) }/ z$ i
  1. void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource)    //该函数在misc.c文件中//
    9 k6 b7 L% S; ?0 |. M, l* e
  2. {7 Y, O* O( L& k% K# `" S+ _
  3. /*Check the parameters */+ K+ O$ u2 ^* Q9 [& q; j1 q
  4. assert_param(IS_SYSTICK_CLK_SOURCE(SysTick_CLKSource));6 R4 Y1 i  e$ ?$ |
  5. if
      m& T1 E5 B/ z  }6 Y" h
  6. (SysTick_CLKSource == SysTick_CLKSource_HCLK)2 B1 o. V: k1 i) F! M  h
  7. {  u2 j) G! _5 X8 H% b
  8. SysTick->CTRL |= SysTick_CLKSource_HCLK;                   /*SysTick_CLKSource_HCLK 为0x00000004*/
    % E$ G8 _9 h/ U3 t/ y# k  [
  9. }
      n$ m/ b4 t! u5 G
  10. Else- _& A" y  u) ?" ^' J+ e2 n* E
  11. {- H; r1 O; k. a  U5 h
  12. SysTick->CTRL &=) N, V; K6 c6 Y5 d6 h: E3 Q
  13. SysTick_CLKSource_HCLK_Div8;                               /* SysTick_CLKSource_HCLK_Div8为0xFFFFFFFB)*/2 R4 ~1 U4 v" d4 b
  14. }
    ! G& T/ D9 N* t* n7 M9 X6 |
  15. }
复制代码
9 D( I  N+ @8 j. S
3.4 SysTick_Config函数:9 U8 d1 o) v2 r. N: \- q& j( K

1 C) t- B, C1 M+ q$ X) W6 \! A6 i
  1. /* @brief  Initialize and start the SysTick counter and its interrupt.*/
    2 r) o; H1 n: s
  2. /* @param  ticks   number of ticks between two interrupts*/5 Y3 c9 P) T- U
  3. /* @return  1 = failed, 0 = successful*/  Z0 N1 Q/ f3 P7 F5 q$ P
  4. /* Initialise the system tick timer and its interrupt and start the system tick timer / counter in free running mode to generate periodical interrupts.*/! x: C: A+ ]. M2 x$ `
  5. static __INLINE uint32_t SysTick_Config(uint32_t ticks)  //该文件位于core_cm3.h中,内核级别//
    2 k3 W9 b# p- A1 ^. M2 P0 M$ ~( U
  6. {
    % x4 ?  [. d! ?9 G
  7. if (ticks >SysTick_LOAD_RELOAD_Msk)  return(1);               /* Reload value impossible */ : T% h- c6 a! M2 M5 _* D
  8. /*SysTick_LOAD_RELOAD_Pos    0;*/  6 a2 H$ g  Y1 }% f
  9. /*SysTick_LOAD_RELOAD_Msk (0xFFFFFFul<<SysTick_LOAD_RELOAD_Pos);*/
    # v" L( E7 E; a! b: V# H7 @
  10. /*将SysTick_LOAD_RELOAD_Msk的值设为0xFFFFFF*/
      J1 X8 Q* T2 t* M, j
  11. /* set reload register */                                                            
    2 T/ p# f- o2 \: \
  12. SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; 8 j5 P6 N" y2 n4 f; j7 _
  13. /*set Priority for Cortex-M0 System Interrupts */$ U$ I& h1 Q8 D& Y4 g0 x. n( L* x( R
  14. NVIC_SetPriority (SysTick_IRQn,(1<<__NVIC_PRIO_BITS) - 1);    /*设置优先级,暂时不细讲*/
    . y* |' E" f5 j3 \7 }: r
  15. SysTick->VAL   = 0;                                           /* Load the SysTick Counter Value */) P: x. {& x/ G2 j" J
  16. SysTick->CTRL= SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk |SysTick_CTRL_ENABLE_Msk;  " A2 J5 H  |" l
  17. /* Enable SysTick IRQ and SysTick Timer */
    0 T- D: ?) b0 H  [8 ?$ I+ o3 _
  18. /*SysTick_CTRL_CLKSOURCE_Pos          2;                                      */
    1 R; n( K% c/ p. g9 g2 `* y9 p/ ^
  19. /*SysTick_CTRL_CLKSOURCE_Msk         (1ul <<SysTick_CTRL_CLKSOURCE_Pos);      */
    $ m0 G5 k, b, L
  20. /*SysTick_CTRL_TICKINT_Pos            1;                                      */
    , W3 n5 n4 u/ O- ]0 `- G  c7 P
  21. /*SysTick_CTRL_TICKINT_Msk           (1ul<< SysTick_CTRL_TICKINT_Pos);        */
    * G; y/ d7 W5 i" I2 i- i
  22. /*SysTick_CTRL_ENABLE_Pos             0;                                      */* f$ U2 l5 ^; a6 g8 o. D
  23. /*SysTick_CTRL_ENABLE_Msk            (1ul<< SysTick_CTRL_ENABLE_Pos);         */
    9 M. N7 Q' A! {& Z
  24. return (0);                                            /* Function successful */% t1 i. I5 a( J& f& K- f+ U
  25. }
复制代码
& |0 n, D5 w/ T& R, h8 z5 _; a
3.5 用中断的方式实现delay延时
" r! f) _2 A# G  O- e8 Q; O; d
7 {1 O4 C& }: k* }7 M
  1. static __IO uint32_t TimingDelay;8 o4 ^; `6 Z( D
  2. void Delay(__IO uint32_t nTime)              /*申明Delay()函数*/
    + D+ @" k& R, O: A8 Q5 U; i% X: I4 g
  3. {   
    6 O6 m1 D6 ?8 f6 Y* X2 T( o
  4. TimingDelay = nTime;  ! k$ c& C3 v) ~
  5. while(TimingDelay != 0);
    + K0 m# E/ @6 F8 n
  6. }6 b) v; T9 J, }& Z" S
  7. void SysTick_Handler(void)                   /*申明SysTick_Handler()函数,作用每隔1ms运行该函数*/
    4 T, f6 w9 ?! T$ y0 t
  8. {
    0 e/ }4 u& e0 W
  9.     if
    / A5 F" n. s4 ~# h3 q- t, ]
  10. (TimingDelay != 0x00)                        /*如果TimingDelay不等于0*/
    5 T$ |( R/ k" P  p5 {
  11.      {         e* X1 M8 h6 N) S) u9 q+ R
  12. TimingDelay--;                               /*则TimingDelay减1*/
    , t" }* X$ h# _
  13.      }
    ! a' V! I+ B8 M! Z! y) I
  14. }, A3 d# W! u, N+ z$ R+ D* ~
  15. int main(void)
    6 r) l( e! `4 U( b
  16. {  …
    ) u  T6 d! U; P3 v+ c
  17. if (SysTick_Config(SystemCoreClock/ 1000))    //systick时钟为HCLK,中断时间间隔1ms//9 C: T+ ^3 g- s0 A. d! h
  18. /*SysTick_Config()函数位于core_cm3.h中,请参考3.4小节,内核级别,若systick时钟选择为HCLK,不分频,则SystemCoreClock时钟频率为72MHz,因此SysTick_Config(72000),意思是以72MHz频率计数72000个ticks需要1ms*/6 X) a" ~' `7 L* g' h% @
  19.      {   & p. e- Z& c, {
  20. while (1);& B+ f  Z* i. y2 _+ S
  21.      }   
    2 F- H5 ?) L5 N. ], v
  22. while(1)5 }& q3 n2 S! f) @9 k  a
  23.      {+ b. y$ N3 `2 I) q
  24. Delay(200);                                 //延时200ms//& K. s! |3 E& y3 H# z8 m/ {
  25.      …
    3 M) u3 W7 u' @# W: V" a
  26.      }
    ) H$ e/ }1 ~* b* z* A: {( Y
  27. }
复制代码

3 ]& M( `; e( G2 i0 ?+ D3.6 delay_init()函数解读:
/ W# k6 R  |4 K3 h: n9 I7 p% h9 P/ N# r) W6 |; I% b
  1. void delay_init()$ c) A7 d) G9 V8 E( e4 G
  2. {  C; Q  S) e# I3 g& Q, ^1 v) T( F2 S
  3. //**#if SYSTEM_SUPPORT_OS**//                                            //如果需要支持OS.//, u# S; J- _4 J7 r7 b; B
  4. //**u32 reload;**//                ) R4 \& v8 L; H" p$ n$ u8 ]
  5. //**#endif**//                  9 c7 z7 l% t+ r' d( x
  6.   SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);                 //选择外部时钟  HCLK/8//6 y8 y0 c$ w( F* f
  7. //**SysTick_CLKSource_HCLK_Div8为0xFFFFFFFB,选择外部时钟源的1/8,请参考3.3小节**//7 H  F( I  D" e7 p  j& h9 n- S0 q% |7 E
  8.   fac_us=SystemCoreClock/8000000;                                       //为系统时钟的1/8//  
    1 Z8 ]8 E4 W: \" w; ^7 v9 @* R
  9. //**SystemCoreClock时钟频率=HCLK=72MHz,SysTick时钟频率为9MHz,则1us时间内SysTick计数9次**//
    % c9 |/ G6 U! a' ?- H* W
  10. //**#if SYSTEM_SUPPORT_OS                                               //如果需要支持OS.//
    5 d. }7 X% u2 X3 F; _5 I
  11. //**     reload=SystemCoreClock/8000000;                                //每秒钟的计数次数 单位为M//  4 d& k1 D, [  M! B- R) U
  12. //**     reload*=1000000/delay_ostickspersec;                           //根据delay_ostickspersec设定溢出时间//8 K. K+ ]0 f: f9 G: O; W- W  h! e
  13. //reload为24位寄存器,最大值:16777216,在72M下,约合1.86s左右//
    1 ]5 r9 `; i: _/ N) I0 C! }
  14. //**     fac_ms=1000/delay_ostickspersec;                               //代表OS可以延时的最少单位//         
    ; e  H. E* A. |
  15. //**     SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;                       //开启SYSTICK中断//4 _2 o, m; _( y
  16. //**     SysTick->LOAD=reload;                                          //每1/delay_ostickspersec秒中断一次//      
    ' x- v0 E. D- p' _$ G: t6 ^
  17. //**     SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk;                        //开启SYSTICK//    1 b0 X7 }  {6 J1 m1 i
  18. #else
      f* i, f" u7 y% {
  19.   fac_ms=(u16)fac_us*1000;                                              //非OS下,代表每个ms需要的systick时钟数//   
    ; O7 O# Q+ y# {7 N( p  ?. n& g
  20. #endif
    & P5 x0 X& ?" R7 I- a8 d
  21. }      
复制代码
9 U) Y8 v4 Z9 B; ^% D# G
3.7 void delay_us()函数解读:; r# x  C; }% ^! a& D
" r* M& H. X  ^! \/ ~+ b
  1. //延时nus,nus为要延时的us数.  //   
    5 X/ Z! m$ [  M2 m  Q6 R6 L/ |3 W% e
  2. void delay_us(u32 nus)
    2 l$ _$ T) P# }5 m+ I
  3. {              0 E9 e  N# ~! l$ E% M# x& x
  4.   u32 temp;                   % ?* x% n: ~4 |1 e: g; f
  5.   SysTick->LOAD=nus*fac_us;                                      //时间加载//                    
    4 j, n2 \8 h5 P" h5 w7 O: O* i; C7 @# U" D
  6.   SysTick->VAL=0x00;                                             //清空计数器//
    ' n# O0 ]- @& Z) B2 {. C- G
  7.   SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;                       //使能SysTick定时器//         
    ; x& a. S/ I. z8 Q
  8. /*SysTick_CTRL_ENABLE_Pos              0;                                      */
    3 G* g4 \+ H$ S, J
  9. /*SysTick_CTRL_ENABLE_Msk             (1ul<< SysTick_CTRL_ENABLE_Pos);         */  
    4 C  z) U6 _% M1 ^
  10.   do
    . {( A. m/ ^9 e. G% q( a+ f; `& C8 u
  11.   {
    ( j6 u0 `1 D& y" @! o
  12.          temp=SysTick->CTRL;5 ]2 c' {2 {* _' x5 N
  13.   }while((temp&0x01)&&!(temp&(1<<16)));                          //等待时间到达//  
    ! t# w; _5 r0 s' m! x
  14. //**temp为xxxx xxxx xxxx xxxx | xxxx xxxx xxxx xxx1,因此跟0x01与运算后,其值肯定不是0,而跟(1<<16)与运算后,不确定,若不为0,则说明COUNTFLAG值为1,经逻辑非运算符!后,值为0,则while(0),跳出循环。否则,while(1)**//
    8 _" E( f! c6 |+ G  J  w
  15.   SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;                       //关闭计数器//
    & l4 G9 \% d. x# C# }( ~* Z, q
  16.   SysTick->VAL =0X00;                                            //清空计数器//      
    ! C6 s( b: v! Z6 Q4 l
  17. }
    . H$ @# O, \( [8 K' f' l" N4 |
复制代码
3 M) B# _; ?7 }
3.8 void delay_ms()函数解读:
2 ?6 `4 w& d1 R* ^& U1 T3 |: N2 d% w! T3 G4 j
  1. //延时nms//( A! u. {: k% m
  2. //SysTick->LOAD为24位寄存器,所以,最大延时为: nms<=0xffffff*8*1000/SYSCLK//. |8 _- Y" y. e
  3. //SYSCLK单位为Hz,nms单位为ms,对72M条件下,nms<=1864 //9 N# e3 g$ S3 D7 t4 e: E& F+ d# I
  4. void delay_ms(u16 nms)
    # c9 p0 j0 F, M  H
  5. {                           
    * @5 A# }  f7 \$ J" O) k; {
  6.   u32 temp;                 ' ^) ~* M4 d$ L: I" y# K/ `; I
  7.   SysTick->LOAD=(u32)nms*fac_ms;                                     //时间加载(SysTick->LOAD为24bit)//7 d$ e- |9 E- Y/ E! ^4 U$ N4 V6 |
  8.   SysTick->VAL =0x00;                                                //清空计数器//- G) |( [1 ?  C9 S& L; X1 g
  9.   SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;                           //使能SysTick定时器// 2 E/ p$ U1 M& M
  10. /*SysTick_CTRL_ENABLE_Pos              0;                                   */
    . d" \8 S$ J. I7 w/ X# g5 \
  11. /*SysTick_CTRL_ENABLE_Msk             (1ul<< SysTick_CTRL_ENABLE_Pos);      */
    + a2 e, K/ i) o6 r: [. }
  12.   do9 X7 d3 Q: t/ Y4 ]4 `$ K: Q- v' D
  13.   {
    ( k% ?1 R( {: g8 p" W, y
  14.          temp=SysTick->CTRL;
    - G, ^1 r4 n3 P6 t. t7 U
  15.   }while((temp&0x01)&&!(temp&(1<<16)));                              //等待时间到达//   
    " w! o/ a8 i/ ^$ y* A
  16. //**temp为xxxx xxxx xxxx xxxx | xxxx xxxx xxxx xxx1,因此跟0x01与运算后,其值肯定不是0,而跟(1<<16)与运算后,不确定,若不为0,则说明COUNTFLAG值为1,经逻辑非运算符!后,值为0,则while(0),跳出循环。否则,while(1)**//
    $ N4 b( c3 j4 f; I6 O: Q1 E
  17.   SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;                           //关闭计数器//
    # Q8 X" V$ K% a3 O
  18.   SysTick->VAL =0X00;                                                //清空计数器//               
    / R8 Z2 x( G; [0 E2 P+ O5 w
  19. } 7 L; ?% r/ e2 I. V
  20. #endif
复制代码
; z$ ?+ V( b6 v
————————————————
9 u5 @5 P  ^  i版权声明:天亮继续睡
3 ?- L! T  r# l- @$ X& t7 ]% a8 H4 U2 n- b" E. J3 _
) ]/ C% N9 G/ b
收藏 评论0 发布时间:2022-11-20 16:46

举报

0个回答
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版