HAL_Delay详解
最近我在使用HAL_Delay时经常出现死机等情况,后来经过调试发现,HAL_Delay()中无法进入SysTick定时器的中断,所以导致程序卡死,今天就来分析一下HAL_Delay函数,并且来探讨一下为什么会无法进入SysTick定时器的中断。
一、HAL_Init()
/**
* @brief This function is used to initialize the HAL Library; it must be the first
* instruction to be executed in the main program (before to call any other
* HAL function), it performs the following:
* Configure the Flash prefetch.
* Configures the SysTick to generate an interrupt each 1 millisecond,
* which is clocked by the HSI (at this stage, the clock is not yet
* configured and thus the system is running from the internal HSI at 16 MHz).
* Set NVIC Group Priority to 4.
* Calls the HAL_MspInit() callback function defined in user file
* "stm32f1xx_hal_msp.c" to do the global low level hardware initialization
*
* @note SysTick is used as time base for the HAL_Delay() function, the application
* need to ensure that the SysTick time base is always set to 1 millisecond
* to have correct HAL operation.
* @retval HAL status
*/
HAL_StatusTypeDef HAL_Init(void)
{
/* Configure Flash prefetch */
#if (PREFETCH_ENABLE != 0)
#if defined(STM32F101x6) || defined(STM32F101xB) || defined(STM32F101xE) || defined(STM32F101xG) || \
defined(STM32F102x6) || defined(STM32F102xB) || \
defined(STM32F103x6) || defined(STM32F103xB) || defined(STM32F103xE) || defined(STM32F103xG) || \
defined(STM32F105xC) || defined(STM32F107xC)
/* Prefetch buffer is not available on value line devices */
__HAL_FLASH_PREFETCH_BUFFER_ENABLE();
#endif
#endif /* PREFETCH_ENABLE */
/* Set Interrupt Group Priority */
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
/* Use systick as time base source and configure 1ms tick (default clock after Reset is HSI) */
HAL_InitTick(TICK_INT_PRIORITY);
/* Init the low level hardware */
HAL_MspInit();
/* Return function status */
return HAL_OK;
}
这里面和systick有关的就时 HAL_InitTick(TICK_INT_PRIORITY);这个函数主要是配置systick的参数,让滴答定时器实现计时1ms秒的功能,并且设置滴答定时器的中断优先级,一般滴答定时器的中断优先级分组为0,优先级是最低的。
/**
* @brief This function configures the source of the time base.
* 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 reconfigured by HAL_RCC_ClockConfig().
* @note In the default implementation, SysTick timer is the source of time base.
* It is used to generate interrupts at regular time intervals.
* Care must be taken if HAL_Delay() is called from a peripheral ISR process,
* The SysTick interrupt must have higher priority (numerically lower)
* than the peripheral interrupt. Otherwise the caller ISR process will be blocked.
* The function is declared as __weak to be overwritten in case of other
* implementation in user file.
* @param TickPriority Tick interrupt priority.
* @retval HAL status
*/
__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
/* Configure the SysTick to have interrupt in 1ms time basis*/
// //配置滴答定时器为1s。
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_Delay()
/**
* @brief This function provides minimum delay (in milliseconds) based
* on variable incremented.
* @note In the default implementation , SysTick timer is the source of time base.
* It is used to generate interrupts at regular time intervals where uwTick
* is incremented.
* @note This function is declared as __weak to be overwritten in case of other
* implementations in user file.
* @param Delay specifies the delay time length, in milliseconds.
* @retval None
*/
__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); //1
}
while ((HAL_GetTick() - tickstart) < wait) //501 - 500 = 1 < 101
{
}
}
Hal_Delay的原理就是,进入时先获取当前程序的运行时间,也就时获取滴答定时器里的值(HAL_GetTick();)然后传入一个等待时间Delay,判断等待的时间是否超过最大等待时间HAL_MAX_DELAY。如果不超过,就加上1,然后每1ms获取一次滴答定时器的时间,当差值大于wait,说明延时结束。
如果HAl_Delay出现卡死在 while ((HAL_GetTick() - tickstart) < wait)这个里面的情况,一般就是
HAL_GetTick() 这个函数出问题了,无法获取滴答时钟的值了。
/**
* @brief This function is called to increment a global variable "uwTick"
* used as application time base.
* @note In the default implementation, this variable is incremented each 1ms
* in SysTick ISR.
* @note This function is declared as __weak to be overwritten in case of other
* implementations in user file.
* @retval None
*/
__weak void HAL_IncTick(void)
{
uwTick += uwTickFreq;
}
/**
* @brief Provides a tick value in millisecond.
* @note This function is declared as __weak to be overwritten in case of other
* implementations in user file.
* @retval tick value
*/
__weak uint32_t HAL_GetTick(void)
{
return uwTick;
}
我们可以看到HAL_GetTick(void)这个函数很简单,就是返回uwTick的值,然后我们在systick的中断里调用void HAL_IncTick(void)这个函数,每次进入中断给 uwTick加1。
三、死机分析
我的使用HAL_Delay死机的原因就是我没有设置systick的中断函数,导致 uwTick无法自增,所以才会死机,所以,我们需要配置一下systick的中断函数,并在中断函数里调用HAL_IncTick(void)这个,来实现每1ms自增一次
/*-------------------------------------------------*/
/*函数名:SysTic系统嘀嗒定时器处理函数 */
/*参 数:无 */
/*返回值:无 */
/*-------------------------------------------------*/
void SysTick_Handler(void)
{
HAL_IncTick();
}
这样死机的问题就解决了。如果不是这个问题,就很有可能是中断优先级的问题了,如果在比systick中断优先级高的中断函数里调用,HAL_Delay,就会出现一直无法进入systcik中断的问题。