STM32中精确延时函数的实现(stm32延迟函数)

STM32中精确延时函数的实现(stm32延迟函数)

编码文章call10242025-07-18 13:40:453A+A-

在与传感器或者模块的总线进行通信的时候,常常需要使用到精确延时,一般我们会封装几个常用延时函数,下面我们以STM32F103芯片为例,详细介绍一下STM32下一种精确延时函数的实现:


时钟树

下图中紫色的 to Cortex System timer(MHz)就是Systick的时钟频率;


SYSTICK原理

SysTick 是一个24位的倒计数定时器,当计到0时,将从RELOAD寄存器中自动重装载定时初值并继续计数,且同时触发中断。只要不把它在SysTick控制及状态寄存器中的使能位清除,就永不停息。

SysTick 的最大使命,就是定期地产生异常请求,作为系统的时基,产生一个周期性的中断。


Systick定时器的四个寄存器:

CTRL: Systick控制和状态寄存器

LOAD: Systick重装载寄存器

VAL: Systick当前值寄存器

CALIB: Systick校准值寄存器 (不常用,可忽略)


/** @addtogroup CMSIS_CM3_SysTick CMSIS CM3 SysTick
  memory mapped structure for SysTick
  @{
*/
typedef struct
{
  __IO uint32_t CTRL;                         /*!< Offset: 0x00  SysTick Control and Status Register */
  __IO uint32_t LOAD;                         /*!< Offset: 0x04  SysTick Reload Value Register       */
  __IO uint32_t VAL;                          /*!< Offset: 0x08  SysTick Current Value Register      */
  __I  uint32_t CALIB;                        /*!< Offset: 0x0C  SysTick Calibration Register        */
} SysTick_Type;


SysTick->CTRL寄存器:

CLKSOURCE-时钟源[2]: select the clock soruce, 0 : AHB / 8, 1 : AHB.

0:STCLK=外部时钟源HCLK(AHB总线时钟)/8=72M/8 = 9M  

1:FCLK=内核时钟=72M

FCLK:空闲运行时钟


SysTick-> LOAD寄存器:


SysTick-> VAL寄存器:


#include "delay.h"

static u8  fac_us=0;                            //us延时倍乘数               
static u16 fac_ms=0;                            //ms延时倍乘数  
           
//初始化延迟函数
//SYSTICK的时钟固定为HCLK时钟的1/8,即SYSTICK=SYSCLK/8
//SYSCLK:系统时钟
void delay_init()
{
    SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);    //选择外部时钟  HCLK/8

    fac_us=SystemCoreClock/8000000;                          //SYSTICK时钟为9M(即8分频)时,fac_us=9,即SysTick倒数9个数,耗时1us
    fac_ms=(u16)fac_us*1000;                                 //非OS下,代表每个ms需要的systick时钟数   
}                                    

//查询SysTick->CTRL寄存器bit0是否为1,当为1时,说明倒计时时间到;
//整个延时方法中,不进入SysTick中断;
//延时nus
//nus为要延时的us数.                                               
void delay_us(u32 nus)
{        
    u32 temp;             
    SysTick->LOAD=nus*fac_us;                       //延时时间加载               
    SysTick->VAL=0x00;                              //清空计数器
    SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;        //开始倒数      
    
    //do while 判断就是 systick 使能(bit0)位为 1 且(bit16)为1的时候等待结束
    do
    {
        temp=SysTick->CTRL;
    }while((temp&0x01)&&!(temp&(1<<16)));           //等待时间到达
   
    SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;        //关闭计数器
    SysTick->VAL =0X00;                             //清空计数器     
}

//延时nms
//注意nms的范围
//SysTick->LOAD为24位寄存器,所以,最大延时为:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK单位为Hz,nms单位为ms
//对72M条件下,nms<=1864
void delay_ms(u16 nms)
{                     
    u32 temp;           
    SysTick->LOAD=(u32)nms*fac_ms;                //时间加载(SysTick->LOAD为24bit)
    SysTick->VAL =0x00;                            //清空计数器
    SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;    //开始倒数  
    do
    {
        temp=SysTick->CTRL;
    }while((temp&0x01)&&!(temp&(1<<16)));        //等待时间到达   
    SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;    //关闭计数器
    SysTick->VAL =0X00;                           //清空计数器              
}


有了上面函数实现,我们就可以在程序中进行精准延时了,比如delay_us(50);

在刚进入delay_us函数的时候,先计算好这段延时需要等待的SysTick计数次数,这里为50*9(假设系统时钟为72MHz,因为systick的频率为系统时钟频率的1/8,那么systick每增加1,就是1/9us),然后我们就一直读取SysTick->CTRL寄存器,当该寄存器bit16的值为1时,说明倒计时了50*9个SysTick,即说明延时50us时间到了。


参考资料:

【正点原子】MiniSTM32开发板资料


喜欢请关注微信公众号:程序员小哈

有啥想玩的模块,留言给我,咱们一起玩


点击这里复制本文地址 以上内容由文彬编程网整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!
qrcode

文彬编程网 © All Rights Reserved.  蜀ICP备2024111239号-4