STM32中精确延时函数的实现

在与传感器或者模块的总线进行通信的时候,常常需要使用到精确延时,一般我们会封装几个常用延时函数,下面我们以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时间到了。
 

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

公众号内容面向在校大学生、电子爱好者、嵌入式工程师;

涉及电子制作、模块使用、单片机技术、物联网相关知识分享;

软硬件全栈工程师,玩模块,学硬件,带你从0走到1

若觉得本次分享的文章对您有帮助,随手关注并转发分享,也是对我的支持。
 

 
原创文章 6 获赞 1 访问量 771

猜你喜欢

转载自blog.csdn.net/rsd102/article/details/106084513
今日推荐