stm32粗延时和精准延时函数

1.stm32粗延时函数

粗延时的意思就是延时时间不太准确,一般用在对延时时间要求不严格的场合。这种延时方式是采用软件延时,但因为编译器会在编译的时候加上一些其他辅助指令,所以不能确定C程序的准确运行时间。我们可以采用下面的方法进行估算:

假设stm32 MCU系统时钟(SYSCLK)为48MHz,指令周期为4个系统时钟,则一个指令周期时长为1/12微秒。
若要让计时单位为微秒(us),则可以让CPU空转约12次,即在软件上可以令减数周期变量为12,但由于存在其他辅助指令,所以可以将这个减数周期减小一些,比如10。

类似地,若以毫秒(ms)为计时单位,则可以让CPU空转12000次。空转次数越多,则其他辅助指令占用时间相对越短。

以微秒(us)为单位进行延时:

void delay_us(u16 us_time)
{    
  u16 i=0;  
  while(us_time--)
  {
     i=10;           // 这要根据系统时钟频率进行计算
     while(i--) ;    // 延时主操作,空操作
  }
}

以毫秒(ms)为单位进行延时:

void delay_ms(u16 ms_time)
{    
    u16 i=0;      
    while(ms_time--)
    { 
        i=12000;       // 以ms为单位
        while(i--) ;    
    }
}

2.stm32精准延时函数

精准延时,就要依靠硬件来完成。这个硬件就是硬核Cortex-Mx的Systick定时器。Systick定时器就是系统滴答定时器(不需要CPU干涉),一个24 位的倒计数定时器,计到0 时,将从RELOAD 寄存器中自动重装载定时初值,并发出中断请求。只要不把它在SysTick 控制及状态寄存器中的使能位清除,就永不停息,即使在睡眠模式下也能工作。

Systick定时器的计数频率可以设定为系统频率或系统频率的1/8。假设系统频率为48MHz,则Systick的频率为6MHz。
这个可以由下面函数设定:

SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);  
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);

若计时单位为微秒,则滴答定时器要计数6次;若计时单位为毫秒,则计数6000次。

有两种方法进行精确定时:中断方式和非中断方式。

2.1 中断方式

①配置SysTick中断函数,让延时时间变量在中断中递减。中断函数:

void SysTick_Handler(void)
{
    if (TimingDelay != 0x00)    //TimingDelay是一个用户全局变量,代表要延时的时间。
    {
        TimingDelay --;
    }
}

在用户源文件中定义:

extern __IO uint32_t TimingDelay;

这里要注意,TimingDelay要是需要被其他位置的源文件调用,则要加extern前缀或不加(默认);若只由自身源文件内调用,则可以加上static前缀,防止被其他源文件混用。这个要根据程序实际情况设定。

②设置SysTick的重装载值,并配置中断触发。

if(SysTick_Config(SystemCoreClock/1000))  //配置滴答计数值及滴答中断,这里一个计时周期为1ms
{                                         //SystemCoreClock为系统时钟频率(Hz)
    while(1);                             // 返回1,表示配置失败;0表示成功
}

//若设定计时周期为1us,则如下
if(SysTick_Config(SystemCoreClock/1000000))
{
    while(1);
}

SysTick_Config(uint32_t ticks)是用来初始化系统定时器及其中断的,并开启系统定时器及产生周期性中断。ticks是定时器计数次数。该函数存放在core_cm0(3/4/7...).h中。

③延时函数

延时函数所能做的就是给延时变量TimeDelay赋值,并等待SysTick中断。

static void Delay(__IO uint32_t nTime)  // 延时nTime单位个时间
{
    TimingDelay = nTime;
    while(TimingDelay != 0) ;         // 静静等待
}

2.2 非中断方式

中断方式最大的弊端就是稍显繁琐,而非中断方式则直接得多。SysTick 主要包含CTRL、LOAD、VAL、CALIB 等4 个寄存器。SysTick 的 counter 从 reload值往下递减到 0 的时候, CTRL 寄存器的位 16:countflag 会置 1,且读取该位的值可清 0,
我们可以直接操作寄存器,在等待中使用软件查询的方法来实现延时。

//不进入systic中断,微秒级延时
void delay_us(__IO uint32_t us)
{
 uint32_t i;
 SysTick_Config(SystemCoreClock/1000000);
 
 for (i=0;i<us;i++)
 {
    while( !((SysTick->CTRL)&(1<<16)) ) ;  //等待,计数器的值减小到0时,CRTL寄存器的位16会置1
 }
 
 SysTick->CTRL &=(~SysTick_CTRL_ENABLE_Msk);  // 关闭定时器
}
 
// 若是延时单位为毫秒级,则将分子1000000改成1000.

在网上有网友用如下更底层的方法,其原理与上面的程序一致:

SYSTICK 的时钟设定为HCLK 时钟的1/8,这里选用系统时钟频率为72MHz,所以SYSTICK的时钟为9M,即SYSTICK定时器以9M的频率递减。

//仿原子延时,不进入systic中断
void delay_us(u32 nus)   //us为单位
{
 u32 temp;
 SysTick->LOAD = 9*nus;
 SysTick->VAL=0X00;   //清空计数器
 SysTick->CTRL=0X01;  //使能,减到零是无动作,采用外部时钟源
 do
 {
  temp=SysTick->CTRL;      //读取当前倒计数值
 }while((temp&0x01)&&(!(temp&(1<<16))));  //等待时间到达
     SysTick->CTRL=0x00;   //关闭计数器
    SysTick->VAL =0X00;    //清空计数器
}


void delay_ms(u16 nms)    // ms为单位
{
 u32 temp;
 SysTick->LOAD = 9000*nms;
 SysTick->VAL=0X00;      //清空计数器
 SysTick->CTRL=0X01;     //使能,减到零是无动作,采用外部时钟源
 do
 {
  temp=SysTick->CTRL;       //读取当前倒计数值
 }while((temp&0x01)&&(!(temp&(1<<16))));   //等待时间到达
    SysTick->CTRL=0x00;     //关闭计数器
    SysTick->VAL =0X00;     //清空计数器
}

2.3 SysTick寄存器介绍

SysTick->CTRL

位段

名称

类型

复位值

描述

16

COUNTFLAG

R

0

如果在上次读本寄存器后systick已为0,则该位为1,若 读该位自动清零

2

CLKSOURCE

RW

0

0:外部时钟源 1:内部时钟

1

TICKINT

RW

0

0:减到0无动作;1:减到0产生systick异常请求

0

ENABLE

RW

0

systick定时器使能位

SysTick-> LOAD

位段

名称

类型

复位值

描述

23:0

RELOAD

RW

0

减到0时被重新装载的值

SysTick-> VAL

位段

名称

类型

复位值

描述

23:0

CURRENT

RW

0

读取时返回当前倒计数的值,写则清零,同时还会清除在systick控制及状态寄存器中的COUNTFLAG标志

SysTick-> CALIB 不常用,在这里也没用到,有兴趣的读者可查阅ST关于Cortex-m0(3/4/..)的manual。

 

  • 6
    点赞
  • 75
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值