在嵌入式系统开发中,PWM(脉冲宽度调制)输出是极其常用且重要的外设功能。无论是控制电机转速、调节 LED 灯亮度、甚至生成简单的音频,都离不开 PWM 的身影。本文将以《嵌入式 – GD32开发实战指南(RISC-V版本)》第8章为基础,深入探讨 GD32 RISC-V 系列单片机的 PWM 输出实现,并通过实战案例,帮助读者掌握其核心原理和应用技巧。
PWM 基础原理回顾
PWM 的核心在于通过改变脉冲的占空比(高电平时间在一个周期内的占比),从而改变输出的平均电压或电流。例如,在控制 LED 亮度时,占空比越大,LED 就越亮。PWM 的频率决定了信号的刷新速度,过低的频率可能会导致 LED 闪烁或电机抖动。
GD32 RISC-V 单片机通常具有多个定时器,每个定时器可以配置成 PWM 输出模式。定时器的计数器决定了 PWM 的频率,而比较寄存器(Capture Compare Register, CCR)则决定了 PWM 的占空比。
GD32 RISC-V PWM 输出的配置步骤
使能定时器时钟和 GPIO 时钟:首先需要使能定时器和对应 GPIO 的时钟。这可以通过 GD32 的 RCC(Reset and Clock Control)模块来实现。
配置 GPIO 为复用功能:将 GPIO 配置为定时器的 PWM 输出功能,这通常涉及到配置 GPIO 的 Alternate Function(AF)模式。GD32 的 GPIO 复用功能需要查找芯片的数据手册,确定哪个 AF 功能对应于哪个定时器通道。

配置定时器基本参数:配置定时器的 prescaler(预分频器)和 period(周期),从而确定 PWM 的频率。PWM 频率的计算公式为:
PWM 频率 = 系统时钟频率 / (Prescaler + 1) / (Period + 1)。配置 PWM 输出通道参数:配置定时器的比较寄存器(CCR),从而确定 PWM 的占空比。占空比的计算公式为:
占空比 = CCR / (Period + 1)。还需要配置 PWM 的极性(高电平有效还是低电平有效)和输出模式(例如 PWM1 模式或 PWM2 模式)。
使能定时器 PWM 输出:最后,使能定时器的 PWM 输出功能,使能相应的通道输出。同时,也要确保定时器的主输出使能位(MOE)被置位。
代码示例:呼吸灯效果
以下是一个简单的 GD32 RISC-V PWM 实现呼吸灯效果的代码示例(基于GD32VF103):
#include "gd32vf103.h"
#include <stdio.h>
#define LED_PIN GPIO_PIN_13 // 连接 LED 的 GPIO 引脚
#define LED_PORT GPIOC // 连接 LED 的 GPIO 端口
void rcu_config(void) {
rcu_periph_clock_enable(RCU_GPIOC); // 使能 GPIOC 时钟
rcu_periph_clock_enable(RCU_TIMER2); // 使能 TIMER2 时钟
}
void gpio_config(void) {
gpio_init(LED_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, LED_PIN); // 配置 GPIO 为复用推挽输出
gpio_pin_remap_config(GPIO_TIMER2_CH2_FULL_REMAP,ENABLE); // GD32VF103需要重映射
}
void timer_config(void) {
timer_parameter_struct timer_initpara;
timer_oc_parameter_struct timer_ocinitpara;
/* 初始化 TIMER2 */
timer_deinit(TIMER2);
/* TIMER2 配置 */
timer_initpara.prescaler = 71; // 72MHz / (71+1) = 1MHz
timer_initpara.alignedmode = TIMER_COUNTER_EDGE; // 边缘对齐模式
timer_initpara.counterdirection = TIMER_COUNTER_UP; // 向上计数
timer_initpara.period = 999; // 1ms 周期
timer_initpara.clockdivision = TIMER_CKDIV_DIV1; // 时钟分频
timer_initpara.repetitioncounter = 0; // 重复计数器
timer_init(TIMER2, &timer_initpara);
/* 配置 TIMER2 CH2 为 PWM 输出 */
timer_ocinitpara.outputstate = TIMER_CCX_ENABLE; // 使能通道输出
timer_ocinitpara.outputnstate = TIMER_CCXN_DISABLE; // 禁止互补通道输出
timer_ocinitpara.ocmode = TIMER_OC_MODE_PWM0; // PWM 模式 1
timer_ocinitpara.ocpolarity = TIMER_OC_POLARITY_HIGH; // 高电平有效
timer_ocinitpara.ocnpolarity = TIMER_OCN_POLARITY_HIGH;
timer_ocinitpara.ocidlemask = TIMER_IDLE_MASK_LOW;
timer_ocinitpara.ocnidlemask = TIMER_IDLE_MASK_LOW;
timer_channel_output_config(TIMER2, TIMER_CH_2, &timer_ocinitpara);
timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_2, 0); // 初始占空比为 0
timer_enable(TIMER2); // 使能定时器
timer_channel_output_shadow_config(TIMER2, TIMER_CH_2, TIMER_OC_SHADOW_DISABLE); //不使用影子寄存器
timer_primary_output_config(TIMER2,ENABLE); // 使能主输出
}
void delay(uint32_t time) {
uint32_t i;
for (i = 0; i < time; i++);
}
int main(void) {
rcu_config();
gpio_config();
timer_config();
uint16_t duty_cycle = 0;
int8_t direction = 1; // 1 表示增加,-1 表示减少
while (1) {
timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_2, duty_cycle);
if (direction == 1) {
duty_cycle++;
if (duty_cycle > 999) {
direction = -1;
}
} else {
duty_cycle--;
if (duty_cycle == 0) {
direction = 1;
}
}
delay(10000); // 调整延时来控制呼吸速度
}
}
实战避坑经验
- GPIO 复用功能选择错误:GD32 的 GPIO 复用功能众多,一定要仔细查阅数据手册,选择正确的 AF 功能,否则 PWM 输出无法正常工作。
- 定时器时钟配置错误:定时器的时钟源和预分频器配置错误会导致 PWM 频率不正确,影响控制效果。要仔细计算和验证 PWM 频率。
- PWM 极性配置错误:PWM 的极性决定了高电平对应的是导通还是截止。配置错误会导致控制逻辑反转,例如 LED 亮度和占空比的关系反转。
- 中断优先级配置不当:如果 PWM 输出需要配合中断使用,一定要合理配置中断优先级,避免中断冲突导致系统异常。
- 调试工具选择:使用 J-Link 或 ST-Link 等调试器,可以方便地在线调试 PWM 输出,观察 PWM 信号的波形和占空比,从而快速定位问题。配合诸如 Saleae Logic Analyzer 这样的逻辑分析仪,可以更精确地分析 PWM 信号。
总结
本文以《嵌入式 – GD32开发实战指南(RISC-V版本)》第8章为基础,详细介绍了 GD32 RISC-V 单片机 PWM 输出的实现方法,并提供了呼吸灯效果的实战案例。掌握 PWM 输出技术,能够为嵌入式系统的开发提供强大的控制能力。希望读者在实际项目中灵活运用,创造出更多有趣的应用。
冠军资讯
代码老中医