STM32单片机延时函数全解析:SysTick和HAL库的奥秘
发布时间: 2024-07-05 20:53:58 阅读量: 335 订阅数: 45
![STM32单片机延时函数全解析:SysTick和HAL库的奥秘](https://img-blog.csdnimg.cn/e699d2537f114033a0103e63ffe6d639.png)
# 1. STM32延时函数概述
STM32微控制器中,延时函数是实现系统定时和控制的重要功能。它允许程序员以精确的时间间隔执行任务,从而实现各种应用。STM32提供多种延时机制,包括SysTick定时器和HAL库函数,每种机制都有其独特的特性和适用场景。本章将概述STM32延时函数,为后续章节的深入探讨奠定基础。
# 2. SysTick延时机制
### 2.1 SysTick寄存器结构与配置
SysTick是STM32系列MCU中用于实现系统定时器的外设。其寄存器结构如下:
```
struct __attribute__((packed)) SysTick_TypeDef
{
volatile uint32_t CTRL; // 控制和状态寄存器
volatile uint32_t LOAD; // 重载值寄存器
volatile uint32_t VAL; // 当前值寄存器
volatile const uint32_t CALIB; // 校准值寄存器
};
```
其中,关键寄存器如下:
- **CTRL:**控制和状态寄存器,用于配置SysTick时钟源、中断使能、计数模式等。
- **LOAD:**重载值寄存器,用于设置SysTick定时器的重载值。
- **VAL:**当前值寄存器,用于读取SysTick定时器的当前值。
### 2.2 SysTick中断处理流程
SysTick中断处理流程如下:
1. 当SysTick定时器计数器减至0时,触发SysTick中断。
2. 进入中断服务程序(ISR),执行中断处理代码。
3. 清除SysTick中断标志位。
4. 重载SysTick定时器,重新开始计数。
### 2.3 SysTick延时函数实现
基于SysTick中断机制,可以实现延时函数。其基本原理如下:
1. 配置SysTick定时器,设置重载值。
2. 进入SysTick中断服务程序,执行延时操作。
3. 在延时操作完成后,退出中断服务程序。
```c
void SysTick_DelayMs(uint32_t ms)
{
// 计算SysTick重载值
uint32_t reload = (ms * (SystemCoreClock / 1000)) - 1;
// 配置SysTick
SysTick->LOAD = reload;
SysTick->VAL = 0;
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk;
// 进入SysTick中断服务程序
while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk));
// 清除SysTick中断标志位
SysTick->CTRL &= ~SysTick_CTRL_COUNTFLAG_Msk;
// 退出SysTick中断服务程序
}
```
**代码逻辑逐行解读:**
- 计算SysTick重载值:根据给定的延时时间ms,计算出SysTick定时器的重载值,以实现ms级的延时。
- 配置SysTick:设置SysTick定时器的重载值、当前值和控制寄存器,使能SysTick定时器和中断。
- 进入SysTick中断服务程序:进入SysTick中断服务程序,等待SysTick定时器计数器减至0。
- 清除SysTick中断标志位:当SysTick定时器计数器减至0时,触发SysTick中断,清除中断标志位。
- 退出SysTick中断服务程序:延时操作完成后,退出SysTick中断服务程序。
# 3. HAL库延时机制
### 3.1 HAL库延时函数分类
HAL库提供了两种延时函数:
- `HAL_Delay()`: 毫秒级延时函数,可实现毫秒级延时。
- `HAL_DelayUS()`: 微秒级延时函数,可实现微秒级延时。
### 3.2 `HAL_Delay()`函数原理与使用
`HAL_Delay()`函数的原理是使用SysTick定时器。其内部实现如下:
```c
void HAL_Delay(uint32_t Delay)
{
__HAL_RCC_SYSCFG_CLK_ENABLE();
HAL_IncTick();
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq() / 1000);
while (Delay--)
{
while ((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) == 0)
{
}
HAL_IncTick();
}
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
}
```
**参数说明:**
- `Delay`: 延时时间,单位为毫秒。
**使用步骤:**
1. 使能SysTick时钟。
2. 调用`HAL_IncTick()`函数增加SysTick计数器。
3. 配置SysTick定时器,使其每1ms产生一个中断。
4. 进入延时循环,等待`Delay`个毫秒。
5. 在延时循环中,不断检查SysTick中断标志位。
6. 每当SysTick中断发生时,调用`HAL_IncTick()`函数增加SysTick计数器。
7. 当延时时间达到时,退出延时循环,关闭SysTick定时器。
### 3.3 `HAL_DelayUS()`函数原理与使用
`HAL_DelayUS()`函数的原理是使用SysTick定时器和CPU时钟频率。其内部实现如下:
```c
void HAL_DelayUS(uint32_t Delay)
{
uint32_t tickstart = HAL_GetTick();
uint32_t wait = Delay * (HAL_RCC_GetHCLKFreq() / 1000000);
while ((HAL_GetTick() - tickstart) < wait)
{
}
}
```
**参数说明:**
- `Delay`: 延时时间,单位为微秒。
**使用步骤:**
1. 获取当前SysTick计数器值。
2. 计算延时时间对应的SysTick计数器增量。
3. 进入延时循环,等待SysTick计数器增量达到。
4. 在延时循环中,不断检查SysTick计数器值。
5. 当延时时间达到时,退出延时循环。
# 4. SysTick与HAL库延时函数对比
### 4.1 延时精度对比
SysTick和HAL库延时函数的延时精度都受限于系统时钟的精度。对于STM32系列MCU,系统时钟的精度一般为1μs。因此,SysTick和HAL库延时函数的延时精度也都在1μs左右。
**SysTick延时精度**
SysTick延时精度主要受以下因素影响:
- 系统时钟精度
- SysTick时钟源选择
- SysTick计数器分辨率
**HAL库延时精度**
HAL库延时精度主要受以下因素影响:
- 系统时钟精度
- HAL库延时函数实现方式
### 4.2 延时范围对比
SysTick和HAL库延时函数的延时范围不同。SysTick延时范围为0~0xFFFFFFFF(约4.29s),而HAL库延时函数的延时范围为0~0x7FFFFFFF(约2.14s)。
**SysTick延时范围**
SysTick延时范围由SysTick计数器的位宽决定。SysTick计数器为32位寄存器,因此其延时范围为0~0xFFFFFFFF。
**HAL库延时范围**
HAL库延时函数的延时范围由其内部实现方式决定。HAL库延时函数一般通过循环的方式实现,因此其延时范围受限于循环次数。HAL_Delay()函数的延时范围为0~0x7FFFFFFF,而HAL_DelayUS()函数的延时范围为0~0x3FFFFFFF。
### 4.3 延时效率对比
SysTick和HAL库延时函数的延时效率也不同。SysTick延时函数的效率更高,而HAL库延时函数的效率稍低。
**SysTick延时效率**
SysTick延时函数的效率较高,主要是因为其直接使用SysTick硬件定时器实现。SysTick硬件定时器是一个独立的定时器,不占用CPU资源。因此,SysTick延时函数的执行不会影响其他任务的执行。
**HAL库延时效率**
HAL库延时函数的效率稍低,主要是因为其通过循环的方式实现。循环的方式需要占用CPU资源,因此HAL库延时函数的执行会影响其他任务的执行。
**性能对比表格**
| 特性 | SysTick延时函数 | HAL库延时函数 |
|---|---|---|
| 延时精度 | 1μs | 1μs |
| 延时范围 | 0~0xFFFFFFFF | 0~0x7FFFFFFF |
| 延时效率 | 高 | 低 |
# 5.1 LED闪烁程序
在LED闪烁程序中,延时函数用于控制LED的亮灭时间。以下是一个使用SysTick延时函数实现LED闪烁的示例代码:
```c
#include "stm32f10x.h"
void SysTick_Handler(void)
{
GPIO_WriteBit(GPIOB, GPIO_Pin_5, (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_5)));
}
int main(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
SysTick_Config(SystemCoreClock / 1000);
while (1)
{
}
}
```
在这个程序中,SysTick中断服务函数用于切换LED的状态。当SysTick中断发生时,它会将LED的输出电平取反,从而实现LED的闪烁。
## 5.2 串口通信程序
在串口通信程序中,延时函数用于控制数据的发送和接收速率。以下是一个使用HAL库延时函数实现串口通信的示例代码:
```c
#include "stm32f10x.h"
#include "usart.h"
int main(void)
{
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
while (1)
{
uint8_t data = USART_ReceiveData(USART1);
HAL_Delay(10);
USART_SendData(USART1, data);
}
}
```
在这个程序中,HAL_Delay()函数用于在数据发送和接收之间引入延迟,以确保数据传输的可靠性。
## 5.3 定时器中断程序
在定时器中断程序中,延时函数用于控制定时器中断的发生频率。以下是一个使用SysTick延时函数实现定时器中断的示例代码:
```c
#include "stm32f10x.h"
void SysTick_Handler(void)
{
TIM_SetCounter(TIM2, 0);
TIM_Cmd(TIM2, ENABLE);
}
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
int main(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Prescaler = 8400 - 1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_Period = 1000 - 1;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
SysTick_Config(SystemCoreClock / 1000);
while (1)
{
}
}
```
在这个程序中,SysTick中断服务函数用于触发定时器中断。当SysTick中断发生时,它会将定时器2的计数器清零并使能定时器2。定时器2中断服务函数用于清除定时器2的中断标志位。
0
0