1. PWM 是什么?
PWM(Pulse Width Modulation,脉宽调制):通过控制“高电平的时间占比”,来调节负载获得的平均能量。
应用:LED 调光、直流电机调速、蜂鸣器音调控制等。
波形特征:
周期 固定(由频率决定)
高电平持续时间 可变(由占空比决定)
2. PWM 频率公式
3. 占空比与平均电压
代码
#include "pwm.h"
/* -------- 硬件相关宏定义 -------- */
#define PWM_GPIO_PORT GPIOA // 使用的 GPIO 端口:A 组
#define PWM_GPIO_PIN GPIO_PIN_6 // 使用的引脚:PA6
#define PWM_GPIO_AF GPIO_AF_2 // 复用功能号:PA6 → TIM2_CH0,选择 AF2
#define PWM_TIMER TIMER2 // 绑定的定时器:TIM2
#define PWM_TIMER_CHANNEL TIMER_CH_0 // 使用的定时器通道:CH0
/* 定时器时钟频率:84 MHz
* 注:APB1 总线如果分频不为 1,定时器时钟=2×APB1。这里假设算下来就是 84 MHz。
*/
#define TIM_CLK 84000000UL
/* ================= GPIO 初始化 ================= */
void pwm_gpio_init(void) {
// 打开 GPIOA 时钟
rcu_periph_clock_enable(RCU_GPIOA);
// 打开定时器 TIM2 的时钟
rcu_periph_clock_enable(RCU_TIMER2);
// 设置 PA6 为复用模式 (AF),不使用上下拉
// 参数1:端口
// 参数2:模式(AF = 复用功能)
// 参数3:上拉/下拉
// 参数4:引脚
gpio_mode_set(PWM_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, PWM_GPIO_PIN);
// 设置 PA6 的输出属性:推挽输出,速度 50MHz
// 参数1:端口
// 参数2:输出类型(推挽/开漏)
// 参数3:速度等级
// 参数4:引脚
gpio_output_options_set(PWM_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, PWM_GPIO_PIN);
// 把 PA6 绑定到 TIM2_CH0 的复用功能 AF2
// 参数1:端口
// 参数2:复用功能号
// 参数3:引脚
gpio_af_set(PWM_GPIO_PORT, PWM_GPIO_AF, PWM_GPIO_PIN);
}
/* ================= 定时器 PWM 初始化 ================= */
void pwm_timer_init(uint32_t freq_hz) {
timer_parameter_struct timer_initpara;
/* --- 1. 计算预分频器 PSC 和 自动重装载值 ARR --- */
uint32_t psc = 83; // PSC=83 → 定时器时钟=84MHz/(83+1)=1MHz → 1us计数一次
uint32_t arr = (TIM_CLK / (psc + 1)) / freq_hz - 1;
// ARR = (定时器时钟/psc)/(目标频率) - 1
// 举例 freq_hz=1000 → arr=999 → 周期1ms → 1kHz PWM
if (arr > 0xFFFF) {
arr = 0xFFFF; // 限制 ARR 不超过 16 位定时器的最大值
}
/* --- 2. 配置定时器基本参数 --- */
timer_struct_para_init(&timer_initpara); // 初始化结构体为默认值
timer_initpara.prescaler = psc; // 设置预分频器
timer_initpara.alignedmode = TIMER_COUNTER_EDGE; // 边沿对齐模式(常用)
timer_initpara.counterdirection = TIMER_COUNTER_UP; // 向上计数
timer_initpara.period = arr; // 设置 ARR 值
timer_initpara.clockdivision = TIMER_CKDIV_DIV1; // 时钟不再分频
timer_initpara.repetitioncounter = 0; // 重复计数器(一般 0)
timer_deinit(PWM_TIMER); // 清空定时器配置(复位)
timer_init(PWM_TIMER, &timer_initpara);// 应用新配置
/* --- 3. 配置通道输出参数 --- */
timer_oc_parameter_struct ocpara;
timer_channel_output_struct_para_init(&ocpara); // 初始化输出比较结构体
ocpara.outputstate = TIMER_CCX_ENABLE; // 打开通道输出
ocpara.ocpolarity = TIMER_OC_POLARITY_HIGH; // 有效电平为高电平
// 应用到指定定时器和通道
// 参数1:定时器
// 参数2:通道号
// 参数3:OC配置结构体指针
timer_channel_output_config(PWM_TIMER, PWM_TIMER_CHANNEL, &ocpara);
/* --- 4. 设置通道为 PWM 模式 --- */
// PWM0 模式:计数器 < CCR → 输出高电平;否则低电平
timer_channel_output_mode_config(PWM_TIMER, PWM_TIMER_CHANNEL, TIMER_OC_MODE_PWM0);
// 禁用影子寄存器(写CCR立即生效)
timer_channel_output_shadow_config(PWM_TIMER, PWM_TIMER_CHANNEL, TIMER_OC_SHADOW_DISABLE);
/* --- 5. 设置初始占空比 --- */
// CCR = (ARR+1)/2 → 50% 占空比
timer_channel_output_pulse_value_config(PWM_TIMER, PWM_TIMER_CHANNEL, (arr + 1) / 2);
/* --- 6. 启动定时器 --- */
timer_auto_reload_shadow_enable(PWM_TIMER); // 允许ARR影子更新
timer_enable(PWM_TIMER); // 启动定时器
}
/* ================= 设置占空比函数 ================= */
void pwm_set_duty(uint16_t ccr_val) {
// 参数1:定时器
// 参数2:通道
// 参数3:比较值 CCR(范围 0~ARR)
timer_channel_output_pulse_value_config(PWM_TIMER, PWM_TIMER_CHANNEL, ccr_val);
}
/************************************************************
* 版权:2025CIMC Copyright。
* 文件:Function.c
* 作者: Lingyu Meng
* 平台: 2025CIMC IHD-V04
* 版本: Lingyu Meng 2025/2/16 V0.01 original
************************************************************/
/************************* 头文件 *************************/
#include "Function.h"
/************************* 宏定义 *************************/
/************************ 变量定义 ************************/
/************************ 函数定义 ************************/
void System_Init(void)
{
systick_config(); // 时钟配置
pwm_gpio_init(); // 配置 PA6 为 PWM 输出引脚
pwm_timer_init(1000); // 启动定时器 PWM 输出,初始占空比 50%
}
void UsrFunction(void)
{
int ccr = 0; // 当前占空比对应的 CCR 值
int step = 1; // 步进值,决定每次加/减多少
while(1)
{
pwm_set_duty(ccr); // 设置当前占空比(CCR)
// 粗略延时 ≈ 2ms,决定“呼吸速度”
for (volatile int i = 0; i < 16000; ++i);
// 改变占空比,呼吸渐变
ccr += step;
if (ccr >= 1000) { ccr = 1000; step = -1; } // 达上限后开始减
if (ccr <= 0) { ccr = 0; step = +1; } // 达下限后开始加
}
}
————————————————
版权声明:本文为CSDN博主「lanhua1304403542」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lanhua1304403542/article/details/151936334
|
|