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

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

[复制链接]
攻城狮Melo 发布时间:2023-4-16 18:53
滴答定时器:- p) d; o6 `5 w0 B4 I* o6 F2 ^
        定时器的本质就是计数器。我们设置一个定值,然后计数器开始计数,从我们给的定值开始往下一直数,当数到0时,就做相应的动作(也可以不做什么,当把它用作延时计时的时候)。
$ b+ p2 S; ^4 \# i
; r  T0 e, d1 h; b  S5 |/ l滴答定时器systick是一个内核外设(即:内核自带的)
8 k: @1 ~( k8 ~1 c9 a1 N. |8 P* _+ Z6 k/ j
所以在《STM32F10xxx参考手册中文版.pdf》手册中没有相关描述,我们需要参考内核手册/ B( d" L% _+ ?  m* D
《STM32F10xxx20xxx21xxxL1xxxx Cortex-M3 programming manual.pdf》0 `* J( p5 i0 p
( s' O) f9 H5 z3 O5 N
        Systick是一个24bit的系统定时器(stm32F407的寄存器名字与位数都与f103一样,但是有些芯片定时器位数不同),向下计数(从定值开始数到0),当计数到0时,在下一个时钟边沿,会重复计数。所以如果我们用完定时器后,不再继续使用它时,记得要将定时器关掉,否则它就一直在重复计数,增加了功耗。: g4 F2 f$ T3 B2 y' }

4 \* D1 b( y, W/ X2 kSystick的作用+ r8 p. v- ?, U7 ^
1.产生一个精准的定时
3 y% L: e2 G' h* t" D2.FreeRTOS时基由Systick提供
6 p/ A# `; s. `! p6 ^
: j& r& V0 H' }# f
MJ5}R@G1S4I9X34C]7ZVQJT.png
1 B- E- q/ b3 ?$ Q& G! E) {; s& \7 ]0 ^8 q. R& E) O
配置滴答定时器:' u! N+ F/ X3 \( k6 N& }  `; o
$ K2 ?' M( p! l2 N
2e4242cbd0334a8dbcc333256a6d9fcb.png
9 e  C+ J# g9 r* @3 X- m% a# i1 W& U/ M: i. W* T  R
        红框中就是滴答定时器的时钟,我们可以看到分频系数为8,所以滴答定时器的频率为:72M/8 = 9Mhz' N3 l4 z  R  `$ w! c( ~" w

3 R6 A) E( v! ^8 R$ T8 C& l配置滴答定时器我们需要下面几个寄存器:2 P9 ^  W' Z7 O7 u$ ^" n3 y
9 o9 }  H, e+ A9 p0 V, S5 r
fb7ea8d208f14b14975bb8c2c2b8daac.png % x6 m& _7 \" _  B9 f
) C; {# l( g/ E  E+ A5 H" V
注意:第16位,是定时完成标志位,每当计数到0时,该标志位就会置1。! O6 l; U0 E. p: H; U; W

7 k2 U  o! j/ P$ O$ p( m4 z( T
439efd5325344681aa687218ae27eeac.png
: [& o2 s9 d* |8 w& [2 P1 O. o5 \
; W- \, \2 ^& V 注意:由于定时器是24位的,所以给定时器装载的定值是有一个范围的,红框中就是范围,范围为0x000001 ~ 0xFFFFFF   
9 R3 e: p! |$ Z8 ^9 a% K
9 e$ D) m$ Z" @6 m
SNCE360KPH_{K{XBYE)3WIW.png
, _9 T0 _0 L: `. o) u7 T

' g# ~* d2 _9 f- w. I0 Z d1b8265cf617451caef6d6a879b01593.png : Q4 D0 O8 Q$ x0 h

6 [1 ~% @8 P& t% P; q0 I  C编程# _, t5 {( u1 W& L  ]
产生Nms定时编程步骤:
6 J2 o  I7 x8 @3 f6 X4 t        1》配置时钟源---AHB8分频,并且关闭定时器-----初始化(while(1)之前): m* S$ T5 h7 T% v7 U. D5 y5 l
        2》设置计数值---N*9000
1 i- E& x- E  L# |6 {3 b6 E7 S        3》清除当前值寄存器----注意:清除当前值寄存器,STK_CTRL寄存器的第16位会自动清0
. k. ]8 p$ T. t$ K* ]1 K        4》打开定时器
1 D8 v# i# a$ u4 \6 i" \        5》等待定时器结束
7 S8 z! H  W# e$ o0 R# p        6》关闭定时器2 m. z7 g7 _( @& S
$ `! n/ `7 K6 b; T. T# s/ I. ~
代码:
0 g- C2 D) a, S# d0 Buint32_t fu_ms;  // 1ms需要计的次数# c& v. `) S. k* j0 W6 b8 J
uint32_t fu_us;  // 1us需要计的次数
6 s8 q9 j! f" b# r" L# i! c1 L. u$ L% k7 F

3 w# w; D5 T3 Y4 }void Systick_Config(uint32_t Sysclk)        //Sysclk就是系统时钟频率72Mhz,但是这里用72表示72Mhz- Y/ M% N8 p) E( W8 N/ M3 X
{
4 ]# U5 t- \$ B- l    // 1》配置时钟源---AHB8分频,并且关闭定时器-----初始化(while(1)之前)9 t0 I) Y8 P5 v. K
    SysTick->CTRL  &=~0x05;        //这里关闭定时器的原因是,有可能之前没有关闭,所以一开始先将其关闭,再进行配置5 n, }3 N# `7 l& {; i$ L' q
5 v. t+ j8 `. r3 T' u! _* d* ~
    fu_us  =Sysclk/8;                // 72/8 = 9,即:表示9Mhz
- X6 U( e4 K" V" b    fu_ms  =fu_us*1000;        //9*1000 = 9000,由前面知道,在9Mhz下,计数9000次为1ms
$ E. P3 d3 X0 C& g% S) p0 N8 A}
" `, M% L! L) P( ~% [, \* Q
5 H. A7 o  C$ K9 |5 H$ P! E+ E# O) F- T* O
// 定时Nms程序,ms级延时
- X7 s9 \4 e0 }5 [3 k! }void Systick_NmsDelay(uint32_t Nms)" K( D5 D2 \. l" |' M6 x
{
/ [6 }* i! d# ?# t    uint32_t temp;
9 J$ D8 A2 I+ e    // 2》设置计数值---N*90000 a% l* D5 \* `" K$ v$ ^* L5 c
    SysTick->LOAD =Nms * fu_ms;3 V6 V* F$ T5 z& Z7 O( Y
9 L5 s1 n5 F+ R2 y# e6 D4 [
    // 3》清除当前值寄存器----因为清除当前值寄存器,STK_CTRL的第16位(定时完成标志位)就会自动清0
! C5 Z  b& y. m6 |    SysTick->VAL =0;        //清除标志位9 Y# t9 W0 H7 ]3 M4 i
, }6 `( Q  Z, I  z
    // 4》打开定时器
$ L9 T' Z' F& E$ _6 G0 ?. w    SysTick->CTRL |=0x01;8 i: `9 V( [: G$ ~3 F* N
& ]0 Q. F. ]. p) q  K# J
    // 5》等待定时器结束; j/ C$ P8 _, ^6 C0 a! y: ]
    do{3 Z4 ]7 j: A) I% u
        temp=SysTick->CTRL;        , ~0 s  c3 o* {" V* Y+ `. H
    }while(!(temp&(1<<16)));        //判断CTRL寄存器第16位是否为1。如果为1,则说明计数数到0了,此时计数完成
# |1 s- z2 ~+ ]: ?' R
9 ]% a+ D+ x! r  A    // 6》关闭定时器9 b7 ~& ]0 N7 l
    SysTick->CTRL &=~0x01;
- }7 ~. `. o* l! T$ c}" i+ c2 B% i8 Q( J6 o7 b

3 C7 g7 K, U& o2 R7 O& \解析:$ f5 n: R; V0 C: @0 y  @' `% m$ D

! X/ V# f0 C! C# |1.我们先来看程序,定时器程序为什么被分成  Systick_Config(uint32_t Sysclk)  、Systick_NmsDelay(uint32_t Nms)两部分来写?
7 }" t! h- V: u0 t& x2 y% f
+ j$ k- E+ N: Z/ N5 t* p        因为,Systick_Config(uint32_t Sysclk) 中的配置,在整个程序中只需要被初始化一次就行了,而Systick_NmsDelay(uint32_t Nms)是延时函数,它会被多次使用。如果将Systick_Config(uint32_t Sysclk)与Systick_NmsDelay(uint32_t Nms)写在一起的话,那么多次调用该函数时,原本只需要被初始化一次的语句,就会被多次执行,浪费CPU资源。) |, E/ U! ~, }5 Z) @- L( Q
5 X( V$ o* i6 D$ t: u, g+ F
2.为什么程序中的这部分需要用do.....while而不用while()?' k% p1 j& n+ P# n; B
& e* D) Q3 O% t- G1 X2 U
8df6add7dbcb4ca6b71a02a7ec75dea9.png
8 ~% k% i* @. o* F) f. l" Q  ~! h: K  Q, f
        这里需要用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循环将不再循环下去,此时计数器计数完成。& t; @2 g9 I# W& _/ I, {  u- c
/ L( D6 O$ ~; V2 A$ I
3.  由上面知滴答定时器的计数器是24位的,其计数范围为 0x000001 ~ 0xFFFFFF ,即:1 ~ 16,777,215,那么如果我需要计的数超过了这个范围呢?我该怎么办?
# z! O; C9 r; _# F% W/ o, A# v5 o  y& P- ~3 _6 E( y4 z
        由前面知,计数9000下,延时1ms。所以,16,777,215可以延时1864ms,即:  =  1864 。也就是说,在9Mhz频率下,将计数器计满,可以实现延时1864ms。
; [; @; J3 A2 Z1 k: _, ~8 r
: o6 B0 N5 d& v0 }: ?/ n1 I6 K# Z        的确,如果我想要延时2000ms,那么由于超过计数器的计数范围,那么滴答定时器就不会正常工作了。# v+ W8 I; _: o
/ `6 C  _" M% _! f" Z8 }
所以,我们改下一下程序:
# Y' E) F' h( j  @// 定时Nms程序
0 i0 T* E% n0 O; P# a: A. mvoid Systick_NmsDelay(uint32_t Nms)
7 D& O" F; P- Z7 g7 `$ z9 }{
' v4 {8 W! C$ E0 u7 F  ^3 I+ c1 I$ E    uint8_t  systick_flag=0;
$ q8 |2 _* H9 A    uint32_t temp;
3 s- X. V7 A. O* |5 e3 m* v: m, v) e" ]0 R
3 o) B9 S/ |+ u: q6 N2 {
    while(0==systick_flag){3 ]; R; v- G8 n( z
9 p* j* S% g5 g+ N
( J/ @' G/ B1 D7 |4 v; y
        if(Nms>1864){
0 ?  E/ d  c! u+ k            SysTick->LOAD =1864 * fu_ms;& N  X7 `3 H5 w8 I# @6 E0 O
            Nms =Nms -1864;6 _# t' V' F' @0 @: z
        }else{
' M+ {: A7 D4 G- g6 {3 o( [            // 2》设置计数值---N*9000
* S% F# ^3 N$ h            SysTick->LOAD =Nms * fu_ms;
, {) t$ t1 `3 E/ t, U3 x4 p            systick_flag=1;
+ r; O  n6 x7 f         }
* U2 w; p7 j; n$ o
5 n% F3 o8 e1 s% N$ r. T5 V& k8 q: w! v# d; Y  `
        // 3》清除当前值寄存器----因为清除当前值寄存器,STK_CTRL的第16位会清08 @3 n4 j' D2 V. D8 y" P
        SysTick->VAL =0;! n$ X3 C9 a+ \. H- ?; l

% V# R9 n( ?7 e9 P        // 4》打开定时器  {8 _  _5 B/ O& t0 O
        SysTick->CTRL |=0x01;
, w# {7 N/ _1 H/ k5 ^" v; C
  m) Q: {2 T) ]  o5 H        // 5》等待定时器结束
$ ^) }/ z; L8 e9 t        do{2 u7 o" [8 m% k* v  h; d0 g& k! `
            temp=SysTick->CTRL;
7 B5 ^' E0 Q( o: ?) c5 a" V; }        }while(!(temp&(1<<16)));
, |0 r% K, C# t& M/ w& w5 `
" R  i" R8 X) ?/ ]6 D' L        // 6》关闭定时器
1 B- M( N; `; F$ p4 r2 W6 b8 X1 G        SysTick->CTRL &=~0x01;
* z: b! I/ `4 ^- Q( E( p  O! }& c, I$ u# _4 a- ]+ s$ a
     }
+ m. Y( T* \9 ~7 v0 O2 q* ]. R; u
( p9 K; ]; r5 E8 H5 B
}
1 }# v( V$ }# {; j
+ e- W( V( n% c8 R  M这样的话,就解决了1864问题了。. A9 @( G) @3 F8 V7 O9 w1 a
$ r) M  b+ _& R- w0 p0 @
9 B& o# ~  c3 ~' n$ r  @+ p
4.us级的延时程序是什么?
' \( r4 R! ^* R( r: K
3 f" o5 a" F8 j- O. B0 s// 定时Nus程序
# u2 e! j5 f) Uvoid Systick_NusDelay(uint32_t Nus)% [  m5 T+ R( M
{
, ?, @: ]% [& J& M: C3 g& D    uint32_t temp;
  q  S, M8 e! u$ i- j5 U  z    // 2》设置计数值---N*95 R' _/ h8 ?: H% n: ]
    SysTick->LOAD =Nus * fu_us;        //us与ms的程序就只有这里改变了
5 T+ I( _4 a& Q
  R) l: C% P& D6 m. T. @    // 3》清除当前值寄存器----因为清除当前值寄存器,STK_CTRL的第16位会清02 n# }# ~* e: G: l# T
    SysTick->VAL =0;: S) I' U- ^2 g- X+ s6 y4 b) v  o
' U% O# I$ Z1 Z6 P/ v- Y
    // 4》打开定时器6 p! p5 S" N' I! t
    SysTick->CTRL |=0x01;+ ~2 n7 ?/ ]5 A* S( f
9 C9 d0 b8 b, d0 R# }
    // 5》等待定时器结束& g$ a" e1 a2 b- o  _
    do{
: t) z1 F1 B, q        temp=SysTick->CTRL;
5 c& [# d" b* I% m; ]    }while(!(temp&(1<<16)));
! ^- l4 L! V1 i" R( T3 J" T
7 \9 c; `) Q9 _. _/ l  x5 N4 ?2 \    // 6》关闭定时器5 s1 n! v; C) G# T& V; y
    SysTick->CTRL &=~0x01;9 ]7 g2 R7 h3 q1 {( u) [
}
+ |5 D' ~0 x$ c! x
# E- L5 y1 t7 o! D注意:' k: E$ [3 r9 D: c$ v
        在FreeRTOS操作系统中,不能使用我们自己设置的Systick来进行延时,因为FreeRTOS时基是由Systick提供(裸机的时基是由晶振提供的)。这是什么意思?/ o: U" w* _; E3 p$ ]

5 c. y. }5 O( F/ m, S        FreeRTOS时基是由Systick提供,时基是需要保持不变的,所以Systick寄存器中的值必须是固定的,是不允许被修改的。但是,如果我们在FreeRTOS中用我们手动设置的Systick来进行延时,我们必定会去修改Systick寄存器中的值来得到我们需要的延时。所以,此时如果在FreeRTOS中使用我们手动设置的Systick去进行延时,那么就会卡死。解决办法:使用软件延时,delay()函数来代替Systick延时。代码如下:# @6 \3 L- o9 J$ D6 }% q" Z1 d
2 {+ Z+ W9 H$ M, e- G
//初始化延迟函数
8 ^) p) f0 `! _. O5 m//SYSTICK的时钟固定为AHB时钟,基础例程里面SYSTICK时钟频率为AHB/8
* G' s/ n6 V) @+ B//这里为了兼容FreeRTOS,所以将SYSTICK的时钟频率改为AHB的频率!
# b0 `& |% e. y" @//SYSCLK:系统时钟频率! K  |' W0 v! F  n% y* w
void delay_init(u8 SYSCLK)8 e8 Q( T' u; R; y; h! j
{8 l4 j* B2 x" U# L# V5 R1 j
    u32 reload;
2 S6 I2 g  w# X/ p- |/ V     SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK); 9 C8 S& M) R/ |) W, `6 r- f* P0 Y
    fac_us=SYSCLK;                            //不论是否使用OS,fac_us都需要使用  I+ n! \/ X9 Z9 Y9 ^- {
    reload=SYSCLK;                            //每秒钟的计数次数 单位为M      
: c9 i0 d* q0 \0 p" d* U3 v/ U' K    reload*=1000000/configTICK_RATE_HZ;        //根据configTICK_RATE_HZ设定溢出时间
: z7 j4 ^9 u2 _$ c# v4 R3 D                                            //reload为24位寄存器,最大值:16777216,在168M下,约合0.0998s左右    + p7 O4 Y9 K* a7 W3 V' \
    fac_ms=1000/configTICK_RATE_HZ;            //代表OS可以延时的最少单位       1 e9 l' o) J5 u' J
    SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;//开启SYSTICK中断8 t$ Z; _1 j. M- O. F
    SysTick->LOAD=reload;                     //每1/configTICK_RATE_HZ断一次   
( u0 z4 L4 I# d8 n+ W" s    SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启SYSTICK     
9 O; \' E! z2 x: E1 F. m# d( w}            
! }7 n4 d3 y' x+ m5 S! }0 H9 b  @+ Z. k$ z5 C! k/ G
. |; T) Q3 y$ w/ @$ e

( K3 j) ]  c4 t) ]//延时nus6 t1 w3 @  V5 S9 B* k2 L+ a
//nus:要延时的us数.   
- r& y: g  ^3 |& Z7 F" J3 i2 V//nus:0~204522252(最大值即2^32/fac_us@fac_us=168)                                          
* B3 f' ]) w! K# U! N! U5 \. Avoid delay_us(u32 nus)
5 b% s/ F+ q& x( T' ^8 X{        + }% O: {9 Q/ O0 E
    u32 ticks;9 q) i/ T2 R0 f3 v* U  N% t; H) B
    u32 told,tnow,tcnt=0;
  k7 |* H' {+ i' M! k, o' _    u32 reload=SysTick->LOAD;                //LOAD的值             & F: P" Z/ W. q) r5 \+ H- c6 s
    ticks=nus*fac_us;                         //需要的节拍数 $ X9 \) M! S( h' n& W# g
    told=SysTick->VAL;                        //刚进入时的计数器值' V  h& E; ^9 {
    while(1)  |* j; f! v( u! o' ?
    {
. y& x& n. B3 N) X6 e" J' E        tnow=SysTick->VAL;    1 E1 W. i0 `- e$ Q5 U
        if(tnow!=told)
* U+ w4 A) n/ k7 C, j; L) R. \        {        
+ d' D/ u2 k* I* u$ F% [3 e            if(tnow<told)tcnt+=told-tnow;    //这里注意一下SYSTICK是一个递减的计数器就可以了.) j3 |6 b1 m$ u+ v3 ^5 p
            else tcnt+=reload-tnow+told;        
  q2 ?$ `+ k  U3 Y. r) z1 Z            told=tnow;% }& }% D' o; ~6 ?2 {
            if(tcnt>=ticks)break;            //时间超过/等于要延迟的时间,则退出.
, h: j. i  U. B9 Q9 {) R        }  
3 H* ^4 y5 Q- L6 w4 q    };                                            
7 M2 D! ]( g# @+ i- N/ U}  
; L- g* Z  y* g, H5 F) c' f
9 y0 v2 [% X4 J8 R. P2 {/ ~4 D! r
//延时nms$ ^3 b" E; P$ p7 h, K- c4 o5 m
//nms:要延时的ms数
, m; s( X9 h+ g& o$ H' ?' m//nms:0~65535
8 t' \! E7 Y% q1 H; Svoid delay_ms(u32 nms)1 Y" \6 o6 B# y! S$ R
{   
: `$ r( m( Z- v+ ~4 [& \! @    if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
0 ~! y" R( r3 T1 T8 @  ?    {        
5 t( `1 l% F. u0 s$ a( }        if(nms>=fac_ms)                        //延时的时间大于OS的最少时间周期 ( _% ~6 K3 u% G% `
        {
8 U: @1 O& q6 b# M$ W$ L               vTaskDelay(nms/fac_ms);             //FreeRTOS延时
$ M: J! S/ i5 n: H$ B" `  ]        }
+ ^! t5 q6 {5 x' v% i        nms%=fac_ms;                        //OS已经无法提供这么小的延时了,采用普通方式延时    ) L, }& {; U6 z( P# B4 J
    }
" D2 B' L) e, p$ e0 _    delay_us((u32)(nms*1000));                //普通方式延时: P- f  p" ?3 W% e" f
}
" H3 @, T7 `% k3 |; J( x- R7 G$ l2 X% l4 {
//延时nms,不会引起任务调度, t6 v4 f( a% D  f
//nms:要延时的ms数
) A0 K/ O9 g  d& w* L. n1 E: Rvoid delay_xms(u32 nms)' k4 \1 _+ r+ W% w/ j) j7 N$ h% @
{' O- b% j8 F+ t. w5 I
    u32 i;
& b$ T) D/ f  c; o9 S0 e: n& o4 m    for(i=0;i<nms;i++) delay_us(1000);  W3 F: R# x! R- I9 V( o; O
}
, X3 @# a. o# o$ |! a————————————————7 @; j  y: R) m5 u4 J& X+ c* c. W
版权声明:无敌小小雷
* q- s# w, r8 O1 _" [$ C7 V% H& a- Y+ X5 y/ H) H! g
5 l* F, S, F8 D4 o: h5 R/ ?! `- H
+ {  R. P( d, U- p, x
收藏 评论0 发布时间:2023-4-16 18:53

举报

0个回答

所属标签

相似技术帖

官网相关资源

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