STM32单片机延时应用指南:从LED闪烁到传感器采样
发布时间: 2024-07-05 20:59:46 阅读量: 3 订阅数: 3 ![](https://csdnimg.cn/release/wenkucmsfe/public/img/col_vip.0fdee7e1.png)
![](https://csdnimg.cn/release/wenkucmsfe/public/img/col_vip.0fdee7e1.png)
![STM32单片机延时应用指南:从LED闪烁到传感器采样](https://img-blog.csdnimg.cn/img_convert/0f5975ca28b885978bfb43077db73a6f.png)
# 1. STM32单片机延时概述**
STM32单片机延时是控制程序执行节奏的重要手段,广泛应用于LED闪烁、传感器采样、PWM波形生成等场景。延时方法主要分为循环计数延时和硬件定时器延时,各有其优缺点和适用场景。
循环计数延时简单易用,但精度受系统时钟频率影响。硬件定时器延时精度高,但配置和使用相对复杂。选择合适的延时方法,需要综合考虑精度、效率和系统资源等因素。
# 2. STM32单片机延时方法**
**2.1 循环计数延时**
循环计数延时是最简单直接的延时方法,通过循环执行空操作来消耗时间。
**2.1.1 常规循环计数延时**
```c
void delay_us(uint32_t us) {
uint32_t i;
for (i = 0; i < us * 12; i++) {
// 空操作
}
}
```
**代码逻辑分析:**
* `us`参数指定延时微秒数。
* 循环执行12次空操作,每次空操作耗时约83纳秒。
* 通过调整循环次数可以实现不同延时时间。
**2.1.2 SysTick定时器循环计数延时**
SysTick定时器是一个32位递减计数器,可以作为循环计数延时的时钟源。
```c
void delay_us_systick(uint32_t us) {
SysTick->LOAD = us * 12;
SysTick->VAL = 0;
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
while ((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) == 0) {}
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
}
```
**代码逻辑分析:**
* `us`参数指定延时微秒数。
* 将SysTick定时器计数器值设置为`us * 12`,表示12微秒的延时。
* 启动SysTick定时器。
* 等待`COUNTFLAG`标志位置1,表示计数完成。
* 停止SysTick定时器。
**2.2 硬件定时器延时**
硬件定时器提供更精确和灵活的延时方式。
**2.2.1 定时器简介**
STM32单片机有多个硬件定时器,它们可以配置为不同的模式,包括定时器模式、计数器模式和PWM模式。
**2.2.2 定时器中断延时**
```c
void delay_us_timer(uint32_t us) {
TIM2->ARR = us - 1;
TIM2->PSC = 84 - 1;
TIM2->CR1 |= TIM_CR1_CEN_Msk;
while ((TIM2->SR & TIM_SR_UIF_Msk) == 0) {}
TIM2->SR &= ~TIM_SR_UIF_Msk;
TIM2->CR1 &= ~TIM_CR1_CEN_Msk;
}
```
**代码逻辑分析:**
* `us`参数指定延时微秒数。
* 将定时器2的自动重装载寄存器(ARR)设置为`us - 1`,表示延时`us`微秒。
* 将定时器2的分频寄存器(PSC)设置为84,表示定时器时钟频率为1MHz。
* 启动定时器2。
* 等待定时器2的更新中断标志位(UIF)置1,表示计数完成。
* 清除UIF标志位。
* 停止定时器2。
**2.2.3 定时器输出比较延时**
```c
void delay_us_timer_oc(uint32_t us) {
TIM2->CCR1 = us - 1;
TIM2->PSC = 84 - 1;
TIM2->CR1 |= TIM_CR1_CEN_Msk;
while ((TIM2->SR & TIM_SR_CC1IF_Msk) == 0) {}
TIM2->SR &= ~TIM_SR_CC1IF_Msk;
TIM2->CR1 &= ~TIM_CR1_CEN_Msk;
}
```
**代码逻辑分析:**
* `us`参数指定延时微秒数。
* 将定时器2的捕获/比较寄存器1(CCR1)设置为`us - 1`,表示延时`us`微秒。
* 将定时器2的分频寄存器(PSC)设置为84,表示定时器时钟频率为1MHz。
* 启动定时器2。
* 等待定时器2的捕获/比较1中断标志位(CC1IF)置1,表示计数完成。
* 清除CC1IF标志位。
* 停止定时器2。
# 3. STM32单片机延时应用**
### 3.1 LED闪烁
LED闪烁是STM32单片机延时应用中最常见的场景之一。通过控制LED的亮灭时间,可以实现各种闪烁效果。
#### 3.1.1 循环计数延时实现LED闪烁
循环计数延时是最简单的延时方法,通过循环计数来消耗时间。实现LED闪烁的步骤如下:
1. 定义LED引脚并将其配置为输出模式。
2. 进入一个无限循环。
3. 在循环中,将LED引脚置为高电平,表示LED亮。
4. 使用循环计数延时函数延时一段时间。
5. 将LED引脚置为低电平,表示LED灭。
6. 再次使用循环计数延时函数延时一段时间。
**代码块:**
```c
#define LED_PIN GPIO_PIN_13
#define DELAY_ON_MS 1000
#define DELAY_OFF_MS 1000
void main() {
// 配置LED引脚
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN;
GPIOC->MODER |= GPIO_MODER_MODER13_0;
while (1) {
// LED亮
GPIOC->ODR |= GPIO_ODR_ODR13;
delay_ms(DELAY_ON_MS);
// LED灭
GPIOC->ODR &= ~GPIO_ODR_ODR13;
delay_ms(DELAY_OFF_MS);
}
}
```
**逻辑分析:**
* `RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN;`:使能GPIOC时钟。
* `GPIOC->MODER |= GPIO_MODER_MODER13_0;`:将PC13引脚配置为输出模式。
* `GPIOC->ODR |= GPIO_ODR_ODR13;`:将PC13引脚置为高电平。
* `delay_ms(DELAY_ON_MS);`:使用循环计数延时函数延时DELAY_ON_MS毫秒。
* `GPIOC->ODR &= ~GPIO_ODR_ODR13;`:将PC13引脚置为低电平。
* `delay_ms(DELAY_OFF_MS);`:使用循环计数延时函数延时DELAY_OFF_MS毫秒。
#### 3.1.2 定时器中断延时实现LED闪烁
定时器中断延时比循环计数延时更加准确和高效。实现LED闪烁的步骤如下:
1. 配置定时器并设置中断。
2. 在中断服务函数中,控制LED的亮灭状态。
**代码块:**
```c
#define LED_PIN GPIO_PIN_13
#define TIMER_PERIOD 1000
void main() {
// 配置LED引脚
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN;
GPIOC->MODER |= GPIO_MODER_MODER13_0;
// 配置定时器
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
TIM2->PSC = 80 - 1;
TIM2->ARR = TIMER_PERIOD - 1;
TIM2->DIER |= TIM_DIER_UIE;
NVIC_EnableIRQ(TIM2_IRQn);
TIM2->CR1 |= TIM_CR1_CEN;
while (1) {
// 进入低功耗模式,等待中断唤醒
__WFI();
}
}
void TIM2_IRQHandler() {
// 清除中断标志位
TIM2->SR &= ~TIM_SR_UIF;
// 控制LED亮灭状态
GPIOC->ODR ^= GPIO_ODR_ODR13;
}
```
**逻辑分析:**
* `RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;`:使能TIM2时钟。
* `TIM2->PSC = 80 - 1;`:设置定时器分频系数,使定时器时钟为1MHz。
* `TIM2->ARR = TIMER_PERIOD - 1;`:设置定时器自动重装载值,使定时器每1ms中断一次。
* `TIM2->DIER |= TIM_DIER_UIE;`:使能定时器更新中断。
* `NVIC_EnableIRQ(TIM2_IRQn);`:使能TIM2中断。
* `TIM2->CR1 |= TIM_CR1_CEN;`:启动定时器。
* `__WFI();`:进入低功耗模式,等待中断唤醒。
* `TIM2_IRQHandler()`:定时器中断服务函数,控制LED亮灭状态。
### 3.2 传感器采样
传感器采样是STM32单片机延时应用的另一个重要场景。通过控制传感器采样的时间间隔,可以获取传感器数据的实时变化。
#### 3.2.1 传感器简介
传感器是一种将物理量转换为电信号的器件。常见的传感器包括温度传感器、湿度传感器、加速度传感器等。
#### 3.2.2 定时器中断延时实现传感器采样
定时器中断延时可以精确控制传感器采样的时间间隔。实现传感器采样的步骤如下:
1. 配置传感器引脚并初始化传感器。
2. 配置定时器并设置中断。
3. 在中断服务函数中,读取传感器数据。
**代码块:**
```c
#define SENSOR_PIN GPIO_PIN_0
#define TIMER_PERIOD 1000
void main() {
// 配置传感器引脚
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
GPIOA->MODER |= GPIO_MODER_MODER0_0;
// 初始化传感器
// 配置定时器
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
TIM2->PSC = 80 - 1;
TIM2->ARR = TIMER_PERIOD - 1;
TIM2->DIER |= TIM_DIER_UIE;
NVIC_EnableIRQ(TIM2_IRQn);
TIM2->CR1 |= TIM_CR1_CEN;
while (1) {
// 进入低功耗模式,等待中断唤醒
__WFI();
}
}
void TIM2_IRQHandler() {
// 清除中断标志位
TIM2->SR &= ~TIM_SR_UIF;
// 读取传感器数据
// 处理传感器数据
}
```
**逻辑分析:**
* `RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;`:使能GPIOA时钟。
* `GPIOA->MODER |= GPIO_MODER_MODER0_0;`:将PA0引脚配置为输入模式。
* `RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;`:使能TIM2时钟。
* `TIM2->PSC = 80 - 1;`:设置定时器分频系数,使定时器时钟为1MHz。
* `TIM2->ARR = TIMER_PERIOD - 1;`:设置定时器自动重装载值,使定时器每1ms中断一次。
* `TIM2->DIER |= TIM_DIER_UIE;`:使能定时器更新中断。
* `NVIC_EnableIRQ(TIM2_IRQn);`:使能TIM2中断。
* `TIM2->CR1 |= TIM_CR1_CEN;`:启动定时器。
* `__WFI();`:进入低功耗模式,等待中断唤醒。
* `TIM2_IRQHandler()`:定时器中断服务函数,读取传感器数据并进行处理。
# 4.1 延时精度的优化
### 4.1.1 循环计数延时的精度优化
循环计数延时精度受时钟频率影响,时钟频率越高,精度越高。对于循环计数延时,可以通过以下方法优化精度:
- **使用高精度时钟源:**STM32单片机提供多个时钟源,如内部RC振荡器、外部晶振、PLL等。选择高精度的时钟源,如外部晶振,可以提高延时精度。
- **使用硬件乘法器:**循环计数延时需要执行大量的乘法运算,使用硬件乘法器可以提高运算速度和精度。
- **使用汇编语言:**汇编语言可以直接操作硬件寄存器,避免了函数调用和编译器的开销,可以提高延时精度。
### 4.1.2 定时器延时的精度优化
定时器延时精度受定时器时钟频率和计数周期影响。以下方法可以优化定时器延时的精度:
- **使用高精度时钟源:**与循环计数延时类似,定时器延时也受时钟频率影响。选择高精度的时钟源可以提高延时精度。
- **使用较长的计数周期:**计数周期越长,延时时间越长,精度越高。但是,计数周期过长会导致定时器溢出,影响精度。
- **使用定时器预分频器:**定时器预分频器可以降低定时器时钟频率,从而延长计数周期,提高精度。
- **使用定时器比较捕获功能:**定时器比较捕获功能可以精确地测量时间间隔,可以用来校准定时器延时。
**示例代码:**
```c
// 使用定时器比较捕获功能校准定时器延时
void calibrate_timer_delay(TIM_TypeDef *timer) {
// 配置定时器为比较捕获模式
timer->CR1 |= TIM_CR1_CEN | TIM_CR1_URS;
timer->CCMR1 |= TIM_CCMR1_CC1S_0;
timer->CCER |= TIM_CCER_CC1E;
// 等待定时器稳定
while (!(timer->SR & TIM_SR_UIF));
// 清除更新中断标志
timer->SR &= ~TIM_SR_UIF;
// 启动定时器
timer->CR1 |= TIM_CR1_CEN;
// 等待定时器溢出
while (!(timer->SR & TIM_SR_UIF));
// 清除更新中断标志
timer->SR &= ~TIM_SR_UIF;
// 计算校准值
uint32_t calibration_value = timer->CNT;
// 停止定时器
timer->CR1 &= ~TIM_CR1_CEN;
// 返回校准值
return calibration_value;
}
```
# 5.1 PWM波形生成
### 5.1.1 PWM波形简介
PWM(脉宽调制)是一种调制技术,通过改变脉冲的宽度来控制输出信号的平均值。PWM波形由一系列重复的脉冲组成,每个脉冲都有一个固定的周期和占空比。占空比定义为脉冲宽度与周期的比值,它决定了输出信号的平均值。
### 5.1.2 定时器输出比较PWM波形生成
STM32单片机可以通过定时器输出比较功能来生成PWM波形。输出比较功能允许定时器在达到特定值时触发一个事件。通过配置定时器的时钟源、预分频器和比较值,可以生成具有特定频率和占空比的PWM波形。
**代码块:**
```c
/* 初始化定时器3为PWM模式 */
TIM3->CR1 |= TIM_CR1_CEN;
TIM3->PSC = 1000; // 预分频器为 1000
TIM3->ARR = 1000; // 自动重装载寄存器为 1000
/* 设置比较值,占空比为 50% */
TIM3->CCR1 = 500;
/* 启用输出比较通道 1 */
TIM3->CCER |= TIM_CCER_CC1E;
```
**代码逻辑分析:**
* `TIM3->CR1 |= TIM_CR1_CEN;`:使能定时器 3。
* `TIM3->PSC = 1000;`:设置预分频器为 1000,即定时器时钟频率为系统时钟频率的 1/1000。
* `TIM3->ARR = 1000;`:设置自动重装载寄存器为 1000,即 PWM 波形的周期为 1000 个时钟周期。
* `TIM3->CCR1 = 500;`:设置比较值 1 为 500,即 PWM 波形的占空比为 50%。
* `TIM3->CCER |= TIM_CCER_CC1E;`:使能输出比较通道 1,输出 PWM 波形。
**参数说明:**
* `TIM3->CR1`:定时器控制寄存器 1,用于控制定时器的使能、时钟源和预分频器等。
* `TIM3->PSC`:预分频器寄存器,用于设置定时器时钟的预分频值。
* `TIM3->ARR`:自动重装载寄存器,用于设置定时器的重装载值。
* `TIM3->CCR1`:比较值寄存器 1,用于设置输出比较通道 1 的比较值。
* `TIM3->CCER`:输出比较控制寄存器,用于控制输出比较通道的使能和极性等。
# 6. STM32单片机延时常见问题及解决方法**
**6.1 延时不准确**
**6.1.1 循环计数延时不准确**
* **问题描述:**循环计数延时的精度受时钟频率的影响,在不同的时钟频率下,延时时间会有偏差。
* **解决方法:**
* 使用SysTick定时器作为时钟源,SysTick定时器具有固定的时钟频率,可以提高延时精度的稳定性。
* 使用硬件定时器作为时钟源,硬件定时器的时钟频率通常比SysTick定时器更高,可以进一步提高延时精度。
**6.1.2 定时器延时不准确**
* **问题描述:**定时器延时的时间精度受定时器时钟频率、预分频器和重装载值的影响,如果这些参数设置不当,会导致延时时间不准确。
* **解决方法:**
* 根据所需的延时时间,计算合适的定时器时钟频率、预分频器和重装载值。
* 使用示波器或其他测量工具验证延时时间的准确性。
**6.2 延时效率低**
**6.2.1 循环计数延时效率低**
* **问题描述:**循环计数延时需要占用大量的CPU时间,在需要频繁延时的场合,会影响系统的整体性能。
* **解决方法:**
* 避免使用循环计数延时,转而使用硬件定时器延时。
* 使用低功耗延时模式,在延时期间让CPU进入低功耗状态。
**6.2.2 定时器延时效率低**
* **问题描述:**定时器延时需要占用定时器资源,如果同时需要多个延时操作,可能会导致定时器资源不足,影响延时效率。
* **解决方法:**
* 合理分配定时器资源,避免同时使用多个定时器进行延时操作。
* 使用SysTick定时器作为延时时钟源,SysTick定时器是一个全局定时器,可以同时为多个延时操作提供时钟源。
0
0
相关推荐
![zip](https://img-home.csdnimg.cn/images/20210720083736.png)
![zip](https://img-home.csdnimg.cn/images/20210720083736.png)
![rar](https://img-home.csdnimg.cn/images/20210720083606.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)