
⭐ 前言
在智能小车、机械臂、小型机器人等嵌入式项目开发中,L298N电机驱动模块是最常用的直流电机驱动器件,它支持双路直流电机独立控制,搭配STM32的PWM功能可实现精准调速。
本文基于STM32F407 芯片,使用TIM2定时器输出两路PWM波,实现L298N双电机的正转、反转、停止、无级调速功能,全程代码可直接移植,避开硬件引脚冲突,适合嵌入式新手快速上手电机驱动开发。
本文目标:
- 掌握L298N模块的硬件接线与工作原理
- 学会STM32F4定时器PWM输出配置
- 实现双电机独立控制与PWM调速
- 提供完整可运行的工程代码,直接移植使用
📌 一、L298N模块与硬件原理
1.1 L298N模块简介
L298N是一款双全桥电机驱动芯片,最大支持2路直流电机独立驱动,工作电压5~35V,驱动电流可达2A,具备过流保护功能,完美适配小型直流减速电机。
1.2 核心引脚功能
| 引脚名称 | 功能说明 |
|---|---|
| IN1/IN2 | 电机A方向控制引脚 |
| IN3/IN4 | 电机B方向控制引脚 |
| ENA | 电机A使能+PWM调速引脚 |
| ENB | 电机B使能+PWM调速引脚 |
| 12V | 电机电源输入(外接电池) |
| GND | 电源地(必须与STM32共地) |
| 5V OUT | 模块输出5V(可给单片机供电) |
1.3 控制逻辑
- 方向控制:通过IN1/IN2、IN3/IN4高低电平控制电机正反转
- 速度控制:ENA/ENB输入PWM波,通过占空比调节转速
- 停止控制:方向引脚拉低,PWM占空比设为0
⚙️ 二、硬件接线设计(STM32F407 + L298N)
2.1 引脚分配(避开冲突,最优方案)
本设计使用TIM2_CH1(PA0)、TIM2_CH2(PA1) 输出PWM,GPIO控制电机方向:
| STM32引脚 | L298N引脚 | 功能 |
|---|---|---|
| PA0 | ENA | 电机A PWM调速 |
| PA4 | IN1 | 电机A方向控制1 |
| PA5 | IN2 | 电机A方向控制2 |
| PA1 | ENB | 电机B PWM调速 |
| PA6 | IN3 | 电机B方向控制1 |
| PA7 | IN4 | 电机B方向控制2 |
| GND | GND | 共地(核心!必须连接) |
| 外部电源12V | 12V | 电机供电 |
2.2 接线注意事项
- STM32与L298N必须共地,否则电机无法正常工作
- 电机电源建议使用独立电池,避免干扰单片机
- PWM引脚必须配置为定时器复用功能
💻 三、STM32工程代码实现
3.1 驱动头文件 l298.h
c
#ifndef __L298N_H
#define __L298N_H
#include "stm32f4xx_hal.h"
// 函数声明
void Motor_Init(void);
void Tim2_PWM_Init(void);
void MotorA_Set(int speed, uint8_t direction);
void MotorB_Set(int speed,uint8_t direction);
void Motor_PWM_Start(void);
#endif
3.2 驱动源文件 l298.c(核心代码)
c
#include "l298.h"
#include "tim.h"
// 声明外部定时器句柄
extern TIM_HandleTypeDef htim2;
// ==================== 电机引脚定义 ====================
// 电机A:ENA=PA0(TIM2_CH1),IN1=PA4,IN2=PA5
#define MOTOR_A_ENA_PIN GPIO_PIN_0
#define MOTOR_A_IN1_PIN GPIO_PIN_4
#define MOTOR_A_IN2_PIN GPIO_PIN_5
#define MOTOR_A_GPIO_PORT GPIOA
// 电机B:ENB=PA1(TIM2_CH2),IN3=PA6,IN4=PA7
#define MOTOR_B_ENB_PIN GPIO_PIN_1
#define MOTOR_B_IN3_PIN GPIO_PIN_6
#define MOTOR_B_IN4_PIN GPIO_PIN_7
#define MOTOR_B_GPIO_PORT GPIOA
/**
* @brief 初始化电机GPIO
*/
void Motor_Init(void)
{
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct={0};
// 配置方向控制引脚
GPIO_InitStruct.Pin = MOTOR_A_IN1_PIN | MOTOR_A_IN2_PIN |
MOTOR_B_IN3_PIN | MOTOR_B_IN4_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
HAL_GPIO_Init(MOTOR_A_GPIO_PORT, &GPIO_InitStruct);
// 配置电机A PWM引脚 PA0
GPIO_InitStruct.Pin = MOTOR_A_ENA_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Alternate = GPIO_AF1_TIM2;
HAL_GPIO_Init(MOTOR_A_GPIO_PORT, &GPIO_InitStruct);
// 配置电机B PWM引脚 PA1
GPIO_InitStruct.Pin = MOTOR_B_ENB_PIN;
GPIO_InitStruct.Alternate = GPIO_AF1_TIM2;
HAL_GPIO_Init(MOTOR_B_GPIO_PORT, &GPIO_InitStruct);
}
/**
* @brief TIM2 PWM初始化
*/
void Tim2_PWM_Init(void)
{
TIM_OC_InitTypeDef sConfig={0};
// 定时器基础配置
htim2.Instance=TIM2;
htim2.Init.Period=999; // PWM周期 1000份
htim2.Init.Prescaler=0; // 不分频
htim2.Init.CounterMode=TIM_COUNTERMODE_UP;
htim2.Init.AutoReloadPreload=TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_PWM_Init(&htim2)!=HAL_OK)
{
Error_Handler();
}
// PWM通道1配置(电机A)
sConfig.OCMode=TIM_OCMODE_PWM1;
sConfig.Pulse=0;
sConfig.OCPolarity=TIM_OCPOLARITY_HIGH;
HAL_TIM_PWM_ConfigChannel(&htim2,&sConfig,TIM_CHANNEL_1);
// PWM通道2配置(电机B)
HAL_TIM_PWM_ConfigChannel(&htim2,&sConfig,TIM_CHANNEL_2);
}
/**
* @brief 电机A控制
* @param speed: 0~999 direction:1正转 2反转 0停止
*/
void MotorA_Set(int speed,uint8_t direction)
{
speed = (speed > 999) ? 999 : (speed < 0) ? 0 : speed;
switch(direction)
{
case 1: // 正转
HAL_GPIO_WritePin(MOTOR_A_GPIO_PORT, MOTOR_A_IN1_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(MOTOR_A_GPIO_PORT, MOTOR_A_IN2_PIN, GPIO_PIN_RESET);
break;
case 2: // 反转
HAL_GPIO_WritePin(MOTOR_A_GPIO_PORT, MOTOR_A_IN1_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(MOTOR_A_GPIO_PORT, MOTOR_A_IN2_PIN, GPIO_PIN_SET);
break;
default: // 停止
HAL_GPIO_WritePin(MOTOR_A_GPIO_PORT, MOTOR_A_IN1_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(MOTOR_A_GPIO_PORT, MOTOR_A_IN2_PIN, GPIO_PIN_RESET);
speed = 0;
break;
}
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, speed);
}
/**
* @brief 电机B控制
*/
void MotorB_Set(int speed,uint8_t direction)
{
speed = (speed > 999) ? 999 : (speed < 0) ? 0 : speed;
switch(direction)
{
case 1:
HAL_GPIO_WritePin(MOTOR_B_GPIO_PORT, MOTOR_B_IN3_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(MOTOR_B_GPIO_PORT, MOTOR_B_IN4_PIN, GPIO_PIN_RESET);
break;
case 2:
HAL_GPIO_WritePin(MOTOR_B_GPIO_PORT, MOTOR_B_IN3_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(MOTOR_B_GPIO_PORT, MOTOR_B_IN4_PIN, GPIO_PIN_SET);
break;
default:
HAL_GPIO_WritePin(MOTOR_B_GPIO_PORT, MOTOR_B_IN3_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(MOTOR_B_GPIO_PORT, MOTOR_B_IN4_PIN, GPIO_PIN_RESET);
speed = 0;
break;
}
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, speed);
}
/**
* @brief 启动PWM输出
*/
void Motor_PWM_Start(void)
{
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);
}
3.3 定时器配置文件 tim.h / tim.c
c
// tim.h
#ifndef __TIM_H
#define __TIM_H
#include "stm32f4xx_hal.h"
extern TIM_HandleTypeDef htim2;
void MX_TIM2_Init(void);
#endif
// tim.c
#include "tim.h"
TIM_HandleTypeDef htim2;
void MX_TIM2_Init(void)
{
htim2.Instance = TIM2;
htim2.Init.Prescaler = 83;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 65535;
HAL_TIM_Base_Init(&htim2);
}
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{
if(tim_baseHandle->Instance==TIM2)
{
__HAL_RCC_TIM2_CLK_ENABLE();
}
}
🚀 四、代码使用说明(主函数调用)
4.1 初始化代码
在main.c中添加初始化代码:
c
#include "l298.h"
int main(void)
{
HAL_Init();
SystemClock_Config(); // 系统时钟配置
MX_GPIO_Init();
MX_TIM2_Init();
// 电机初始化
Motor_Init();
Tim2_PWM_Init();
Motor_PWM_Start(); // 启动PWM
while(1)
{
// 电机A:50%速度正转
MotorA_Set(500, 1);
// 电机B:80%速度反转
MotorB_Set(800, 2);
HAL_Delay(2000);
// 停止所有电机
MotorA_Set(0, 0);
MotorB_Set(0, 0);
HAL_Delay(1000);
}
}
4.2 函数参数说明
MotorA_Set(speed, dir)/MotorB_Set(speed, dir)speed:转速,范围0~999,数值越大转速越快dir:1=正转,2=反转,0=停止
❌ 五、常见问题与解决方案
| 问题现象 | 解决方案 |
|---|---|
| 电机不转 | 1. 检查共地 2. 检查PWM是否启动 3. 检查电源电压 |
| 电机抖动 | PWM占空比过低,提高初始速度 |
| 只有一个电机工作 | 检查对应引脚接线与通道配置 |
| 电机发热严重 | 降低PWM占空比,检查电机是否堵转 |
📝 六、总结
本文完整实现了STM32F407驱动L298N双电机的功能,核心知识点:
- L298N模块的硬件接线与共地要求
- STM32定时器PWM输出配置(TIM2_CH1/CH2)
- 双电机独立控制、正反转、无级调速逻辑
- 标准化驱动代码,可直接移植到其他STM32芯片
本代码适配智能小车、机器人等项目,修改引脚定义即可适配不同硬件平台,非常适合嵌入式学习与项目开发。
👨💻 作者简介
作者:嵌入式开发者,专注于STM32、机器人、嵌入式Linux开发
主页:@开发者-曼亿点
原创不易,欢迎点赞👍、收藏⭐、关注✅,持续更新嵌入式干货!