本帖最后由 gaoyang9992006 于 2023-4-10 16:48 编辑
#申请原创# @21小跑堂 在STM32开发中,延时函数是一种常见的工具,可以帮助我们实现一些简单的时间控制功能,如LED闪烁、蜂鸣器发声等。而HAL库则是ST公司针对STM32系列微控制器开发的一套软件库,提供了丰富的函数接口,方便我们进行STM32的开发。其中,HAL_Delay()和HAL_Delay_us()是HAL库提供的两个常用的延时函数,本文将通过与chatGPT对话的方式深入介绍这两个函数的实现原理及使用方法,希望能对大家有所帮助。
问:STM32 HAL库提供了哪些延时函数
chatGPT:
STM32 HAL库提供了以下几个延时函数:
HAL_Delay()函数:该函数可以在毫秒级别上进行延时。它接受一个参数,表示需要延时的毫秒数。
HAL_SuspendTick()和HAL_ResumeTick()函数:这两个函数用于暂停和恢复HAL库的滴答定时器,从而在低功耗模式下实现延时功能。
HAL_Delay_us()函数:该函数可以在微秒级别上进行延时。它接受一个参数,表示需要延时的微秒数。注意:这个函数依赖于系统时钟频率和编译器优化等因素,因此实际的延时可能会有一定的误差。
需要注意的是,使用延时函数会占用CPU资源,因此在实际应用中应该尽量避免过多地使用延时函数,而是采用中断或DMA等方式来实现需要定时或延时的功能。
问:介绍一下HAL_Delay_us()函数
chatGPT:
HAL_Delay_us()函数是STM32 HAL库提供的一个延时函数,可以用于在微秒级别上进行延时。该函数的定义如下:
void HAL_Delay_us(uint32_t microseconds)
该函数接受一个参数microseconds,表示需要延时的微秒数。函数内部会使用HAL库提供的滴答定时器进行计时,并在达到指定的延时时间后返回。需要注意的是,由于该函数的实现依赖于系统时钟频率和编译器优化等因素,因此实际的延时可能会有一定的误差。
在使用HAL_Delay_us()函数时,需要确保滴答定时器已经正确初始化。如果滴答定时器未初始化,则会导致延时函数无法正常工作。另外,由于该函数会占用CPU资源,因此在实际应用中应该尽量避免过多地使用延时函数,而是采用中断或DMA等方式来实现需要定时或延时的功能。
问:我想看该函数的实现代码
chatGPT:
HAL_Delay_us()函数的实现代码如下:
void HAL_Delay_us(uint32_t microseconds)
{
uint32_t tickstart = 0;
tickstart = HAL_GetTick();
while ((HAL_GetTick() - tickstart) < (microseconds / 1000));
}
该函数的实现比较简单,主要使用了HAL_GetTick()函数获取系统滴答定时器的计数值,并在计数值达到指定的延时时间后返回。具体实现过程如下:
首先,使用HAL_GetTick()函数获取当前的系统滴答定时器计数值,即tickstart。
然后,使用一个while循环不断检查当前的系统滴答定时器计数值与tickstart之间的差值是否达到指定的延时时间。这里需要注意的是,由于HAL_GetTick()函数返回的是以毫秒为单位的计数值,因此需要将延时时间从微秒转换为毫秒,并除以1000来得到需要循环的次数。
当系统滴答定时器计数值与tickstart之间的差值达到指定的延时时间时,while循环结束,函数返回。
需要注意的是,由于HAL_GetTick()函数返回的计数值是一个32位无符号整数,因此在延时时间较长时,需要考虑计数器溢出的情况。此外,由于该函数的实现依赖于系统时钟频率和编译器优化等因素,因此实际的延时可能会有一定的误差。
问:HAL_Delay()的实现代码也给我提供一下
chatGPT:
当然,下面是HAL_Delay()函数的实现代码:
void HAL_Delay(uint32_t milliseconds)
{
uint32_t tickstart = 0;
tickstart = HAL_GetTick();
while ((HAL_GetTick() - tickstart) < milliseconds);
}
HAL_Delay()函数的实现方式与HAL_Delay_us()函数类似,不同之处在于HAL_Delay()函数的参数表示需要延时的毫秒数,而不是微秒数。具体实现过程如下:
首先,使用HAL_GetTick()函数获取当前的系统滴答定时器计数值,即tickstart。
然后,使用一个while循环不断检查当前的系统滴答定时器计数值与tickstart之间的差值是否达到指定的延时时间。由于HAL_GetTick()函数返回的计数值已经是以毫秒为单位的,因此不需要进行单位转换。
当系统滴答定时器计数值与tickstart之间的差值达到指定的延时时间时,while循环结束,函数返回。
与HAL_Delay_us()函数一样,HAL_Delay()函数的实现也依赖于系统时钟频率和编译器优化等因素,因此实际的延时时间也可能会有一定的误差。
最后我们通过一个简单的例子,演示如何使用HAL_Delay()函数来实现LED灯的闪烁。
假设我们的STM32开发板上连接了一个LED灯,我们希望让它每隔一段时间就闪烁一次。为了实现这个功能,我们可以编写如下的代码:
#include "stm32f4xx_hal.h"
#define LED_PIN GPIO_PIN_5
#define LED_PORT GPIOA
int main(void)
{
HAL_Init();
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = LED_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(LED_PORT, &GPIO_InitStruct);
while (1)
{
HAL_GPIO_TogglePin(LED_PORT, LED_PIN);
HAL_Delay(500);
}
}
在这个例子中,我们使用了HAL库提供的GPIO_InitTypeDef结构体和HAL_GPIO_Init()函数初始化了LED灯所连接的GPIO端口,然后使用了HAL_GPIO_TogglePin()函数来切换LED灯的状态。在每次状态切换后,我们使用HAL_Delay()函数来延时500毫秒,实现了LED灯每隔一段时间闪烁一次的功能。
需要注意的是,HAL_Delay()函数会阻塞程序的执行,因此在使用它时需要谨慎考虑,避免出现无法响应其他事件的情况。此外,HAL_Delay()函数的延时精度也受到系统时钟频率和编译器优化等因素的影响,因此在实际使用中需要进行一定的测试和调试。
于是我补充提问
问:如何使用非阻塞实现该例子中的效果?
要使用非阻塞方式实现该例子中的效果,我们可以使用定时器中断来代替延时函数。下面是一个使用TIM6定时器来实现LED灯闪烁的例子代码:
#include "stm32f4xx_hal.h"
#define LED_PIN GPIO_PIN_5
#define LED_PORT GPIOA
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);
int main(void)
{
HAL_Init();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_TIM6_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = LED_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(LED_PORT, &GPIO_InitStruct);
TIM_HandleTypeDef htim6 = {0};
htim6.Instance = TIM6;
htim6.Init.Prescaler = 84 - 1;
htim6.Init.Period = 5000 - 1;
HAL_TIM_Base_Init(&htim6);
HAL_TIM_Base_Start_IT(&htim6);
while (1)
{
// Do other tasks here
}
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM6)
{
HAL_GPIO_TogglePin(LED_PORT, LED_PIN);
}
}
在这个例子中,我们使用了HAL库提供的定时器功能,使用TIM6定时器产生500ms的定时器中断,从而触发HAL_TIM_PeriodElapsedCallback()回调函数,实现LED灯的闪烁效果。需要注意的是,定时器中断是一种非阻塞方式,可以在闪烁LED灯的同时处理其他任务,避免了使用延时函数时的阻塞问题。
|