玩转 STM32 定时器:从多任务并发到呼吸灯的实战指南

一.定时器中断实现多任务并发

在嵌入式开发中,定时器中断是实现精准定时和多任务并发的高效方式。相比传统的循环延时,定时器中断能让 CPU 在等待期间处理其他任务,大幅提高资源利用率。

1. 功能需求

  • 5 秒定时:每隔 5 秒通过串口发送 “hello windows!”
  • 2 秒定时:控制 LED 周期性闪烁
  • 实现上述两个任务的并发执行

2. 硬件设计

  • 主控:STM32F103C8T6 最小系统板
  • LED:外接 LED+ 板载 LED
  • 串口:USART1
  • 定时器:TIM2(2 秒定时)、TIM3(5 秒定时)

3. STM32CubeMX 配置步骤

(1)基础配置

  • 时钟配置:外部晶振 (HSE),系统时钟 72MHz
  • LED 引脚:PA0 设为 GPIO 输出,PC13 默认输出
  • 串口配置:USART1 异步模式,波特率 115200,无校验位

(2)定时器配置

定时器时钟频率为 72MHz,通过预分频器 (PSC) 和自动重装载值 (ARR) 计算定时时间:定时时间 = (PSC+1)×(ARR+1) / 时钟频率

  • TIM2(2 秒定时)
    • 预分频器 (PSC):7199 → 72MHz/(7199+1)=10kHz
    • 自动重装载值 (ARR):19999 → 10kHz×20000=2 秒
    • 使能更新中断 (Update Interrupt)
  • TIM3(5 秒定时)
    • 预分频器 (PSC):7199 → 同 TIM2,10kHz 计数频率
    • 自动重装载值 (ARR):49999 → 10kHz×50000=5 秒
    • 使能更新中断 (Update Interrupt)

配置过程如下:
在这里插入图片描述

定时器3与上述类似

4.代码实现

(1)定时器中断回调函数

main.c中重写 HAL 库定时器中断回调函数:

/* USER CODE BEGIN 4 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
  if (htim == &htim2) {  // 2秒定时:LED闪烁
    HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0);
  }
  else if (htim == &htim3) {  // 5秒定时:串口发送
    uint8_t data[] = "hello windows!\r\n";
    HAL_UART_Transmit(&huart1, data, sizeof(data)-1, 100);
  }
}
/* USER CODE END 4 */

(2)主函数启动定时器

int main(void) {
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  MX_TIM2_Init();
  MX_TIM3_Init();
  // 启动定时器中断
  HAL_TIM_Base_Start_IT(&htim2);
  HAL_TIM_Base_Start_IT(&htim3);
  while (1) {
    // 主循环可执行其他任务,不阻塞
  }
}

实验结果如下:

定时器中断

定时器中断

定时器中断

5.非定时器实现方案(轮询法)

若不使用定时器,可通过系统滴答定时器的时间戳实现伪并发:

int main(void) {
  uint32_t led_prev = 0, uart_prev = 0;
  uint32_t current;

  while (1) {
    current = HAL_GetTick();  // 获取毫秒级时间戳

    // 2秒LED闪烁
    if (current - led_prev >= 2000) {
      HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0);
      led_prev = current;
    }

    // 5秒串口发送
    if (current - uart_prev >= 5000) {
      uint8_t data[] = "hello windows!\r\n";
      HAL_UART_Transmit(&huart1, data, sizeof(data)-1, 100);
      uart_prev = current;
    }
  }
}

缺点:精度受主循环执行速度影响,若存在耗时操作会导致定时不准。

二.PWM 实现呼吸灯效果

PWM(脉冲宽度调制)通过改变高低电平占空比实现模拟输出效果,适合控制 LED 亮度变化(呼吸灯)。

1. 功能需求

  • 使用 TIM3 和 TIM4 输出 PWM 波形
  • 占空比随时间动态变化,实现两个 LED 的呼吸效果
  • 控制对象:板载 LED和外接 LED

2. STM32CubeMX 配置

(1)定时器 PWM 配置

  • TIM3_CH1(PA6):控制外接 LED
    • 时钟源:内部时钟
    • 预分频器 (PSC):71 → 72MHz/(71+1)=1MHz
    • 自动重装载值 (ARR):999 → 频率 = 1MHz/1000=1kHz(人眼无闪烁)
    • 模式:PWM Generation CH1,PWM 模式 1
  • TIM4_CH2(PB7):控制板载 LED(需通过 GPIO 重映射)
    • 配置同 TIM3,PSC=71,ARR=999
    • 使能 TIM4 重映射:部分重映射→TIM4_CH2 映射到 PB7

配置过程如下图:

在这里插入图片描述

3. 呼吸灯代码实现

(1)PWM 占空比动态调整逻辑

// 全局变量
uint16_t pwm_val = 0;       // PWM占空比(0-999)
uint8_t dir = 1;            // 变化方向(1-递增,0-递减)

int main(void) {
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_TIM3_Init();
  MX_TIM4_Init();

  // 启动PWM输出
  HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
  HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_2);

  while (1) {
    // 占空比调整(0→999→0循环)
    if (dir) {
      pwm_val++;
      if (pwm_val >= 999) dir = 0;
    } else {
      pwm_val--;
      if (pwm_val <= 0) dir = 1;
    }

    // 更新PWM占空比
    __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, pwm_val);
    __HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_2, pwm_val);
    
    HAL_Delay(5);  // 控制变化速度
  }
}

4.实现原理

  • 呼吸灯效果通过 PWM 占空比从 0 到 100% 平滑过渡再反向过渡实现
  • TIM3 和 TIM4 的 PWM 频率设置为 1kHz(高于人眼视觉暂留频率)
  • 通过__HAL_TIM_SET_COMPARE函数实时更新比较值,改变高低电平占空比

实验结果如下:

PWM呼吸灯

三.总结

该内容围绕 STM32F103C8T6 的定时器中断与 PWM 应用展开,介绍了两部分核心内容:一是利用 TIM2 和 TIM3 定时器中断实现多任务并发,通过配置合适的预分频器和自动重装载值,分别实现 2 秒 LED 闪烁与 5 秒串口发送消息的功能,并对比了轮询法的不足;二是借助 TIM3 和 TIM4 的 PWM 功能实现呼吸灯效果,通过动态调整占空比让外接 LED 和板载 LED 呈现亮度平滑变化的效果,同时说明了相关配置与实现原理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值