欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

STM32:HAL库使用定时器作为Systick时钟的方法

程序员文章站 2022-06-09 11:08:41
...

前言

记录一下,在使用CubeMx生成的裸机模版移植完STemWin后,想再移植官方下载FreeRTOS的时候,遇到的一些问题。主要现象是编译没有错误,然后下载程序后,没办法运行对应程序,调试后发现进入HardFault_Handler,经过排查发现是FreeRTOS的心跳时钟与SysTick心跳有冲突。即SysTick没选择其他方式作为心跳源。

一、STM32CubeMX生成模版代码中的Systick的配置

1、Systick的初始化位于 HAL_Init()  中

STM32:HAL库使用定时器作为Systick时钟的方法

2、具体位于HAL_InitTick( TICK_INT_PRIORITY ); 中

STM32:HAL库使用定时器作为Systick时钟的方法

3、HAL_InitTick 中的详细代码

  • __weak 关键字:若两个或两个以上全局符号(函数或变量名)名字一样,而其中之一声明为weak属性,则这些全局符号不会引发重定义错误。链接器会忽略弱符号,去使用普通的全局符号来解析所有对这些符号的引用,但当普通的全局符号不可用时,链接器会使用弱符号。当有函数或变量名可能被用户覆盖时,该函数或变量名可以声明为一个弱符号
  • 我们所需要做的就是重新写一个HAL_InitTick( ) ,然后在里面配置我们定时器,然后在定时器中断服务函数中添加HAL_IncTick() 就行了。
__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
  /* Configure the SysTick to have interrupt in 1ms time basis*/
  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;
}

二、STM32CubeMX生成的TIM2作为Systick心跳的模版代码

HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
  RCC_ClkInitTypeDef    clkconfig;
  uint32_t              uwTimclock = 0;
  uint32_t              uwPrescalerValue = 0;
  uint32_t              pFLatency;
  
  /*Configure the TIM2 IRQ priority */
  HAL_NVIC_SetPriority(TIM2_IRQn, TickPriority ,0); 
  
  /* Enable the TIM2 global Interrupt */
  HAL_NVIC_EnableIRQ(TIM2_IRQn); 
  
  /* Enable TIM2 clock */
  __HAL_RCC_TIM2_CLK_ENABLE();
  
  /* Get clock configuration */
  HAL_RCC_GetClockConfig(&clkconfig, &pFLatency);
  
  /* Compute TIM2 clock */
  uwTimclock = 2*HAL_RCC_GetPCLK1Freq();
   
  /* Compute the prescaler value to have TIM2 counter clock equal to 1MHz */
  uwPrescalerValue = (uint32_t) ((uwTimclock / 1000000) - 1);
  
  /* Initialize TIM2 */
  htim2.Instance = TIM2;
  
  /* Initialize TIMx peripheral as follow:
  + Period = [(TIM2CLK/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
  */
  htim2.Init.Period = (1000000 / 1000) - 1;
  htim2.Init.Prescaler = uwPrescalerValue;
  htim2.Init.ClockDivision = 0;
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  if(HAL_TIM_Base_Init(&htim2) == HAL_OK)
  {
    /* Start the TIM time Base generation in interrupt mode */
    return HAL_TIM_Base_Start_IT(&htim2);
  }
  
  /* Return function status */
  return HAL_ERROR;
}

//TIM2中断服务函数
void TIM2_IRQHandler(void)
{
  HAL_TIM_IRQHandler(&htim2);
}

//TIM中断回调函
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  if(htim->Instance == TIM2)
  {
        HAL_IncTick();      //Systick的心跳
  }
}

三、步骤总结

  • 重写 HAL_InitTick 
  • 在 HAL_InitTick 中配置对应定时器、使能定时器基本功能中断功能
  • 在中断服务函数中使用HAL提供的统一回调函数(会自动清除对应标志位),然后在里面添加HAL_IncTick()