硬件需求
| 器件 |
型号 |
数量 |
| 主控 |
STM32F103C8T6 最小系统板 |
1 |
| LED |
5mm 发光二极管 |
1 |
| 电阻 |
220Ω(限流) |
1 |
| 下载器 |
ST-Link V2 |
1 |
硬件连接
1 2
| STM32F103C8T6 LED PA0 ──── PWM ──── (220Ω) ──── LED(+) ──── GND
|
TIM2_CH1 复用 PA0 输出 PWM,占空比 0%→100%→0% 循环实现呼吸效果。
PWM 原理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| PWM(Pulse Width Modulation)脉冲宽度调制: 固定频率下,调节高电平占比(占空比)来控制平均输出电压。
周期 T = 1/f,占空比 = t_on / T × 100%
高电平 ┌────┐ ┌────┐ 25% → LED 微亮 低电平 └────┴────┴────┘
高电平 ┌──────┐ ┌──────┐ 50% → LED 半亮 低电平 └──────┴──┴──────┘
高电平 ┌────────┐┌────────┐ 90% → LED 接近全亮 低电平 └────────┴┴────────┘
人眼对亮度感知有惯性,PWM 频率 >100Hz 即无频闪感。
|
CubeMX 配置
| 外设 |
配置 |
说明 |
| TIM2 |
CH1: PWM Generation, PA0 |
呼吸灯 PWM 输出 |
| PSC |
71 |
72MHz / 72 = 1MHz |
| ARR |
999 |
1MHz / 1000 = 1KHz PWM |
| Pulse |
0 |
初始占空比 0% |
Clock: HSE 8MHz → PLL ×9 = 72MHz。
实现代码
基础版(主循环轮询)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| #include "stm32f1xx_hal.h"
TIM_HandleTypeDef htim2;
static void MX_TIM2_Init(void) { TIM_OC_InitTypeDef sConfigOC = {0};
__HAL_RCC_TIM2_CLK_ENABLE();
htim2.Instance = TIM2; htim2.Init.Prescaler = 71; htim2.Init.CounterMode = TIM_COUNTERMODE_UP; htim2.Init.Period = 999; htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; HAL_TIM_PWM_Init(&htim2);
sConfigOC.OCMode = TIM_OCMODE_PWM1; sConfigOC.Pulse = 0; sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1); }
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim) { GPIO_InitTypeDef GPIO_Init = {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_Init.Pin = GPIO_PIN_0; GPIO_Init.Mode = GPIO_MODE_AF_PP; GPIO_Init.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOA, &GPIO_Init); }
int main(void) { HAL_Init(); SystemClock_Config(); MX_TIM2_Init();
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
int16_t duty = 0; int8_t dir = 1;
while (1) { __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, duty); duty += dir;
if (duty >= 999) dir = -1; if (duty <= 0) dir = 1;
HAL_Delay(2); } }
|
进阶版(定时中断 + 正弦呼吸)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| #include <math.h>
static void MX_TIM3_Init(void) { TIM_ClockConfigTypeDef sClockSourceConfig = {0}; __HAL_RCC_TIM3_CLK_ENABLE();
htim3.Instance = TIM3; htim3.Init.Prescaler = 7199; htim3.Init.Period = 199; htim3.Init.CounterMode = TIM_COUNTERMODE_UP; HAL_TIM_Base_Init(&htim3);
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig);
HAL_NVIC_SetPriority(TIM3_IRQn, 0, 0); HAL_NVIC_EnableIRQ(TIM3_IRQn); HAL_TIM_Base_Start_IT(&htim3); }
void TIM3_IRQHandler(void) { static float phase = 0; const float step = 2.0f * 3.14159265f / 400.0f;
phase += step; if (phase > 2.0f * 3.14159265f) phase = 0;
float brightness = (sinf(phase) + 1.0f) / 2.0f; uint16_t duty = (uint16_t)(brightness * 999);
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, duty); HAL_TIM_IRQHandler(&htim3); }
|
正弦呼吸节奏更自然,不线性突兀。
占空比与亮度关系
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 占空比 0% 25% 50% 75% 100% LED ░░░ ▒▒▒ ▓▓▓ ███ ███ 灭 微亮 半亮 很亮 全亮
实际亮度 (人眼): 0% ───────────────────────────────── 100% ╱ ╱ 人眼感知近似对数曲线 ╱ → 低占空比时亮度变化更敏感 ╱ ╱ 0%────────────────────100% 占空比
→ 正弦波呼吸比线性渐变更自然
|
调参指南
| 参数 |
推荐值 |
效果 |
| ARR=999 |
1KHz |
无频闪,标准配置 |
| ARR=19999 |
50Hz |
可见闪烁,不建议 |
HAL_Delay(1) |
1ms/步 |
呼吸周期 ~1s,稍快 |
HAL_Delay(3) |
3ms/步 |
呼吸周期 ~3s,适中 |
HAL_Delay(5) |
5ms/步 |
呼吸周期 ~5s,缓慢 |
| ARR=255 |
8 位精度 |
配合 uint8_t duty |
| 正弦 step 分母 |
200 |
快呼吸 ~4s |
| 正弦 step 分母 |
400 |
慢呼吸 ~8s |
多路 PWM 扩展
TIM2 有 4 个通道,可同时驱动 4 路:
| 通道 |
引脚 |
寄存器 |
| CH1 |
PA0 |
TIM_CHANNEL_1 |
| CH2 |
PA1 |
TIM_CHANNEL_2 |
| CH3 |
PA2 |
TIM_CHANNEL_3 |
| CH4 |
PA3 |
TIM_CHANNEL_4 |
1 2 3 4 5 6 7 8 9 10 11
| HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2); HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_3); HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_4);
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, sinf(phase + 0) * 500 + 500); __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, sinf(phase + 1.57f) * 500 + 500); __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_3, sinf(phase + 3.14f) * 500 + 500); __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_4, sinf(phase + 4.71f) * 500 + 500);
|
扩展方向
| 方向 |
方案 |
| RGB 呼吸灯 |
3 路 PWM → R/G/B 三色 LED,不同相位产生彩虹渐变 |
| 按键调速 |
GPIO 中断切换呼吸速度,双击/长按不同模式 |
| 串口调光 |
USART 接收指令设置目标亮度,PWM 缓动过渡 |
| 音乐律动 |
ADC 采集麦克风 → PWM 亮度随音量变化 |
| WS2812 灯带 |
TIM2 DMA + PWM 模拟 WS2812 时序,驱动 60 灯珠 |