1. 这篇文章真正要解决的问题
如果你做过电机控制、LED调光、舵机驱动或者DC-DC电源开发,那你一定绕不开 PWM。PWM 这个外设看起来简单,但真正上手时,很多人会在几个地方卡住:
- CubeMX 里配置好了定时器,但输出引脚始终没有波形;
- PWM 能输出,但频率不对,和理论计算值差了一倍;
- 程序运行中需要动态修改占空比和频率,不知道是直接改寄存器,还是调用 HAL 库函数;
- 频率修改后占空比的百分比完全变了,PWM 波形表现和预期不一致。
这些问题在 STM32 传统型号上已经有了不少讨论,但换到STM32C542R这颗相对较新的芯片上,很多人会沿用旧系列的习惯,然后踩到一些新坑。STM32C5 系列采用了 ARM Cortex-M33 内核,相比上一代产品线,在主频、外设资源以及低功耗特性上都有明显变化。如果你的项目正在评估这颗芯片,或者已经切到 STM32C542R,那么这篇内容值得收藏。
这篇文章不会只停留在“怎么用 CubeMX 生成代码”这种基础层面,而是会从 PWM 的频率和占空比计算原理出发,带你在 STM32C542R 上完成一份可运行的 PWM 输出工程,并重点解决两个问题:
- 如何在运行时动态修改 PWM 占空比;
- 如何在运行时动态修改 PWM 频率,同时保证占空比语义不跑偏。
读完这篇文章,你应该能做到:不需要重新生成代码,仅通过 HAL 库函数或寄存器操作,就能让 PWM 输出按你的控制需求变化。
2. STM32C542R 与 PWM 核心概念
2.1 PWM 的本质:频率、占空比与分辨率
PWM,即脉冲宽度调制,它是通过在高电平和低电平之间快速切换,来模拟一个“平均电压”或者“等效能量”的输出方式。PWM 信号有三个核心参数:
- 频率(Frequency):单位时间内 PWM 波形重复的次数,单位是 Hz。频率决定了 PWM 的周期 T,T = 1 / Frequency。
- 占空比(Duty Cycle):一个周期内高电平持续时间与整个周期的比例,通常用百分比表示。50% 占空比表示高电平和低电平各占一半。
- 分辨率(Resolution):占空比可以调节的最小粒度。比如 8 位分辨率,占空比调节步进是 1/256;16 位分辨率,步进是 1/65536。
用一个比喻来理解:PWM 就像水龙头。频率是你开关水龙头的快慢,占空比是每次打开时水流的时间占比。你不需要改变水压,只需要控制开关的节奏,就能得到不同大小的平均水流。这也是为什么 Buck 电路能通过修改占空比来改变输出电压——占空比决定平均输出能量。
在 STM32 上,PWM 由定时器外设产生。定时器内部有一个自由运行的计数器,计数器从 0 增长到自动重装载值(ARR),然后归零重新开始。在这个计数周期内,如果你把计数器的当前值与比较值(CCR)做比较,计数器小于 CCR 时输出高电平,大于等于 CCR 时输出低电平,就得到了一路频率由 ARR 决定、占空比由 CCR 决定的 PWM 波形。
2.2 STM32C542R 的 PWM 硬件资源
STM32C542R 属于 STM32C5 系列,采用 ARM Cortex-M33 内核。从外设资源角度看,它集成了多路通用定时器(TIM2、TIM3、TIM4、TIM5 等)以及高级定时器(TIM1、TIM8)。
其中:
- 通用定时器:可以输出标准 PWM,适合 LED 调光、蜂鸣器控制、普通电机调速等场景。
- 高级定时器:支持互补 PWM 输出、死区插入、刹车功能,适合电机控制、逆变器等需要上下桥臂互补驱动的场景。
在后续示例中,我们会以通用定时器 TIM3 为例,因为它的配置方式在 STM32C542R 上比较有代表性,也容易迁移到其他定时器。
需要注意的一点是,STM32C542R 的定时器时钟源与 APB 总线时钟之间的关系,和 F1 系列不同。不同系列芯片的定时器时钟倍频系数不同,这直接影响 PWM 频率的计算。在使用 CubeMX 生成代码后,最好先确认SystemClock_Config()中 APB1 和 APB2 的时钟频率,以及定时器时钟是否被倍频。这一步是很多人 PWM 频率算错的第一原因。
2.3 频率与占空比的计算公式
在使用定时器输出 PWM 时,频率由三个因素决定:
- 定时器输入时钟频率
PCLK; - 预分频器值
PSC; - 自动重装载值
ARR。
计算公式如下:
PWM频率 = 定时器时钟频率 / ((PSC + 1) * (ARR + 1))占空比的计算公式如下:
占空比 = CCR / (ARR + 1) * 100%从公式可以看出一件重要的事:修改频率时,如果只改 ARR 而不改 CCR,占空比会随之改变。因为 ARR 是频率分母的一部分,而 CCR 决定了高电平的绝对时间。当 ARR 变小(频率变高)时,周期变短,如果 CCR 不变,高电平时间不变,占空比就会变大;反之亦然。
这意味着什么?如果你的应用需要保持占空比不变的前提下动态修改频率,就必须在修改 ARR 的同时,按比例调整 CCR。这个细节非常容易漏掉,后面我们会专门处理。
3. 开发环境准备
在开始写代码之前,先把开发环境准备好。以下是本文使用的软件环境:
- 集成开发环境(IDE):STM32CubeIDE 或 Keil MDK,本文示例代码兼容这两者;
- 图形化配置工具:STM32CubeMX(生成初始化代码);
- HAL 库版本:建议使用与 STM32CubeIDE 配套的固件包版本,版本号以你实际安装的为准;
- 调试器:ST-LINK 或 DAP-LINK 均可;
- 硬件:STM32C542R 核心板或最小系统板。
需要说明的是,本文不会把某个具体固件版本号写死,因为不同时期下载的 STM32Cube 固件包版本不同,生成的代码会有细微差异。本文的重点是通用思路,只要你的固件包支持 STM32C5 系列,就可以按这个流程操作。
如果你还没有创建 STM32C542R 的工程,建议先在 CubeMX 中选择正确的芯片型号,然后按照下面的流程配置时钟和定时器。关于时钟树的配置,理论上 APB1 定时器时钟最高可达 64MHz,APB2 定时器时钟最高可达 128MHz,但具体上限以你实际安装的芯片数据手册为准。在 CubeMX 的 Clock Configuration 页面中,可以直接看到 APB1 和 APB2 的定时器时钟频率,把它记录下来,后面计算 PSC 和 ARR 时需要用到。
4. 使用 CubeMX 配置定时器 PWM
4.1 创建工程并选择芯片
打开 STM32CubeMX,新建工程,在芯片选择界面输入 STM32C542R,选中对应型号并双击创建。
创建完成后,第一步是配置时钟。在System Core -> RCC中,将 HSE 设为Crystal/Ceramic Resonator,然后在Clock Configuration页面中配置时钟树。PLL 的配置方式取决于你板子上的外部晶振频率,常见的是 8MHz 或 24MHz。配置完成后,记录 APB1 Timer Clocks 和 APB2 Timer Clocks 的值。
4.2 配置定时器输出 PWM
以 TIM3 为例,操作路径是:
Timers -> TIM3 -> PWM Generation CH1
在这里需要配置以下参数:
- Prescaler (PSC - 16 bits value):预分频值,决定计数器的计数时钟。
- Counter Mode:选择
Up(向上计数模式),这是最常用的 PWM 模式。 - Counter Period (ARR - 16 bits value):自动重装载值,决定 PWM 周期。
- Auto-reload preload:建议选择
Enabled,这样修改 ARR 时会在更新事件后生效,避免在计数器运行中直接修改导致异常波形。 - Pulse (CCR):比较值,决定初始占空比。
- Output compare preload:建议选择
Enabled。 - CH Polarity:选择
High作为输出极性。
为了使 PWM 输出到引脚,还需要在System Core -> GPIO或直接点击芯片引脚图,将 TIM3_CH1 对应的引脚设置为复用功能输出。
这里有一个常见的困惑:PSC 和 ARR 该优先算哪个。实际项目的推荐策略是:先确定目标频率,再根据目标 PWM 分辨率需求分配 PSC 和 ARR。比如目标频率是 10kHz,定时器时钟是 64MHz,那么:
(PSC + 1) * (ARR + 1) = 64MHz / 10kHz = 6400如果希望占空比分辨率达到 0.1%(即 1000 级),ARR 至少要取 1000 左右,那么 PSC + 1 大约为 6.4,取 PSC = 6,那么 ARR + 1 = 914,实际频率约为 10.01kHz,误差 0.1%,完全可以接受。
如果你的分辨率要求不高,比如只需要 256 级,那么 ARR 取 255,PSC + 1 取 25,实际频率也是 10kHz。所以 PSC 和 ARR 的组合并没有唯一答案,它取决于你优先保证频率精度还是占空比分辨率。
4.3 生成代码
配置完成后,在Project Manager页面设置工程名称和存储路径,工具链选择你使用的 IDE,然后点击GENERATE CODE。CubeMX 会生成包含定时器初始化逻辑的 HAL 库工程。
5. PWM 输出完整代码实现
5.1 初始化代码解读
CubeMX 生成的定时器初始化函数通常位于tim.c文件中,核心代码类似下面这样:
// 文件路径:Core/Src/tim.c void MX_TIM3_Init(void) { TIM_OC_InitTypeDef sConfigOC = {0}; htim3.Instance = TIM3; htim3.Init.Prescaler = 64 - 1; htim3.Init.CounterMode = TIM_COUNTERMODE_UP; htim3.Init.Period = 100 - 1; htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; if (HAL_TIM_PWM_Init(&htim3) != HAL_OK) { Error_Handler(); } sConfigOC.OCMode = TIM_OCMODE_PWM1; sConfigOC.Pulse = 50; sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1) != HAL_OK) { Error_Handler(); } }上面的配置中,PSC = 63,ARR = 99,CCR = 50。假设定时器时钟为 64MHz,那么 PWM 频率为:
频率 = 64MHz / (64 * 100) = 10kHz 占空比 = 50 / 100 = 50%注意,初始化结构体里Prescaler和Period的值都减了 1,这是因为硬件计数从 0 开始,实际参与计算的值是寄存器值加 1。
5.2 启动 PWM 输出
初始化只是配置了定时器寄存器,要让 PWM 真正在引脚上输出,需要启动定时器的 PWM 通道。在main.c的main()函数中,在MX_TIM3_Init()之后调用:
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);这一步是很容易漏掉的。只执行了初始化函数,引脚上是没有波形的。这一点和 GPIO 输出不同:GPIO 初始化完成即可输出电平,但 PWM 通道需要显式启动。
启动之后,用示波器或者逻辑分析仪连接 TIM3_CH1 对应引脚,应该能看到 10kHz、50% 占空比的方波。如果你测量到的频率是 20kHz 或者 5kHz,优先检查 APB1 定时器时钟是否配置正确,以及 PSC 和 ARR 的值是否符合预期。
5.3 动态修改占空比
在程序运行中修改占空比,最直接的方式是调用 HAL 库函数:
// 将占空比设置为 25% __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 25);这个函数实际上是修改定时器的 CCR 寄存器,也就是比较值。修改 CCR 后,PWM 输出的占空比会立即按照新的比较值改变。
如果你希望占空比以百分比的方式控制,可以封装一个函数。假设 ARR 的值是 100,那么百分比和 CCR 的关系很直观:
void set_pwm_duty_percent(TIM_HandleTypeDef *htim, uint32_t channel, uint8_t percent) { if (percent > 100) { percent = 100; } __HAL_TIM_SET_COMPARE(htim, channel, (htim->Instance->ARR + 1) * percent / 100); }上面这段代码先读取定时器的 ARR 寄存器值,再乘以百分比,得到一个与 ARR 成正比的 CCR 值,从而保证占空比语义是线性的。
5.4 动态修改频率
修改频率比修改占空比稍微复杂一些,因为频率直接与 ARR 相关,而且修改 ARR 后,现有 CCR 对应的占空比会变化。
在 HAL 库中,修改 ARR 有两种路径:
- 直接写寄存器:
htim3.Instance->ARR = new_arr_value; - 使用宏:
__HAL_TIM_SET_AUTORELOAD(&htim3, new_arr_value);
但需要注意,如果只是修改 ARR 而不修改 CCR,占空比可能会发生不可预期的变化。所以我们建议封装一个函数,同时修改 ARR 和 CCR,保持占空比不变。
void set_pwm_frequency_preserve_duty(TIM_HandleTypeDef *htim, uint32_t channel, uint32_t psc, uint32_t arr, uint32_t current_ccr) { // 计算当前占空比(以百分比为单位,保留小数) float duty_percent = (float)current_ccr / (float)(htim->Instance->ARR + 1); // 修改预分频器 __HAL_TIM_SET_PRESCALER(htim, psc); // 修改自动重装载值 __HAL_TIM_SET_AUTORELOAD(htim, arr); // 根据新的 ARR 重新计算 CCR,保持占空比不变 __HAL_TIM_SET_COMPARE(htim, channel, (uint32_t)(duty_percent * (float)(arr + 1))); }这里有一个需要注意的地方:修改 PSC 时,预分频器的重装载也会有一个生效时机问题。在计数器运行中直接修改 PSC,新的预分频值通常要等到当前计数周期结束后的更新事件才会生效。如果你需要频率切换非常快速且精准,建议在修改 PSC 之前,先将定时器关闭,修改完成后再重新开启。
void set_pwm_frequency_restart(TIM_HandleTypeDef *htim, uint32_t channel, uint32_t psc, uint32_t arr, uint8_t duty_percent) { // 停止 PWM 输出 HAL_TIM_PWM_Stop(htim, channel); // 重新设置预分频和自动重装载值 __HAL_TIM_SET_PRESCALER(htim, psc); __HAL_TIM_SET_AUTORELOAD(htim, arr); // 重新计算比较值 __HAL_TIM_SET_COMPARE(htim, channel, (uint32_t)((arr + 1) * duty_percent / 100)); // 重新启动 PWM 输出 HAL_TIM_PWM_Start(htim, channel); }这种先停再改、改完再启动的方式,适合对输出波形连续性要求不高的场景,比如步进电机按不同频率分段驱动、蜂鸣器播放不同音调等。如果要求切换频率时波形不中断,那就需要直接操作寄存器,并在更新事件中断中同步修改 ARR 和 CCR。
5.5 修改频率时占空比精度问题
当你使用上面的set_pwm_frequency_preserve_duty函数时,可能遇到一个问题:计算出的新 CCR 是一个浮点数取整后的值,取整会带来量化误差。例如原来的占空比是 33.3%,新的 ARR 是 100,那么 CCR = 33.3,取整后得到 33,实际占空比变为 33%,误差 0.3%。如果 ARR 很小,比如 ARR = 20,那么这个误差会被放大。
解决办法是:在应用层面明确定义“频率优先级”还是“占空比优先级”。如果你需要非常精确的频率,那就接受占空比存在微小量化误差;如果你需要非常精确的占空比,那就固定 ARR,只通过 PSC 来粗调频率。大多数实际应用允许 PWM 频率在一个范围内波动,因此这种量化误差通常可以接受。
6. 运行结果与波形验证
代码写完并编译下载后,不能只看程序没报错就认为完成,必须进行波形验证。
6.1 验证工具
验证 PWM 波形最常用的工具是示波器。如果手头没有示波器,也可以用逻辑分析仪。逻辑分析仪虽然无法测量真实电压幅值,但对的频率、占空比测量完全够用。
6.2 验证步骤
第一步:将示波器探头连接到 TIM3_CH1 的引脚,确保共地。
第二步:给板子上电,观察波形是否输出。如果没有任何波形,逐一检查:
- 是否调用了
HAL_TIM_PWM_Start; - 引脚复用功能是否配置正确;
- 定时器时钟是否使能。
第三步:测量波形频率和占空比。将示波器的频率测量功能打开,对比理论值。例如初始化配置是 10kHz、50% 占空比,实测应该非常接近。
第四步:调用动态修改占空比的函数,观察示波器上的占空比是否变化。比如执行set_pwm_duty_percent(&htim3, TIM_CHANNEL_1, 25)后,占空比应该变为 25%。
第五步:调用动态修改频率的函数,观察频率是否变化,同时占空比是否保持在目标值附近。
6.3 预期结果
如果一切正常,你会看到:
- 修改占空比后,波形高低电平的宽度变化,周期不变;
- 修改频率后,波形周期变化,如果使用了按比例调整 CCR 的方法,占空比保持不变;
- 如果使用
set_pwm_frequency_restart,在切换瞬间会出现一小段无波形输出,这是正常的。
如果发现输出的 PWM 频率是理论值的一半,这是 STM32 定时器时钟配置的经典问题。可以回到 CubeMX 的 Clock Configuration 页面,确认 APBx Timer Clocks 的值,并检查RCC->CFGR中 TIMPRE 位是否设置正确。在 STM32C542R 上,定时器时钟并不总是等于 APB 总线时钟,当 APB 预分频器不为 1 时,定时器时钟通常是 APB 时钟的 2 倍。
7. 常见问题与排查方法
在实际开发中,PWM 相关的问题通常集中表现在波形不对或者完全没有输出。下面整理一份排查表:
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 引脚完全没有波形 | 未调用HAL_TIM_PWM_Start | 检查代码中是否启动 PWM 通道 | 在初始化后调用启动函数 |
| 引脚完全没有波形 | GPIO 复用功能未配置 | 检查 CubeMX 引脚配置 | 确认引脚选择的是 TIM3_CH1 复用功能 |
| 引脚完全没有波形 | 定时器时钟未使能 | 查看 RCC 时钟使能代码 | 检查__HAL_RCC_TIM3_CLK_ENABLE()是否执行 |
| 频率与理论值偏差一倍 | APB 预分频后定时器时钟倍频未考虑 | 查看 APBx Timer Clocks 配置 | 根据定时器时钟实际值重新计算 PSC 和 ARR |
| 修改频率后占空比变化 | ARR 改变,但 CCR 未按比例调整 | 检查修改频率的代码 | 同时修改 ARR 和 CCR |
| 波形抖动或偶尔跳变 | 修改 ARR 时没有使能预装载 | 查看AutoReloadPreload配置 | 使能 ARR 预装载 |
| 高电平时间不精确 | CCR 取整误差 | 查看当前 ARR 与目标占空比 | 增大 ARR 以提高分辨率 |
| PWM 输出不稳定 | 中断频繁抢占 | 检查中断优先级配置 | 调整优先级或关掉不必要的中断 |
| 修改 PSC 后频率不变化 | PSC 更新需要等待更新事件 | 查看是否产生更新事件 | 停止定时器后修改,或触发软件更新事件 |
7.1 启动后没有波形,先查什么?
按照经验,90% 的“没有波形”问题出在三个方面:没有调用启动函数、引脚复用配置错、定时器时钟没有使能。排查顺序建议是:
- 在调试器中打断点,确认
MX_TIM3_Init()和HAL_TIM_PWM_Start()确实执行了; - 打开寄存器窗口,查看
htim3.Instance->CR1的 CEN 位是否为 1; - 检查 GPIO AFR 寄存器,确认引脚复用功能编号正确。
7.2 如何正确使用逻辑分析仪测 PWM?
如果使用逻辑分析仪,注意采样率要足够高。测 10kHz 的 PWM 信号,采样率至少 1MHz 才能比较准确地描绘占空比;测 100kHz 以上的 PWM,建议采样率 10MHz 以上。采样率太低会导致占空比测量值非常不准。
8. PWM 开发的最佳实践与工程建议
8.1 不要让魔法数字散落在代码里
在工程代码中,PSC、ARR、CCR 这些参数应该集中定义,并且给出清晰命名,而不是在初始化结构体中直接写64 - 1、100 - 1。建议在头文件中定义宏:
// 文件路径:Core/Inc/pwm_config.h #ifndef PWM_CONFIG_H #define PWM_CONFIG_H #define PWM_TIMER_CLOCK_HZ 64000000UL /* 以实际定时器时钟为准 */ #define PWM_TARGET_FREQUENCY 10000UL /* 目标频率 10kHz */ #define PWM_DUTY_INIT_PERCENT 50 /* 初始占空比 50% */ #define PWM_PRESCALER ((PWM_TIMER_CLOCK_HZ / PWM_TARGET_FREQUENCY / 100) - 1) #define PWM_PERIOD (100 - 1) #define PWM_COMPARE (PWM_PERIOD * PWM_DUTY_INIT_PERCENT / 100) #endif这样在 CubeMX 生成代码后,手动调整PWM_TIMER_CLOCK_HZ和PWM_TARGET_FREQUENCY,初始化代码的数值就会自动计算出来。当然,如果 CubeMX 生成代码时你直接使用了图形化配置,这些宏主要用于运行时动态计算。
8.2 频率与占空比的运行时管理
在稍微复杂的工程中,PWM 频率和占空比往往是多个模块共同控制的。比如一个 LED 驱动模块可能同时接收亮度指令和呼吸频率指令;一个电机控制模块可能在启动阶段使用较低频率,运行后切换到较高频率。
我的建议是,把 PWM 控制封装成一个独立的模块,对外提供两个接口:
pwm_set_duty(channel, percent),用于设置占空比;pwm_set_frequency(channel, hz),用于设置频率,并且在函数内部重新计算 CCR,保证占空比不变。
不要让业务代码直接调用__HAL_TIM_SET_COMPARE这类底层宏。这样做有两个好处:统一修改逻辑、方便加入边界检查和日志输出。
8.3 注意 ARR 预装载和 CCR 预装载
前面提到过,CubeMX 默认会将AutoReloadPreload设为Enabled。这个选项非常关键。如果禁用预装载,直接修改 ARR 寄存器时,新值会在下一个计数周期立即生效,可能造成一个不完整的 PWM 周期。实际项目中建议始终使能 ARR 预装载。
CCR 同样有预装载选项,即Output compare preload。使能预装载后,修改 CCR 会在更新事件时统一加载,避免在同一周期内出现占空比跳变。
8.4 频率切换时的 PWM 波形连续性
如果你的应用需要在运行过程中切换 PWM 频率,且不能出现波形中断或异常周期,直接调用 HAL 库的HAL_TIM_PWM_Stop再Start是行不通的,因为停止再启动会导致输出引脚立即拉低并重新初始化,波形会有一个明显的中断。
要实现在运行中无痕切换频率,通常需要在更新事件中断中修改 ARR 和 CCR。基本思路是:
- 使能定时器更新中断;
- 在需要切换频率时,先把目标 PSC、ARR、CCR 写入影子寄存器(使能预装载后就是影子寄存器);
- 在更新事件中断服务函数中,判断切换标志,然后做必要的后处理。
这种方式需要你对定时器的工作机制有更深入的了解,但在电机控制、音频输出等对时序敏感的系统中非常有用。
8.5 使用调试器实时观察 CCR 和 ARR
在调试阶段,建议在修改占空比和频率的代码行上设置断点,然后查看定时器寄存器中的 CCR、ARR、PSC 值。通过观察这些值的变化,可以快速定位公式计算错误或者寄存器写错地址的问题。这一步比直接看波形更高效。
9. 进阶拓展:PWM 触发 ADC 采样
如果你的项目不只是输出 PWM,而是需要让 PWM 和 ADC 采样协同工作,比如在 PWM 周期的特定点采样电流,那么 STM32C542R 的定时器触发 ADC 功能非常实用。
基本思路是:将定时器的某个事件(例如更新事件或比较事件)配置为 ADC 外部触发源。这样 ADC 会在 PWM 的固定相位开始采样,从而保证每次采样点相对 PWM 波形的位置一致,不会因为采样时机漂移导致测量结果不准确。
如果使用 CubeMX 配置,在 ADC 配置页面中,将External Trigger Conversion Source设置为 TIM3 的触发输出事件,然后配置触发边沿即可。生成代码后,还需要在初始化代码中调用HAL_ADC_Start_DMA()或HAL_ADC_Start_IT()以启动 ADC 转换。
这里容易踩坑的地方是:定时器的触发输出可能来自不同的子事件,比如更新事件、通道比较事件等。你需要根据实际需求选择正确的触发源。如果是电机控制场景,通常希望在 PWM 中心位置采样电流,这时候用定时器的中心对齐模式和比较事件触发会更合适。如果是电源转换器,可能希望在 PWM 开关管导通稳定后采样电压,那通常会选择更新事件延迟一段时间后的比较事件。
10. 总结与后续学习方向
本文从 STM32C542R 的 PWM 输出出发,重点做了几件事:
- 解释了 PWM 频率、占空比和分辨率的基本关系;
- 演示了在 CubeMX 中配置定时器 PWM 的完整步骤;
- 给出了基于 HAL 库的动态修改占空比和频率的代码,并说明了修改频率时保持占空比不变的注意事项;
- 整理了 PWM 开发中的常见问题和排查方法;
- 补充了 DMA、PWM 触发 ADC 采样、软件 PWM 等进阶方向。
如果你现在准备在 STM32C542R 上开发实际项目,建议下一步做两件事:第一,用示波器把你板子上能输出的每一路 PWM 都测一遍,记录频率误差和占空比精度,这些数据对你后续调整控制算法非常重要;第二,尝试把 PWM 和 ADC 触发结合起来,完成一个简单的闭环控制实验,比如 LED 亮度闭环或者电机电流采样。这样你才算真正把 STM32 的定时器用起来,而不仅仅是点亮一个灯。