STM32F103与L298N电机驱动:PWM调速与H桥控制完整例程

发布时间:2026/10/28 18:05:18
STM32F103与L298N电机驱动:PWM调速与H桥控制完整例程 简介基于STM32F103的L298N直流减速电机驱动例程由KEIL标准库开发面向嵌入式单片机初学者与物联网项目开发者解决直流电机驱动控制中的PWM、GPIO配置与外设联调问题代码均经过实战验证示例简单好用。压缩包共一百二十八个文件、约一点五二兆包含.c/.h源码、可直接打开的Keil工程文件、可烧录的.hex与仿真用的.axf还有.map/.lst等链接编译信息文件、重置KEIL编译环境的.bat脚本以及.s启动文件可完整还原编译下载环境。电机与单片机的接线定义已在代码中逐一注明硬件差异可自行调整更换同系列芯片时只需修改芯片型号与FLASH容量并可按需选择J-Link或ST-Link调试。已有二百零三人浏览学习适合借此理解定时器PWM输出控制电机转速、直流电机调速原理也可在工程中对照外设初始化代码进一步拓展ADC、USART、CAN等开发。1. 单片机小车上绕不开的组合STM32F103 与 L298N 的驱动例程做嵌入式小车、机器人底盘或者毕设里的智能车第一件要面对的事往往不是算法而是怎么让直流减速电机听话地转起来。很多新手第一次拿到电机直接拿 IO 高低电平去点结果要么不转要么转速完全没法控制最后老老实实回到 STM32F103 加 L298N 这套经典方案上。这套例程包干的就是这件事用 F103 输出 PWM 调速用 L298N 双 H 桥决定方向和电流换向驱动常见的 6V/12V 直流减速电机。适合刚开始玩小车、准备做课程设计或者想快速把手头电机模块跑起来的人也适合想看看别人工程里 GPIO、定时器、PWM 是怎么组织的人。我这次拆包时重点看了它的接线定义、驱动函数封装和使能脚处理这几处恰恰是同类例程最容易翻车的地方。2. 硬件接线与 H 桥逻辑先看清电流从哪里走到哪里拿到例程包先别急着烧录我习惯先把板子上的电流路径捋一遍。直流减速电机和普通小电机不一样的地方在于它带减速齿轮箱扭矩大但电流也不小空载几百毫安、堵转轻松上 1A 以上所以不能用单片机 IO 直接推需要 L298N 这种功率驱动级在中间做电流放大。2.1 L298N 模块引脚功能与接线表L298N 是意法半导体经典的双 H 桥驱动芯片模块上除了芯片本体一般还带两个输出端、一个 5V 稳压管和一些跳线帽。模块上需要关心的引脚分三组逻辑输入 IN1-IN4 接单片机 GPIO使能端 ENA/ENB 控制每个 H 桥的开关接 PWM 就能调速电源端 VS 接电机供电VCC 是模块内部稳压出来的 5V 逻辑电。这次例程用的是双电机底盘我按例程里的定义整理了一份接线表STM32F103 引脚L298N 模块引脚作用PA0IN1左电机方向控制 APA1IN2左电机方向控制 BPA2IN3右电机方向控制 APA3IN4右电机方向控制 BPA6TIM3_CH1ENA左电机使能 / 调速 PWMPA7TIM3_CH2ENB右电机使能 / 调速 PWM3.3V 或 5VVCC / 5V逻辑电源共地后 3.3V 可用GNDGND与电机电源共地必须接12V 电池正极VS / 12V电机主电源电池负极GND电机电源地这里有个容易忽略的细节很多 L298N 模块上 ENA/ENB 默认插着跳线帽插上后使能端被直接拉到高电平电机只要 IN 有电平就全速转PWM 信号根本进不去。用 PWM 调速前必须把这两个跳线帽拔掉把 ENA/ENB 引出来接到 STM32 的定时器输出脚。例程里如果直接烧录后电机全速转八成就是跳线帽没拔。2.2 方向控制的电平逻辑表H 桥的四个功率管两两配对工作同一时刻只有对角的一对导通电流方向不同电机转向就不同。L298N 的 IN1/IN2 控制左侧桥IN3/IN4 控制右侧桥逻辑关系完全对称。一个桥的状态可以总结成下表IN1 / IN3IN2 / IN4电机状态高电平低电平正转低电平高电平反转低电平低电平停止滑行高电平高电平刹车能耗制动把使能端 ENA 拉低时无论 IN 是什么电平该桥输出都会被切断这是例程里停机函数惯用的处理方式。注意滑行和刹车的区别两个 IN 同为高电平时电机两端被短路转动惯量会被迅速耗掉车会急停同为低电平时电机处于开路状态靠惯性滑行。代码里做停止函数时如果你希望小车短距离停下而不是溜坡可以尝试把两个 IN 同时拉高而不是拉低。2.3 电源与共地很多上电翻车都出在这一步例程包的接线文档里通常都会画一个很粗的 GND 线因为共地是这个方案里最关键的电源规则。L298N 的 VS 接电机电源单片机的 GND 必须和这块电源的负极接在一起否则 IN 引脚的参考地不一致H 桥输入电平就是悬空的表现为电机要么不转要么乱转。具体操作时我一般把电池组负极分成两路一路直接进 L298N 的 GND 端子另一路接 STM32 最小系统板的 GND而不是依赖模块上的 5V 输出给单片机供电。原因很简单L298N 模块上那颗 5V 稳压芯片在 VS 高于 12V 时发热很大负载稍重就把电压拉垮让单片机用自己板载的 AMS1117 之类降压可靠性高得多。另外电机电源电压不要按 L298N 最大支持电压选而要看电机额定电压。给额定 6V 的减速电机供 12V空载转速会翻倍堵转电流也许会超过驱动能力。3. 从 CubeMX 到驱动代码定时器 3 输出 PWM 的完整工程路径这套例程用的是 STM32F103 标准外设库还是 HAL 库其实影响不大关键是 PWM 通道和 GPIO 的映射关系要对。我按目前项目里最常见的做法——STM32CubeMX 生成工程加 HAL 库——把例程里驱动部分的组织方式拆开讲。和我一样习惯手写寄存器的人也能看懂因为定时器通道和引脚位置是硬件决定的。3.1 用 CubeMX 配出定时器 3 PWM 和 GPIO样例工程里把定时器 3 的两个通道用作 PWM 输出这正是很多检索词里会提到的“stm32f103 cubemx 定时器3”用法。PA6 是 TIM3_CH1PA7 是 TIM3_CH2用来给两个电机调速PA0-PA3 配置为普通推挽输出控制方向。在 CubeMX 里的配置结果落到代码上就是下面这段初始化void MX_TIM3_Init(void) { TIM_OC_InitTypeDef sConfigOC {0}; htim3.Instance TIM3; htim3.Init.Prescaler 71; // 72MHz / 72 1MHz htim3.Init.CounterMode TIM_COUNTERMODE_UP; htim3.Init.Period 99; // 1MHz / 100 10kHz htim3.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; htim3.Init.AutoReloadPreload TIM_AUTORELOAD_PRELOAD_ENABLE; HAL_TIM_PWM_Init(htim3); sConfigOC.OCMode TIM_OCMODE_PWM1; sConfigOC.Pulse 0; // 初始占空比 0电机不转 sConfigOC.OCPolarity TIM_OCPOLARITY_HIGH; HAL_TIM_PWM_ConfigChannel(htim3, sConfigOC, TIM_CHANNEL_1); HAL_TIM_PWM_ConfigChannel(htim3, sConfigOC, TIM_CHANNEL_2); HAL_TIM_PWM_Start(htim3, TIM_CHANNEL_1); HAL_TIM_PWM_Start(htim3, TIM_CHANNEL_2); }这套参数的含义要拆开看预分频器 Prescaler 设置为 71等于把 72MHz 的定时器时钟 72 分频得到 1MHz 计数频率自动重装载值 Period 设置 99意味着计数器从 0 数到 99 需要 100 个计数周期PWM 频率就是 1MHz / 100 10kHz。10kHz 对直流减速电机是一个不错的起点听不到明显啸叫H 桥开关损耗也不算大。代价是占空比只有 100 档调速时转速步进感会比较明显。想要更细腻的调速可以把 Period 改到 999同时 Prescaler 改成 7这样频率还是 1MHz / 1000 10kHz但占空比精度变成了 1000 档。例程里保留 100 档的原因很简单——新手看pulse speed_percent时占空比和百分比能直接对上好理解。3.2 电机驱动函数把方向与速度封装成两个入口例程的核心驱动代码一般会拆成l298n.c / l298n.h对外只暴露“运行”和“停止”两个函数。我在工程里见过几种写法最顺手的是把速度百分比直接在函数内换算成 CCR 值调用方不用关心定时器细节void motor_run(uint8_t motor_id, motor_dir_t dir, uint8_t speed_percent) { uint16_t pulse speed_percent; // ARR99 时百分比就是 CCR uint16_t ccr1 __HAL_TIM_GET_COMPARE(htim3, TIM_CHANNEL_1); uint16_t ccr2 __HAL_TIM_GET_COMPARE(htim3, TIM_CHANNEL_2); if (motor_id MOTOR_LEFT) { if (dir MOTOR_FORWARD) { HAL_GPIO_WritePin(IN1_GPIO_Port, IN1_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(IN2_GPIO_Port, IN2_Pin, GPIO_PIN_RESET); } else if (dir MOTOR_BACKWARD) { HAL_GPIO_WritePin(IN1_GPIO_Port, IN1_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(IN2_GPIO_Port, IN2_Pin, GPIO_PIN_SET); } else { HAL_GPIO_WritePin(IN1_GPIO_Port, IN1_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(IN2_GPIO_Port, IN2_Pin, GPIO_PIN_RESET); } __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, pulse); } if (motor_id MOTOR_RIGHT) { /* 同样的逻辑操作 IN3/IN4PWM 写到 TIM_CHANNEL_2 */ } } void motor_stop(uint8_t motor_id) { __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, 0); __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_2, 0); HAL_GPIO_WritePin(IN1_GPIO_Port, IN1_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(IN2_GPIO_Port, IN2_Pin, GPIO_PIN_RESET); /* 右电机同理 */ }这里要说清楚一个设计习惯方向用普通 GPIO 控制速度用 PWM 控制两者分开。有人图省事把 PWM 直接接到 IN 脚上想通过占空比调速——这在 L298N 上行不通因为占空比低于某个阈值时 H 桥输入端达不到高电平门槛电机会直接停转调速区间极度非线性。把 PWM 放在使能端IN 只是满幅的逻辑电平调速就只受占空比影响这是 L298N 数据手册推荐的用法。停止函数里我习惯把 CCR 清零同时把 IN 全部拉低这样即使 PWM 还在输出由于命令是滑行电机不会突然被反向制动。如果你要做急停把 IN1/IN2 都拉高此时电机接线端被 H 桥短路动能会被快速消耗。3.3 主循环里的调用节奏与参数怎么改驱动函数封装好后主循环里只用关心控制逻辑。例程里通常有一段这样的冒烟测试代码用来确认四个 IN 引脚和两个 PWM 通道的映射没接错while (1) { motor_run(MOTOR_LEFT, MOTOR_FORWARD, 50); motor_run(MOTOR_RIGHT, MOTOR_FORWARD, 50); HAL_Delay(2000); motor_run(MOTOR_LEFT, MOTOR_BACKWARD, 50); motor_run(MOTOR_RIGHT, MOTOR_BACKWARD, 50); HAL_Delay(2000); motor_stop(MOTOR_LEFT); motor_stop(MOTOR_RIGHT); HAL_Delay(1000); }跑这一段时如果左轮正转、右轮反转说明其中一个电机接线极性不对直接对调 L298N 上对应 OUT1/OUT2 或 OUT3/OUT4 的两根线比在代码里改定义要直观。PWM 频率和占空比档位如果改了记得同步检查__HAL_TIM_GET_COMPARE取回的值范围很多例程在占空比精度改成 1000 档后忘记把pulse speed_percent改成pulse speed_percent * 10结果整车乏力。有些例程包里还会附带“嵌入式按键非阻塞扫描”的写法用定时器节拍代替 HAL_Delay 做按键消抖和模式切换这个思路很适合放在小车例程里主循环里每 5ms 扫描一次按键10ms 刷新一次状态机50ms 更新一次 PWM 占空比。我在这类电机项目里一般会建议保留一个非阻塞节拍因为HAL_Delay在电机启停瞬间容易受中断影响产生抖动多一个节拍变量的事不值得后面排查奇怪时序。4. 避坑实录四个新手翻车现场与排查顺序驱动例程本身不长但电机是强电感性负载加上 L298N 模块各家做工参差不齐新手照着接线图焊完上电大概率会踩到下面几个坑。我按现场现象、原因、解决办法的顺序各写一条你烧录前可以先对一遍。4.1 电机不转但模块电源灯亮先查使能脚和共地现象是 L298N 模块上电源 LED 正常点亮IN 引脚电平也量到高低了但电机就是纹丝不动。用万用表量 OUT 端发现输出电压为零。这类问题七成出在使能脚。模块出厂时 ENA/ENB 的跳线帽如果拔掉了而例程初始化里没有把对应引脚拉高或者输出 PWMH 桥的输出级就一直处于关闭状态。剩下三成是共地问题电机电源负极和 STM32 的 GND 如果各走各的IN 引脚的电平参考点漂移H 桥识别不到有效逻辑高电平。解决方法是先拔掉所有跳线帽用杜邦线把 ENA/ENB 引到 STM32 的 PA6/PA7然后用万用表二极管挡确认电池负极到单片机 GND 通畅。量完再上电通常这一遍就能转起来。4.2 上电瞬间电机偷跑一下复位瞬间 GPIO 浮空现象是程序刚烧录完或者一上电电机猛地转一小段然后停住之后按按钮又能正常控制。原因是 STM32 复位期间 GPIO 处于浮空输入状态PA0-PA3 上没有确定的电平L298N 的 IN 输入端如果被干扰拉到高电平H 桥就会在单片机初始化完成前先导通一下。解决分两步CubeMX 初始化 GPIO 时把 PA0-PA3 设置为下拉输入或推挽输出低电平同时把motor_stop的调用放到 GPIO 初始化之后、PWM 启动之前。例程里如果上电偷跑大概率是初始化顺序写成了先开 PWM 再拉低 IN顺序换过来之后这个问题就绝迹了。这也是我玩过几个例程包之后养成的习惯任何电机驱动项目复位后第一条指令必然是停机。4.3 方向反了或者左右轮各转各的线序问题现象是正转指令下去左轮前进右轮后退车在原地打转或者反转指令变成了正转。这是因为电机两根引线在 L298N 上的 OUT 接线没有统一约定左边电机接成 OUT1/OUT2右边也接成 OUT1/OUT2 不会出错但一边接 OUT1/OUT2 一边接 OUT2/OUT1方向就反了。这类问题代码层面无解必须物理上调整。我一般把左右电机线都贴上标签规定红色线接 OUT 奇数口、黑色线接偶数口整车接线就统一了。也可以在motor_run里给右电机单独定义MOTOR_BACKWARD时把 IN3/IN4 反过来写但这样代码可读性很差后续换电机又容易糊涂。4.4 芯片烫手发热快、电池掉电猛压降与堵转电流问题现象是跑一两分钟 L298N 芯片烫到不敢摸电池电压掉得很快电机转速明显低于预期。原因是 L298N 的饱和压降在输出电流 1A 时大约有 2V 左右也就是说 12V 电源实际到电机上最多 10V这部分压降全变成芯片的热量。电机堵转时电流更大热量上升更快。我见过有人给额定 6V 电机接 12V 电源结果电机转速高、电流大、芯片发热严重最后用一个 5V 3A 的电源适配器才正常。解决方法是先确认电机额定电压和 VS 匹配再给 L298N 模块贴上自带散热片实测时注意堵转时间不要超过几秒。如果你发现电机带负载后扭矩总是不够先考虑换更高额定电压的电机或者改用带大电流 MOS 管的驱动模块而不是一味调大占空比。5. 进阶验证用逻辑分析仪给 PWM 做一次体检电机能转了不算完我拿到这套例程后习惯性地用逻辑分析仪给 PWM 通道做了一次波形体检。把逻辑分析仪的通道夹在 PA6 上地线接 STM32 的 GND然后在主循环里让电机以 70% 占空比正转抓到的波形应该是频率 10kHz、高电平占比约 70% 的方波。如果频率不是 10kHz先查 APB1 定时器时钟是不是 72MHz如果占空比和设置值对不上检查__HAL_TIM_SET_COMPARE的赋值是不是被中断或其他代码改过。第二个测量点是 ENA 引脚的占空比和电机两端电压的关系。用万用表直流挡量 OUT1 和 OUT2 之间的电压会发现它约等于 VS 电压乘以占空比再减去 L298N 压降。比如 VS 是 12V占空比 50%理论上量到约 6V实际可能只有 5V 多。如果你量到电压和计算值差很多说明芯片压降异常或者电源在负载下掉压了这时候需要把万用表串到电机回路里量工作电流看是不是超过模块额定。做完这两步这套电机驱动才算被真正验收了。从那以后我每次做电机驱动验证都强制自己先抓波形、再量电压、最后才看转速这套流程帮我挡掉了不少玄学问题。烧录前也一定先检查跳线帽和共地线宁可多花两分钟不想再被电机的反向电动势阴一把。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询