【STM32/FreeRTOS】精准延时的实现

目录

前言

SysTick定时器寄存器

裸机编程下实现

FreeRTOS中实现


前言

       在使用通讯协议来驱动外设的时候需要遵循严格的时序逻辑,往往用到微秒(us)级别的延时,在裸机编程中可以使用SysTick定时器来实现;在FreeRTOS中,SysTick定时器则是用来作为FreeRTOS系统时钟的,但也可以用来做延时使用,只是与裸机编程下方式不同。

SysTick定时器寄存器

       要使用SysTick定时器,首先要了解一下其相关寄存器。

裸机编程下实现

       步骤:

       1、根据延时时间和定时器所选时钟频率,计算出定时器要计数的时间数值;

       2、将该数值加载到重装载寄存器中;

       3、将当前值寄存器清零,打开定时器开始计数;

       4、等待控制及状态寄存器的位16变为1;

       5、关闭定时器,退出。

函数实现(正点原子例程):

//延时nus
//nus为要延时的us数.     
//注意:nus的值,不要大于798915us(最大值即2^24/fac_us@fac_us=21)
void delay_us(u32 nus)
{            
       u32 temp;                  
       SysTick->LOAD=nus*fac_us; //时间加载,fac_us与时钟频率有关,例如:168Mhz时
                                 //fac_us=168,168/168M=1us               
       SysTick->VAL=0x00;         //清空计数器
       SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数      
       do
       {
              temp=SysTick->CTRL;    //读取控制及状态寄存器的值

       }while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达,使能且位16为0(未计到0)
       SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器
       SysTick->VAL =0X00;                      //清空计数器

}

FreeRTOS中实现

       在FreeRTOS中,SysTick定时器的重装载值是固定的,且定时器是一直在工作的。因此像裸机编程中将时间数值加载到重装载寄存器是不可行的。既然定时器是一直在工作的,那么肯定有一个时间段定时器的计数值与延时时间计数值相等。可以用此来实现精准延时。

       步骤:

       1、根据延时时间和定时器所选时钟频率,计算出定时器要计数的时间数值;

       2、获取当前数值寄存器的数值;

       3、以当前数值为基准开始计数;

       4、当所计数值等于(大于)需要延时的时间数值时退出。

 

 计数时间值的计算:

时钟周期 = 1 / 频率(s)

指定时间的计数周期(值):时间值 / 时钟周期 = 时间值 x 频率

以时间值1us,频率168Mhz为例:

计数周期(值)= 0.000001(s) x 168000000

                         = 168000000 / 1000000

                         = 168

程序实现:

初始版:

//使用该函数前需先初始化sysytick定时器
//参照正点原子例程进行修改
void delay_us(u32 nus)
{
       u32 ticks;
       u32 told,tnow,reload,tcnt=0;

       reload = SysTick->LOAD;                     //获取重装载寄存器值
       ticks = nus * (SystemCoreClock / 1000000);  //计数时间值
       told=SysTick->VAL;                          //获取当前数值寄存器值(开始时数值)

       while(1)
       {
              tnow=SysTick->VAL;          //获取当前数值寄存器值
              if(tnow!=told)              //当前值不等于开始值说明已在计数
              {         

                     if(tnow<told)             //当前值小于开始数值,说明未计到0
                          tcnt+=told-tnow;     //计数值=开始值-当前值

                     else                  //当前值大于开始数值,说明已计到0并重新计数
                            tcnt+=reload-tnow+told;   //计数值=重装载值-当前值+开始值  (已
                                                      //从开始值计到0) 

                     told=tnow;                //更新开始值
                     if(tcnt>=ticks)break;     //时间超过/等于要延迟的时间,则退出.
              } 
       }     
}

//SystemCoreClock为系统时钟(system_stmf4xx.c中),通常选择该时钟作为
//systick定时器时钟,根据具体情况更改




改进版:

//在此函数中加入初始化sysytick定时器步骤
//参照正点原子例程进行修改
void delay_us(u32 nus)
{
       u32 ticks;
       u32 told,tnow,reload,tcnt=0;
       if((0x0001&(SysTick->CTRL)) ==0)    //定时器未工作
              vPortSetupTimerInterrupt();  //初始化定时器

       reload = SysTick->LOAD;                     //获取重装载寄存器值
       ticks = nus * (SystemCoreClock / 1000000);  //计数时间值
       told=SysTick->VAL;                          //获取当前数值寄存器值(开始时数值)

       while(1)
       {
              tnow=SysTick->VAL;          //获取当前数值寄存器值
              if(tnow!=told)              //当前值不等于开始值说明已在计数
              {         

                     if(tnow<told)             //当前值小于开始数值,说明未计到0
                          tcnt+=told-tnow;     //计数值=开始值-当前值

                     else                  //当前值大于开始数值,说明已计到0并重新计数
                            tcnt+=reload-tnow+told;   //计数值=重装载值-当前值+开始值  (已
                                                      //从开始值计到0) 

                     told=tnow;                //更新开始值
                     if(tcnt>=ticks)break;     //时间超过/等于要延迟的时间,则退出.
              } 
       }     
}

//SystemCoreClock为系统时钟(system_stmf4xx.c中),通常选择该时钟作为
//systick定时器时钟,根据具体情况更改

  • 18
    点赞
  • 91
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值