HAL库中使用“SysTick定时器”用作精确延时和有无RTOS系统有关

HAL库中使用“SysTick定时器”用作精确延时和有无RTOS系统有关。HAL默认是使用SysTick定时器作为时基定时器,在没有RTOS系统支持时,我们可以使用SysTick定时器用作精确延时。但是带有ROTS系统,我一样可以使用SysTick定时器用作精确延时,此时HAL库要使用TIM1作为时基定时器。

1、不带RTOS系统的HAL库延时程序

不带RTOS系统的HAL库,无需使用其它定时器作为时基定时器。但有的人喜欢用TIM1作为时基定时器,而将SysTick定时器用作精确延时。这种写法,有点浪费硬件资源

#include "delay.h"

static uint8_t  fac_us=0;                            //us延时倍乘数               
static uint16_t fac_ms=0;                            //ms延时倍乘数,在ucos下,代表每个节拍的ms数

void My_delay_us(__IO uint32_t nCount);

//systick中断服务函数,1ms中断一次
//在调用HAL_Init()时,SysTick定时器初始化为使能中断

void SysTick_Handler(void)
{
}

//函数功能:delay函数初始化
//在调用HAL_Init()时,已经对SysTick定时器初始化了,因此这里无需初始化

void delay_init(void)
{
    uint32_t reload;

    fac_us=SystemCoreClock/1000000;
    //相当于将170MHz经过1000000分频用作SysTick的输入时钟,即周期为1us 

    reload=SystemCoreClock/1000000;
  //相当于将170MHz经过1000000分频用作SysTick的输入时钟,即周期为1us 
    reload*=1000;
    //SysTick溢出时间为1000*1us=1ms
    //reload为24位寄存器,最大值:16777216,在170MHz下,约合0.098s左右  
 
    fac_ms=1;
}

//函数功能:延时nus微妙
//nus:要延时的us数.    
//nus:0~204522252(最大值即2^32/fac_us@fac_us=168)    
                                      
void delay_us(uint32_t nus)
{        
    uint32_t ticks;
    uint32_t told,tnow,tcnt=0;
    uint32_t reload=SysTick->LOAD;
    //读取SysTick的LOAD寄存器的值
    
    ticks=nus*fac_us; //计算需要的节拍数 
    told=SysTick->VAL;//刚进入时的计数器值
    while(1)
    {
        tnow=SysTick->VAL;//读SYSTICK计数器值    
        if(tnow!=told)
        {        
            if(tnow<told)tcnt+=told-tnow;    //SysTick定时器是一个递减的计数器.
            else tcnt+=reload-tnow+told;        
            told=tnow;
            if(tcnt>=ticks)break;            //时间超过/等于要延迟的时间,则退出.
        }        
    }                                            
}

//函数功能:延时nms毫秒
//nms:要延时的ms数
//nms:0~65535

void delay_ms(uint32_t nms)
{  
    delay_us((uint32_t)(nms*1000));//普通方式延时
}

2、带FrssRTOS系统的HAL库延时程序

在使用HAL库时,若又使用了FrssRTOS系统,因为他们都使用了SysTick定时器,所以,我们用TIM1作为时基定时器给HAL库使用,而将SysTick定时器让给FreeRTOS系统和用作程序精确延时。

#include "delay.h"
#include "FreeRTOS.h"           
#include "task.h"
 

static uint8_t  fac_us=0;                            //us延时倍乘数               
static uint16_t fac_ms=0;                            //ms延时倍乘数,在ucos下,代表每个节拍的ms数
    
    
extern void xPortSysTickHandler(void);//xPortSysTickHandler()在port.c中

void My_delay_us(__IO uint32_t nCount);

//systick中断服务函数,1ms中断一次
//在FreeRTOSConfig.h中,有一个宏定义”#define xPortSysTickHandler SysTick_Handler“
//xPortSysTickHandler()的函数体位于port.c中
//我们在这里实现SysTick_Handler(),那个宏定义就被我屏蔽了

void SysTick_Handler(void)
{    
    if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
    {
        xPortSysTickHandler();
        //若带FreeRTOS系统,则xPortSysTickHandler()在port.c中            
    }
}
           
//函数功能:SysTick定时器初始化
//SYSTICK的时钟固定为AHB时钟,基础例程里面SYSTICK时钟频率为AHB/8
//这里为了兼容FreeRTOS,所以将SYSTICK的时钟频率改为AHB的频率!
//SYSCLK:系统时钟频率
//在port.c中有一个vPortSetupTimerInterrupt(),用来配置SysTick定时器
//由于vPortSetupTimerInterrupt()没有被调用,所以不会被执行
//我们使用delay_init()来替换vPortSetupTimerInterrupt(),使程序具有可读性

void delay_init(void)
{
    uint32_t reload;

    fac_us=SystemCoreClock/1000000;
    //相当于将170MHz经过1000000分频用作SysTick的输入时钟,即周期为1us 

    reload=SystemCoreClock/1000000;
  //相当于将170MHz经过1000000分频用作SysTick的输入时钟,即周期为1us 
    reload*=1000000/configTICK_RATE_HZ;
    //在“FreeRTOSConfig.h”中configTICK_RATE_HZ为1000
    //SysTick溢出时间为1000*1us=1ms
    //reload为24位寄存器,最大值:16777216,在170MHz下,约合0.098s左右  
 
    fac_ms=1000/configTICK_RATE_HZ;                //代表OS可以延时的最少单位       

    SysTick->VAL   = 0UL;  //设置计数器值为0,Load the SysTick Counter Value
    HAL_SYSTICK_Config(reload);//配置SysTick定时器

//  SysTick->LOAD  = reload;
    //设置计数reload个输入时钟,则溢出一次    
//  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |
//                   SysTick_CTRL_TICKINT_Msk   |
//                   SysTick_CTRL_ENABLE_Msk;
    //SysTick_CTRL_CLKSOURCE_Msk表示设置STK_CTRL寄存器bit2(CLKSOURCE),CLKSOURCE=1使用AHB时钟
    //SysTick_CTRL_TICKINT_Msk表示使能SYSTICK中断
    //SysTick_CTRL_ENABLE_Msk表示使能SysTick计数
    //Enable SysTick IRQ and SysTick Timer  
 
}                                    


//函数功能:延时nus
//nus:要延时的us数.    
//nus:0~204522252(最大值即2^32/fac_us@fac_us=168)  
                                        
void delay_us(uint32_t nus)
{        
    uint32_t ticks;
    uint32_t told,tnow,tcnt=0;
    uint32_t reload=SysTick->LOAD;
    //读取SysTick的LOAD寄存器的值
    
    ticks=nus*fac_us; //计算需要的节拍数 
    told=SysTick->VAL;//刚进入时的计数器值
    while(1)
    {
        tnow=SysTick->VAL;//读SYSTICK计数器值    
        if(tnow!=told)
        {        
            if(tnow<told)tcnt+=told-tnow;    //这里注意一下SYSTICK是一个递减的计数器就可以了.
            else tcnt+=reload-tnow+told;        
            told=tnow;
            if(tcnt>=ticks)break;            //时间超过/等于要延迟的时间,则退出.
        }        
    }                                            
}  
//延时nms
//nms:要延时的ms数
//nms:0~65535

void delay_ms(uint32_t nms)
{    
    if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
    {        
        if(nms>=fac_ms)                        //延时的时间大于OS的最少时间周期 
        { 
               vTaskDelay(nms/fac_ms);             //FreeRTOS延时
        }
        nms%=fac_ms;  
    }
    delay_us((uint32_t)(nms*1000));                //普通方式延时
}

HAL库使用TIM1作为时基定时器程序:

//stm32g4xx_hal_timebase_TIM.c
//HAL time base based on the hardware TIM.
#include "stm32g4xx_hal.h"
#include "stm32g4xx_hal_tim.h"

TIM_HandleTypeDef        HTIM1;

/**
  * @brief  This function configures the TIM1 as a time base source.
  *         The time source is configured  to have 1ms time base with a dedicated
  *         Tick interrupt priority.
  * @note   This function is called  automatically at the beginning of program after
  *         reset by HAL_Init() or at any time when clock is configured, by HAL_RCC_ClockConfig().
  * @param  TickPriority: Tick interrupt priority.
  * @retval HAL status
  */
//HAL_InitTick()供HAL_Init()调用
//函数功能:将TIM1配置为向上计数,1ms中断一次
//TickPriority=TICK_INT_PRIORITY,滴答中断优先级为TICK_INT_PRIORITY=0
HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
  RCC_ClkInitTypeDef    clkconfig;
  uint32_t              uwTimclock = 0;
  uint32_t              uwPrescalerValue = 0;
  uint32_t              pFLatency;
  HAL_StatusTypeDef     status;

  /* Enable TIM1 clock */
  __HAL_RCC_TIM1_CLK_ENABLE();

  /* Get clock configuration */
  HAL_RCC_GetClockConfig(&clkconfig, &pFLatency);

  /* Compute TIM1 clock */
  uwTimclock = HAL_RCC_GetPCLK2Freq();
	//读取PCLK2的时钟频率,Return the PCLK2 frequency
	//若PCLK2的分频器值为1,则和SystemCoreClock的值相等

  /* Compute the prescaler value to have TIM1 counter clock equal to 1MHz */
  uwPrescalerValue = (uint32_t) ((uwTimclock / 1000000U) - 1U);

  /* Initialize TIM1 */
  HTIM1.Instance = TIM1;

  /* Initialize TIMx peripheral as follow:

  + Period = [(TIM1CLK/1000) - 1]. to have a (1/1000) s time base.
  + Prescaler = (uwTimclock/1000000 - 1) to have a 1MHz counter clock.
  + ClockDivision = 0
  + Counter direction = Up
  */
  HTIM1.Init.Period = (1000000U / 1000U) - 1U;
	//定时器周期999
  HTIM1.Init.Prescaler = uwPrescalerValue;
	//设置TIM1预分频器为uwPrescalerValue
  HTIM1.Init.ClockDivision = 0;
	//设置时钟分频系数,TIM1_CR1中的CKD[9:8]=00b,tDTS=ttim_ker_ck;
	//溢出时间为(999+1)*1*170/170000000/1
  HTIM1.Init.CounterMode = TIM_COUNTERMODE_UP;

  status = HAL_TIM_Base_Init(&HTIM1);
  if (status == HAL_OK)
  {
    /* Start the TIM time Base generation in interrupt mode */
    status = HAL_TIM_Base_Start_IT(&HTIM1);
		//使能HTIM1更新中断
    if (status == HAL_OK)
    {
    /* Enable the TIM1 global Interrupt */
        HAL_NVIC_EnableIRQ(TIM1_UP_TIM16_IRQn);//使能TIM1产生中断
      /* Configure the SysTick IRQ priority */
      if (TickPriority < (1UL << __NVIC_PRIO_BITS))
      {//若使用“NVIC中断分组4”,STM32G4XX uses 4 Bits for the Priority Levels
				//HAL库使用中断优先级组为4
				//在“stm32g474xx.h”中定义“__NVIC_PRIO_BITS=4”
				//Interrupt and exception vectors
        /* Configure the TIM IRQ priority */
        HAL_NVIC_SetPriority(TIM1_UP_TIM16_IRQn, TickPriority, 0U);
	      //设置NVIC中断分组4:4位抢占优先级,0位响应优先级
	      //选择中断优先级组4,即抢占优先级为4位,取值为0~15,响应优先级组为0位,取值为0
				//这里设置TIM1中断优先级为TickPriority
        uwTickPrio = TickPriority;//记录滴答时钟的中断优先级
      }
      else
      {//若没有使用“NVIC中断分组4”,则设置status
        status = HAL_ERROR;
      }
    }
  }

 /* Return function status */
  return status;
}

/**
  * @brief  Suspend Tick increment.
  * @note   Disable the tick increment by disabling TIM1 update interrupt.
  * @param  None
  * @retval None
  */
void HAL_SuspendTick(void)
{
  /* Disable TIM1 update Interrupt */
  __HAL_TIM_DISABLE_IT(&HTIM1, TIM_IT_UPDATE);//不使能定时器更新中断
}

/**
  * @brief  Resume Tick increment.
  * @note   Enable the tick increment by Enabling TIM1 update interrupt.
  * @param  None
  * @retval None
  */
void HAL_ResumeTick(void)
{
  /* Enable TIM1 Update interrupt */
  __HAL_TIM_ENABLE_IT(&HTIM1, TIM_IT_UPDATE);//使能定时器更新中断
}

/**
  * @brief  Period elapsed callback in non blocking mode
  * @note   This function is called  when TIM1 interrupt took place, inside
  * HAL_TIM_IRQHandler(). It makes a direct call to HAL_IncTick() to increment
  * a global variable "uwTick" used as application time base.
  * @param  htim : TIM handle
  * @retval None
  */
//HAL_TIM_IRQHandler()调用HAL_TIM_PeriodElapsedCallback(),触发条件是TIM产生“向上溢出中断”
//TIM1_UP_TIM16_IRQHandler()调用了HAL_TIM_IRQHandler()函数
//TIM_DMAPeriodElapsedCplt也调用HAL_TIM_PeriodElapsedCallback()
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  if (htim->Instance == TIM1)
	{
    HAL_IncTick();
  }
}

/**
  * @brief This function handles TIM1 update interrupt and TIM16 global interrupt.
  */
//TIM1更新中断和TIM16全局中断,他们公用一个中断源
void TIM1_UP_TIM16_IRQHandler(void)
{
  HAL_TIM_IRQHandler(&HTIM1);
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值