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

【经验分享】填坑-关于SysTick定时器

[复制链接]
STMCU小助手 发布时间:2021-9-28 10:00
文主要来填坑,更正之前文章的错误。也进一步加深了我对SysTick定时器的理解,希望对你有帮助。6 Y7 b1 c$ [2 o5 w  I) `; O

' d( n3 S% r6 [

/ R, s6 B+ ?( O: a# F. z/ w( Z
01
坑的由来

3 b$ U1 }, j. c6 v+ R+ b4 ~
3 F8 w( b  N# a1 s7 v2 S/ ]
在之前的推文中《STM32延时的四种方法》介绍了使用查询定时器精确延时,使用的就是systick定时器,具体代码如下2 P. P3 x+ ]6 B7 v9 z: ^* H
  1. void delay_us(uint32_t nus)
    0 c: I) M3 W9 _) k- }/ U
  2. {
    2 ]# R- \2 R* ]4 i8 c' K5 H
  3.   uint32_t temp;
    6 ]3 o7 y; P/ P7 X3 B1 C* c/ w, P
  4.   SysTick->LOAD = RCC_Clocks.HCLK_Frequency/1000000/8*nus;
    " W5 w/ X! _) C- t3 l4 ]4 _9 R
  5.   SysTick->VAL=0X00;//清空计数器
    # R& F/ X# t0 _8 T8 q& }/ D9 k/ g
  6.   SysTick->CTRL=0X01;//使能,减到零是无动作,采用外部时钟源. u4 Z, ]& f, b8 h5 |5 J
  7.   do' d# g* r" U6 L( [% X4 t8 d
  8.   {
    ) h4 `' Y9 O7 F  B% g2 x& \2 m
  9.     temp=SysTick->CTRL;//读取当前倒计数值9 d' X6 y& J  q* N1 T' c
  10.   }while((temp&0x01)&&(!(temp&(1<<16))));//等待时间到达; V% ?' T! B4 Y3 e7 J
  11.   SysTick->CTRL=0x00; //关闭计数器% F2 j3 Y1 H9 |: u7 }" m
  12.   SysTick->VAL =0X00; //清空计数器
    ( K! f9 v* m) j- D1 ]
  13. }  n& m7 G/ ?, `/ W
  14. void delay_ms(uint16_t nms)0 S. ^" y$ r9 s9 a9 k, i4 A- Z
  15. {$ q; u7 p, P( P! i
  16.   uint32_t temp;# S8 Y4 r8 T; A7 h
  17.   SysTick->LOAD = RCC_Clocks.HCLK_Frequency/1000/8*nms;& s9 q' ?: I. v4 G+ @4 t
  18.   SysTick->VAL=0X00;//清空计数器
    4 e/ F. E3 }% C. f
  19.   SysTick->CTRL=0X01;//使能,减到零是无动作,采用外部时钟源  s: ~1 a- l' K1 k
  20.   do
    # E7 j0 K6 R6 e6 E% a3 z5 A* O
  21.   {2 S, `: a' t* s: Q, M
  22.     temp=SysTick->CTRL;//读取当前倒计数值
    ' ?) M1 \  `& D* a8 z0 ]
  23.   }while((temp&0x01)&&(!(temp&(1<<16))));//等待时间到达7 i2 _) N- o" `7 N+ Y
  24.   SysTick->CTRL=0x00; //关闭计数器! R  S  w% Z. l7 M& D/ S6 z
  25.   SysTick->VAL =0X00; //清空计数器
    0 `6 |$ F& i3 U  [
  26. }
复制代码
对于《STM32延时的四种方法》文中所说的内容如下# m. j2 X. g! F+ l' B/ k
13.png
3 X" K/ ^  S) l7 b( E- Y7 h
也就是下面代码中/8的原因。
$ P4 o( R# g( a* o( [
2 o( b: S- P+ A/ n% o- c) S
  1. SysTick->LOAD = RCC_Clocks.HCLK_Frequency/1000/8*nms;
复制代码
我对此深信不疑,并在STM32F207参考手册(RM0033)上找到“证据”。9 P4 T8 @6 S& U9 J
14.png

- G* x/ z# e& @1 l7 r# j+ N上图①处直接是8分频,而不像②出的1/2/4/8分频。所以我确信是SYSTICK的时钟固定为HCLK时钟的1/8。
; K1 P; X- o0 R6 s' e5 d. N" o- Z7 Z, N. g7 L
我在学习RTThread的时候,看到配置SysTick定制器代码如下
8 f0 I" E! f! M& J6 K! Q% q+ N6 B

! d+ Z* {1 i0 G, ~: ?" i2 u
. G% Y9 O" e. s7 C" B. i
$ ?( z3 S! A$ M9 G: F8 j
我心里一堆问号,STM32官方手册,明明写了SYSTICK的时钟固定为HCLK时钟的1/8。我使用示波器测量,RTThread的配置是没有问题,可以正常延时的。
5 t! u3 c8 J8 g6 o0 }( p/ g% C9 `9 C3 \$ J3 Q6 j
02

) }/ r4 I: q5 b+ n- I. ?* B% x1 |) A
填坑
4 k# V$ u9 N7 A" z7 e
" U  f" v2 q4 x/ F  }% }
5 i  g3 u$ w2 l9 d7 _9 J* g" d7 n6 k
这个坑其实很简单,在《STM32延时的四种方法》也提到了,只是自己没有注意这个细节。2 d1 Q& ^* H6 k" X
15.png
1 _- {8 e1 j' g/ q" w8 J
位2置1,表示时钟频率为AHB,也就是默认的120000000Hz。$ U$ @, _7 M  V

8 }- i8 J; Q' O) x, n

9 A! I! R3 U3 U+ g; ]& s& U位2清0,表示时钟频率为AHB/8,也就是120000000/8Hz。' x0 _9 Q0 k6 ]: z

; e+ H1 T& j* q/ M1 g7 JRTThread配置为内部时钟
& j8 f. t3 y& q7 W' Y* E
16.png

- }& e6 V" j% |8 ]0 V" b0 ?之前的文章配置为外部时钟源6 N* J3 G( T  Q' ?/ Q
17.png

, c3 T( Q1 [$ f/ G& a% K这个细节我没有留意,导致我看RTThread代码时有点懵逼。在这里我更正《STM32延时的四种方法》中的错误描述5 Z* d* D" g$ U0 y4 ]( n# N
18.png

9 z, P8 p4 N% ]准确的描述是:- M! p. v) i3 U5 b9 r1 h5 v
$ R0 O  K& q" f

' F; o- E* ^, r& @$ DSYSTICK的时钟可以为HCLK时钟的1分频或8分频,在这里我们选用外部时钟源120M,所以SYSTICK的时钟为(120/8)M。- {/ [5 l2 p3 C% l" l5 C: o
3 B) h. ?% _& \8 N
4 ?1 x' R2 y4 Z" T) u/ {
特此更正。: F) }" ~, X: ~3 T8 K0 U$ p

! Y+ g; c4 C: x+ }关于这点,STM32的标准外设库提供的SysTick_Config函数,也是使用内部时钟的
. f: m$ ~( T) p# o
  1. /** \brief  System Tick Configuration
    * N; n% `5 u% s9 M. ^

  2. : a# D1 P" I. U2 Y8 E8 d& S( K
  3.     This function initialises the system tick timer and its interrupt and start the system tick timer./ r1 G1 E) v! @3 E
  4.     Counter is in free running mode to generate periodical interrupts.! r8 }, Z% M5 p. g. M' B" a  `

  5. ; d3 N$ E- d9 `; K7 o
  6.     \param [in]  ticks  Number of ticks between two interrupts3 P5 S7 B6 u. h9 i2 i1 j6 ?
  7.     \return          0  Function succeeded
    4 ]5 [$ }) T) G' @3 z, b8 n
  8.     \return          1  Function failed. h: L& v. C# ?& F0 J" k
  9. */$ a  k# _8 n$ C
  10. static __INLINE uint32_t SysTick_Config(uint32_t ticks)( s5 V. u" t" s
  11. {
    # h1 i9 }$ g6 [4 e
  12.   if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);            /* Reload value impossible */0 s! w4 |/ p) C* e

  13. 6 p6 g7 T. H. h9 S- V' x& S( o
  14.   SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;      /* set reload register */
    + G# b1 w+ S- m# e7 x3 Y4 |5 @! D& W
  15.   NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);  /* set Priority for Cortex-M0 System Interrupts */
    9 w% h; c" i% ^0 p  c
  16.   SysTick->VAL   = 0;                                          /* Load the SysTick Counter Value */
    # b3 J- h, X8 q( z" o' R
  17.   SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |
    ) W3 Z/ q. v7 l* ~
  18.                    SysTick_CTRL_TICKINT_Msk   |
    ' x5 E, z+ g' G5 m. S
  19.                    SysTick_CTRL_ENABLE_Msk;                    /* Enable SysTick IRQ and SysTick Timer */2 p2 ]" z1 S  R( f
  20.   return (0);                                                  /* Function successful */3 \1 j3 b6 l3 Z' k3 _& g
  21. }
复制代码
调用方法,产生1ms中断调用方法. Z2 @4 a8 Q4 y6 ^3 m  r" |
' v5 d7 |2 E. e8 H1 K
  1. SysTick_Config(SystemCoreClock / 1000);
复制代码
* k* G7 h) U7 j
关于时钟源的选择,除了操作寄存器外,还有库函数可以选择。
% J5 a% ?- M% K# G1 u; v$ H
  1. 5 h9 ~- F( x; }% i) G
  2. /**- O( `& ?, Z9 ?' T% X1 t3 c4 H: `
  3.   * @brief  Configures the SysTick clock source.1 r$ s/ J' t' U9 R% O6 A" R; O
  4.   * @param  SysTick_CLKSource: specifies the SysTick clock source.% s1 @. Q: z& O  M3 L
  5.   *   This parameter can be one of the following values:  ^6 N* }  [- i/ {+ Q6 ]  f. g
  6.   *     @arg SysTick_CLKSource_HCLK_Div8: AHB clock divided by 8 selected as SysTick clock source.
    8 G0 B: \5 p% C6 ~- M$ n
  7.   *     @arg SysTick_CLKSource_HCLK: AHB clock selected as SysTick clock source.
    8 k5 j: a; E8 ^. ~& \& A: A0 t
  8.   * @retval None) b* \, |+ ]9 t  ?( v( X
  9.   */
    3 a- T, X4 I0 H, m$ F7 J
  10. void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource)
    4 Z' y- r, s: ]; _; q& W
  11. {
    2 t! `3 e$ \% i2 s# u
  12.   /* Check the parameters */
    - X4 g% P/ ?- M/ c% g& W
  13.   assert_param(IS_SYSTICK_CLK_SOURCE(SysTick_CLKSource));' ]( R* ^3 l. I) j) d. ~- b
  14.   if (SysTick_CLKSource == SysTick_CLKSource_HCLK)
    8 @/ z) G# O- X
  15.   {
    # T* H! z" a! \! Q$ G* A
  16.     SysTick->CTRL |= SysTick_CLKSource_HCLK;2 N) l; J# e! A6 h# E
  17.   }: U( A- ~" ~) ~$ H' d
  18.   else
    8 y9 w( N, f1 E; ^, m- W
  19.   {
    8 g7 g. D/ s! `4 f1 O
  20.     SysTick->CTRL &= SysTick_CLKSource_HCLK_Div8;
    # N) h" o9 \. B' x2 X
  21.   }0 ^9 t: x1 g% e0 y5 j# g% \
  22. }
复制代码
除上外,我找到了其他证据来说明,SYSTICK的时钟可以为HCLK时钟的1分频或8分频。
/ ~! J0 j' |. c  Y: C0 D: ~
19.png
5 E$ R. w0 v. d  p2 a4 c
在STM32CubeMx配置软件中,可以选择1分频或8分频。4 ?* P3 U& x6 b0 L
# ~; i- W2 ~' M9 M
03

( T! k! y5 k; w; m3 D! x
修改代码验证

( `" V, x( N7 t" |3 |( T8 d% ]) J! f% y) ]+ _: ^2 i& V8 @

$ w0 g& O3 r3 h) E把《STM32延时的四种方法》文中涉及的代码修改成1分频的。
  y7 h$ G" n! _. v) m  t* l- p
3 Y6 u, d7 u1 l5 F8 r
  1. void delay_ms(uint16_t nms)
    4 ~3 F* C1 u$ r
  2. {) ~- f& D& a, v3 u$ Z5 r
  3.   uint32_t temp;# E# r3 h, D6 H0 u. z" L
  4.   SysTick->LOAD = RCC_Clocks.HCLK_Frequency/1000*nms-1;
    . }6 @5 h3 t4 X# k' {$ |
  5.   SysTick->VAL=0X00;//清空计数器
    6 q9 ?. a/ t0 Y( r$ i3 g
  6.   SysTick->CTRL=0X01;
    0 c" r' r, D1 I
  7.   SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);) T; g4 l7 q! t" [7 I4 r
  8.   do* S8 E( X) j, N- t; }, }/ b9 q
  9.   {
    % |' Y4 Y' v: L2 Q5 O6 D
  10.     temp=SysTick->CTRL;//读取当前倒计数值/ D* s3 O: o& q- r5 H
  11.   }while((temp&0x01)&&(!(temp&(1<<16))));//等待时间到达7 q% a# L  l2 B4 x
  12.   SysTick->CTRL=0x00; //关闭计数器
    ) C4 k/ {# U! [6 a6 T
  13.   SysTick->VAL =0X00; //清空计数器
    # h$ U4 x) e* o0 s. D: k
  14. }
复制代码
然后调用8 i5 z' N" d+ o) J4 j
  1. ; p& [# Z/ g8 F2 H8 P
  2. GPIO_SetBits(GPIOE,GPIO_Pin_4);  //熄灭LED灯                     
    : _4 Z) V4 z. x2 _. H( A0 r# P
  3. delay_ms(500);//延时500ms
    " y* a' v. V2 v2 R1 {# j
  4. GPIO_ResetBits(GPIOE,GPIO_Pin_4);//点亮LED灯                     9 k8 }! B, B/ L/ {) W& |. A
  5. delay_ms(500);//延时500ms
复制代码
就踩到另一个坑,延时不准。
2 Z8 k, y8 S& n3 l1 V) S8 s
; \$ m% Y( M; t
8 l6 d5 _" f' w3 W% n
原因是:此时SYSTICK时钟频率是120MHz的24位的倒计数定时器,也就是说一个周期,最多定时139.810125ms。不能延时500ms。" B/ P6 X* W7 ?! ?% j- {
这里再更正之前的一个错误,如下图
3 a5 f6 [" S+ z: B2 {" o0 p/ U
20.png
/ L( G% d. ^2 |
这个计数器的值,我们减去了1,这样才更准确。需要减1的具体原因在定时器讲解的文章中讲解过了,不明白的同学请看《STM32基础定时器讲解》。
1 n! w$ G, F" |5 m+ [6 `' V1 F0 v: w, t3 Y4 {; q
04
8 ~" `& w3 V& N" K8 t- Y7 E
总结
- R2 N% d3 A1 N+ m: i( K
  Q2 m1 b- o& x7 [. ]) d
5 _8 d/ q. b4 T/ k. O
总结:STM32官方手册并不一定是准确的,要亲自做实验,自己动手验证。这是个老生常谈的问题,大家都知道,关键还在于实践。/ |; T: J7 W4 `) y

& q7 t. b/ @  n4 U: U7 u+ o* T( M3 I' j2 X( x8 G' x

7 l  `0 N, L$ y  ^6 _+ b" @9 @& e- @* v& m! B9 n* E

2 L7 \7 z8 {3 d% @2 t" K  ]( |+ G
- _. M4 o' G9 j$ c
收藏 1 评论0 发布时间:2021-9-28 10:00

举报

0个回答

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版