硬件需求

器件 型号 数量
主控 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
/* main.c */
#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; // 72MHz / (71+1) = 1MHz
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 999; // 1MHz / (999+1) = 1KHz PWM
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; // 初始占空比 0
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1);
}

/* HAL TIM PWM 底层初始化 */
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(); // 72MHz
MX_TIM2_Init();

HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);

int16_t duty = 0;
int8_t dir = 1; // 1=增亮, -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); // 2ms × 1000 步 ≈ 2s 一个周期
}
}

进阶版(定时中断 + 正弦呼吸)

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; // 72MHz / 7200 = 10KHz
htim3.Init.Period = 199; // 10KHz / 200 = 50Hz 更新中断
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; // 400 步一个周期 → 8s

phase += step;
if (phase > 2.0f * 3.14159265f) phase = 0;

// sin 范围 [-1,1] → [0,1] → [0,999]
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
/* 同时启动 4 路,独立呼吸相位 */
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 灯珠