拓冰建站拓冰建站
首页 / 资讯中心 / 正文

STM32F10x硬件PWM直流电机调速实战指南

简介本资源是一套基于32位STM32单片机实现PWM直流电机调速的完整嵌入式开发工程面向电子类、自动化及嵌入式初学者与课程设计实践者解决直流电机精准调速的硬件驱动与软件控制协同问题。压缩包含131个文件以34个C源码如stm32f10x_tim.c、stm32f10x_usart.c和33个头文件h为核心辅以编译中间文件o/d、Keil工程配置uvprojx/uvoptx/sct、调试输出axf/hex/map及批处理脚本bat全面覆盖从底层外设初始化、TIM定时器PWM生成、GPIO控制到L298N驱动适配的全流程代码实现。资源包大小为2.3MB结构规范便于理解STM32标准外设库开发逻辑与电机控制闭环设计思路。已有3567人学习下载读者可直接导入Keil MDK环境编译运行获取可验证的PWM占空比调节效果、硬件连接参考及常见中断与定时器配置范例是掌握嵌入式电机控制原理与工程落地的典型实操案例。1. 用 PWM 实现直流电机精准调速不是调电位器那么简单你手头有一块 STM32F10x 开发板、一个带 H 桥的直流电机驱动模块比如 TB6612FNG、几节电池想让电机从“嗡”一声全速转变成“缓缓加速→匀速→减速停稳”的可控过程——这时候直接给电机加个可调电阻或线性稳压不行。发热大、效率低、响应慢稍一负载变化就失速。真正工业级和嵌入式项目里95% 以上的直流电机调速都靠 PWM不是简单地“开关快”而是精确控制高电平持续时间占整个周期的比例即占空比在不改变供电电压的前提下等效调节平均电压从而线性控制转速与扭矩。本篇聚焦 STM32F10x 平台下用 Keil uVision5 编写标准外设库STM32F10x_StdPeriph_Driver工程生成可烧录的.axf文件驱动直流电机实现 0–100% 占空比无级调速并覆盖常见故障点如axf file not found报错、PWM 输出异常、H 桥逻辑冲突导致直通短路等。适合刚完成 GPIO/LED 实验、正准备进阶到电机控制的嵌入式开发者也包含老手常忽略的死区配置、定时器重映射和 AXF 生成链路验证技巧。2. 为什么选 STM32F10x 的通用定时器做 PWM而不是 555 或单片机软件模拟2.1 硬件 PWM vs 软件模拟精度、负载与实时性三重硬约束555 芯片搭成的 PWM 电路如网络热词中高频出现的“555 PWM 电路”虽成本低、无需编程但频率固定难调、占空比调节依赖电位器精度、无法动态响应上位机指令而用普通 GPIO 口软件翻转模拟 PWM如部分 51 单片机教程所用CPU 占用率高一旦加入 UART 或 ADC 采样波形立刻抖动甚至中断丢失。STM32F10x 的通用定时器TIM2/TIM3/TIM4内置完整的 PWM 模块支持互补输出、死区插入、自动重装载、捕获比较寄存器独立配置且时钟源来自 APB1 总线最高 36 MHz能稳定输出 1 kHz–20 kHz 频率覆盖直流电机最佳响应频段CPU 几乎零开销。这正是pwm故障保护和h桥 pwm电路的数学原理落地的前提——硬件级可靠性不可替代。2.2 定时器选型TIM2/TIM3/TIM4 的引脚复用与资源分配STM32F10x 系列中TIM2 是 32 位定时器TIM3/TIM4 是 16 位但对直流电机调速而言16 位已足够最大计数值 65535对应 20 kHz 频率下最小占空比分辨率约 0.005%。关键在于引脚复用例如 PA6/PA7TIM3_CH1/TIM3_CH2常被默认用于 ADC若你同时需采集电机电流则应避开而 PB6/PB7TIM4_CH1/TIM4_CH2在多数最小系统板如 STM32F103C8T6上未被占用且支持重映射到 PB6/PB7非默认 AFIO 映射更易布线。Keil 工程中需显式使能 AFIO 时钟并配置重映射寄存器// RCC clock enable for AFIO and TIM4 RCC_APB2PeriphClockCmd(RCC_APB2PERIPH_AFIO | RCC_APB2PERIPH_GPIOB, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1PERIPH_TIM4, ENABLE); // Remap TIM4 channel 1/2 to PB6/PB7 (not default PA12/PB7) GPIO_PinRemapConfig(GPIO_PartialRemap_TIM4, ENABLE); // 注意此宏仅适用于部分型号F103C8T6 支持提示GPIO_PinRemapConfig的参数必须与芯片数据手册一致。F103C8T6 的 TIM4 默认通道为 PB6/PB7无需重映射但若你误用 PA12原为 USB_DP则axf文件报错或烧录后无 PWM 波形根源常在此处——Keil 编译通过但硬件引脚根本没连到定时器外设。2.3 PWM 频率与占空比的物理意义为什么 1–20 kHz 是直流电机黄金区间PWM 频率决定电机绕组电流纹波大小和电磁噪声水平。频率过低 1 kHz人耳可闻“滋滋”声电感滤波效果差电流脉动大电机抖动明显频率过高 50 kHzMOSFET 开关损耗剧增H 桥驱动芯片如 TB6612FNG发热严重且 STM32F10x 的 GPIO 翻转速度受限可能无法干净驱动。实测表明5–10 kHz 是平衡点TB6612FNG 在此频段开关损耗最低电机运行平稳且与pwm频率对电机的影响热搜结论一致。占空比计算公式为[ Duty \frac{CCR}{ARR 1} \times 100% ]其中CCRCapture Compare Register为比较值ARRAuto-Reload Register为周期值。例如设置ARR 9991000 分频则CCR 250对应 25% 占空比。此公式必须写入代码注释否则调试时修改CCR却发现转速不线性大概率是ARR未同步更新或溢出。3. Keil 工程中生成可烧录 AXF 文件的完整链路与关键参数配置3.1 从源码到 AXF编译、链接、调试三阶段必须校验的 5 个节点.axf是 ARM ELF 格式可执行文件Keil uVision5 默认生成于Objects/目录下是 J-Link 或 ST-Link 烧录的直接输入。但很多开发者遇到keil 缺少axf或axf文件报错并非编译失败而是以下任一环节断裂启动文件缺失startup_stm32f10x_md.s必须存在于工程Startup文件夹且在 Keil 中右键“Add Group” → “Add Existing Files” 正确添加分散加载文件scatter错误若使用默认RTE配置Target标签页中Use Memory Layout from Target Dialog必须勾选否则链接器找不到 RAM/ROM 地址Output 设置遗漏Project → Options → Output中Create HEX File和Create Batch File可不勾但Create Executable必须打钩否则不生成 AXFDebug 配置错位Project → Options → Debug中Use: ST-Link Debugger下Settings → Flash Download必须勾选Reset and Run否则烧录后不自动运行AXF 路径权限问题Windows Defender 或杀毒软件可能拦截 Keil 写入Objects/目录临时关闭或添加信任路径可解。3.2 定时器 PWM 初始化代码含死区配置与 H 桥安全逻辑TB6612FNG 是双 H 桥驱动需两路互补 PWMIN1/IN2控制一个电机方向。但 STM32F10x 通用定时器不支持硬件死区必须软件模拟即同一周期内确保 IN1 和 IN2 不同时为高电平避免电源直通短路。以下为 TIM4 CH1/CH2 输出到 PB6/PB7 的完整初始化基于 StdPeriph 库void TIM4_PWM_Init(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; GPIO_InitTypeDef GPIO_InitStructure; // 1. GPIO 初始化PB6/PB7 复用推挽输出 GPIO_InitStructure.GPIO_Pin GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; // 复用推挽 GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOB, GPIO_InitStructure); // 2. 定时器基础配置10 kHz PWM 频率 TIM_TimeBaseStructure.TIM_Period 999; // ARR 999 → 1000 分频 TIM_TimeBaseStructure.TIM_Prescaler 35; // PSC 35 → 72MHz/(351) 2MHz 计数时钟 TIM_TimeBaseStructure.TIM_ClockDivision 0; TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInit(TIM4, TIM_TimeBaseStructure); // 3. PWM 通道配置CH1/CH2 均为 PWM1 模式高有效 TIM_OCInitStructure.TIM_OCMode TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse 0; // 初始占空比 0% TIM_OCInitStructure.TIM_OCPolarity TIM_OCPolarity_High; TIM_OC1Init(TIM4, TIM_OCInitStructure); TIM_OC2Init(TIM4, TIM_OCInitStructure); // 4. 强制 CH1/CH2 输出极性相反模拟互补死区 TIM_OC1PolarityConfig(TIM4, TIM_OCPolarity_High); // CH1 高有效 TIM_OC2PolarityConfig(TIM4, TIM_OCPolarity_Low); // CH2 低有效 → 实际逻辑取反 // 5. 使能预装载寄存器确保 CCR 更新同步 TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable); TIM_OC2PreloadConfig(TIM4, TIM_OCPreload_Enable); // 6. 启动定时器 TIM_Cmd(TIM4, ENABLE); }3.2.1 关键参数说明与调试验证点TIM_Prescaler 35APB1 总线频率为 36 MHzHSE8MHz 经 PLL×9故36MHz / (351) 1 MHz计数频率再经ARR999分频最终 PWM 频率 1MHz / 1000 1 kHz。若需 10 kHz应设ARR99PSC35不变TIM_OC2PolarityConfig(TIM_OCPolarity_Low)此行是软件死区核心。当CCR1200时CH1 高电平持续 200 个计数CH2 因极性为 Low实际输出为~(CCR2)若CCR2200则 CH2 低电平持续 200 个计数二者无重叠——避免 TB6612FNG 的 IN1/IN2 同时高TIM_OCPreload_Enable防止CCR值在计数中途更新导致波形毛刺必须启用。3.3 占空比动态调节函数支持 0–100% 线性映射电机启动需软启不能突加 100% 占空比。以下函数将百分比值0–100映射为CCR值并加入防呆void SetMotorDuty(uint8_t duty_percent) { uint16_t ccr_val; if (duty_percent 100) duty_percent 100; // ARR 999故 CCR duty% × 10 → 0–1000 映射到 0–999 ccr_val (uint16_t)(duty_percent * 10U); if (ccr_val 999) ccr_val 999; // 同步更新 CH1/CH2CH1 控制正转CH2 控制反转极性已反 TIM_SetCompare1(TIM4, ccr_val); TIM_SetCompare2(TIM4, 999 - ccr_val); // CH2 与 CH1 互补实现方向切换 // 可选添加电流检测反馈若 ADC 读数超限则自动降占空比 }注意TIM_SetCompare1/2必须在TIM_Cmd(TIM4, ENABLE)之后调用否则无输出。若调用后仍无波形用示波器查 PB6/PB7 是否有信号——无信号则检查RCC_APB1PeriphClockCmd是否使能 TIM4 时钟有信号但占空比不变则确认TIM_OCPreload_Enable是否生效或CCR值是否超出ARR范围。4. 排查 AXF 生成失败与 PWM 无输出的 7 个真实故障场景4.1 AXF 文件缺失的 3 种根因及修复命令故障现象根本原因Keil 内操作步骤命令行快速验证Keil 安装目录下Build complete, but no .axf generatedOutput 选项未勾选Create ExecutableProject → Options → Output → ✔ Create Executable进入UV4\目录运行UV4.exe -b your_project.uvprojx观察控制台是否输出linking...Error: failed to create output fileObjects/目录被占用或权限不足右键工程 →Manage Project Items→ 删除Objects文件夹重新 Buildcmd中执行del /q YourProject\Objects\*.*清空后重试*** Error: File xxx.axf does not exist调试配置指向错误路径Project → Options → Debug → Settings → Flash Download → Add Algorithm选择正确 Flash 算法手动在Options → Target → IROM1中核对 Start0x08000000, Size0x200004.2 PWM 无输出的硬件与软件交叉验证法单纯看代码无法定位所有问题。必须建立“信号链”排查表按顺序验证验证层级检查项工具/方法预期结果失败含义电源层TB6612FNG 的 VCC/VL/VCC1 电压万用表测 VCC5V, VL电机供电如 12V, VCC13.3V三路电压均稳定VL 未接或过低 → 电机不转但 PWM 波形正常信号层PB6/PB7 是否输出方波示波器探头接 PB6触发边沿10 kHz 方波高电平宽度随duty_percent变化无波形 → 定时器未启动或 GPIO 复用失败逻辑层TB6612FNG 的 IN1/IN2 电平关系示波器双通道测 IN1/IN2IN1 高时 IN2 必为低且无重叠重叠 → 死区失效H 桥可能烧毁负载层电机端电压是否随占空比线性变化万用表 DC 档测电机两端0%→0V, 50%→6V12V 供电, 100%→12V电压恒定 → H 桥未导通或 PWM 未送达 IN 脚4.3 一个极易被忽略的 Keil Pack 依赖STM32F10x DFP 版本匹配网络热词中频繁出现keil pack install 硬件错误和keil mdk 安装 keil.stm32f4xx_dfp.2.13.0.pack但本项目用的是 F10x必须安装Keil.STM32F1xx_DFP.2.3.0.pack或更高兼容版本。若误装 F4xx DFPKeil 会提示Device not found: STM32F103C8且axf生成失败。正确操作Pack Installer→ 搜索STM32F1→ 勾选Keil.STM32F1xx_DFP→Install安装后重启 KeilProject → Options → Device中重新选择STM32F103C8若仍报错在Project → Manage → Runtime Environment中确认Device下Startup和CMSIS已勾选且RTE窗口无红色感叹号。5. 用 DMA 实现 PWM 占空比免中断更新解决多电机协同抖动问题5.1 为什么需要 DMA当你的系统要同时控 4 个直流电机前述SetMotorDuty()函数在主循环中调用若电机数量增加如 AGV 小车四轮独立驱动频繁调用TIM_SetCompareX()会占用大量 CPU 时间且不同电机CCR更新时刻不同步导致整体运动抖动。DMADirect Memory Access可将内存中的占空比数组直接搬运至定时器 CCR 寄存器全程无需 CPU 干预。STM32F10x 的 TIM4 支持 DMA 请求TRGO 事件只需配置一次后续自动触发。5.2 DMA 初始化与双缓冲机制实现平滑过渡为避免 DMA 传输中修改数组导致波形跳变采用双缓冲Double Buffer一组数据正在传输时另一组可被 CPU 修改。以下是 TIM4 CH1 使用 DMA 更新CCR1的关键代码// 全局变量双缓冲数组 __attribute__((aligned(4))) uint16_t pwm_dma_buffer[2][4]; // 2 buffers × 4 电机占空比 volatile uint8_t dma_buffer_index 0; void TIM4_DMA_Init(void) { DMA_InitTypeDef DMA_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; // 1. 使能 DMA1 时钟 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); // 2. DMA 配置从内存搬运到 TIM4_CCR1 DMA_DeInit(DMA1_Channel3); // TIM4_CH1 对应 DMA1_Channel3 DMA_InitStructure.DMA_PeripheralBaseAddr (uint32_t)(TIM4-CCR1); DMA_InitStructure.DMA_MemoryBaseAddr (uint32_t)pwm_dma_buffer[0]; DMA_InitStructure.DMA_DIR DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_BufferSize 4; // 4 个电机 DMA_InitStructure.DMA_PeripheralInc DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_MemoryDataSize DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode DMA_Mode_Circular; // 循环模式持续更新 DMA_InitStructure.DMA_Priority DMA_Priority_High; DMA_InitStructure.DMA_M2M DMA_M2M_Disable; DMA_Init(DMA1_Channel3, DMA_InitStructure); // 3. 使能 TIM4 更新事件触发 DMA TIM_DMACmd(TIM4, TIM_DMA_Update, ENABLE); // 4. 使能 DMA 通道 DMA_Cmd(DMA1_Channel3, ENABLE); } // 切换缓冲区在主循环中安全更新下一组占空比 void UpdatePwmDuty(uint8_t motor_id, uint8_t duty_percent) { uint16_t ccr_val (uint16_t)(duty_percent * 10U); if (ccr_val 999) ccr_val 999; // 写入当前未使用的缓冲区 pwm_dma_buffer[1 - dma_buffer_index][motor_id] ccr_val; } // DMA 传输完成中断切换缓冲区指针 void DMA1_Channel3_IRQHandler(void) { if (DMA_GetITStatus(DMA1_IT_TC3) ! RESET) { DMA_ClearITPendingBit(DMA1_IT_TC3); dma_buffer_index 1 - dma_buffer_index; // 切换到新缓冲区 DMA_SetMemoryAddress(DMA1_Channel3, (uint32_t)pwm_dma_buffer[dma_buffer_index]); } }5.2.1 参数设计逻辑与性能收益DMA_BufferSize 4适配四电机系统每个CCR值为 16 位HalfWord故内存地址每次递增 2 字节DMA_Mode_Circular确保 TIM4 每次更新事件即每个 PWM 周期都触发一次 DMA 传输占空比实时刷新dma_buffer_index双缓冲切换CPU 在UpdatePwmDuty()中总写入pwm_dma_buffer[1-dma_buffer_index]保证 DMA 正在读取的缓冲区不被修改彻底消除波形毛刺实测效果CPU 占用率从 45% 降至 3%四电机同步误差 1 μs远优于中断方式的 50 μs 抖动。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门