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

目录

前言

SysTick定时器寄存器

裸机编程下实现

FreeRTOS中实现


前言

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

SysTick定时器寄存器

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

【STM32/FreeRTOS】精准延时的实现_第1张图片

裸机编程下实现

       步骤:

       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、当所计数值等于(大于)需要延时的时间数值时退出。

 【STM32/FreeRTOS】精准延时的实现_第2张图片

 计数时间值的计算:

时钟周期 = 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=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=ticks)break;     //时间超过/等于要延迟的时间,则退出.
              } 
       }     
}

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

你可能感兴趣的:(FreeRTOS,stm32,stm32,freertos)