STM32F103C8T6 控制 L298N 驱动直流电机:从接线到PWM调速与闭环避坑指南

发布时间:2026/10/15 20:14:55

STM32F103C8T6 控制 L298N 驱动直流电机:从接线到PWM调速与闭环避坑指南 简介这份资源面向STM32入门与电机控制初学者围绕STM32F103C8T6通过L298N双H桥驱动两个直流电机实现正转、反转与停止的完整方案展开配套可直接参考的工程代码帮助读者快速理解GPIO输出控制与电机驱动之间的配合逻辑。资源包共1个文件为PDF格式大小约6.97MB内容涵盖硬件连接说明、main.c与motor.c等关键代码片段及优化建议便于对照学习引脚配置与函数调用关系。目前已有939人学习下载说明其在同类入门资料中具有一定参考价值。读者可从中获取L298N与STM32的接线思路、按键扫描控制电机的程序结构、电机正反转与停止的电平设定方法以及电源管理与中断优化等改进方向适合作为课程设计、电子DIY或嵌入式入门练习的参考资料。1. STM32F103C8T6 控制 L298N 驱动直流电机从最小系统到能跑起来很多人第一次拿到 STM32F103C8T6 和 L298N 模块接线图看了十几遍代码也照着抄了电机就是不转或者只会单向疯转、PWM 调速完全没反应。问题往往不在代码本身而在三个地方共地没做、使能跳线帽没拔、PWM 定时器通道选错了引脚。这篇笔记就按“最小系统能转 → 能调速 → 能正反转 → 能稳定跑”的顺序把 STM32F103C8T6 控制 L298N 驱动直流电机这件事拆开讲清楚。适合刚上手 STM32 的嵌入式新手也适合从 51 单片机转过来、第一次用 HAL 库配 PWM 的开发者。整套方案成本很低一块最小系统板、一个 L298N 模块、一节 18650 或 12V 电源、两个直流电机就能复现代码基于 STM32CubeMX 生成的 HAL 工程逻辑清晰改起来也方便。2. 硬件选型与接线L298N 到底该怎么和 STM32F103C8T6 接2.1 为什么是 L298N而不是 TB6612 或 DRV8833L298N 是老牌双 H 桥驱动芯片最大特点是皮实、便宜、资料多能同时驱动两路直流电机或一个步进电机。它的逻辑供电和电机供电是分开的逻辑 5V 可以来自 STM32 板上的 5V 输出电机供电 7V 到 12V 单独接。缺点是内部压降大典型每路约 1.4V 到 2V大电流下发热明显效率不如 TB6612 或 DRV8833。但如果你只是做小车、云台、简单传动L298N 完全够用而且它对 3.3V 逻辑的兼容性比很多人想象的好STM32F103C8T6 的 GPIO 输出 3.3V 高电平L298N 的输入高电平门槛典型在 2.3V 左右直接接就能识别。选型上我一般这样判断电流小于 1A、对体积没要求、预算极低选 L298N电流 1A 到 3A、要求效率高、板子空间小选 TB6612需要更精细的电流控制或静音选 DRV8833。这篇只讲 L298N因为标题锁死了而且它最容易踩坑把它的坑踩完换别的驱动基本是降维。2.2 接线表STM32F103C8T6 与 L298N 的引脚对应先给一张我实际用的接线表基于 STM32F103C8T6 最小系统板和常见红色 L298N 模块。注意 L298N 模块上通常有 5V 使能跳线帽如果你用外部 5V 给逻辑供电必须把跳线帽拔掉否则会倒灌。STM32F103C8T6 引脚L298N 引脚作用备注PA0ENA电机 A 使能/PWM必须接定时器通道PA1IN1电机 A 方向 1普通 GPIOPA2IN2电机 A 方向 2普通 GPIOPA3ENB电机 B 使能/PWM必须接定时器通道PA4IN3电机 B 方向 1普通 GPIOPA5IN4电机 B 方向 2普通 GPIO5V5V逻辑供电跳线帽拔掉时接GNDGND共地必须和电源地接一起12V 电源正12V电机供电7V 到 12V12V 电源负GND电机地和 STM32 地共地这里有个关键点ENA 和 ENB 必须接在 STM32 有定时器通道的引脚上否则没法输出 PWM。PA0 对应 TIM2_CH1PA3 对应 TIM2_CH4这两个正好在 TIM2 上配置起来方便。如果你用 PA8 和 PA9那就分别是 TIM1_CH1 和 TIM1_CH2也可以但 TIM1 是高级定时器配置稍复杂。新手建议直接用 TIM2。2.3 共地一个被说烂但每天都有人翻车的地方L298N 的 GND 必须和 STM32F103C8T6 的 GND 接在一起同时还要和电机电源的负极接在一起。很多人用两路电源一路给 STM32 供电一路给 L298N 电机供电然后只把 L298N 的 GND 和电机电源负极接了忘了和 STM32 共地结果就是电机乱转、PWM 无效、芯片发烫。血泪经验只要看到电机行为诡异先拿万用表蜂鸣档测 STM32 的 GND 和 L298N 的 GND 是不是通的不通就补线。3. 用 STM32CubeMX 配 TIM2 输出 PWM参数怎么算、怎么填3.1 时钟树与定时器频率72MHz 怎么分到 1kHz PWMSTM32F103C8T6 最高主频 72MHzTIM2 挂在 APB1 总线上APB1 预分频系数不为 1 时定时器时钟是 APB1 时钟的 2 倍。CubeMX 默认配置下APB1 是 36MHzTIM2 时钟就是 72MHz。我们要生成 1kHz 的 PWM方便驱动普通直流电机频率太低电机会有啸叫太高 L298N 响应跟不上。计算公式PWM 频率 定时器时钟 / ((Prescaler 1) * (Counter Period 1))。取 Prescaler 71则计数时钟为 72MHz / 72 1MHz。要 1kHz则 Counter Period 1000 - 1 999。这样每个计数周期 1us1000 个计数就是 1ms即 1kHz。占空比分辨率是 1000 级足够调速。在 CubeMX 里这样填Timers → TIM2 → Clock Source 选 Internal ClockChannel1 选 PWM Generation CH1Channel4 选 PWM Generation CH4Prescaler 填 71Counter Period 填 999PWM Generation CH1 的 Pulse 先填 0后面代码里改GPIO 里 PA1、PA2、PA4、PA5 设为 Output Push PullLow 速度即可生成代码后HAL 会自动初始化 TIM2 和 GPIO。3.2 代码启动 PWM 并封装电机控制函数下面是我常用的电机控制封装基于 HAL 库放在 main.c 的 USER CODE 区域。先启动 PWM再封装正转、反转、停止、调速四个动作。/* 启动 TIM2 的 PWM 输出必须在 while(1) 之前调用 */ HAL_TIM_PWM_Start(htim2, TIM_CHANNEL_1); // ENA HAL_TIM_PWM_Start(htim2, TIM_CHANNEL_4); // ENB /* 电机 A 正转速度 0 到 1000 */ void MotorA_Forward(uint16_t speed) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET); // IN1 高 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET); // IN2 低 __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, speed); // 改占空比 } /* 电机 A 反转速度 0 到 1000 */ void MotorA_Backward(uint16_t speed) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET); __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, speed); } /* 电机 A 停止IN1 和 IN2 都低PWM 给 0 */ void MotorA_Stop(void) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET); __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, 0); }逻辑说明L298N 的方向由 IN1 和 IN2 决定IN1 高 IN2 低是正转反过来是反转两个都低是停止两个都高是刹车。使能端 ENA 的 PWM 占空比决定速度占空比越大转速越高。__HAL_TIM_SET_COMPARE直接改比较寄存器不影响定时器运行适合实时调速。参数说明speed 范围 0 到 999对应 0% 到 99.9% 占空比。不要填 1000因为计数器从 0 到 999填 1000 会一直满占空比虽然也能转但边界不干净。实际使用中低于 200 的占空比很多电机启动不了会听到嗡嗡声但不转这是正常现象不是代码问题。3.3 主循环里怎么调用一个简单的往返测试在 while(1) 里写一个正转两秒、停一秒、反转两秒的循环用来验证接线和代码。while (1) { MotorA_Forward(700); // 70% 速度正转 HAL_Delay(2000); MotorA_Stop(); HAL_Delay(1000); MotorA_Backward(700); // 70% 速度反转 HAL_Delay(2000); MotorA_Stop(); HAL_Delay(1000); }这段代码能跑通说明 PWM、GPIO、L298N 和电机基本都正常。如果正转和反转方向反了把 IN1 和 IN2 的接线对调或者代码里对调 SET 和 RESET 即可。如果电机不转但 L298N 发烫先拔掉电机测 ENA 对 GND 电压正常应该是 1.65V 左右70% 占空比如果一直是 0 或 3.3V说明 PWM 没起来回去检查HAL_TIM_PWM_Start有没有调用。4. 避坑与排查电机不转、调速失效、芯片发烫的 5 个真实原因4.1 现象电机完全不转L298N 指示灯亮但无输出原因最常见的是 ENA 或 ENB 的跳线帽没拔。很多 L298N 模块出厂时 5V 使能跳线帽是插上的此时 ENA 被强制拉高STM32 的 PWM 根本进不去电机要么全速转要么不转。另一个原因是共地没做STM32 的 GND 和 L298N 的 GND 之间没有低阻通路PWM 信号没有参考地L298N 识别不到。解决拔掉 5V 使能跳线帽用杜邦线把 STM32 的 5V 接到 L298N 的 5VGND 接 GND。用万用表测 ENA 对 GND 的电压如果随占空比变化说明 PWM 正常如果一直是 0检查HAL_TIM_PWM_Start是否在 while(1) 之前调用以及 CubeMX 里通道是否选对。4.2 现象电机只能单向转换向无效原因IN1 和 IN2 接反了或者其中一个 GPIO 没有正确初始化。还有一种情况是代码里只改了 PWM没改方向引脚比如正转和反转都只调了__HAL_TIM_SET_COMPAREIN1 和 IN2 一直保持同一状态。解决先确认 IN1 和 IN2 分别接在 PA1 和 PA2代码里正转时 PA1 高 PA2 低反转时反过来。如果接线没错用万用表测 PA1 和 PA2 在正反转时的电平正常应该一个高一个低。如果两个都低检查 GPIO 初始化代码确认HAL_GPIO_Init被调用。4.3 现象PWM 调速没反应电机要么全速要么不转原因PWM 频率太低比如低于 100Hz电机会听到明显抖动调速线性度差或者占空比设置不对比如__HAL_TIM_SET_COMPARE填了 1000 以上导致比较寄存器溢出。另一个常见原因是把 ENA 接在了普通 GPIO 上而不是定时器通道这样只能开关不能调速。解决确认 ENA 接在 PA0TIM2_CH1或 PA3TIM2_CH4CubeMX 里对应通道选 PWM Generation。PWM 频率设 1kHz 到 20kHz 之间我一般用 1kHz兼顾 L298N 响应和电机噪音。占空比范围控制在 0 到 999低于 200 启动不了就适当提高下限。4.4 现象L298N 发热严重电机转速慢原因L298N 内部压降大12V 供电时电机实际只能得到 10V 左右电流大时压降更明显芯片温度能到 60 度以上。如果电机堵转或负载太重电流超过 2AL298N 会触发过热保护输出断续。解决加散热片或者换 TB6612。如果必须用 L298N电机供电可以提高到 12V 到 15V但不要超过 35V。堵转测试不要超过几秒否则芯片容易损坏。实际项目中我会在 L298N 的 12V 和 GND 之间并一个 100uF 电解电容吸收电机启停时的电压波动。4.5 现象电机启动时 STM32 复位或程序跑飞原因电机启动瞬间电流很大如果 STM32 和 L298N 共用一路电源电压会被拉低导致 STM32 欠压复位。另一个原因是电机电源线离 STM32 太近干扰了晶振或复位电路。解决STM32 和 L298N 分开供电STM32 用 USB 或单独 3.3VL298N 用 12V 电池。如果必须共电源在 STM32 的 3.3V 输入端并一个 100uF 和 0.1uF 电容电机电源线上串一个磁环。代码里可以在启动电机前先拉低使能延时 100ms 再给 PWM减少冲击。5. 进阶用编码器做闭环调速和串口实时调参5.1 编码器测速TIM 编码器模式读脉冲开环调速在负载变化时转速会漂加上编码器就能做闭环。常见做法是用 TIM3 或 TIM4 的编码器模式读电机轴上的增量式编码器。以 TIM3 为例PA6 和 PA7 分别接编码器 A 相和 B 相CubeMX 里 TIM3 的 Combined Channels 选 Encoder ModeCounter Period 填 65535其他默认。代码里每 100ms 读一次计数算出转速。int16_t last_cnt 0; int16_t curr_cnt 0; float speed_rpm 0; /* 每 100ms 调用一次 */ void Encoder_Update(void) { curr_cnt (int16_t)__HAL_TIM_GET_COUNTER(htim3); int16_t diff curr_cnt - last_cnt; last_cnt curr_cnt; /* 假设编码器 11 线四倍频后 44 脉冲每转减速比 30 */ speed_rpm (float)diff * 600.0f / (44.0f * 30.0f); }逻辑说明__HAL_TIM_GET_COUNTER读当前计数值两次差值就是这段时间的脉冲数。乘以 600 是因为 100ms 换算成分钟是 600 倍。44 是四倍频后的每转脉冲数30 是减速比这两个参数根据实际电机填。算出的 speed_rpm 就是输出轴转速。参数说明编码器模式要设置好极性如果读出来方向反了把其中一个通道的极性取反。Counter Period 填 65535 是为了避免频繁溢出100ms 内一般不会溢出。5.2 简单 PI 闭环让转速稳住有了转速反馈就可以写一个简单的 PI 控制器输出 PWM 占空比。下面是一个位置式 PIKp 和 Ki 需要根据电机调。float Kp 2.0f; float Ki 0.5f; float target_rpm 100.0f; float integral 0; float output 0; void PI_Control(float actual_rpm) { float error target_rpm - actual_rpm; integral error * 0.1f; // 0.1s 周期 if (integral 500) integral 500; // 积分限幅 if (integral -500) integral -500; output Kp * error Ki * integral; if (output 999) output 999; if (output 0) output 0; __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, (uint16_t)output); }逻辑说明误差是目标转速减实际转速积分项累加误差输出是比例加积分。积分限幅防止启动时积分饱和输出限幅保证占空比在 0 到 999。每 100ms 调用一次和编码器更新同周期。参数说明Kp 太大会震荡太小响应慢Ki 太大会超调太小消除静差慢。我一般先调 Kp 到电机开始轻微震荡再退回 60%然后加 Ki 到静差消失。这套参数没有万能值和电机、负载、编码器线数都有关。5.3 串口调参不用反复烧录就能改目标转速调试时最烦的是改一个参数就要重新编译烧录。用串口接收目标转速就能实时调。在 CubeMX 里开 USART1波特率 115200中断接收。代码里解析简单协议比如收到S500就把目标转速设为 500。uint8_t rx_byte; char rx_buf[16]; uint8_t rx_idx 0; void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart-Instance USART1) { if (rx_byte \n || rx_idx 15) { rx_buf[rx_idx] \0; if (rx_buf[0] S) { target_rpm atof(rx_buf[1]); } rx_idx 0; } else { rx_buf[rx_idx] rx_byte; } HAL_UART_Receive_IT(huart1, rx_byte, 1); } }逻辑说明每收到一个字节进一次中断遇到换行或缓冲区满就解析。如果首字符是 S就把后面的数字转成浮点赋给 target_rpm。这样在串口助手里发S300就能把目标转速改成 300。参数说明波特率 115200 足够缓冲区 16 字节够用。atof需要包含stdlib.h。注意中断里不要做太多事解析完就退出实际控制放在主循环。5.4 一个我常犯的错误刚开始做闭环时我把 PI 计算放在中断里结果中断频率太高主循环被拖慢串口都来不及收。后来改成定时器中断置标志位主循环里查标志再算 PI就稳了。还有一次编码器计数溢出没处理电机转了几圈后速度突然变负查了半天才发现是int16_t溢出。现在我会在每次读计数后判断差值是否超过 30000超过就认为溢出手动修正。这些坑不写出来下次还会踩。希望帮到你。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/10/15 20:14:55

Vue3+Vant4移动端部门树实战:递归组件与懒加载性能优化解析

简介:面向移动端开发者的H5部门树展示示例包,定位在企业内部管理系统、协作平台等场景下的组织架构与员工层级呈现,帮助前端工程师快速实现跨手机、平板的树形列表交互。压缩包共含十一个文件,其中包括一个HTML入口、两个JavaScri…

2026/10/15 20:14:55

干扰消除实战指南:从物理根源到算法落地的全栈工程方法

1. 项目概述:为什么“干扰消除”不是锦上添花,而是信号链的生死线 你有没有遇到过这样的场景:实验室里刚搭好的射频接收前端,示波器上信号波形清晰漂亮,但一接入ADC做频谱分析,底噪突然抬高15dB&#xff0c…

2026/10/15 20:09:53

Android MediaScanner 扫描性能优化:从原理到实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 21:20:10

Gradle 8.6 all.zip 快速下载与离线构建配置避坑指南

简介:Gradle 8.6 是面向 Java、Android 及 JVM 语言开发者的开源构建自动化工具,这份 all 版压缩包适合需要快速获取完整发行版、搭建本地构建环境或研究 Gradle 内部实现的开发者。包内共 2000 个文件,以 1962 个 java 源码文件为主&#xf…

2026/10/15 21:20:10

Gradle 8.3 完整版下载与离线配置实战指南

简介:本资源为 Gradle 8.3 完整发行包,面向使用 Java、Kotlin 及 Android 构建的开发者与构建工程师,用于升级或搭建本地构建环境。该版本支持持久性 Java 编译器守护进程以加速 Java 编译,并降低依赖解析的内存占用,对…

2026/10/15 21:20:10

校园网DDNS实战:借助IPv6实现稳定远程访问的完整方案

先说结论:如果你在校园网里折腾过DDNS,多半也经历过这种“看似很简单,实际就是不通”的尴尬。域名填好了,客户端也登录成功了,解析记录看着也是最新IP,可出了宿舍楼再访问,依然打不开。早期我也…

2026/10/15 21:20:10

vLLM Semantic Router 与 Decision 3.0:多模态语义路由实战

最近在给一个多智能体项目做路由层,翻了不少开源方案,意外发现了一个值得关注的新东西:vLLM Semantic Router 发布的开源多模态决策模型 Decision 3.0,一共给了 5 个尺寸。如果你也在做 Agent 路由、RAG 查询分发、或者多模态内容…

2026/10/15 21:20:10

superpowers:给 Codex CLI 装上 AI 编码的“技能包”

最近这两个月,我几乎把 Codex CLI 当成了日常编码的半个搭档。生成单测、补文档、做小范围重构,它确实能省不少事。可每次代码量一上来,问题就暴露了:生成的代码风格飘忽,该写测试的地方不写,提交信息全凭心…

2026/10/15 21:15:09

2026 数字人技术栈全景图:从一张照片到 7×24 小时直播

如果这篇文章对你有帮助,欢迎关注我的CSDN账号「来福猿」, 有问题可以在评论区留言,我会一一回复。1. 引言:为什么 2026 年数字人直播真正可以落地数字人直播在过去两年完成了从「概念 Demo」到「常态化运营」的跨越。2026 年&…

2026/10/14 11:31:03

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/14 17:41:07

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/15 13:42:16

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/15 0:02:22

防关联浏览器哪个好?三款产品实测差异解析

新手做多店铺,最纠结的就是防关联浏览器选哪个。飞跨、网易、站斧这三款被问得最多,先给结论:要一体化省心、长期便宜,选站斧;要网易招牌和稳,选网易;预算紧做东南亚 Lazada,选飞跨。…

2026/10/15 0:02:22

影刀RPA深度实战:大模型Fine-tuning定制RPA专用Prompt与指令生成

影刀RPA深度实战:大模型Fine-tuning定制RPA专用Prompt与指令生成 一、技术背景 大模型(LLM)在通用场景下表现强大,但当我们将 GPT-4 或 Qwen 直接用于生成影刀RPA操作指令和 XPath 定位时,准确率通常只有 40%-60%。原因…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑