STM32 HAL库 L298N电机驱动模块实现

发布于:2025-04-18 ⋅ 阅读:(22) ⋅ 点赞:(0)

一、引言

在机器人、自动化设备等众多应用场景中,电机驱动是一个关键的部分。L298N 是一款常用的电机驱动模块,它可以驱动两个直流电机或一个步进电机。STM32F407 是一款高性能的 ARM Cortex-M4 内核微控制器,结合 HAL 库可以方便地实现对 L298N 电机驱动模块的控制。本文将详细介绍如何使用 STM32F407 的 HAL 库来实现对 L298N 电机驱动模块的控制。

二、L298N 电机驱动模块介绍

2.1 基本原理

L298N 是一种双 H 桥电机驱动芯片,H 桥电路是一种常用于控制直流电机正反转和调速的电路结构。通过控制 H 桥电路中各个开关管的导通和截止状态,可以改变电机两端的电压极性,从而实现电机的正反转。同时,通过调节输入信号的占空比,可以实现对电机转速的控制。

2.2 引脚说明

L298N 模块通常有以下主要引脚:

  • 电源引脚:包括电源输入(VCC)、接地(GND)和电机电源输入(+12V)。
  • 控制引脚:用于控制电机的正反转和调速,通常有 4 个输入引脚(IN1 - IN4)和 2 个使能引脚(ENA 和 ENB)。
  • 电机输出引脚:用于连接电机,有 4 个输出引脚(OUT1 - OUT4),分别对应两个电机。

三、STM32F407 开发环境搭建

3.1 硬件准备
  • STM32F407 开发板
  • L298N 电机驱动模块
  • 直流电机
  • 杜邦线若干
3.2 软件准备
  • Keil MDK-ARM 开发环境
  • STM32CubeMX 图形化配置工具
3.3 配置 STM32CubeMX
  1. 创建新项目:打开 STM32CubeMX,选择对应的 STM32F407 芯片型号,创建一个新的项目。
  2. 配置时钟:在 RCC 选项中,选择外部高速时钟(HSE),并配置系统时钟为 168MHz。
  3. 配置 GPIO:选择需要连接到 L298N 模块的 GPIO 引脚,将其配置为推挽输出模式。
  4. 配置定时器(可选):如果需要实现电机调速功能,可以配置一个定时器用于产生 PWM 信号。选择一个合适的定时器,配置其时钟源、预分频器和自动重载值,使其产生所需的 PWM 频率。
3.4 生成代码

完成配置后,点击 “Generate Code” 按钮,生成 Keil MDK 项目代码。

四、代码实现

4.1 初始化 GPIO 引脚

在生成的代码中,找到 main.c 文件,添加以下代码来初始化连接到 L298N 模块的 GPIO 引脚:

#include "main.h"
#include "stm32f4xx_hal.h"

// 定义连接到 L298N 模块的 GPIO 引脚
#define IN1_PIN GPIO_PIN_0
#define IN1_PORT GPIOA
#define IN2_PIN GPIO_PIN_1
#define IN2_PORT GPIOA
#define ENA_PIN GPIO_PIN_2
#define ENA_PORT GPIOA

void SystemClock_Config(void);
static void MX_GPIO_Init(void);

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();

  while (1)
  {
    // 主循环代码
  }
}

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** 初始化 RCC 振荡器 
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 8;
  RCC_OscInitStruct.PLL.PLLN = 336;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 7;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** 初始化 RCC 时钟 
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
  {
    Error_Handler();
  }
}

static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOA_CLK_ENABLE();

  /*Configure GPIO pins : IN1_PIN IN2_PIN ENA_PIN */
  GPIO_InitStruct.Pin = IN1_PIN|IN2_PIN|ENA_PIN;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

void Error_Handler(void)
{
  while(1)
  {
  }
}
4.2 控制电机正反转

在 main 函数的主循环中,添加以下代码来控制电机的正反转:

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();

  while (1)
  {
    // 电机正转
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_SET);
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(ENA_PORT, ENA_PIN, GPIO_PIN_SET);
    HAL_Delay(2000);

    // 电机反转
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_SET);
    HAL_GPIO_WritePin(ENA_PORT, ENA_PIN, GPIO_PIN_SET);
    HAL_Delay(2000);

    // 电机停止
    HAL_GPIO_WritePin(ENA_PORT, ENA_PIN, GPIO_PIN_RESET);
    HAL_Delay(2000);
  }
}
4.3 实现电机调速(使用 PWM)

如果需要实现电机调速功能,可以使用定时器产生 PWM 信号来控制电机的转速。以下是一个简单的示例代码:

#include "main.h"
#include "stm32f4xx_hal.h"

// 定义连接到 L298N 模块的 GPIO 引脚
// IN1 引脚用于控制电机正反转的逻辑输入,此处使用 GPIOA 的第 0 号引脚
#define IN1_PIN GPIO_PIN_0
// IN1 引脚所在的 GPIO 端口为 GPIOA
#define IN1_PORT GPIOA
// IN2 引脚用于控制电机正反转的逻辑输入,此处使用 GPIOA 的第 1 号引脚
#define IN2_PIN GPIO_PIN_1
// IN2 引脚所在的 GPIO 端口为 GPIOA
#define IN2_PORT GPIOA
// ENA 引脚用于使能电机,控制电机的启动和停止,此处使用 GPIOA 的第 2 号引脚
#define ENA_PIN GPIO_PIN_2
// ENA 引脚所在的 GPIO 端口为 GPIOA
#define ENA_PORT GPIOA

// 定义定时器句柄,用于操作 TIM3 定时器
TIM_HandleTypeDef htim3;

// 函数声明:配置系统时钟
void SystemClock_Config(void);
// 函数声明:初始化 GPIO 引脚
static void MX_GPIO_Init(void);
// 函数声明:初始化 TIM3 定时器
static void MX_TIM3_Init(void);

// 主函数,程序的入口点
int main(void)
{
  // 初始化 HAL 库,为后续使用 HAL 库函数做准备
  HAL_Init();
  // 配置系统时钟,使系统以合适的时钟频率运行
  SystemClock_Config();
  // 初始化连接到 L298N 模块的 GPIO 引脚
  MX_GPIO_Init();
  // 初始化 TIM3 定时器,用于产生 PWM 信号控制电机转速
  MX_TIM3_Init();

  // 启动 TIM3 定时器的通道 1 的 PWM 输出
  HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);

  // 主循环,程序将在此循环中不断执行
  while (1)
  {
    // 电机正转,不同转速

    // 设置 IN1 引脚为高电平,IN2 引脚为低电平,使电机正转
    HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_SET);
    HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_RESET);

    // 低转速
    // 设置 TIM3 定时器通道 1 的比较值为 200,从而改变 PWM 信号的占空比,实现低转速控制
    __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 200);
    // 延时 2000 毫秒,让电机以低转速运行一段时间
    HAL_Delay(2000);

    // 中转速
    // 设置 TIM3 定时器通道 1 的比较值为 500,改变 PWM 信号的占空比,实现中转速控制
    __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 500);
    // 延时 2000 毫秒,让电机以中转速运行一段时间
    HAL_Delay(2000);

    // 高转速
    // 设置 TIM3 定时器通道 1 的比较值为 800,改变 PWM 信号的占空比,实现高转速控制
    __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 800);
    // 延时 2000 毫秒,让电机以高转速运行一段时间
    HAL_Delay(2000);

    // 电机停止
    // 设置 ENA 引脚为低电平,使电机停止转动
    HAL_GPIO_WritePin(ENA_PORT, ENA_PIN, GPIO_PIN_RESET);
    // 延时 2000 毫秒,让电机停止一段时间
    HAL_Delay(2000);
  }
}

// 配置系统时钟的函数
void SystemClock_Config(void)
{
  // 定义 RCC 振荡器初始化结构体
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  // 定义 RCC 时钟初始化结构体
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** 初始化 RCC 振荡器 
  */
  // 设置振荡器类型为外部高速时钟(HSE)
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  // 使能外部高速时钟
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  // 使能 PLL(锁相环)
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  // 设置 PLL 的时钟源为外部高速时钟
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  // 设置 PLL 的 M 分频系数
  RCC_OscInitStruct.PLL.PLLM = 8;
  // 设置 PLL 的 N 倍频系数
  RCC_OscInitStruct.PLL.PLLN = 336;
  // 设置 PLL 的 P 分频系数
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  // 设置 PLL 的 Q 分频系数
  RCC_OscInitStruct.PLL.PLLQ = 7;
  // 配置 RCC 振荡器,如果配置失败则调用错误处理函数
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** 初始化 RCC 时钟 
  */
  // 设置要配置的时钟类型,包括 HCLK、SYSCLK、PCLK1 和 PCLK2
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  // 设置系统时钟源为 PLL 时钟
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  // 设置 AHB 总线时钟分频系数为 1
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  // 设置 APB1 总线时钟分频系数为 4
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  // 设置 APB2 总线时钟分频系数为 2
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

  // 配置 RCC 时钟,如果配置失败则调用错误处理函数
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
  {
    Error_Handler();
  }
}

// 初始化 TIM3 定时器的函数
static void MX_TIM3_Init(void)
{
  // 定义定时器时钟配置结构体
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  // 定义定时器主模式配置结构体
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  // 定义定时器输出比较配置结构体
  TIM_OC_InitTypeDef sConfigOC = {0};

  // 指定要初始化的定时器为 TIM3
  htim3.Instance = TIM3;
  // 设置定时器的预分频器值为 83,用于对时钟进行分频
  htim3.Init.Prescaler = 83;
  // 设置定时器的计数模式为向上计数
  htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
  // 设置定时器的自动重载值为 999,决定定时器的计数周期
  htim3.Init.Period = 999;
  // 设置定时器的时钟分频系数为 1
  htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  // 禁用自动重载预装载功能
  htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  // 初始化定时器的基本参数,如果初始化失败则调用错误处理函数
  if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
  {
    Error_Handler();
  }
  // 设置定时器的时钟源为内部时钟
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  // 配置定时器的时钟源,如果配置失败则调用错误处理函数
  if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  // 初始化定时器的 PWM 模式,如果初始化失败则调用错误处理函数
  if (HAL_TIM_PWM_Init(&htim3) != HAL_OK)
  {
    Error_Handler();
  }
  // 设置定时器的主输出触发为复位状态
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  // 禁用定时器的主从模式
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  // 配置定时器的主模式同步,如果配置失败则调用错误处理函数
  if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  // 设置定时器的输出比较模式为 PWM 模式 1
  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  // 设置定时器的脉冲值为 0,即初始占空比为 0
  sConfigOC.Pulse = 0;
  // 设置定时器输出比较的极性为高电平有效
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  // 禁用定时器的快速模式
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  // 配置定时器的 PWM 通道 1,如果配置失败则调用错误处理函数
  if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
  // 定时器的 MSP 后初始化函数,通常用于配置定时器的 GPIO 引脚等
  HAL_TIM_MspPostInit(&htim3);
}

// 初始化 GPIO 引脚的函数
static void MX_GPIO_Init(void)
{
  // 定义 GPIO 初始化结构体
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  // 使能 GPIOA 端口的时钟,以便可以对 GPIOA 端口的引脚进行操作
  __HAL_RCC_GPIOA_CLK_ENABLE();

  // 配置 GPIO 引脚:IN1_PIN、IN2_PIN 和 ENA_PIN
  // 指定要配置的引脚为 IN1_PIN、IN2_PIN 和 ENA_PIN
  GPIO_InitStruct.Pin = IN1_PIN|IN2_PIN|ENA_PIN;
  // 设置引脚的工作模式为推挽输出模式
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  // 不使用上拉或下拉电阻
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  // 设置引脚的输出速度为低频
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  // 初始化 GPIOA 端口的指定引脚
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

// 错误处理函数,当出现错误时,程序将进入此函数的无限循环
void Error_Handler(void)
{
  while(1)
  {
  }
}

五、代码解释

5.1 初始化 GPIO 引脚

在 MX_GPIO_Init 函数中,将连接到 L298N 模块的 GPIO 引脚配置为推挽输出模式。这样可以方便地控制引脚的电平状态,从而控制电机的正反转和调速。

5.2 控制电机正反转

通过设置 IN1 和 IN2 引脚的电平状态,可以控制电机的正反转。当 IN1 为高电平,IN2 为低电平时,电机正转;当 IN1 为低电平,IN2 为高电平时,电机反转。通过设置 ENA 引脚的电平状态,可以控制电机的启动和停止。

5.3 实现电机调速(使用 PWM)

使用定时器产生 PWM 信号来控制电机的转速。通过调整 PWM 信号的占空比,可以改变电机的平均电压,从而实现电机的调速。在代码中,使用 __HAL_TIM_SET_COMPARE 函数来设置 PWM 信号的占空比。

六、注意事项

  • 电源问题:L298N 模块需要外接电源,确保电源的电压和电流能够满足电机的需求。同时,要注意电源的极性,避免接反导致模块损坏。
  • 引脚连接:在连接 STM32F407 开发板和 L298N 模块时,要确保引脚连接正确,避免短路或引脚冲突。
  • 散热问题:L298N 模块在工作时会产生一定的热量,特别是在驱动大功率电机时,要注意散热,避免模块过热损坏。

七、总结

通过使用 STM32F407 的 HAL 库,我们可以方便地实现对 L298N 电机驱动模块的控制。本文详细介绍了 L298N 模块的基本原理、引脚说明,以及如何搭建 STM32F407 开发环境、配置 GPIO 引脚和定时器,最后给出了控制电机正反转和调速的代码示例。希望本文能够帮助你快速掌握使用 STM32F407 控制 L298N 电机驱动模块的方法。


网站公告

今日签到

点亮在社区的每一天
去签到