问题
- HAL_Delay是怎么做到可以延迟控制的?
分析记录
步骤01:看函数本身
void HAL_Delay(uint32_t Delay);
/**
* @brief 此函数根据变量递增提供最小延迟(以毫秒为单位)。
* @note 在默认的实现中,SysTick计时器是时基的来源。它用于以固定的时间间隔生成中断,其中 uwTick 递增
* @note 此函数声明为__weak在用户文件中出现其他实现时要覆盖。
* @param Delay 指定延迟时间长度(以毫秒为单位)。
* @retval None
*/
__weak void HAL_Delay(uint32_t Delay)
{
// 获取一个计数值,简单来说就是一个记录当前时刻的记录值
uint32_t tickstart = HAL_GetTick();
uint32_t wait = Delay;
/* 添加频率以保证最短的等待时间 */
if (wait < HAL_MAX_DELAY)
{
wait += (uint32_t)(uwTickFreq);
}
// 死循环,直到不满足要求时结束
while ((HAL_GetTick() - tickstart) < wait)
{
}
}
uint32_t HAL_GetTick(void)
/**
* @brief 提供以毫秒为单位的刻度值。
* @note 此函数声明为__weak在用户文件中出现其他实现时要覆盖。
* @retval tick value
*/
__weak uint32_t HAL_GetTick(void)
{
return uwTick;
}
void HAL_IncTick(void)
/**
* @brief 调用此函数以递增用作应用程序时基的全局变量“uwTick”。
* @note 在默认实现中,此变量在 SysTick ISR 中每 1 毫秒递增一次。
* @note 此函数声明为__weak在用户文件中出现其他实现时要覆盖。
* @retval None
*/
__weak void HAL_IncTick(void)
{
uwTick += uwTickFreq;
}
步骤02:观察相关联的变量
步骤03:观察变化的地方
1. 在SysTick_Handler中被调用
步骤04:中断要执行是需要开启的
- 在程序的开头HAL_Init();
/**
* @brief 此函数用于初始化 HAL 库;它必须是在主程序中执行的第一个指令(在调用任何其他 HAL 函数之前), 它执行以下操作:
* 1. 配置闪存预取。
* 2. 将 SysTick 配置为每 1 毫秒生成一次中断,
* 3. 由 HSI 计时(在此阶段,时钟尚未配置,因此系统从内部 HSI 以 16 MHz 运行).
* 4. 将 NVIC 组优先级设置为 4
* 5. 调用用户文件 “stm32f1xx_hal_msp.c” 中定义的 HAL_MspInit() 回调函数来执行全局低级硬件初始化
*
* @note SysTick 用作 HAL_Delay() 函数的时基,应用程序需要确保 SysTick 时基始终设置为 1 毫秒,以便具有正确的 HAL 操作
* @retval HAL status
*/
HAL_StatusTypeDef HAL_Init(void) {
/* 配置闪存预取 */
#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)
/* 预取缓冲区在超值行设备上不可用 */
__HAL_FLASH_PREFETCH_BUFFER_ENABLE();
#endif
#endif /* PREFETCH_ENABLE */
/* NVIC 组优先级设置 */
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
/*将 SysTick 配置为每 1 毫秒生成一次中断(重置后的默认时钟为 HSI) */
HAL_InitTick(TICK_INT_PRIORITY);
/* 初始化低级硬件 */
HAL_MspInit();
/* 返回函数状态 */
return HAL_OK;
}
步骤04:查看HAL_InitTick执行函数
/**
* @brief 此函数配置时基源。时间源配置为具有 1ms 时基,具有专用的 Tick 中断优先级
* @note 此函数在程序开始时由 HAL_Init() 重置后或在时钟由 HAL_RCC_ClockConfig() 重新配置时自动调用。
* @note 在默认实现中,SysTick 计时器是时基的来源。它用于以固定的时间间隔生成中断。
* 如果从外设 ISR 进程调用 HAL_Delay(),则必须小心,SysTick 中断的优先级必须高于外设中断(数值较低)。否则,调用方 ISR 进程将被阻止。
* @param TickPriority Tick interrupt priority.
* @retval HAL status
*/
__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority) {
/* 将系统滴答配置为在 1ms 时间内中断*/
if (HAL_SYSTICK_Config(SystemCoreClock / (1000U / uwTickFreq)) > 0U) {
return HAL_ERROR;
}
/* 配置系统滴答 IRQ 优先级 */
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;
}
步骤05: 查看HAL_SYSTICK_Config函数
- 这一步就是对外再包装了一层
步骤06: 查看SysTick_Config函数(实际执行的)
步骤07: 重点记录SystemCoreClock的变化:
第一次使用的地方
第一次变化的地方