🔬 实验15:定时器——PWM
📌 实验目标
- 掌握定时器 PWM 输出模式的配置
- 理解占空比和频率的计算
- 实现 LED 呼吸灯效果
- 了解 PWM 在直流电机调速中的应用
一、实验内容
- 配置 TIM2 CH2 (PA1) 为 PWM 输出
- 设置 PWM 频率 20KHz
- 动态改变占空比实现呼吸灯
- 使用 PWM 控制蜂鸣器和 LED
二、PWM 基本原理
PWM(脉冲宽度调制)的本质是:定时器计数器 CNT 从 0 递增到自动重装值 ARR,同时与捕获/比较寄存器 CCRx 比较——CNT 小于 CCRx 时输出有效电平,否则输出无效电平。因此:
- ARR 决定 PWM 周期(频率)
- CCRx 决定 PWM 占空比(高电平所占比例)
频率与占空比计算
// 定时器输入时钟 72MHz(APB1 分频后翻倍得到)
// 设 PSC = 0, ARR = 71 → 计数周期 = (ARR+1)/(Tclk/(PSC+1))
// PWM 频率 = Tclk / ((PSC+1) * (ARR+1))
// = 72MHz / (1 * 72) = 1MHz
// 占空比 = CCR / (ARR+1) * 100%
// 例:ARR=9(10 级亮度),CCR=8 → 占空比 80%📌 本实验硬件映射
- LED0 → PB5 = TIM3_CH2(默认在 PA7,需部分重映射到 PB5)
- 蜂鸣器 BEEP → PB8 = TIM4_CH3(默认引脚,无需重映射)
三、示例程序:PWM 呼吸灯 + 蜂鸣器
timer.c — TIM3_CH2(PB5) 与 TIM4_CH3(PB8) PWM 初始化
#include "stm32f10x.h"
#include "timer.h"
//====================================================================
// TIM4_CH3 → PB8 蜂鸣器
// PWM 频率 1KHz,占空比 80%(任务要求)
// 周期 1ms → ARR=9,可分成 10 个等级
//====================================================================
void TIM4_PWM_Init(u16 arr, u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
// 1. 使能定时器 4 与 GPIOB 时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
// 2. PB8 复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// 3. 初始化定时器
TIM_TimeBaseStructure.TIM_Period = arr; // ARR 决定周期
TIM_TimeBaseStructure.TIM_Prescaler = psc; // PSC 决定分频
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
// 4. 初始化输出比较(PWM 模式 2,输出使能,极性高)
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC3Init(TIM4, &TIM_OCInitStructure); // CH3
// 5. 使能预装载寄存器,使能定时器
TIM_OC3PreloadConfig(TIM4, TIM_OCPreload_Enable);
TIM_Cmd(TIM4, ENABLE);
}
//====================================================================
// TIM3_CH2 → PB5 LED0(需部分重映射)
// PWM 频率 1KHz,占空比 30%(任务要求)
//====================================================================
void TIM3_PWM_Init(u16 arr, u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
// 1. 使能 TIM3、GPIOB 时钟 + AFIO 时钟(重映射必须)
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
// 2. 部分重映射 TIM3_CH2 → PB5
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE);
// 3. PB5 复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// 4. 初始化定时器(周期 1ms)
TIM_TimeBaseStructure.TIM_Period = arr;
TIM_TimeBaseStructure.TIM_Prescaler = psc;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
// 5. 初始化 PWM 输出(CH2)
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC2Init(TIM3, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
TIM_Cmd(TIM3, ENABLE);
}main.c — 主程序与呼吸灯实现
#include "stm32f10x.h"
#include "delay.h"
#include "timer.h"
int main(void)
{
u16 pwmval = 0; // 当前占空比
u8 dir = 1; // 方向:1 变亮,0 变暗
delay_init(); // 延时函数初始化
LED_Init(); // LED 端口初始化
// TIM4_CH3 → PB8 蜂鸣器,ARR=9 分 10 级,80% 占空比
TIM4_PWM_Init(9, 0);
// TIM3_CH2 → PB5 LED,ARR=19,30% 占空比(可在此修改比较值)
TIM3_PWM_Init(19, 0);
TIM_SetCompare3(TIM4, 8); // 蜂鸣器 8/10 = 80%
TIM_SetCompare2(TIM3, 6); // LED 6/20 = 30%
while (1)
{
// 呼吸灯:占空比 0→100→0 循环
if (dir == 1) { pwmval++; if (pwmval > 100) dir = 0; }
else { pwmval--; if (pwmval == 0) dir = 1; }
// ARR=9 时 10 级亮度,映射到 0~9
TIM_SetCompare3(TIM4, pwmval * 9 / 100);
delay_ms(10);
}
}💡 这段代码怎么用?
- 在工程中加入
timer.c / timer.h,并把固件库的stm32f10x_tim.c、stm32f10x_tim.h加入编译 - 先编译下载观察 LED0(PB5)的亮度、蜂鸣器(PB8)声音
- 修改占空比:改
TIM_SetCompare3(TIM4, x)/TIM_SetCompare2(TIM3, x)的第二个参数即可,这就是「它有什么用」——改变亮度/音量 - 呼吸灯核心:在
while(1)里循环改占空比,配合delay_ms控制变化速度
⚠️ 常见错误
- 忘记使能 AFIO 时钟 → 重映射不生效,PB5 无 PWM 输出
- 引脚模式必须为
GPIO_Mode_AF_PP(复用推挽),设成普通输出则无波形 - LED 通常低电平点亮,需结合硬件验证有效极性
四、参考资料
| 资料 | 文件 |
|---|---|
| PWM PPT | 定时器-PWM.pptx |
| 实验指导文档 | 实验6 定时器-PWM.docx |
| 代码工程 | 15.pwm-led-beep-2022-a.rar |