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

STM32F1----SysTick

[复制链接]
STMCU小助手 发布时间:2021-7-19 13:39
1.SysTick定时器初始化流程
) }  }2 f: c: T2 K) B( B<1>调用SysTick_Config()函数完成SysTick定时器初始化。5 O  f) j& n7 R2 H: y, }# ]/ Q
<2>完成中断服务函数,在中断服务函数中递减定时变量,当定时变量递减到0时即到达定时时间。
7 b6 J# ]5 g+ ~# U<3>封装延时函数以供调用。' c8 v9 i* j4 h. I

/ X) L8 S0 D0 Z2.SysTick定时器中断延时例程2 z3 x) h) X8 H$ \" L# G
  1. <font face="微软雅黑" size="3">#include "systick.h"! O, `  F' S6 A& Y! R% x& N
  2. #include "misc.h"
    5 k. R& K3 k1 v
  3. #include "core_cm3.h"
    1 X4 |: `3 W5 b% R. b* P1 ]

  4. 5 N& y# a8 j# E
  5. static __IO uint32_t  delay_ticks;  //__IO == volatile & e, J, l  f7 y' N
  6. + U! C# J2 V, K
  7. /*freqency为systick计数周期的分频系数*/
    * P+ d, z! n4 Q# G" E- q- P1 T/ U
  8. /*freqency一般取值100000,10us中断一次*/
    ) i* U9 w' ?0 z% O+ x0 F
  9. void SysTick_Init(freqency)8 ^, A6 s1 Y! s7 ?* T, U, `
  10. {% ^0 f9 l" z( ^) Y$ ^0 I- `4 i5 c# f
  11.         /* SystemFrequency / 1000    1ms中断一次
    0 g8 i% @- \4 j( ?- X# Z
  12.          * SystemFrequency / 100000         10us中断一次
    ; k, @7 ^9 o8 s9 n, b. s
  13.          * SystemFrequency / 1000000 1us中断一次
    , E/ i& l9 i* R' x
  14.          */- J5 d3 `$ [# t5 T2 ~  \; v( w
  15.    
    : w: u  ^% r  c0 Z( {$ |
  16.     //初始化并启动SysTick计数器及其中断。
    0 ^% z* [. U1 W- k
  17.     //1 = failed, 0 = successful6 {. G: W! \' f+ y9 H  ]: l
  18.         if (SysTick_Config(SystemCoreClock / freqency))! P3 ~* ~4 B+ O
  19.         {
    ( f6 P$ k7 y$ M
  20.                 /* 捕获到错误,SysTick启动失败 */
    . Z$ g: J# A9 C
  21.                 while (1);+ s6 U! P6 i9 M$ W7 ]
  22.         }
    . c( f. a! \! T+ ^& H' q
  23. }9 S9 t- W% i) f$ L

  24. 7 `, B: P: b1 Q  y) d) ]2 R
  25. /*该函数延时times个1us*/
    # ?, _0 ~5 ]' ~% I# i. O5 b9 I
  26. void delay_us(uint32_t times)
    $ N- m% K9 ]. g# @
  27. {
    . f) s7 J! }4 t- l: y0 u' e2 ]
  28.         delay_ticks = times;# D) c+ [. Z! H$ z/ s
  29.     SysTick_Init(1000000);
    0 b+ U  A  _' p6 \8 C
  30.     while(delay_ticks != 0);3 V. H' E* S* F  D
  31.     & n# O  {! v' ?! C. \  |$ T
  32.         SysTick->CTRL = 0x00;   //失能SysTick+ k9 L$ I. e& Z( K1 K4 q) O: M
  33.         SysTick->VAL = 0x00;    //当前值清零
    6 g+ Y) |, z, d- Q* j* _1 ?5 q5 P
  34. }
    $ `6 o5 f/ G: c: l, o. Q
  35. 2 s5 U5 Z; g/ R% y  L) W% W1 q

  36. ' g1 x  M5 n5 d$ a0 {5 ~
  37. /*该函数延时times个1ms*/
    / A2 H7 L1 h. W% N3 w3 J
  38. void delay_ms(uint32_t times)
    $ I% ~7 R# U* Y. ?& Z4 Y" V
  39. {, ~1 @' z. ?! X4 L9 W
  40.         delay_ticks = times;
    . e3 H7 w% |- P$ o
  41.     SysTick_Init(1000);
    ( v7 F4 k8 W, [- M
  42.     while(delay_ticks != 0);# v6 H& H  i& e/ a' m3 @
  43.    
    ! t6 ^! o! B6 l5 T' |; j
  44.         SysTick->CTRL = 0x00;   //失能SysTick
    : D/ Q" u8 i5 O. b5 q* Y7 L
  45.         SysTick->VAL = 0x00;    //当前值清零
    + o, ?9 e1 S! H6 A% B
  46. }
    9 x, Y! J" ~0 [$ b5 k6 z

  47. 8 K3 H6 p) B( B/ d7 F7 G
  48. /*在中断函数中判断是否到达定时时间*/7 D) W/ e" Q4 z3 g) T# f
  49. void SysTick_Handler(void), m$ k5 Y7 C7 T8 t; j8 h
  50. {0 f  m4 Z' @. K& k
  51.     if(0 != delay_ticks)
    ; w$ P! H8 d5 ^5 k3 {& J, o% u& z
  52.         delay_ticks--;
    & I4 f2 @  I6 P8 p4 Z  W
  53. }
    % k! Z& a9 E4 N. o' ?, E+ x
  54. </font>
复制代码
这种延时函数只有在调用延时函数后SysTick才会开始计数,相较于野火那种一开始就完成SysTick初始化,然后一直SysTick一直计数,永不停歇,调用延时函数才阻塞的方式我觉得有以下几个方面的优势。
# ?$ s7 p2 m9 h, s
. L4 w3 {0 X) N/ [* b3 }

. [( M) r. K  y, n2 ^3 E1:降低MCU功耗,定时器工作毕竟是耗电的。只有在调用中断延时程序时才打开SysTick定时器,函数调用完成后就关闭SysTick定时器- b, o9 v! v# F, \! |- ^
2:提高程序稳定性,野火的方式下SysTick定时器中断一直在发生,只不过没有调用延时函数时,中断里什么也不做。在多任务多中断程序中,这样的延时方式可能会有潜在的风险。
# Q; M* C! N2 y6 ~* ~3:降低MCU负担,如果在野火的方式SysTick初始化为1us的中断,那么,每1usMCU就要响应一次SysTick无意义的中断,浪费了大量系统资源。并且频繁的进出中断会让主程序无法进行。
( [7 R2 T* g7 ?# s# v  ?注意:使用这种延时方式在普通情况下是可以的,但是一旦在其他中断中调用此延时函数,便会使程序卡死,比如在按键外部中断中进行按键消抖延时。
4 K  B) O4 F9 }3 e! P0 N
  1. <font face="微软雅黑" size="3">void EXTI1_IRQHandler(void)6 P* R3 x# N1 w  Y( S! g) \4 x
  2. {* F$ s5 n# h; W$ k
  3.         delay_ms(10);/*此时采用SysTick定时器中断延时就不妙*/
    5 J/ L6 b. ?) i. O8 ?
  4.         if(key == 0)% ]0 |8 ]' R' o# P# p
  5.         {
    * i, W5 U% M+ V
  6.                 LED = ~LED;6 f% q% t$ x5 J- K8 v
  7.         }: T( f0 r$ u1 ?7 ^) p" {
  8.         EXTI_ClearITPendingBit(EXTI_Line1);* M+ v, a+ t, v+ Z
  9. }
    # ^- [. w3 X% f$ I' O
  10. </font>
复制代码
原因 :触发中断进入延时函数后会死在 while(delay_ticks != 0)这个循环中; 这是因为SysTick_Handler(void) 函数是内核中断,内核将其中断优先级设置为最低,导致在外部中断函数里无法抢占执行,此时延时参数delay_ticks也就无法减小,程序在此处卡死。# f/ a) n, c+ s0 B8 t
* p4 g$ c! d" O; H

8 {) l& A% e% t解决:此内核中断优先级的设置在core_cm3.h中 ,比如将优先级设置为0后可以抢占使用,但是需注意调用延时函数的中断的优先级要小于systick所改的优先级,经实验是可行的。! [2 G; F5 }! n3 v
  1. <font face="微软雅黑" size="3">static __INLINE uint32_t SysTick_Config(uint32_t ticks)9 {/ C  q% m: e; Q% M
  2. { 2 e9 Y- ?- }4 {3 {
  3.   if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);            /* Reload value impossible */  j" C: D- r$ @2 G& ~$ ]
  4.                                                                
    2 Y9 h/ S! _1 J( l& |
  5.   SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;      /* set reload register */9 ]3 D& z# W1 ]% }8 r4 e* B
  6.   NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);  /*中断优先级设置为-1,这也是系统默认的设置 */ ! v$ D& e3 x  X, u
  7.   //NVIC_SetPriority (SysTick_IRQn, 0);    //将中断优先级改为09 V" m7 i: p* x9 X
  8.   SysTick->VAL   = 0;                                          /* Load the SysTick Counter Value */
    ; N) j1 A: ?" G7 r
  9.   SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |
    # e) \% w2 K! W1 B$ r1 {
  10.                    SysTick_CTRL_TICKINT_Msk   | 6 P$ K6 M$ q/ H' S
  11.                    SysTick_CTRL_ENABLE_Msk;                    /* Enable SysTick IRQ and SysTick Timer */
    1 K; W6 h: u- s+ l8 I
  12.   return (0);                                                  /* Function successful */
      }) K' @3 o) `8 @* a. ]6 j
  13. }
    " {  X/ _* O/ G4 M1 s4 }+ H* G1 w
  14. </font>
复制代码
3.SysTick定时器查询延时例程0 W3 ~2 A/ Q  g6 I7 j4 }
  这种方式利用设置Systick时钟的重装载值,然后重装载值倒数完毕后会将SysTick->CTRL的位15置1,对该位进行判断便可知道延时完毕否。这种延时方式无需进入 void SysTick_Handler(void) 中断,在中断中使用也不怕优先级问题了,就不需要改内核文件了。这种方式虽然看起来有点繁琐,但是也不难理解。\
7 Y1 [! j$ D; K* U
  1. <font face="微软雅黑" size="3">#include "delay.h"
    ! z' U1 z/ z* Z9 N# i

  2. 9 I6 _/ G# K5 v# H* G0 r
  3. static uint8_t  fac_us=0;                                        //us延时倍乘数                          
    ! Q# {# x. M$ b! Q' [8 M0 b
  4. static uint16_t fac_ms=0;                                        //ms延时倍乘数
    & \/ ?' p( v9 J7 C8 y

  5. 5 P: J$ \+ T! K: W
  6. //SYSTICK的时钟固定为AHB时钟的1/8
    0 o1 F' J; t& ]1 H$ ]7 i
  7. //SYSCLK:系统时钟频率/ O7 z) D* W9 `* j0 c
  8. void SysTick_Init(u8 SYSCLK)
    ' F4 ~6 O5 C& k8 n
  9. {$ |! c6 \$ R4 D( w4 f
  10.         /*此函数来源于misc.c*/
    ; I; |* I: f/ o
  11.         SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); 4 Q& \. D! J0 Q  n" L
  12.         fac_us=SYSCLK/8;                                        9 b3 Z; }' U4 u1 t9 k& V
  13.         fac_ms=(u16)fac_us*1000;                                   % r  I; b! P  u% w! G9 J
  14. }                                                                    9 K9 A. M: Y7 N8 P+ u2 ?- [. `
  15. / J2 S; m5 t# G" r- P6 F% V
  16. ! d$ w7 z: |- k* S1 u7 H* D3 {3 }
  17. /*延时nus*/                                                                                       9 y+ M7 N8 \; f7 q8 I4 `0 \
  18. void delay_us(u32 nus)
    1 C5 f5 R3 \+ ~  {+ o
  19. {                  i9 w! `, C, B( b1 p2 U- \
  20.         u32 temp;                     : b) R! c9 p  r, f/ m  E6 l" P
  21.         SysTick->LOAD=nus*fac_us;                                         //时间加载                           ' o3 d2 V/ m' c4 a  i
  22.         SysTick->VAL=0x00;                                                //清空计数器
    5 T$ x* n$ |7 J0 i  \$ @3 o8 q
  23.         SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;        //开始倒数          
    7 T; _/ \* |9 p" m. O
  24.         do+ g; l8 ]4 s) d$ f* S' A
  25.         {8 C, S5 u& L- r  [% U- F8 q
  26.                 temp=SysTick->CTRL;$ W5 y. J' K6 x8 D8 s8 `& L) e
  27.         }while((temp&0x01)&&!(temp&(1<<16)));                //等待时间到达   
    2 x0 X( Z" t8 @- T
  28.         SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;        //关闭计数器
    # F1 D4 K) Y8 N* j( b6 C1 l
  29.         SysTick->VAL =0X00;                                               //清空计数器         / h, o" h7 W) e/ K' s3 j
  30. }
    ; t8 f2 ^% I( D, d2 V' b1 d- V5 N" F

  31. 0 w% p4 D( W* h+ I/ d3 j( p8 H
  32. /*延时nms*/2 K8 J! m/ P5 Z. C
  33. /*注意nms的范围2 A( X8 @$ |9 `- U/ f/ f
  34. SysTick->LOAD为24位寄存器,所以,最大延为:nms<=0xffffff*8*1000/SYSCLK' P, O0 R& W$ B
  35. SYSCLK单位为Hz,nms单位为ms,在72M条件下,nms<=1864 */# w8 l7 o- D7 k+ c
  36. void delay_ms(u16 nms)
    ) r, z# ?4 m4 }5 ^% J$ k
  37. {                                     ( z% n- y" b7 z2 I% j
  38.         u32 temp;                   3 W$ E" P4 K8 s% B
  39.         SysTick->LOAD=(u32)nms*fac_ms;                                //时间加载(SysTick->LOAD为24bit)+ o5 P( h/ d& I, Y/ O) [- [
  40.         SysTick->VAL =0x00;                                                        //清空计数器
    + r$ N$ a4 P: D! W9 E
  41.         SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;        //开始倒数  " R- W  P; ^4 t  s4 n4 P  p* ^7 [
  42.         do
    # O( ]" x# y, Y9 F
  43.         {) b$ @4 W, R3 L7 g' u
  44.                 temp=SysTick->CTRL;
      R0 v' h4 D! J9 f  ?$ n: v3 w. R. W
  45.         }while((temp&0x01)&&!(temp&(1<<16)));                //等待时间到达   
    / ^% L! Y& @6 q4 p& G
  46.         SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;        //关闭计数器
    2 y: d3 W* X, ^
  47.         SysTick->VAL =0X00;                                               //清空计数器                      ) R0 R, \1 r! e$ _% }
  48. }
    6 L  C' c! f7 X/ O
  49. </font>
复制代码
4.SysTick相关函数- \" [' g) g' E) \0 Q" ]# Q
  1. <font face="微软雅黑" size="3">/*Systick时钟源选择  misc.c文件中*/
    ) [; K  D, F8 A( o+ P5 G
  2. @arg ticks
    & ~1 ]3 z  s/ l6 P+ [" ]1 e
  3. /*也就是两次中断间的计数次数,即两次中断之间有多少个sysTick时钟周期,归根结底也就是规定两次中断的时间间隔。*/, r2 ?4 m" H5 q, E, {
  4. static __INLINE uint32_t SysTick_Config(uint32_t ticks)  
      ?( L( P: T2 E
  5. 5 H4 G8 f& z- ~/ ?. T# b# U
  6. /*初始化systick,时钟为HCLK,并开启中断*/; |# R' O' Z1 j& t& w
  7. @arg SysTick_CLKSource_HCLK8 ~2 N( a: w1 p7 Q) `& r
  8. @arg SysTick_CLKSource_HCLK_Div8& {$ @+ T0 d1 Z/ c5 q2 O" G8 i
  9. void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource) //core_cm3.h/core_cm4.h文件中- a6 G9 F0 S3 w) M& o* p$ S" l
  10. % I" ?( m1 F: S* `+ ^
  11. </font>
复制代码
举个例子:假设SysTick定时器的时钟为HCLK即72M,tick为1000,即两次中断之间时间间隔为1ms(此处并非外设定时器的计1000个数,而是定1ms时)。
. X$ ^" _' }$ F! T
+ v/ g0 G9 {9 N- R$ U9 a

) o. C1 r, _) I; `* u9 g2 X1 m5.SysTick相关补充
$ e: u% y9 V# s0 Z3 w1 \# g
& Y0 r& e2 J2 v* B9 g* Q

+ I. w2 |' i& T9 F% I; h9 B6 y& ~1.Systick 是一个24位数据宽度的倒计数定时器,其计数范围只能到 1677215(2^24),当计数到0时会从RELOAD寄存器 中自动重装定时初值。只要不把SysTick的控制及状态寄存器中的使能位清除,计数器就不会停止。
4 j/ _* B- P: V$ N2.SysTick 可以产生中断、设置中断优先级,有专门的中断处理函数SysTick_Handler()。SysTick定时器被捆绑在NVIC中(这也就是为什么SysTick的设置在misc.c文件中),用于产生SYSTICK异常(异常号:15)。即它从计数初值记到0时可以产生中断。Systick中断的优先级也可以设置。
& g7 t1 [( N1 `( |3 b3.Systick相关的寄存器有4个。: `+ G8 R& M. Q; l
  1. <font face="微软雅黑" size="3">1.CTRL SysTick 控制和状态寄存器
    & x  c1 @7 N- i7 k
  2. 2.LOAD 自动重装载初值寄存器
    ' ]2 E$ C) w4 I$ s" |
  3. 3.VAL 当前值寄存器
    ' G% ]8 }6 T/ ~! v  S
  4. 4.CALIB 标准值寄存器(用于校准,不常用)</font>
复制代码

' C5 h; y! P' ?  E" ~/ B
  l+ V. A/ i, P4 h6 K4 c9 q$ n  b
赞 收藏 评论0 发布时间:2021-7-19 13:39

举报

0个回答

所属标签

相似技术帖

官网相关资源

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