FreeRTOS---SysTick移植介绍

1. STM32 SysTick

STM32的SysTick(系统滴答定时器)是一个为ARM Cortex-M内核提供的系统定时器,主要用于实现定时中断、延时等功能。以下是对SysTick的详细介绍:

1.1 SysTick介绍

SysTick 是个24位的递减定时器,它使用系统时钟作为时基,通过设置寄存器的值来确定定时周期。当计数器从装载值开始递减到0时,会触发一个中断(如果使能了中断),将从 RELOAD 寄存器中自动重装载定时初值,从而继续下一个周期的计数。只要不把它在 SysTick 控制及状态寄存器中的使能位清除, 就永不停息

每经过1个系统时钟周期,计数值就减1
在这里插入图片描述

1.2 SysTick寄存器介绍

SysTick包含四个主要寄存器,都是24位的:

  • SysTick->CTRL: SysTick控制及状态寄存器,用于配置SysTick的工作模式和状态。
  • SysTick->LOAD: SysTick重装载寄存器,存储了SysTick的初始计数值(预期值减1)。
  • SysTick->VAL: SysTick当前值寄存器,存储了SysTick的当前计数值。
  • SysTick->CALIB: SysTick校准值寄存器,提供了校准SysTick所需的信息。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

1.3 SysTick相关函数

若Timebase Source改为其他Timer需重新定义HAL_InitTick函数

HAL_Init(void)
---HAL_InitTick(TICK_INT_PRIORITY);//

//主要是调用HAL_SYSTICK_Config()函数,函数每隔1ms中断一次
__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
  if (HAL_SYSTICK_Config(SystemCoreClock / (1000U / uwTickFreq)) > 0U)
  {
    return HAL_ERROR;
  }
  /* Configure the SysTick IRQ priority */
  if (TickPriority < (1UL << __NVIC_PRIO_BITS))
  {
    HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority, 0U);
    uwTickPrio = TickPriority;
  }
  else
  {
    return HAL_ERROR;
  }

  /* Return function status */
  return HAL_OK;
}

//HAL库的SYSTICK配置函数,在文件:stm32f1xx_hal_context.c 中
uint32_t HAL_SYSTICK_Config(uint32_t TicksNumb)
{
	//内核的 Systick 配置函数,配置每隔 ticks 个 systick 周期中断一次
	//文件 core_cm3.h 中
   return SysTick_Config(TicksNumb);
}

SysTick_Handler函数处理SysTick中断。

 void SysTick_Handler(void)
{
  HAL_IncTick();
}

HAL_IncTick函数被调用都会给uwTick加1

__IO uint32_t uwTick;
HAL_TickFreqTypeDef uwTickFreq = HAL_TICK_FREQ_DEFAULT;  /* 1KHz */
HAL_TICK_FREQ_DEFAULT      = HAL_TICK_FREQ_1KHZ
HAL_TICK_FREQ_1KHZ         = 1U,


__weak void HAL_IncTick(void)
{
  uwTick += uwTickFreq;
}

1.4 HAL_Delay的实现

HAL_GetTick函数获取SysTick中断加1的uwTick值

__weak uint32_t HAL_GetTick(void)
{
  return uwTick;
}

不停的获取uwTick值,和刚被调用时的uwTick值相减,差值达到设定的延时Delay值,即推出这个函数,该延时函数是阻塞,不适用在OS系统中

__weak void HAL_Delay(uint32_t Delay)
{
  uint32_t tickstart = HAL_GetTick();
  uint32_t wait = Delay;

  /* Add a freq to guarantee minimum wait */
  if (wait < HAL_MAX_DELAY)
  {
    wait += (uint32_t)(uwTickFreq);
  }

  while ((HAL_GetTick() - tickstart) < wait)
  {
  }
}

2. FreeRTOS SysTick

在FreeRTOS中SysTick定时器尤为重要,因为它是给FreeRTOS系统提供时钟的。在FreeRTOS中任务的切换即每个任务运行的时间是由SysTick定时器提供的。

2.1 TIM作为Timebase

在移植RTOS系统时,原STM32的Timebase Source需要改为其他Timer,并且重新定义HAL_InitTick函数;这里以改为TIM6为示例

Timer的中断回调函数中,要调用HAL_IncTick(),即可实现系统tick按节奏增加;

HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
  RCC_ClkInitTypeDef    clkconfig;
  uint32_t              uwTimclock, uwAPB1Prescaler = 0U;

  uint32_t              uwPrescalerValue = 0U;
  uint32_t              pFLatency;
  HAL_StatusTypeDef     status = HAL_OK;

  /* Enable TIM6 clock */
  __HAL_RCC_TIM6_CLK_ENABLE();

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

  /* Get APB1 prescaler */
  uwAPB1Prescaler = clkconfig.APB1CLKDivider;
  /* Compute TIM6 clock */
  if (uwAPB1Prescaler == RCC_HCLK_DIV1)
  {
    uwTimclock = HAL_RCC_GetPCLK1Freq();
  }
  else
  {
    uwTimclock = 2UL * HAL_RCC_GetPCLK1Freq();
  }

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

  /* Initialize TIM6 */
  htim6.Instance = TIM6;

  /* Initialize TIMx peripheral as follow:

  + Period = [(TIM6CLK/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
  */
  htim6.Init.Period = (1000000U / 1000U) - 1U;//999
  htim6.Init.Prescaler = uwPrescalerValue;//71
  htim6.Init.ClockDivision = 0;
  htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;

  status = HAL_TIM_Base_Init(&htim6);
  if (status == HAL_OK)
  {
    /* Start the TIM time Base generation in interrupt mode */
    status = HAL_TIM_Base_Start_IT(&htim6);
    if (status == HAL_OK)
    {
    /* Enable the TIM6 global Interrupt */
        HAL_NVIC_EnableIRQ(TIM6_IRQn);
      /* Configure the SysTick IRQ priority */
      if (TickPriority < (1UL << __NVIC_PRIO_BITS))
      {
        /* Configure the TIM IRQ priority */
        HAL_NVIC_SetPriority(TIM6_IRQn, TickPriority, 0U);
        uwTickPrio = TickPriority;
      }
      else
      {
        status = HAL_ERROR;
      }
    }
  }

 /* Return function status */
  return status;
}
/**
 * @brief This function handles TIM6 global interrupt.
 */
void TIM6_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&htim6);
}

/**
 * @brief  Period elapsed callback in non blocking mode
 * @note   This function is called  when TIM6 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
 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM6)
    {
        HAL_IncTick();
    }
}

2.2 FreeRTOS SysTick函数

FreeRTOS使用SysTick作为系统时基,寄存器与之前一样,本质还是操作硬件寄存器状态
在FreeRTOS中已经提供了SysTick配置的函数vPortSetupTimerInterrupt(),函数在port.c文件中。当调用了开启任务调度函数vTaskStartScheduler()后里面就会调用该函数完成SysTick的配置

vTaskStartScheduler();
---xPortStartScheduler();
------vPortSetupTimerInterrupt();//启动定时器,产生tick ISR。这里已经禁用了中断
__weak void vPortSetupTimerInterrupt( void )
{
	/* Calculate the constants required to configure the tick interrupt. */
	#if ( configUSE_TICKLESS_IDLE == 1 )//条件编译,这段不编译
	{
	}
	#endif /* configUSE_TICKLESS_IDLE */

	/* Stop and clear the SysTick. */
	portNVIC_SYSTICK_CTRL_REG = 0UL;//清空控制及状态寄存器
	portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;//清空当前值寄存器

	/* Configure SysTick to interrupt at the requested rate. */
	/*设置重装载寄存器的值*/
	portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;
	/*设置控制及状态寄存器的值*/
	portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT );
								/*( 1UL << 2UL ) | ( 1UL << 1UL ) | ( 1UL << 0UL ) */
								/*选择处理器时钟、开定时器中断、使能定时器*/
}

FreeRTOS 中systick寄存器相关宏,寄存器地址与1.2章节一样

/* Constants required to manipulate the core.  Registers first... */
#define portNVIC_SYSTICK_CTRL_REG             ( *( ( volatile uint32_t * ) 0xe000e010 ) )
#define portNVIC_SYSTICK_LOAD_REG             ( *( ( volatile uint32_t * ) 0xe000e014 ) )
#define portNVIC_SYSTICK_CURRENT_VALUE_REG    ( *( ( volatile uint32_t * ) 0xe000e018 ) )

FreeRTOS 中systick对应的中断处理函数xPortSysTickHandler(),功能类似与SysTick_Handler()

void xPortSysTickHandler( void )
{
    /* The SysTick runs at the lowest interrupt priority, so when this interrupt
     * executes all interrupts must be unmasked.  There is therefore no need to
     * save and then restore the interrupt mask value as its value is already
     * known - therefore the slightly faster vPortRaiseBASEPRI() function is used
     * in place of portSET_INTERRUPT_MASK_FROM_ISR(). */
    vPortRaiseBASEPRI();
    {
        /* Increment the RTOS tick. */
        if( xTaskIncrementTick() != pdFALSE )
        {
            /* A context switch is required.  Context switching is performed in
             * the PendSV interrupt.  Pend the PendSV interrupt. */
            portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
        }
    }

    vPortClearBASEPRIFromISR();
}
  • 24
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值