1. 硬件准备与环境搭建拿到一块STM32F103C8T6核心板时很多新手会感到无从下手。其实只需要准备以下三样东西就能开始PWM实验一块核心板20元左右的蓝色小板、一根USB转TTL串口线用于烧录程序、以及4.7kΩ电阻和LED各一个用于验证效果。我建议初学者先用LED做测试比直接驱动电机更安全直观。安装软件时要注意版本匹配问题。STM32CubeMX建议用6.x版本Keil MDK用5.25以上这两个工具的版本兼容性很重要。我遇到过CubeMX 6.7生成的项目在Keil 5.15编译报错的情况升级后问题就解决了。驱动安装有个小技巧先插上ST-Link调试器在设备管理器里更新驱动时手动指定到Keil安装目录的ARM/STLink/USBDriver文件夹。2. 时钟树配置详解时钟配置是STM32最让人头疼的部分但理解后会发现很有规律。我们的目标是把外部8MHz晶振倍频到72MHz系统时钟这需要经过三个关键步骤在RCC配置中选择HSE外部高速时钟作为时钟源在Clock Configuration界面将PLL倍频系数设为9确保系统时钟源选择PLLCLK这里有个容易出错的地方APB1总线时钟不能超过36MHz。当系统时钟为72MHz时APB1预分频器会自动设置为2分频得到36MHz。我曾因为手动修改这个值导致定时器工作异常。时钟树配置完成后可以点击HSI_STATUS旁边的图标生成时钟图这个可视化功能对理解时钟路径特别有帮助。3. 定时器PWM模式配置以TIM1为例配置PWM需要关注5个关键参数参数名建议值作用说明Prescaler71时钟预分频系数Counter ModeUp计数方向Period9999自动重装载值Pulse5000初始占空比ClockDivisionNone时钟分频PWM频率的计算公式很多人会记错正确的应该是PWM频率 定时器时钟频率 / [(Prescaler 1) × (Period 1)]举个例子当定时器时钟为72MHzPrescaler71Period9999时 72,000,000 / (72 × 10,000) 100Hz配置时有个实用技巧先确定需要的PWM频率然后在CubeMX的Parameter Settings界面直接修改Period值软件会实时显示计算出的实际频率这比手动计算方便得多。4. 动态调节占空比实战生成代码后在main.c中添加这些关键操作// 启动PWM通道 HAL_TIM_PWM_Start(htim1, TIM_CHANNEL_1); // 动态修改占空比 void Set_DutyCycle(uint16_t duty) { __HAL_TIM_SET_COMPARE(htim1, TIM_CHANNEL_1, duty); }占空比计算公式需要特别注意实际占空比 (Pulse值) / (Period 1) × 100%所以当Period9999时要50%占空比就设Pulse5000要30%占空比就设Pulse3000我做过一个实验用电位器连接ADC引脚将ADC读取的值映射为占空比实现了旋钮控制LED亮度的效果。关键代码如下uint16_t adc_val HAL_ADC_GetValue(hadc1); uint16_t duty adc_val * 10000 / 4095; // 12位ADC转0-10000范围 __HAL_TIM_SET_COMPARE(htim1, TIM_CHANNEL_1, duty); HAL_Delay(10); // 适当延时防止频繁修改5. 常见问题排查调试时最常遇到的三个问题没有PWM输出首先检查GPIO是否配置为复用推挽输出模式然后用示波器测量引脚。如果没有示波器可以接LED观察但要注意LED可能不会完全熄灭因为即使1%占空比也会短暂点亮。频率不对检查时钟树配置是否正确特别是PLL倍频系数。有个快速验证方法在SystemCoreClock变量处设断点运行时查看其值是否为72,000,000。占空比异常确认Period和Pulse值的计算是否正确。我曾犯过一个错误把Period设成了6553516位最大值导致占空比调节不线性后来发现是因为Period值过大会降低PWM分辨率。6. 进阶应用电机控制当PWM用于电机控制时需要特别注意死区时间的配置。在CubeMX的TIM1配置中找到Dead Time参数通常设置为100-500ns不等具体取决于使用的MOSFET型号。以下是典型的三相电机控制配置// 启动三相PWM HAL_TIM_PWM_Start(htim1, TIM_CHANNEL_1); HAL_TIM_PWM_Start(htim1, TIM_CHANNEL_2); HAL_TIM_PWM_Start(htim1, TIM_CHANNEL_3); // 设置互补通道 HAL_TIMEx_PWMN_Start(htim1, TIM_CHANNEL_1); HAL_TIMEx_PWMN_Start(htim1, TIM_CHANNEL_2); HAL_TIMEx_PWMN_Start(htim1, TIM_CHANNEL_3);电机控制还需要加入过流保护可以通过ADC检测电流当超过阈值时立即关闭PWMif(HAL_ADC_GetValue(hadc2) 3000) { HAL_TIM_PWM_Stop(htim1, TIM_CHANNEL_1); // 其他保护逻辑... }7. 性能优化技巧当需要更高精度的PWM时可以考虑以下优化方案使用32位定时器如TIM2可以设置更大的Period值最高2^32-1适合低频高精度场景。调整时钟源将定时器连接到APB2总线最高72MHz而不是APB1总线最高36MHz。DMA传输需要复杂PWM波形时可以用DMA自动更新CCR寄存器// 配置DMA循环传输 HAL_TIM_PWM_Start_DMA(htim1, TIM_CHANNEL_1, (uint32_t*)pwm_buffer, buffer_length);实际项目中我把这些技术应用在了机械臂控制上通过PWM精确控制舵机角度关键是要建立占空比与角度之间的线性映射关系。经过实测使用72MHz时钟和16位分辨率时角度控制精度可以达到0.1度。