电子产业一站式赋能平台

PCB联盟网

搜索
查看: 371|回复: 0
收起左侧

?STM32延时函数的四种方法

[复制链接]

561

主题

561

帖子

3987

积分

四级会员

Rank: 4

积分
3987
发表于 2020-6-11 08:20:00 | 显示全部楼层 |阅读模式
关注、星标公众,不错过精彩内容
; m8 {; j2 u# z8 f) o  ?3 V5 a

ithcypthpyo64010476110.png

ithcypthpyo64010476110.png
+ [/ J. t9 s9 e: F8 B% T6 Z
单片机编程过程中经常用到延时函数,最常用的莫过于微秒级延时delay_us()和毫秒级delay_ms()。本文基于STM32F207介绍4种不同方式实现的延时函数。- u3 d4 S  Z, v) a

znmzftkzivc64010476210.gif

znmzftkzivc64010476210.gif

/ G6 k( M/ R! \' `5 h( H/ ~2 O' T普通延时
. |9 N1 X. c! v' ]这种延时方式应该是大家在51单片机时候,接触最早的延时函数。这个比较简单,让单片机做一些无关紧要的工作来打发时间,经常用循环来实现,在某些编译器下,代码会被优化,导致精度较低,用于一般的延时,对精度不敏感的应用场景中。
  W3 |/ t; `9 o. J0 m
  • //微秒级的延时void delay_us(uint32_t delay_us){      volatile unsigned int num;  volatile unsigned int t;5 e$ M3 @! a( l" d8 U
        for (num = 0; num   {    t = 11;    while (t != 0)    {      t--;    }  }}//毫秒级的延时void delay_ms(uint16_t delay_ms){      volatile unsigned int num;  for (num = 0; num   {    delay_us(1000);  }}上述工程源码仓库:https://github.com/strongercjd/STM32F207VCT6/tree/master/02-Template
    2 g3 E6 i% Z. @2 w$ w& W8 n' ^(提示:公众号不支持外链接,请复制链接到浏览器下载)- H- Z+ a7 v, \+ V8 d8 `) f
    + ~- ?# ^4 N# R& y
    8 |' ?' n6 x) W0 v) @$ Y" ?
    定时器中断5 q- {& i7 X* s. \5 C$ m1 K
    定时器具有很高的精度,我们可以配置定时器中断,比如配置1ms中断一次,然后间接判断进入中断的次数达到精确延时的目的。这种方式精度可以得到保证,但是系统一直在中断,不利于在其他中断中调用此延时函数,有些高精度的应用场景不适合,比如其他外设正在输出,不允许任何中断打断的情况。
      L. F. R9 x/ Q; VSTM32任何定时器都可以实现,下面我们以SysTick 定时器为例介绍:9 z2 k: [$ N0 ]
      `) L. e! M7 _# `& e% T" p7 L
    初始化SysTick 定时器:
  • /* 配置SysTick为1ms */RCC_GetClocksFreq(&RCC_Clocks);SysTick_Config(RCC_Clocks.HCLK_Frequency / 1000);中断服务函数:
    : F% M  J9 x" \8 t; W. x8 w1 S
  • void SysTick_Handler(void){  TimingDelay_Decrement();}void TimingDelay_Decrement(void){  if (TimingDelay != 0x00)  {     TimingDelay--;  }}延时函数:- N* V% E/ m0 h1 K" _
  • void Delay(__IO uint32_t nTime){  TimingDelay = nTime;  while(TimingDelay != 0);}上述工程源码仓库:https://github.com/strongercjd/STM32F207VCT6/tree/master/02-Template
    , T' {5 T. g2 b' x; j. s(提示:公众号不支持外链接,请复制链接到浏览器下载)0 F+ ]& w+ j- h1 G' ]

    : D4 U. l  u; r" N( E: |( y: I2 q( e  u& X- T4 B# O6 p8 M4 F& a) K" k
    查询定时器- X5 ]( _# s2 w7 E; [) Q6 U
    为了解决定时器频繁中断的问题,我们可以使用定时器,但是不使能中断,使用查询的方式去延时,这样既能解决频繁中断问题,又能保证精度。) v( L, K! t7 j! G$ g  |) Y
    STM32任何定时器都可以实现,下面我们以SysTick 定时器为例介绍。% C/ {" [% E# H
    STM32的CM3内核的处理器,内部包含了一个SysTick定时器,SysTick是一个24位的倒计数定时器,当计到0时,将从RELOAD寄存器中自动重装载定时初值。只要不把它在SysTick控制及状态寄存器中的使能位清除,就永不停息。9 f1 ^. n, `: v3 J
    SYSTICK的时钟固定为HCLK时钟的1/8,在这里我们选用内部时钟源120M,所以SYSTICK的时钟为(120/8)M,即SYSTICK定时器以(120/8)M的频率递减。SysTick 主要包含CTRL、LOAD、VAL、CALIB 等4 个寄存器。
    , J; D' G5 I( T- c  L/ P* {* y9 ~! {▼CTRL:控制和状态寄存器) @% @9 k# u7 e' A' y

    qv14k5hqlod64010476310.png

    qv14k5hqlod64010476310.png
    . Z4 w! ^2 G" k; r3 Q4 c4 @) I
    ▼LOAD:自动重装载除值寄存器0 c4 \5 b! k5 y1 B, p. C

    ecajtqpgfi064010476410.png

    ecajtqpgfi064010476410.png

    " [: p% d3 }+ k2 N1 m6 w4 k▼VAL:当前值寄存器( K3 w7 |% p/ \3 L

    0ale1e2pasa64010476510.png

    0ale1e2pasa64010476510.png

    4 c- J3 u2 J* r8 r, ^  o▼CALIB:校准值寄存器
    ( K# t' i! ^) b使用不到,不再介绍% P2 `2 V2 P. ?  s7 @
    代码. g) w! t; }  ^1 ?+ s4 ^! _( H
  • void delay_us(uint32_t nus){  uint32_t temp;  SysTick->LOAD = RCC_Clocks.HCLK_Frequency/1000000/8*nus;  SysTick->VAL=0X00;//清空计数器  SysTick->CTRL=0X01;//使能,减到零是无动作,采用外部时钟源  do  {    temp=SysTick->CTRL;//读取当前倒计数值  }while((temp&0x01)&&(!(temp&(116))));//等待时间到达  SysTick->CTRL=0x00; //关闭计数器  SysTick->VAL =0X00; //清空计数器}void delay_ms(uint16_t nms){  uint32_t temp;  SysTick->LOAD = RCC_Clocks.HCLK_Frequency/1000/8*nms;  SysTick->VAL=0X00;//清空计数器  SysTick->CTRL=0X01;//使能,减到零是无动作,采用外部时钟源  do  {    temp=SysTick->CTRL;//读取当前倒计数值  }while((temp&0x01)&&(!(temp&(116))));//等待时间到达  SysTick->CTRL=0x00; //关闭计数器  SysTick->VAL =0X00; //清空计数器}上述工程源码仓库:https://github.com/strongercjd/STM32F207VCT6/tree/master/04-Delay
    ; d0 `5 D$ y$ T* [(提示:公众号不支持外链接,请复制链接到浏览器下载)
    , {, e/ V, s& E" \& y
    5 O3 ?& G" v; B9 Q" S
    " T) s" z6 m' V. C1 b) A! a汇编指令
      H( [7 f0 s8 b7 D9 c, X1 C8 M如果系统硬件资源紧张,或者没有额外的定时器提供,又不想方法1的普通延时,可以使用汇编指令的方式进行延时,不会被编译优化且延时准确。
    / m3 U5 ]0 s3 e* U: QSTM32F207在IAR环境下
    3 F7 F6 G7 i1 V  q9 t7 S# O; e1 f/ U
  • /*! *  @brief   软件延时  *  @param  ulCount:延时时钟数 *  @return none *  @note   ulCount每增加1,该函数增加3个时钟 */void SysCtlDelay(unsigned long ulCount){    __asm("    subs    r0, #1
    & h/ n: M& d) f, j( }. F) Q- Q"          "    bne.n   SysCtlDelay
    6 J3 v& \9 W; ~- ]9 L2 o# A2 T"          "    bx      lr");}这3个时钟指的是CPU时钟,也就是系统时钟。120MHZ,也就是说1s有120M的时钟,一个时钟也就是1/120us,也就是周期是1/120us。3个时钟,因为执行了3条指令。
    / \' s( ~. A# E- F; _使用这种方式整理ms和us接口,在Keil和IAR环境下都测试通过。# x( b( u# T$ P3 `: C
  • /*120Mhz时钟时,当ulCount为1时,函数耗时3个时钟,延时=3*1/120us=1/40us*//*SystemcoreClock=1200000006 Z* }7 e2 ^: I* g( k* t% g
    us级延时,延时n微秒SysCtlDelay(n*(SystemCoreClock/3000000));' ]% m5 V" x$ X1 k' r+ l7 W
    ms级延时,延时n毫秒SysCtlDelay(n*(SystemCoreClock/3000));
    3 h7 {, ]4 _5 c& g% X6 |m级延时,延时n秒SysCtlDelay(n*(SystemCoreClock/3));*/
    ; C# r8 a" l$ z0 F% v) \#if defined   (__CC_ARM) /*!__asm voidSysCtlDelay(unsigned long ulCount){    subs    r0, #1;    bne     SysCtlDelay;    bx      lr;}#elif defined ( __ICCARM__ ) /*!voidSysCtlDelay(unsigned long ulCount){    __asm("    subs    r0, #1
    $ o: e$ x5 ^' [  M7 l"       "    bne.n   SysCtlDelay
    : J* ]; D. q% M6 Y& \* Q"       "    bx      lr");}" Q+ N" x3 B( V* W# o5 }( g' X' V
    #elif defined (__GNUC__) /*!void __attribute__((naked))SysCtlDelay(unsigned long ulCount){    __asm("    subs    r0, #1
    9 N0 a2 g: v( e4 b0 t: K"       "    bne     SysCtlDelay* @, |6 @6 k8 P" }8 O+ F
    "       "    bx      lr");}
    - _  h* L- S  c# W9 S- x#elif defined  (__TASKING__) /*!                           /*无*/#endif /* __CC_ARM */上述工程源码仓库:https://github.com/strongercjd/STM32F207VCT6/tree/master/03-ASM
    8 M; w" g( a' q1 [. _% i(提示:公众号不支持外链接,请复制链接到浏览器下载)$ b6 ~  V6 h# @% [
    7 }; q7 F) T! U- u
    注释7 _  M5 B- W0 A( J: N' O- N2 t
    理论上:汇编方式的延时也是不准确的,有可能被其他中断打断,最好使用us和ms级别的延时,采用for循环延时的函数也是如此。采用定时器延时理论上也可能不准确的,定时器延时是准确的,但是可能在判断语句的时候,比如if语句,判断延时是否到了的时候,就在判断的时候,被中断打断执行其他代码,返回时已经过了一小段时间。不过汇编方式和定时器方式,只是理论上不准确,在实际项目中,这两种方式的精度已经足够高了。
    - f2 U- e- l; H: k- ^3 P# e2 X: r& y: w. }

    uaz1xdi0i5h64010476610.gif

    uaz1xdi0i5h64010476610.gif

    , i: P) O7 T8 ~9 d' Y9 j: j1 v! q, _8 [) I1 m

    0kwqvma4awt64010476710.gif

    0kwqvma4awt64010476710.gif

    % [2 Y$ Y& g( O- Y" X. v设计一款兼容ST207和GD207的开发板9 G9 C- U! u3 h' f5 Z$ I
    MCU心脏-晶振9 g0 W# _: w' w  v# N! U6 f
    晶振原理解析
      K3 @; f- z. k3 r; m6 a9 u复位电路设计
  • 回复

    使用道具 举报

    发表回复

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则


    联系客服 关注微信 下载APP 返回顶部 返回列表