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

【经验分享】STM32的Systick滴答定时器及延时函数编写(内附代码)

[复制链接]
STMCU小助手 发布时间:2022-5-18 21:00
一、Systick滴答定时器是什么?9 U  h0 W2 m: L2 n

% I/ u2 l5 [$ \5 \! d    Systick定时器就是系统滴答定时器,一个24 位的倒计数定时器,计到0 时,将从RELOAD 寄存器中自动重装载定时初值。只要不把它在SysTick 控制及状态寄存器中的使能位清除,就永不停息,即使在睡眠模式下也能工作。) g$ b5 w; x: x' @- ]8 p( ~

/ o# [/ G" X0 C! R, ?  定时器既是在规定时间之后执行某个操作。
- C( K1 A* `" r* F* _3 `1 v) U# w4 }
    Systick定时器常用来做延时,或者实时系统的心跳时钟。这样可以节省MCU资源,不用浪费一个定时器。比如UCOS中,分时复用,需要一个最小的时间戳,一般在STM32+UCOS系统中,都采用Systick做UCOS心跳时钟。% G9 Y2 F# A, \/ m5 x. R
$ N  R  l* W4 j+ s! p# L6 k
二、Systick一般用来做延时,精确延时
; @) C8 R6 K6 b+ E8 F
* E% Z+ D3 {4 z- G/ z; E8 M一共4个Systick寄存器
' F) p( R1 \+ x1 J9 [
4 L  T% P0 r1 ~- h: h1 j    CTRL             SysTick 控制和状态寄存器 ' b. l, M* W) s# m

" u1 B2 d9 B. u# I    LOAD             SysTick 自动重装载初值寄存器
) y2 K* G. `# r2 L. ^( a/ A/ B2 ]* {7 Z8 D4 h/ {0 b
    VAL              SysTick 当前值寄存器 0 ?% O2 _& b/ b; q0 A' v
, M! ~, }) }' }
    CALIB            SysTick 校准值寄存器7 N( y3 P1 ^7 G' g2 F
! R9 V2 t1 R4 N: L& ^0 W
三、systick定时器原理6 T0 N) g5 H# v9 T4 j1 M

8 ]7 C$ G# e% y. {这个定时器是设置一个初值,然后这个初值减数到0,就是定时完成,完成之后可以产生中断,也可以不使用中断。) E- t* |% ^$ }( g9 `- p5 B
5 W/ u* F: M7 `- R
EZM`KG2081BWE(GB}VRW1YU.png
  ~- R* c+ ]1 u. U" A- A& W# C  ]( |
第0位是使能位,可以使能或者使能定时器7 g5 a( I' {, Q9 q" c' m+ g5 j
; s/ \4 u6 ]2 T7 ~; [) _1 z+ M
第1位是使能中断位,减数结束之后是否产生中断。
: x: Q* H6 U) W8 ~. ~( o5 Y0 N1 @; n- i
第2位是时钟选择位,可以选择外部的或者内部时钟作为时钟源。4 R# X) O( Z  s, _$ {3 U% v

% j6 p, L& J* r7 w0 n6 K第16位是标志位,减数到0之后该位被置位1,读取过后自动清清零。
+ s# |) z6 y6 T9 D( g
- |9 `6 Y$ N# F/ r1 I四、uSysTick 重装载数值寄存器-LOAD
& j2 K$ \1 Q, u( Z
6 A" t1 E9 W* N1 x/ B( O ICO9H2XKEDQJK}B@}KG{8VS.png " t( Z/ p& q0 v% T8 f/ A

& u( o4 s- b: \0 h! y; D* o, v当当前值寄存器减数到0,自动会将把这个RELOAD的值赋给当前值寄存器。
1 q. d- b+ L! Y2 O- @# W  j
5 ?2 O& [; i$ nif(VAL == 0)
0 Y! L5 Z7 l& r) B5 E' Q4 p" h0 v
$ M; ?$ j: {) ]- Y    VAL = RELOAD/ W* E1 P: k" |  B) ]

. y: H& J$ @% p$ A$ K五、SysTick 当前值寄存器- VAL, z: {( }4 t1 B( ]! [

- N1 Y4 |- X# m' O UFS`Z_@M4~9DKVW@2D9T2]5.png
0 I8 }! w1 k6 Y' q5 o  _: D2 J/ s4 z; O: n4 {- {
VAL就是从初值(RELOAD)一直减一,倒数到0的时候,重新将初值(RELOAD)赋给VAL。) ]& T- H- d  ~3 f
$ L# J& o: p7 |" o' v* u9 \; u- q( V
六、滴答定时器的实现# _8 @1 L5 T2 X5 M: Y2 H# K

7 O% r' P' f) P4 q  f7 ~# L/ U对于STM32,外部时钟源是HCLK(AHB总线时钟)的1/8内核时钟是HCLK时钟
5 P& u, |1 C0 C1 s& T, [8 v3 T+ e" o
: a  r2 {+ {2 V! x) ^; F1、选择时钟源
' {/ H( P9 t& C2 Z
: \, J" a# `5 K! s8 U* ~8 F    SysTick_CLKSourceConfig();//选择时钟源 misc.c文件中8 u3 c! o: Q3 C& p& g( U1 z

$ ^' z" P: f! | (]]A00RCAKI6Z0MN2P.png
0 n3 a3 J( u& h& _: I0 b; b* k8 L. q( k$ s9 u$ \( M( ^4 W; Y: N8 c
    SysTick_Config(uint32_t ticks) //初始化systick,时钟为HCLK,并开启中断 core_cm4.h文件中
2 g4 _2 |: r  t% N3 J; h
) n" ?$ L: i/ F7 j, V6 e 6]NZ4(S)@AQ(5GM($LD5YVK.png 2 b: Z" i' @9 y9 W  ^7 p+ J; W# l: q2 ]

: ^- {: P  Y6 I. G9 ]2、编写延时函数: ?3 q& X6 @: g: D  Y
, u0 K; q4 F/ Q" Y- b7 V9 [+ n
3、设置中断优先级分组( @9 g! }' N9 r% t" r. Z

. F. D" ~: V8 b, d* Q# J6 W    NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2);
2 j/ R  m2 K/ E* k. Z
3 J- {0 {0 U* \7 M5 ]% T' X    一般使用中断都需要在最开始对我们的优先级进行分组,分好之后再进行优先级的设置。2 R1 {4 J7 v  q$ `

) y, d7 E! i( c    尽量不要在同一个程序里面频繁的改变分组,频繁切换分组可能会使程序出现不可预料错误。8 {6 f7 n- V. y$ p0 i/ C

; b0 p& f* _; M/ h& Z8 T七、用中断的方式实现delay延时
) v( [! D  }5 d
Systick中断服务函数:void SysTick_Handler(void);
' @$ F( V; \, Q" A
0 ^. X! H4 u3 F1 x/ y 2Z5JCHW%)M47(KDNRP3`8(L.png 0 E9 a( W+ A. Z7 w# _8 D& G7 W

) @5 q2 }7 ]$ U! O- R注意:
& n. U; {4 B  P& {6 Q% I
/ I+ e7 r+ z; D/ J3 T% q1 G$ p  uCortex-M系统中,Systick代码可以通用。3 L- S0 A1 H' B: ?3 T) R0 D
8 x# o! [6 x! ^8 p; |1 p
  如果使用中发现延时不一致,问题一般都是因为不同内核时钟不一样而已。修改ticks值即可。, ~1 K4 Y" d7 l# D0 `3 `
& P' x( u2 r! g9 B: q( j" F
参考代码如下:. i$ @; O) f& V( s# Q( E) V

2 c4 ^5 E5 U1 K& ?
  1. void init_delay(void)# s: }' V' _  i) n  ~* W, v
  2. {8 J# u* r, o+ g9 K* _
  3.     /*我们外部晶振为8MHz,然后倍频到168M,那么Systick时钟即为21M,也就是Systick的计数器2 o+ _' L) |1 r; ]: w; R
  4.     VAL每减1就代表过了1/21us*/2 o: a6 j3 D! P+ t' Z, I" G

  5. 5 ^% h1 V1 W( A- Y
  6.     //1.选择时钟源(选择外部时钟)3 L0 y2 Y5 W- R
  7.     //外部时钟要/8,内部始终不用
    6 W+ `& {- p' P7 ^  L
  8.     SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); , r% l6 W8 Y8 a+ a
  9. }4 ~7 }/ [6 f+ F( q3 C' S0 @
  10. 2 R. n# R0 `8 z) y4 `% f$ [  x7 n
  11. //2.编写延时函数
    2 a; o9 ?8 V1 x8 J, X7 p4 L' P
  12. //最大延时不超过798.915ms,即(2^24)/21/1000
    " _. q% q2 J/ e, N" ~
  13. void delay_us(u32 nus)
    " a$ _$ {0 V' E% E8 \; F
  14. {
    / d* y6 ?& _7 Z% w
  15.     u32 temp = 0;$ e. K: Y2 _$ k% y

  16. . F; \6 @2 w, h  y* g  u; @
  17.     //1、实现1us*nus的延时- V# t  j) h; \4 k
  18.     //21外是外部时钟频率,如果选择内部时钟就是168M/80 M( J3 V6 j- X6 J4 I, f( X
  19.     SysTick->LOAD = 21*nus;//设置自动装载值为21(1us)9 k, Q- b) C2 a
  20.     SysTick->VAL = 0x00;//设置当前初值为0
    " a. X# e6 g3 s% c# r! N
  21.     SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; // 开启(使能)计数:使能SysTick定时器+ E+ ]8 {5 U6 S5 d

  22. ' d+ w0 L1 L5 o/ a/ v- U7 M
  23.     do  @" W- Z, s. t; n2 j! S
  24.     {7 A! I9 y" e0 m1 j
  25.         //读取控制寄存器
    / h$ c6 {2 w. t# [4 H+ G+ `
  26.         temp = SysTick->CTRL;
    # ~# Y3 M; C, }
  27.     }while(!(temp & (1<<16)));//等待计数时间到达(位16)
      }, n. a) @6 G8 |( ?

  28. " m5 L! _: J" A# `" j# j" z
  29.     SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; // 关闭(失能)计数
    . J/ @2 J& }0 V. ^: t* |
  30.     SysTick->VAL = 0x00;//再设置当前初值为0,重置VAL : q( k9 Q( [: a7 H7 y
  31. }
    . [/ |' g! U3 f0 x9 J2 [. }( i
  32. & l  _6 I; \9 l6 b9 V5 Q
  33. void delay_ms(u32 nms)
    * R8 ?8 d2 ^% \+ r
  34. {
    & s2 |. ?, k& Q, L5 {/ \
  35.     u32 temp = 0;7 U& m" h  F- d( M) w" w

  36. , z. h. p; g# `* O7 Z
  37.     //1、实现1us*nus的延时
    % \1 ]2 q# z1 t  ~) @" @
  38.     SysTick->LOAD = 21000*nms;//设置自动装载值为21(1us)8 c7 Q7 X$ }' ^9 C6 m- C: u
  39.     SysTick->VAL = 0x00;//设置当前初值为0# q/ _# n  @$ h, s
  40.     SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; // 开启(使能)计数
    5 z) E* ~4 l% j" P5 q4 G
  41. , {: W7 i4 F- W9 `2 d6 J  N$ o+ i
  42.     do1 v4 S, w: T& U+ m
  43.     {
    9 j6 _  E; K- N# @  [
  44.         //读取控制寄存器
    - D4 x& U3 X# @* i  _1 `! C; S% l
  45.         temp = SysTick->CTRL;
    + A3 F5 s9 z" J' [: k' J* V
  46.     }while(!(temp & (1<<16)));
    # T  B1 y& u8 J8 r9 ]2 H

  47. # K6 h( @  J& l* {& a* `7 ~
  48.     SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; // 关闭(失能)计数
    0 W6 X5 ?+ Q# B" I
  49.     SysTick->VAL = 0x00;//再设置当前初值为0 2 V5 g# v+ N5 B6 |" @8 I8 W
  50. }0 |' p& ~/ x1 U2 f2 e7 C1 S. Q

  51. & |, a8 L) R6 q# M- X, Q) o/ o
  52. void delay_s(u32 s)6 X1 s1 j5 ^4 r$ I
  53. {. Y0 R+ Q" O8 {# T( Q! U* _% c' n
  54.     while(s--)
    * U  L# C3 W8 |8 W- N9 L" d
  55.     {
    5 t* B: A& J2 ]1 j- e) r% r5 c& H8 ?
  56.         delay_ms(500);
    * W  e  K& e' ^/ t
  57.         delay_ms(500);4 n. G  I+ |4 r& e  o- L: |
  58.     }
    2 E: S' ?' t3 Z) A: n8 _" Q7 v# `& c
  59. }
    6 L6 ~- o' x+ L# O* U# k
  60. + p1 K, e7 w: d) Y
复制代码

: l5 ^/ R0 A( k! r$ P/ n$ `* ?% @3 O

- h/ Q$ H: d7 G/ |. Q
赞 收藏 评论0 发布时间:2022-5-18 21:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

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