从零到一:基于STM32的L298N电机驱动与PWM调速实战

张开发
2026/4/15 22:55:38 15 分钟阅读

分享文章

从零到一:基于STM32的L298N电机驱动与PWM调速实战
1. 认识你的硬件伙伴STM32与L298N第一次拿到STM32开发板和L298N电机驱动模块时我就像拿到新玩具的孩子一样兴奋。但很快发现如果不懂它们的语言这些硬件根本不会听你指挥。让我们先来认识这两位主角STM32最小系统板就像机器的大脑它负责发出控制指令。我用的是一款常见的STM32F103C8T6核心板价格便宜但功能强大特别适合初学者。板子上那些密密麻麻的引脚中我们需要重点关注GPIO通用输入输出和定时器引脚它们将是控制电机的关键。L298N则是肌肉担当这个蓝色的小板子能承受最高46V电压和2A电流驱动我们的小电机绰绰有余。模块上有几个重要接口需要特别注意电源输入口VCC建议使用7-12V电源供电5V输出口可以给单片机供电但建议分开供电更稳定电机接口OUT1-OUT4接两个电机控制引脚IN1-IN4和ENA/ENB这里有个新手常踩的坑当使用不同电源给STM32和L298N供电时一定要记得把两者的GND连在一起否则控制信号无法形成回路。我就曾经因为忘记共地调试了半天发现电机纹丝不动。2. 硬件连接手把手教你接线现在让我们把大脑和肌肉连接起来。以控制单个直流电机为例你需要准备以下连线电源部分给L298N接上7-12V电源我用的是9V电池STM32通过USB或3.3V稳压电源供电用杜邦线连接两者的GND引脚信号控制部分选择STM32的PA0作为PWM输出连接ENAPA1和PA2作为方向控制连接IN1和IN2电机部分将电机两根线接到L298N的OUT1和OUT2实际接线时建议先用万用表确认没有短路。我第一次实验时因为线头裸露导致短路差点烧了芯片。另外如果电机功率较大建议在电源输入端加个电解电容滤波能有效减少电机启动时的电压波动。注意L298N模块上有两个使能跳线帽如果用PWM控制转速需要拔掉对应电机的跳线帽否则使能引脚始终是高电平。3. 三种控制模式的原理与实战3.1 基础版电平控制这是最简单的控制方式适合快速验证硬件是否正常工作。原理很简单通过IN1和IN2的高低电平组合控制电机转向。// GPIO初始化 void Motor_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStruct.GPIO_Pin GPIO_Pin_1 | GPIO_Pin_2; GPIO_InitStruct.GPIO_Mode GPIO_Mode_Out_PP; GPIO_InitStruct.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStruct); } // 正转函数 void Motor_Forward(void) { GPIO_SetBits(GPIOA, GPIO_Pin_1); GPIO_ResetBits(GPIOA, GPIO_Pin_2); } // 反转函数 void Motor_Backward(void) { GPIO_SetBits(GPIOA, GPIO_Pin_2); GPIO_ResetBits(GPIOA, GPIO_Pin_1); }这种方式的缺点是速度不可调而且直接切换方向会产生较大电流冲击。我在实验中发现如果频繁正反转切换最好在中间加个停止状态给电机一个缓冲时间。3.2 进阶版PWM调速控制想要精确控制转速就必须请出PWM脉冲宽度调制技术了。简单理解PWM就是通过快速开关来控制平均电压。占空比越大电机转速越快。配置STM32的定时器产生PWM需要几个步骤void PWM_Init(void) { // 1. 开启时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 2. 配置GPIO为复用推挽输出 GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.GPIO_Pin GPIO_Pin_1 | GPIO_Pin_2; GPIO_InitStruct.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStruct.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStruct); // 3. 定时器基础配置 TIM_TimeBaseInitTypeDef TIM_InitStruct; TIM_InternalClockConfig(TIM2); TIM_InitStruct.TIM_Period 100 - 1; // ARR值 TIM_InitStruct.TIM_Prescaler 72 - 1; // 分频值 TIM_InitStruct.TIM_ClockDivision TIM_CKD_DIV1; TIM_InitStruct.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, TIM_InitStruct); // 4. PWM模式配置 TIM_OCInitTypeDef TIM_OCInitStruct; TIM_OCStructInit(TIM_OCInitStruct); TIM_OCInitStruct.TIM_OCMode TIM_OCMode_PWM1; TIM_OCInitStruct.TIM_OutputState TIM_OutputState_Enable; TIM_OCInitStruct.TIM_OCPolarity TIM_OCPolarity_High; TIM_OC2Init(TIM2, TIM_OCInitStruct); // 通道2对应PA1 TIM_OC3Init(TIM2, TIM_OCInitStruct); // 通道3对应PA2 TIM_Cmd(TIM2, ENABLE); }这里TIM_Period和TIM_Prescaler的取值决定了PWM频率。我设置的是10kHz72MHz/(72*100)这个频率既能保证调速平稳又不会让驱动芯片过热。实际测试发现频率太低电机会有啸叫声太高则会导致L298N发热严重。3.3 终极版使能端PWM控制第三种方式是把PWM信号接到ENA/ENB使能端这种方式控制逻辑更简单代码也更容易维护void Motor_Control(int direction, uint8_t speed) { // 设置方向 if(direction FORWARD) { GPIO_SetBits(GPIOA, GPIO_Pin_1); GPIO_ResetBits(GPIOA, GPIO_Pin_2); } else { GPIO_SetBits(GPIOA, GPIO_Pin_2); GPIO_ResetBits(GPIOA, GPIO_Pin_1); } // 设置速度 TIM_SetCompare1(TIM2, speed); // PA0 PWM }这种方式的优点是方向控制和速度控制完全解耦在需要频繁变速的应用中特别有用。我在一个小车项目中使用这种方法实现了非常平滑的加速减速效果。4. 完整工程代码与调试技巧经过多次迭代我整理出一个稳定可靠的电机控制库主要包含以下功能电机初始化正反转控制无级调速刹车功能软启动/软停止调试时建议按照以下步骤先用万用表确认所有电源电压正常单独测试GPIO电平控制确保基础功能正常用示波器观察PWM波形是否符合预期逐步提高PWM占空比观察电机响应常见问题排查电机不转检查使能端、共地、电源电压转速不稳定尝试增加电源滤波电容驱动芯片发烫降低PWM频率或检查是否短路记得在代码中加入保护措施比如限制最大占空比、避免突然反转等。我曾经因为暴力测试烧毁过两个L298N模块这些都是血泪教训。

更多文章