使用SysTick作爲HAL的基礎時鐘

HAL需要設置一個定時器作爲基礎時鐘。基礎時鐘通過定時溢出中斷產生嘀嗒信號,嘀嗒信號的缺省頻率是1000Hz,也就是基礎時鐘的定時週期是1ms。基礎時鐘主要用於實現延時函數HAL_Delay(),或在一些有超時(timeout)設置的函數裏確定延時。

在不使用FreeRTOS的時候,STM32CubeMX裏默認地將基礎時鐘源設置爲SysTick定時器,如圖1所示。SysTick是Cortex-M內核自帶的一個24位的定時器,將SysTick作爲HAL的基礎時鐘後,在NVIC中會自動啓用SysTick的中斷,並且優先級設置爲最高,如圖2所示。可以修改SysTick的中斷優先級,但是不能在圖2中禁用SysTick中斷。

圖1 在SYS中設置HAL的基礎時鐘源爲SysTick

圖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的基礎時鐘

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章