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

基于stm32f103滴答定时器经验分享

[复制链接]
攻城狮Melo 发布时间:2023-4-16 18:53
滴答定时器:# }( Q1 v2 r% X. H3 n
        定时器的本质就是计数器。我们设置一个定值,然后计数器开始计数,从我们给的定值开始往下一直数,当数到0时,就做相应的动作(也可以不做什么,当把它用作延时计时的时候)。$ B2 t: I/ S4 K. b
4 m. m9 f/ X8 t1 r- d; P
滴答定时器systick是一个内核外设(即:内核自带的)
( u4 ], w9 [/ a' F6 f2 W( {2 f; s
- W2 j% \! q8 m: c/ _" M 所以在《STM32F10xxx参考手册中文版.pdf》手册中没有相关描述,我们需要参考内核手册6 e% ]; c! G6 ?% m1 l
《STM32F10xxx20xxx21xxxL1xxxx Cortex-M3 programming manual.pdf》) K3 u2 Y4 v  l! w% Q7 s! Y

0 _# @% r3 r: K5 E5 L        Systick是一个24bit的系统定时器(stm32F407的寄存器名字与位数都与f103一样,但是有些芯片定时器位数不同),向下计数(从定值开始数到0),当计数到0时,在下一个时钟边沿,会重复计数。所以如果我们用完定时器后,不再继续使用它时,记得要将定时器关掉,否则它就一直在重复计数,增加了功耗。: L, i8 e1 G* q! A5 i! p; v

8 ?- u2 _7 f! i/ ^3 s/ ]Systick的作用) w" o' G7 S; m* B
1.产生一个精准的定时# y2 x2 t) Y. Z
2.FreeRTOS时基由Systick提供
5 v( [" o: P, x, e( P6 s+ B
( [6 w2 c# G% J  L7 C3 l; w# L* x: D
MJ5}R@G1S4I9X34C]7ZVQJT.png $ K! u8 u2 H% X, p3 m# B
" n/ R  L, L$ R/ f3 e5 d, p% E
配置滴答定时器:
% |& }5 o9 P7 {7 G* f4 F+ {: |8 p6 Z
2e4242cbd0334a8dbcc333256a6d9fcb.png
' |" f( D4 ?- y3 _7 J$ Y% r) {% y2 r
* _/ A4 G' Q" }0 H' u        红框中就是滴答定时器的时钟,我们可以看到分频系数为8,所以滴答定时器的频率为:72M/8 = 9Mhz$ [# P) m2 {, v( X4 ~' d$ U4 h

) v# ^  J% Q% U6 M! {% Z; [配置滴答定时器我们需要下面几个寄存器:! S7 I& e* G/ U4 {, {% S
# q( V4 q6 T% |9 H
fb7ea8d208f14b14975bb8c2c2b8daac.png
; \  R2 h! |: m% H9 G9 V
+ @% V- {' S5 J4 U& \1 c! y! O/ _ 注意:第16位,是定时完成标志位,每当计数到0时,该标志位就会置1。
5 r0 o0 s% Q7 t
& i9 i, `+ D) t, ~1 {. S
439efd5325344681aa687218ae27eeac.png - l8 ]( v) r; N9 S% @
' a* ]0 a  V* L) e9 t& d* Y# y4 i
注意:由于定时器是24位的,所以给定时器装载的定值是有一个范围的,红框中就是范围,范围为0x000001 ~ 0xFFFFFF   ) M+ F- d7 N8 Y% O" K* u
/ a1 V  P, p" h, W& P* Y
SNCE360KPH_{K{XBYE)3WIW.png
5 l; y' N& K9 j6 t

, Z* q! w4 N) t/ R( p' f# C d1b8265cf617451caef6d6a879b01593.png
1 V) `6 l4 s# j* d. T7 Y. ?2 G) C  C
编程
; U/ }* E! d' e, V0 ^7 v产生Nms定时编程步骤:. Q. C0 V0 q+ P' U, n- j
        1》配置时钟源---AHB8分频,并且关闭定时器-----初始化(while(1)之前)8 t  K, D4 J3 Q. `  b6 y* b+ R
        2》设置计数值---N*90001 h: {9 R0 c* [2 A5 C! E
        3》清除当前值寄存器----注意:清除当前值寄存器,STK_CTRL寄存器的第16位会自动清0: g& M9 b& E; V$ k( p" Q" E8 _
        4》打开定时器
0 i. D) {7 a. x0 `: r6 g! X% x        5》等待定时器结束5 d1 V# k( e% [
        6》关闭定时器  ?- b8 r3 F0 z! J# t' H8 |2 }, D
7 l5 W, z9 l  A
代码:
2 G. E0 h* X+ ]. c, a: r. m- nuint32_t fu_ms;  // 1ms需要计的次数1 n$ K3 e- d1 c5 F" S/ f0 s4 X
uint32_t fu_us;  // 1us需要计的次数8 q9 ^. p" _0 T$ Z7 U7 ^) h6 D

' V4 e- q+ B4 S& u5 x( G: }# z; h* x" L* n6 M6 Q& l5 |3 N
void Systick_Config(uint32_t Sysclk)        //Sysclk就是系统时钟频率72Mhz,但是这里用72表示72Mhz
. E* z" W9 @' I1 N- j- p{1 W6 _0 \2 j8 y
    // 1》配置时钟源---AHB8分频,并且关闭定时器-----初始化(while(1)之前)1 G. T/ F3 B+ T, K# \, C/ w5 y( T* n
    SysTick->CTRL  &=~0x05;        //这里关闭定时器的原因是,有可能之前没有关闭,所以一开始先将其关闭,再进行配置- q$ T& Z( a. S, O2 J) K

  P) y7 y; @0 y' ]1 v    fu_us  =Sysclk/8;                // 72/8 = 9,即:表示9Mhz
9 d- O8 i! J  `, L* |5 W7 r( o1 l/ _    fu_ms  =fu_us*1000;        //9*1000 = 9000,由前面知道,在9Mhz下,计数9000次为1ms, a% U5 D5 ~( i; I- d
}- m& ~) c* X& K1 z4 [! ]( A

/ m+ ]1 X4 _. {! ?# e9 S  ^: f' N, f' C
// 定时Nms程序,ms级延时
% M! y$ ~6 [6 P; Wvoid Systick_NmsDelay(uint32_t Nms)
; A7 h5 K7 a  Q{" ?3 u( j- W0 U* l, T
    uint32_t temp;2 @7 j; {- O! L) K3 Q% p" l
    // 2》设置计数值---N*9000
; s4 [4 V) J) x# T0 t( H    SysTick->LOAD =Nms * fu_ms;
; m. g( @+ [% Z+ b
0 q* j4 u! k1 E  l    // 3》清除当前值寄存器----因为清除当前值寄存器,STK_CTRL的第16位(定时完成标志位)就会自动清0
! M" q8 l9 g3 j1 b# s5 M+ B    SysTick->VAL =0;        //清除标志位
5 d$ E# p& x6 O! V9 @+ Y8 H- P1 P- {1 w$ O$ G( b- J0 H5 ]
    // 4》打开定时器
! i3 v$ T0 r: |, G8 O, m    SysTick->CTRL |=0x01;$ j4 O7 i, R+ S' ]/ c2 B
- Z& ]( g; @( u/ [
    // 5》等待定时器结束- y4 q2 L$ q7 A' F: @) O
    do{
2 A3 @; w+ |" K7 `. S0 p        temp=SysTick->CTRL;        
/ k6 e( t7 `3 i' B4 ?1 s5 g! i    }while(!(temp&(1<<16)));        //判断CTRL寄存器第16位是否为1。如果为1,则说明计数数到0了,此时计数完成: ^3 j7 `2 M9 T- ^. i+ ?: ]

6 o. J* i3 D& @. M% ^    // 6》关闭定时器- l9 Z( ?; L9 ~1 n( i4 f! M: ~
    SysTick->CTRL &=~0x01;
/ T- J# X! y! I2 I}$ t2 e3 n4 W1 y" D) h

/ P# |* j0 B6 t0 X: n" Y$ |解析:
7 N" @# ?! P+ C$ R  P% Y4 f0 \/ g8 `+ n
1.我们先来看程序,定时器程序为什么被分成  Systick_Config(uint32_t Sysclk)  、Systick_NmsDelay(uint32_t Nms)两部分来写?
7 Q6 P1 M% f' ^8 a, _) [9 O
8 u$ {# D+ L/ Y$ _% c7 L4 l. a        因为,Systick_Config(uint32_t Sysclk) 中的配置,在整个程序中只需要被初始化一次就行了,而Systick_NmsDelay(uint32_t Nms)是延时函数,它会被多次使用。如果将Systick_Config(uint32_t Sysclk)与Systick_NmsDelay(uint32_t Nms)写在一起的话,那么多次调用该函数时,原本只需要被初始化一次的语句,就会被多次执行,浪费CPU资源。
4 q/ {, v% x# F/ \3 J/ f  n9 B' f) W8 p% @% }1 B8 w, S
2.为什么程序中的这部分需要用do.....while而不用while()?8 X: F7 ]4 ]( K* y9 M! F
+ E( ~! \9 l6 ~: U. i
8df6add7dbcb4ca6b71a02a7ec75dea9.png
. L/ d* }8 f3 `. S/ K1 h& [2 P" C! h6 b% \3 N8 x; z
        这里需要用do....while而不用while,是因为需要先执行temp = SysTick->CTRL; 将SysTick->CTRL寄存器中的值赋值给变量temp,然后再对temp进行真假判断,即:!(temp&(1<<16))。temp&(1<<16)为读取SysTick->CTRL中的第16位是1还是0,如果第16位是1,则为真,但是由于非 "!" 的存在,所以 ! (temp & (1<<16))就为假,所以此时do.....while循环不再循环下去。如果第16位是0,则为假,但是由于非 "!" 的存在,所以 ! (temp & (1<<16))为真,所以此时do.....while循环将继续循环下去。等到第16位变为1,也就是定时完成标志位置1时,如上所述,while循环将不再循环下去,此时计数器计数完成。
5 m( r* X) \2 r2 D8 [; t% f4 D. B. W$ o! c2 Z- I  C+ [
3.  由上面知滴答定时器的计数器是24位的,其计数范围为 0x000001 ~ 0xFFFFFF ,即:1 ~ 16,777,215,那么如果我需要计的数超过了这个范围呢?我该怎么办?
* v4 _3 ]% O! O/ \8 }) @+ }$ _5 i- N& g; C: E$ k
        由前面知,计数9000下,延时1ms。所以,16,777,215可以延时1864ms,即:  =  1864 。也就是说,在9Mhz频率下,将计数器计满,可以实现延时1864ms。0 J8 J5 n/ ?% C5 z& f

- r/ {2 D* C$ l8 M0 L3 m/ Y        的确,如果我想要延时2000ms,那么由于超过计数器的计数范围,那么滴答定时器就不会正常工作了。; B4 i4 ?3 o$ I8 z

" m  |" ?; j& v/ u9 l- l所以,我们改下一下程序:
7 d( X, E, ?- u& D$ W// 定时Nms程序: ]8 d/ f1 u9 w4 I9 D! M
void Systick_NmsDelay(uint32_t Nms)
* A0 g- [  q. N{
" g, x2 k! z$ w. s6 F6 x' Z    uint8_t  systick_flag=0;
9 r  U$ R1 Y. C* R: b    uint32_t temp;1 }% p1 g; m. O. Q, E

+ R/ [4 O+ ^6 z& W; Q& A  s* K) g/ a9 @) g0 H1 Y% V" i: i/ i6 _9 `  n
    while(0==systick_flag){) k1 }6 Q/ O3 f! W2 q
8 }5 x' I  r  Q: q) A
# Y. E  z! G; O1 r5 }& c5 {
        if(Nms>1864){
' `9 ^% a1 k3 s( B            SysTick->LOAD =1864 * fu_ms;8 Y) a# v) e, v: b2 v
            Nms =Nms -1864;
( \2 g: o3 k+ D        }else{  D1 m+ h& d$ e
            // 2》设置计数值---N*9000
0 w( t; y0 A' u2 _+ N/ A0 }* M            SysTick->LOAD =Nms * fu_ms;
. H$ c( r; Y# ^1 O  g            systick_flag=1;
- Y4 w- w. W3 i         }
! G6 c7 X! r1 ~5 X% [9 K! x3 g6 `. {& n5 M/ s) {3 O. X* e
6 l/ j% N7 |7 {
        // 3》清除当前值寄存器----因为清除当前值寄存器,STK_CTRL的第16位会清0) i0 Z7 A* Q# M2 G' l
        SysTick->VAL =0;+ m; `. Y, P  n) }1 ?+ }

8 z" J, ^6 e+ B3 v        // 4》打开定时器6 {% D: e/ X  b+ ?* d5 U
        SysTick->CTRL |=0x01;
2 G2 I$ J7 f7 B
3 [1 v( w8 Q; ^5 t# x/ u: h        // 5》等待定时器结束, a% Q) M- G# N2 a
        do{
5 b. U; ?+ n# t" ]# U" b( i* `" G            temp=SysTick->CTRL;
( G1 l% [; X! X$ ?7 h# w5 v        }while(!(temp&(1<<16)));
3 h1 F1 v- c* U$ h; }+ c
% q1 A1 D4 r0 h; W        // 6》关闭定时器
! F) j( b- K4 j8 Z' y' x; J        SysTick->CTRL &=~0x01;
. p" B! Y3 K. k5 Z! ]3 J" Z; @2 y5 V  s9 S1 n6 T
     }  B- h" O" f$ _, I# p4 I

% P' I# S- e8 y; V9 g! }& y+ Y7 X' i; q" x
}
& m1 K( I1 z1 z+ a: Z: z" D7 O$ z3 v1 m* P
这样的话,就解决了1864问题了。
5 C: Z5 C* u8 _) z3 U9 F( d# ]/ E+ }- y9 ~$ K5 |
' i: [, {# L% U
4.us级的延时程序是什么?2 s* }; z9 |8 r& u6 s
- d/ C+ y6 C/ M
// 定时Nus程序4 `' f5 q6 h; Z' k) Z
void Systick_NusDelay(uint32_t Nus); V" f* b& f9 c$ N' F# p* C7 @
{
  k' G5 A/ k' J$ `5 R    uint32_t temp;, X" X" Y$ B9 V' J' |9 v
    // 2》设置计数值---N*9
5 [, V" e0 N0 X; R* H    SysTick->LOAD =Nus * fu_us;        //us与ms的程序就只有这里改变了
9 N# T0 g6 O# M, x  f% Z% y
5 l& A7 F1 P  g0 `    // 3》清除当前值寄存器----因为清除当前值寄存器,STK_CTRL的第16位会清0
! a" H7 Q) Q# x4 \/ |6 \/ ?* h    SysTick->VAL =0;
( @* R7 K. X* Q$ C& B
+ q% \+ |# n, I/ ~3 M5 {0 E: L5 M: q( P    // 4》打开定时器
' m  ]! o0 G8 l- ~6 y5 \    SysTick->CTRL |=0x01;2 B; b$ r' A7 C; v4 d

( c* ~1 Y7 M. @9 M0 U/ G    // 5》等待定时器结束8 s+ r0 g" P: {' r# H+ S
    do{) B, h, H3 }$ ^5 o8 P0 E7 `* W# [
        temp=SysTick->CTRL;
$ u  j" l8 m0 g7 q: V; }8 s    }while(!(temp&(1<<16)));
: V% N! I- H7 p$ o7 Z8 S
* ^2 \% j, A9 N7 X( p* ~    // 6》关闭定时器
; H/ ^  A9 ^8 [! I+ P: Z5 [    SysTick->CTRL &=~0x01;, ]1 M2 G7 t; z# T7 J! f0 e
}0 g/ u* R% V' X6 I

( E' j* x' P% z% M( ?注意:; a6 L; ]5 ]$ \1 [( K
        在FreeRTOS操作系统中,不能使用我们自己设置的Systick来进行延时,因为FreeRTOS时基是由Systick提供(裸机的时基是由晶振提供的)。这是什么意思?6 W; d$ ~$ a. i% }* Y/ c

6 e6 \* P# h2 g( |/ d        FreeRTOS时基是由Systick提供,时基是需要保持不变的,所以Systick寄存器中的值必须是固定的,是不允许被修改的。但是,如果我们在FreeRTOS中用我们手动设置的Systick来进行延时,我们必定会去修改Systick寄存器中的值来得到我们需要的延时。所以,此时如果在FreeRTOS中使用我们手动设置的Systick去进行延时,那么就会卡死。解决办法:使用软件延时,delay()函数来代替Systick延时。代码如下:
) O7 i% J9 ]( v5 Z2 A
. J* P6 R: T; {; B3 Z$ U& q//初始化延迟函数
: D% L* x. V" M4 E, \* B9 ~//SYSTICK的时钟固定为AHB时钟,基础例程里面SYSTICK时钟频率为AHB/82 V- x5 a3 ^! D4 z
//这里为了兼容FreeRTOS,所以将SYSTICK的时钟频率改为AHB的频率!
! S& S3 ?2 u, L; V: d" e4 v//SYSCLK:系统时钟频率2 Z7 G) A' N' }, [4 k& C0 W5 k
void delay_init(u8 SYSCLK)  k% S$ c/ {) V  Q1 H
{
( i0 p) ^4 z0 e; S7 p9 @0 s    u32 reload;8 C) T, v  ]$ @8 `, Z
     SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK); 2 t, p" i0 T# y1 Z! n! Y' u
    fac_us=SYSCLK;                            //不论是否使用OS,fac_us都需要使用
3 `3 [# c% o$ ^5 w    reload=SYSCLK;                            //每秒钟的计数次数 单位为M       ' ?: Q6 t# K6 m% n
    reload*=1000000/configTICK_RATE_HZ;        //根据configTICK_RATE_HZ设定溢出时间8 T; s+ W' x. ]* O, a
                                            //reload为24位寄存器,最大值:16777216,在168M下,约合0.0998s左右   
' ^& V* n) M! ]) h. i2 y    fac_ms=1000/configTICK_RATE_HZ;            //代表OS可以延时的最少单位      
" m  y# p, O$ Z4 E  \8 X    SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;//开启SYSTICK中断
# e! r# ^5 ^# a* D    SysTick->LOAD=reload;                     //每1/configTICK_RATE_HZ断一次   
0 k: b$ ~/ m: e! i. B    SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启SYSTICK     
) _) f4 Z- T! i6 o! t/ \}             3 ~) x0 N, _% m/ |

- z3 Z- j1 p) e+ ?  X* Z) i' A% k( c9 S- D6 _2 Q5 z
$ x) p/ z$ B7 l) W3 e
//延时nus: r, W0 D1 M$ L7 g! N) B! P/ M; W
//nus:要延时的us数.    0 i4 N8 R- j+ p, ^  ]
//nus:0~204522252(最大值即2^32/fac_us@fac_us=168)                                          
: Y/ f7 I- @3 S$ ^, X5 g& Ivoid delay_us(u32 nus)
" M3 Z# M9 w( c{        % ?. k  D, W" i! l8 v6 [
    u32 ticks;
9 O' F- e: H) m& ]    u32 told,tnow,tcnt=0;
( w& _! H9 G0 i) g    u32 reload=SysTick->LOAD;                //LOAD的值            
" q4 @6 P0 W' @    ticks=nus*fac_us;                         //需要的节拍数 7 h; D, H3 k, N; [0 o2 Y
    told=SysTick->VAL;                        //刚进入时的计数器值* A: q+ w% k2 {
    while(1)' I9 E7 p! ?5 b
    {
  q: @. ]) N/ r; ~+ _! h        tnow=SysTick->VAL;    ) r8 y% t' p: j5 p7 V
        if(tnow!=told)
/ t- l& X, X" r        {        # K6 R0 \" W' S$ C' j
            if(tnow<told)tcnt+=told-tnow;    //这里注意一下SYSTICK是一个递减的计数器就可以了.
: L4 b3 }* I" e+ v+ u' F( O0 t7 x' z            else tcnt+=reload-tnow+told;        
& q1 F" D- l% E2 G: O2 b9 [            told=tnow;
7 K  M7 q! C% g4 L8 a6 p1 W            if(tcnt>=ticks)break;            //时间超过/等于要延迟的时间,则退出.
9 J* M( D. U$ i; J' E. y        }  + h- F- B# P, Z
    };                                            
' Q8 o0 q" B  n7 ?9 [' x0 X}  + L" A- o0 K( ?3 G+ i* h9 I+ x
6 y$ E1 e8 g/ P

+ b0 x* t/ l" a& E, k//延时nms
4 p$ |8 Y- O$ Q: f4 k" z1 f//nms:要延时的ms数3 g5 B( v! u) H
//nms:0~65535! P. L; `% j# G1 t$ @8 J) e# A
void delay_ms(u32 nms)9 _# g) \7 O. ~4 }, f
{   
) f3 y1 X( F# {  ~' k    if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
8 {9 \& |% }/ W$ j( g4 N# i    {        
. c/ t0 c$ ^! r" i; d8 A        if(nms>=fac_ms)                        //延时的时间大于OS的最少时间周期
* i2 E4 J  J; V+ w- m8 M        {
9 \( P+ Y  a% h9 W* x) T               vTaskDelay(nms/fac_ms);             //FreeRTOS延时
9 F  v# x2 x6 l) C        }: w: d* f+ v" w* D
        nms%=fac_ms;                        //OS已经无法提供这么小的延时了,采用普通方式延时    # z6 f6 k) o) u4 {, R
    }* r+ {+ G- J+ k
    delay_us((u32)(nms*1000));                //普通方式延时
" V, U" a' C/ G6 n8 Z2 |}
7 T0 F' t6 ?- |
1 n  ?: x3 r" `/ Q" l! E//延时nms,不会引起任务调度
5 h% Z7 d4 q3 [0 j1 t1 L9 Y//nms:要延时的ms数
/ P; e4 J5 H4 e8 `+ Q; lvoid delay_xms(u32 nms)# ?, K" H' m* x9 n9 B" e' K
{% r* u) u5 l! R) b5 U
    u32 i;  J2 e* Q$ s+ P" U7 B) g
    for(i=0;i<nms;i++) delay_us(1000);
8 x3 ?. F: L3 O4 V) `, Y# s9 T1 n5 h}
) c9 n4 P5 ~: f8 S8 l————————————————
6 h0 _9 o. H7 c# c) J9 u  }; |; o版权声明:无敌小小雷
. }7 W" o/ {' q: L. X+ X
8 k9 b1 w( P3 {) [0 R

3 o* u2 B' t- c: M% \! ~: f& f- A7 R& O* h3 H! x, u4 v
收藏 评论0 发布时间:2023-4-16 18:53

举报

0个回答

所属标签

相似分享

官网相关资源

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