使用SysTick作为HAL的基础时钟

HAL需要设置一个定时器作为基础时钟。基础时钟通过定时溢出中断产生嘀嗒信号,嘀嗒信号的缺省频率是1000Hz,也就是基础时钟的定时周期是1ms。基础时钟主要用于实现延时函数HAL_Delay(),或在一些有超时(timeout)设置的函数里确定延时。

在不使用FreeRTOS的时候,STM32CubeMX里默认地将基础时钟源设置为SysTick定时器,如图1所示。SysTick是Cortex-M内核自带的一个24位的定时器,将SysTick作为HAL的基础时钟后,在NVIC中会自动启用SysTick的中断,并且优先级设置为最高,如图2所示。可以修改SysTick的中断优先级,但是不能在图2中禁用SysTick中断。

使用SysTick作为HAL的基础时钟_第1张图片

图1 在SYS中设置HAL的基础时钟源为SysTick

使用SysTick作为HAL的基础时钟_第2张图片

图2 使用SysTick作为HAL基础时钟源的NVIC设置

1. 基础时钟的初始化

在STM32CubeMX生成的初始化代码中,HAL_Init()是在main()函数中执行的第一行代码。HAL_Init()对SysTick定时器进行设置,使其定时中断周期为1ms。函数HAL_Init()的代码如下。

	HAL_StatusTypeDef HAL_Init(void)
	{
	  /* Configure Flash prefetch, Instruction cache, Data cache */ 
	#if (INSTRUCTION_CACHE_ENABLE != 0U)
	  __HAL_FLASH_INSTRUCTION_CACHE_ENABLE();
	#endif /* INSTRUCTION_CACHE_ENABLE */
	
	#if (DATA_CACHE_ENABLE != 0U)
	  __HAL_FLASH_DATA_CACHE_ENABLE();
	#endif /* DATA_CACHE_ENABLE */
	
	#if (PREFETCH_ENABLE != 0U)
	  __HAL_FLASH_PREFETCH_BUFFER_ENABLE();
	#endif /* PREFETCH_ENABLE */
	
	  /* 设置中断优先级分组策略 */
	  HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
	
	  /* 使用SysTick作为基础时钟,配置嘀嗒周期为1ms  */
	  HAL_InitTick(TICK_INT_PRIORITY);
	
	  HAL_MspInit();  /* 底层硬件初始化*/
	  return HAL_OK;
	}

其中,执行的HAL_InitTick(TICK_INT_PRIORITY)是对SysTick定时器进行定时周期和中断的设置,HAL_InitTick()是在文件stm32f4xx_hal.c中用__weak修饰符定义的弱函数,代码如下:

	__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
	{
	  /* 配置SysTick定时周期为1ms */
	  if (HAL_SYSTICK_Config(SystemCoreClock / (1000U / uwTickFreq)) > 0U)
	  {
	    return HAL_ERROR;
	  }
	  /* 配置SysTick定时器的中断优先级 */
	  if (TickPriority < (1UL << __NVIC_PRIO_BITS))
	  {
	    HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority, 0U);
	    uwTickPrio = TickPriority;
	  }
	  else
	  {
	    return HAL_ERROR;
	  }
	  return HAL_OK;
	}

作为弱函数,HAL_InitTick()可以被重新实现。在使用SysTick作为基础时钟时,使用的就是文件stm32f4xx_hal.c中的HAL_InitTick()。

函数HAL_Init()中最后调用的函数HAL_MspInit()也是一个弱函数,在HAL驱动中就是个空函数。在STM32CubeMX生成的初始化代码中,在文件stm32f4xx_hal_msp.c中重新实现了这个函数,功能就是启用了RCC的时钟信号,并设置中断优先级分组策略,也就是图2中用户设置的优先级分组策略。

	void HAL_MspInit(void)
	{
	  __HAL_RCC_SYSCFG_CLK_ENABLE();
	  __HAL_RCC_PWR_CLK_ENABLE();
	  HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);
	}

所以,执行函数HAL_Init()后,就设置了SysTick定时器的定时周期和中断优先级。默认的SysTick定时周期为1ms,产生的嘀嗒信号频率为1000Hz。

2. 基础时钟的中断处理

在文件stm32f4xx_it.c中自动生成了SysTick定时器中断的ISR函数,其代码如下:

	void SysTick_Handler(void)
	{
	  HAL_IncTick();
	}

在SysTick定时器的定时溢出中断里就执行了函数HAL_IncTick(),这是在文件stm32f4xx_hal.c中实现的函数,函数的代码如下:

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

它的功能就是使得全局变量uwTick递增,这个变量就是嘀嗒信号的计数值。当嘀嗒信号频率为1000Hz时,uwTickFreq的值为1;当嘀嗒信号频率为100Hz时,uwTickFreq的值为10。

在文件stm32f4xx_hal.c中还定义了操作滴答定时器的两个函数,用于暂停和恢复滴答定时器,都是用__weak定义的弱函数,代码如下:

	__weak void HAL_SuspendTick(void)
	{  /* 禁止 SysTick 中断 */
	  SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk;
	}
	__weak void HAL_ResumeTick(void)
	{  /* 开启 SysTick 中断 */
	  SysTick->CTRL  |= SysTick_CTRL_TICKINT_Msk;
	}

常用的延时函数HAL_Delay()就是利用嘀嗒信号来实现的,函数HAL_Delay()的代码如下:

	__weak void HAL_Delay(uint32_t Delay)
	{
	  uint32_t tickstart = HAL_GetTick();	//获取嘀嗒信号当前计数值
	  uint32_t wait = Delay;
	  if (wait < HAL_MAX_DELAY)   //最少延时1ms
	  {
	    wait += (uint32_t)(uwTickFreq);   // uwTickFreq默认值为1
	  }
	  while((HAL_GetTick() - tickstart) < wait)
	  {
	  }
	}

程序中调用的函数HAL_GetTick()的功能就是返回全局变量uwTick,也就是嘀嗒信号的当前计数值。函数HAL_Delay()的输入参数Delay是以毫秒为单位的延时时间。延时的原理就是先读取嘀嗒信号的当前计数值保存到变量tickstart,计算在此基础上延时所需要的计数值差量wait,然后在while循环中不断用函数HAL_GetTick()读取滴答信号当前技术值,计算相对于tickstart差量,当差量超过wait时就达到了延时时间。

在HAL库中使用SysTick作为基础定时器时,其作用就是用于产生嘀嗒信号计数值,然后用于延时计算。如果不需要用到延时计算,停掉SysTick定时器对系统运行是没有什么影响的。例如,在使用低功耗设计时,为了使系统进入睡眠模式后不被SysTick的中断唤醒,就停掉了SysTick定时器。

下一篇  使用其他定时器作为HAL的基础时钟

主题      HAL和FreeRTOS的基础时钟

你可能感兴趣的:(TrueSTUDIO,STM32Cube)