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

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

[复制链接]
攻城狮Melo 发布时间:2023-4-16 18:53
滴答定时器:' ]! q; J1 X+ V3 q
        定时器的本质就是计数器。我们设置一个定值,然后计数器开始计数,从我们给的定值开始往下一直数,当数到0时,就做相应的动作(也可以不做什么,当把它用作延时计时的时候)。
4 m4 D" @2 q* Q+ J* y! g; G, A
0 u/ K* {" [) c- t( o7 i" M滴答定时器systick是一个内核外设(即:内核自带的)
' M: Z/ I! j1 u) t' o
6 X+ c# G( C6 A% g* d 所以在《STM32F10xxx参考手册中文版.pdf》手册中没有相关描述,我们需要参考内核手册+ d0 @# \7 a) t4 f1 I) z
《STM32F10xxx20xxx21xxxL1xxxx Cortex-M3 programming manual.pdf》  t7 W, f/ o/ N' J" C
2 [- `# w4 D" _1 U. p
        Systick是一个24bit的系统定时器(stm32F407的寄存器名字与位数都与f103一样,但是有些芯片定时器位数不同),向下计数(从定值开始数到0),当计数到0时,在下一个时钟边沿,会重复计数。所以如果我们用完定时器后,不再继续使用它时,记得要将定时器关掉,否则它就一直在重复计数,增加了功耗。( s# [( e/ H" ]+ _
0 C1 S! V. d$ n; l# R8 k
Systick的作用
& Y5 |6 x  D$ ^8 c$ P5 E2 K1.产生一个精准的定时
; [) Q7 |" R% e2.FreeRTOS时基由Systick提供1 s6 ^% w4 g$ _: p

5 a2 G! J  V1 T6 p& M. H/ M8 y
MJ5}R@G1S4I9X34C]7ZVQJT.png
: P. G7 P1 Q6 d* n6 i4 m. d. ~- R
1 t$ _6 k; G7 O6 i3 o$ t配置滴答定时器:
; A$ m9 Z- v" S' I+ t2 `: ]; T( r/ Z4 y! B* z. K/ c
2e4242cbd0334a8dbcc333256a6d9fcb.png
" Y+ a0 `3 }( v9 s! U- I& Q" }3 F1 ]5 p" O6 h7 e( |
        红框中就是滴答定时器的时钟,我们可以看到分频系数为8,所以滴答定时器的频率为:72M/8 = 9Mhz+ x  R  B; O2 g
/ p% K$ M, `7 _' `9 J3 p
配置滴答定时器我们需要下面几个寄存器:
+ V: M  v; ^% i" r; r, y
. r7 Y  \. q: p- v( _6 O6 i7 ]
fb7ea8d208f14b14975bb8c2c2b8daac.png , }4 E" z8 E& W8 D! n! \
. D* i2 |# v& A  I
注意:第16位,是定时完成标志位,每当计数到0时,该标志位就会置1。# t* q0 [. [% T
) N* S, j) b+ I/ s3 n+ s2 P1 M
439efd5325344681aa687218ae27eeac.png
% y- `. y7 R) w5 f
; n( W: m+ \/ F" c$ S5 o6 Y 注意:由于定时器是24位的,所以给定时器装载的定值是有一个范围的,红框中就是范围,范围为0x000001 ~ 0xFFFFFF   
- M' T; \  I6 j8 P4 d/ s/ M9 G
$ t$ t6 ?0 B/ I7 q5 z0 F2 i
SNCE360KPH_{K{XBYE)3WIW.png
: Z& I, V, H: i3 I3 F- g

: X1 T6 e5 g' r: c d1b8265cf617451caef6d6a879b01593.png & W5 a& G! J" e- D, `, g/ X: M  p
9 W6 i  J; E) ~. ~7 \
编程
/ K% g- p+ Z4 @: B2 D0 I产生Nms定时编程步骤:
. o- I! s8 \# j( K# d  e' c        1》配置时钟源---AHB8分频,并且关闭定时器-----初始化(while(1)之前)
* C  U1 Q# ?! D6 f& ^, O/ w5 t        2》设置计数值---N*9000
9 G0 e) D& E( _" [- C$ p$ U        3》清除当前值寄存器----注意:清除当前值寄存器,STK_CTRL寄存器的第16位会自动清04 A" F. M" O  C- E5 V- Z
        4》打开定时器0 n" d9 r/ P& Q) d
        5》等待定时器结束. O- x" @; }* }3 @$ |9 k
        6》关闭定时器7 J" I/ m$ a0 o4 I' l* ?! F& Z7 q+ Z6 c

, {+ K  h- j+ `$ P) Z3 y代码:
  D# b1 S+ x! \4 H- O+ ~uint32_t fu_ms;  // 1ms需要计的次数
+ D1 D( s4 z' A" @# r% @8 c! u& luint32_t fu_us;  // 1us需要计的次数* Z: q9 `# E/ u0 I
% V3 E4 G+ o/ w) B$ p) W8 D

% \  ?; i8 Z& M, w' w6 o6 l( t: Wvoid Systick_Config(uint32_t Sysclk)        //Sysclk就是系统时钟频率72Mhz,但是这里用72表示72Mhz
( n8 n& i7 n+ w+ Y- G& Y{
+ R+ W% s" Z+ U( m2 ]2 u    // 1》配置时钟源---AHB8分频,并且关闭定时器-----初始化(while(1)之前)
( t# b- g# h6 Y  b9 }6 s    SysTick->CTRL  &=~0x05;        //这里关闭定时器的原因是,有可能之前没有关闭,所以一开始先将其关闭,再进行配置, ?) g( G5 Q+ N1 c2 p
3 ~2 l3 J4 j4 N
    fu_us  =Sysclk/8;                // 72/8 = 9,即:表示9Mhz5 ^: d& e, _5 z- {& G
    fu_ms  =fu_us*1000;        //9*1000 = 9000,由前面知道,在9Mhz下,计数9000次为1ms5 A0 X2 B- Y% x; ^: ?
}
0 D( Q) t2 ~: V  a* D
- I' H9 K) q: K4 r! A1 m1 T
3 }- _! y! T3 R% b! d; E) j// 定时Nms程序,ms级延时
) K$ f7 e$ `/ H4 p: R1 Nvoid Systick_NmsDelay(uint32_t Nms)
3 m: _' S8 D: V# G{
: Q5 t1 |% G+ q% j, n; C    uint32_t temp;
$ t  M+ y' R, t+ L& j. Y. U7 p    // 2》设置计数值---N*9000
4 {6 a4 K& @" D/ g6 _8 A$ c    SysTick->LOAD =Nms * fu_ms;( B* b7 H! @9 Y6 ]: n7 T3 S

& k6 T/ |. J- a% Z/ s    // 3》清除当前值寄存器----因为清除当前值寄存器,STK_CTRL的第16位(定时完成标志位)就会自动清0
; }9 J3 s8 M* m, G8 k& Q    SysTick->VAL =0;        //清除标志位- G2 L6 s3 f8 j$ D' f7 A2 |: B

4 `) l2 \. U8 g# |! h    // 4》打开定时器* s: s: r5 ?) g( H; p0 O) s
    SysTick->CTRL |=0x01;! C0 [$ \( w# _3 E# A  z9 u
. y* H5 a: D" }3 ^' \- a& l, Q7 o
    // 5》等待定时器结束0 W5 N$ A& _6 W1 p( u1 I
    do{
6 ]7 t" _! Z0 i; m        temp=SysTick->CTRL;        5 q; u4 V- }% h. I; [* Z
    }while(!(temp&(1<<16)));        //判断CTRL寄存器第16位是否为1。如果为1,则说明计数数到0了,此时计数完成
# G( u  U7 d, v5 U5 P1 Y1 ^4 b0 D, D2 @7 b, r. @: U5 W. {
    // 6》关闭定时器
7 g+ x' }8 J9 V2 x  g# Y    SysTick->CTRL &=~0x01;5 {2 R* g. h, u, g/ @& w1 @) s
}
2 \5 g# f: B+ }8 Z* B( V
' _$ T. ~3 h4 d' q* |解析:& P9 e1 M3 ]2 A/ Q( z" b- v
% b; I! @* U  C2 j) w
1.我们先来看程序,定时器程序为什么被分成  Systick_Config(uint32_t Sysclk)  、Systick_NmsDelay(uint32_t Nms)两部分来写?$ M9 t! d1 y& |1 R: w2 I
, D1 L- D& [8 a1 Q
        因为,Systick_Config(uint32_t Sysclk) 中的配置,在整个程序中只需要被初始化一次就行了,而Systick_NmsDelay(uint32_t Nms)是延时函数,它会被多次使用。如果将Systick_Config(uint32_t Sysclk)与Systick_NmsDelay(uint32_t Nms)写在一起的话,那么多次调用该函数时,原本只需要被初始化一次的语句,就会被多次执行,浪费CPU资源。
) i) _& Q9 v! y% Y( R; h* W' ]- Z* i! e3 Y% i, e1 K
2.为什么程序中的这部分需要用do.....while而不用while()?. G9 \; _8 x9 U- s

  B6 _: y0 V! @9 c; i
8df6add7dbcb4ca6b71a02a7ec75dea9.png
" R) C1 |  y& C; o! s
9 r. |& b+ n/ x* p: x        这里需要用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循环将不再循环下去,此时计数器计数完成。
2 w$ P5 z' V) D
( ~9 E9 F. J4 b. J- i+ u3.  由上面知滴答定时器的计数器是24位的,其计数范围为 0x000001 ~ 0xFFFFFF ,即:1 ~ 16,777,215,那么如果我需要计的数超过了这个范围呢?我该怎么办?% ?- V  d: }) g; H& z/ {& F% C& ^

  a0 D9 J. K- y% W        由前面知,计数9000下,延时1ms。所以,16,777,215可以延时1864ms,即:  =  1864 。也就是说,在9Mhz频率下,将计数器计满,可以实现延时1864ms。
* k2 Y: v. _  f1 Z' O( h8 ?+ Y' E7 }. P  h+ h
        的确,如果我想要延时2000ms,那么由于超过计数器的计数范围,那么滴答定时器就不会正常工作了。5 V6 b+ L& b8 r) a
6 |7 b; ^0 t, E: ~7 M
所以,我们改下一下程序:
2 w/ D* S$ E+ ~3 y// 定时Nms程序
* f2 |% H) q- xvoid Systick_NmsDelay(uint32_t Nms)' _6 V# t+ `$ x
{& b! i1 m1 F3 c( V9 ?
    uint8_t  systick_flag=0;
: A  r: |3 o5 D( l/ q0 b    uint32_t temp;
$ U. h7 g' k3 t* g  @8 j8 I4 M" Z+ u5 a% U

  B2 t' H! \. [/ }    while(0==systick_flag){! W9 h# \: k  d4 e$ V; ?
# K8 d; N  K! }" x5 ^

1 N: k3 ?* }) n2 Q* A- p+ d+ x0 j% h        if(Nms>1864){
4 N$ E1 k! B$ O& z3 B7 X            SysTick->LOAD =1864 * fu_ms;
( N# r% j/ j+ s9 G- a9 s( D            Nms =Nms -1864;
# m" \1 k; K$ c2 h2 C        }else{3 |7 O6 i$ y% c# X/ ~' ~
            // 2》设置计数值---N*9000
  Z0 c, ~3 b6 R+ L& `. J! a0 ?            SysTick->LOAD =Nms * fu_ms;
! S/ Y8 ]5 l  r5 C            systick_flag=1;" D2 D: ?, I4 e) s# l# x  r
         }
( f! f' x1 n( g* p4 t1 {$ |4 @% }1 s# G2 B3 J' n

/ v3 \' k. e: g) p9 e9 P        // 3》清除当前值寄存器----因为清除当前值寄存器,STK_CTRL的第16位会清0" ?. q! T- d0 @4 s* s: X
        SysTick->VAL =0;) @% o7 Y4 n3 K' [8 g( Z
0 U$ D9 f( a% ]. m+ H+ j
        // 4》打开定时器
) n$ B4 _) ~& N3 n8 g) J( c+ ]        SysTick->CTRL |=0x01;
3 ?: p4 t) t  B, ?
" c2 W3 a; n% ]8 m: X& A        // 5》等待定时器结束
) b2 _: f% F3 l. y' p$ ?' {3 c        do{
0 p3 \( t" _9 l/ _2 i1 p            temp=SysTick->CTRL;
& D$ X8 Q9 o# h6 @: D        }while(!(temp&(1<<16)));
# x3 V1 {  L8 I5 \9 b9 P* ~0 d/ X) y5 X, s
        // 6》关闭定时器
  a5 E) H% Z% v: q* n6 m        SysTick->CTRL &=~0x01;
& t3 F5 h3 A( Z" ^7 O$ A7 k% v
0 n) Z: T8 t' p1 _! k     }4 l% L4 F9 m& |* {, h8 V

4 D* E$ ~' l. w1 g
0 S1 ?# ?5 _; k6 r3 r}
% d) y3 b5 ]% e5 `) s! |9 w2 P: `: m) H# Q# @+ F. @" C& |# l' `
这样的话,就解决了1864问题了。, a4 H* ~' P& Z" u0 l2 J
5 i) G& ^$ m$ p+ R! X$ b. C

& [  k# O2 t4 Q" N( u4.us级的延时程序是什么?
  T7 _/ g, W, l# R/ w) y5 ^4 [
" L8 l% N) E% P$ w$ P6 l// 定时Nus程序
7 O# `8 v5 \1 {, }# ]$ ~void Systick_NusDelay(uint32_t Nus)  y% q- \$ R( k, @+ u
{
8 u% j8 u# f7 r6 t    uint32_t temp;5 u! e% J5 O5 Y/ L
    // 2》设置计数值---N*9
/ c  S4 T" ]" @8 V    SysTick->LOAD =Nus * fu_us;        //us与ms的程序就只有这里改变了
3 s2 j" ]3 U, b- q" p& i
! h- x' d# |$ Q" q5 M0 l& V    // 3》清除当前值寄存器----因为清除当前值寄存器,STK_CTRL的第16位会清07 X7 T" ^# A/ D  Z
    SysTick->VAL =0;4 V# z: Q& X) H+ }% m: W
; x( A) y( B2 M: \  S0 K
    // 4》打开定时器9 U3 Z( r6 O% U- R
    SysTick->CTRL |=0x01;
$ v9 t1 Q  H. f1 N0 e$ w3 f- l+ e0 }; M5 w
    // 5》等待定时器结束
! b- t" q% e% X  s6 i    do{
# X( L# i' t* B" i        temp=SysTick->CTRL;+ x* u+ @7 [) y- P- d5 q
    }while(!(temp&(1<<16)));
3 j8 f/ {# `" y7 x$ `" g) g
4 d7 F/ z& ]/ p8 t) B; {; O! J    // 6》关闭定时器
7 Z, G% z- K+ S* s    SysTick->CTRL &=~0x01;
  K/ P+ T7 P  K/ b* u}
/ I' f- E. K" M1 }0 C0 G- b1 X' E2 M! B" {
注意:! ~2 [- D, u) n
        在FreeRTOS操作系统中,不能使用我们自己设置的Systick来进行延时,因为FreeRTOS时基是由Systick提供(裸机的时基是由晶振提供的)。这是什么意思?8 `  z5 @! {8 B4 K
% E) L1 [, }* x. [. h" x
        FreeRTOS时基是由Systick提供,时基是需要保持不变的,所以Systick寄存器中的值必须是固定的,是不允许被修改的。但是,如果我们在FreeRTOS中用我们手动设置的Systick来进行延时,我们必定会去修改Systick寄存器中的值来得到我们需要的延时。所以,此时如果在FreeRTOS中使用我们手动设置的Systick去进行延时,那么就会卡死。解决办法:使用软件延时,delay()函数来代替Systick延时。代码如下:! S1 r8 q- p0 v8 F
9 @' `0 h  q) W1 K# @
//初始化延迟函数
8 s- E) m* H$ e//SYSTICK的时钟固定为AHB时钟,基础例程里面SYSTICK时钟频率为AHB/8
( m/ K8 T8 s% ^//这里为了兼容FreeRTOS,所以将SYSTICK的时钟频率改为AHB的频率!
( G( `5 O6 x. Y. z3 T+ V//SYSCLK:系统时钟频率
$ d$ c. b' U0 P8 tvoid delay_init(u8 SYSCLK)0 t" \. P! y$ q6 W/ a
{1 U( d% ]# t: p& d( W
    u32 reload;- G5 ^/ K* l8 G$ N7 ]2 |
     SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);
6 J4 }, T8 E+ `' c5 c' y    fac_us=SYSCLK;                            //不论是否使用OS,fac_us都需要使用
- q' F! l# A) ?! }    reload=SYSCLK;                            //每秒钟的计数次数 单位为M       ; T( u# k( v* K5 ^% k
    reload*=1000000/configTICK_RATE_HZ;        //根据configTICK_RATE_HZ设定溢出时间
/ A0 O  l3 `6 b- S. i                                            //reload为24位寄存器,最大值:16777216,在168M下,约合0.0998s左右   
# ]; x5 ^2 G, I- V5 U    fac_ms=1000/configTICK_RATE_HZ;            //代表OS可以延时的最少单位       5 v4 t7 B" l) N8 `& s
    SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;//开启SYSTICK中断1 v" s& @( H- K- X( W
    SysTick->LOAD=reload;                     //每1/configTICK_RATE_HZ断一次      \, M. X: V; r3 L5 l& b0 d
    SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启SYSTICK     
6 K( O  p; I2 N/ h, V}            
1 p1 t9 Z( t$ F
7 u9 ?: G* {" M4 l$ o# w% K: e' D; k

* m0 n, v8 `4 \//延时nus$ x4 @( `2 d' ^3 @) f6 R
//nus:要延时的us数.      M* {7 \7 M% G9 ^+ C8 E
//nus:0~204522252(最大值即2^32/fac_us@fac_us=168)                                          
* k4 m0 s: M, Z( @4 T9 d5 uvoid delay_us(u32 nus); T, h; L9 s# D" f6 D/ s( }
{        & Q4 s2 B2 n5 U& `3 N
    u32 ticks;4 \% _) t& E; r) `' e* S
    u32 told,tnow,tcnt=0;% j5 P6 b9 M3 j+ V, W7 K
    u32 reload=SysTick->LOAD;                //LOAD的值            
6 Z8 ]. _& D7 \2 w    ticks=nus*fac_us;                         //需要的节拍数
+ ?& Q$ g$ Z- r/ I, i6 F4 ^* @    told=SysTick->VAL;                        //刚进入时的计数器值* r9 g" G" Y9 w( n
    while(1)7 `  e  T: p* G( [5 }* d! n
    {
1 o4 q! _9 t9 c) T* [: T        tnow=SysTick->VAL;    ' k7 l7 V: q$ |8 w) c
        if(tnow!=told); }% l3 n- F( V& ~* c( k% J( X
        {        2 L3 V- W% w8 l4 B
            if(tnow<told)tcnt+=told-tnow;    //这里注意一下SYSTICK是一个递减的计数器就可以了.' y1 B$ B  b4 B# X
            else tcnt+=reload-tnow+told;        : K) y- b3 ]9 h8 P
            told=tnow;) i' N9 J+ j' t5 T+ g
            if(tcnt>=ticks)break;            //时间超过/等于要延迟的时间,则退出.7 v- E- q6 y  Y; b7 B7 G
        }  9 s! y7 w  b  l' A7 V7 z. E
    };                                            3 N" G$ k+ f* h0 A
}  ) q" f: b5 C) \8 T: D1 N+ u; w+ Q
( l6 \" \# Q0 T! F, |& g
+ X& l9 C: q1 n/ G, l2 J
//延时nms
2 Q' P; j& Q. v# ^6 Y4 }1 Q//nms:要延时的ms数
9 w" y2 }5 z1 p0 x! f& b//nms:0~655353 Q8 E$ W) P: m+ }( Y- k2 `0 s
void delay_ms(u32 nms)
* F0 c9 i- ~5 V1 |- }% Z{    2 i4 V2 o8 A3 _* d
    if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行% p5 D; `2 b# C' X8 k1 j4 L
    {        
0 [' N& f% c) A! _8 X; g        if(nms>=fac_ms)                        //延时的时间大于OS的最少时间周期 - {: B1 P3 v% M
        {
( g. W/ q5 m7 G* F- b7 R               vTaskDelay(nms/fac_ms);             //FreeRTOS延时+ h, f8 M+ N1 e# b
        }1 s5 z5 p. |, q" P" P  {- {5 @" R; X" y
        nms%=fac_ms;                        //OS已经无法提供这么小的延时了,采用普通方式延时    ( d6 y* E# A. T5 X8 S/ n
    }5 Y' K: y) w" d5 O
    delay_us((u32)(nms*1000));                //普通方式延时
3 B" r# ^0 S  G: x3 d+ j$ f2 ?$ I}6 h9 o% f4 q/ u/ U2 G" c

1 s9 Y% e. [8 L5 r, h# r& v//延时nms,不会引起任务调度' `" S2 U. C: k
//nms:要延时的ms数# L* R- p: F" _- l
void delay_xms(u32 nms)8 I" ^* z% _0 k+ Q, f7 ~
{6 C/ [0 m2 [+ [* P. v
    u32 i;
$ V9 O4 R* ~8 i0 z$ D- K4 U    for(i=0;i<nms;i++) delay_us(1000);
; A* I7 Y1 _: j8 @; Z7 o}
7 m5 r) Q' l6 n6 T% k————————————————! V( F  Q( i# S$ \7 ^& }8 [
版权声明:无敌小小雷9 d4 L0 X4 G% R4 ]4 M& g

8 Z1 K7 x* `0 F2 {, l* q
( E4 w; A! z5 g! [: A

; Q5 K8 I' k* N' E- H
收藏 评论0 发布时间:2023-4-16 18:53

举报

0个回答

所属标签

相似技术帖

官网相关资源

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