STM32 H-Bridge Motor Driver: Control DC Motor Direction

by Marwen Maghrebi

In this article, we will explore the STM32 H-Bridge Motor Driver, an effective solution for controlling DC motor direction.

Features of the STM32 H-Bridge Motor Driver Project

Things used in this project

Software apps and online services:

1- STMicroelectronics STM32CubeMX

2- STMicroelectronics STM32CubeIDE

3- Proteus 8

Introduction to H-Bridge DC Motor Control

An H-Bridge DC motor control circuit is a popular method for controlling the direction and operation of direct current (DC) motors. The design gets its name from the configuration of transistors that resemble the letter “H.” This circuit is particularly effective for medium-power motors, balancing performance with the limitations of the transistors used in the design.

How the H-Bridge Works:

The H-Bridge circuit allows a DC motor to rotate in both clockwise and counterclockwise directions. This is accomplished by controlling the voltage levels at two key points in the circuit labeled “Forward” and “Reverse / Backward.”

Operating Principles

  • Forward Motion:
    • When a high voltage is applied to the “Forward” input, transistor Q3 becomes saturated, allowing current to flow through it.
    • Concurrently, transistors Q1 and Q6 also saturate, facilitating the flow of current through the DC motor in one direction.
  • Reverse Motion:
    • When the “Reverse / Backward” input receives a high voltage, transistor Q7 enters the saturation region, while transistors Q5 and Q2 also activate.
    • This configuration allows the current to flow through the motor in the opposite direction, reversing its rotation.
How the H-Bridge Works in STM32 Motor Driver Circuit

Speed Control Options

While the H-Bridge circuit efficiently controls the direction of the motor, it does not directly manage the motor’s speed. To vary the speed, you can adjust the supply voltage or implement Pulse Width Modulation (PWM) techniques.

Interfacing H-Bridge DC Motor with STM32:

To effectively control an H-Bridge DC motor using an STM32 microcontroller, follow these guidelines to ensure optimal performance and protection.

Components and Connections

Start by gathering essential components, including the STM32 microcontroller, an H-Bridge driver (such as the L298N or discrete transistors), protection diodes, and resistors. Connect the STM32’s GPIO pins to the H-Bridge’s “Forward” and “Reverse” inputs to control motor direction. Ensure the H-Bridge and motor receive an adequate power supply. Additionally, integrate fast recovery diodes across the motor terminals to protect against back EMF.

Firmware Implementation

In your firmware, configure the GPIO pins as outputs and utilize Pulse Width Modulation (PWM) for speed control. This configuration allows efficient management of the motor’s direction and velocity, providing precise control over the H-Bridge DC motor’s operation in your projects.

To kickstart this project, we will control a DC motor using an STM32 microcontroller and an H-Bridge driver. We’ll use GPIO pins and TIM2 to generate PWM signals for motor control. The code will handle user input debouncing for start/stop functions and duty cycle adjustments. Additionally, we will calculate angular acceleration and send the motor’s status via UART for real-time feedback.

STM32CubeMX Configuration:

  • Open CubeMX & Create New Project Choose The Target MCU STM32F103C6 & Double-Click Its Name
  • Go To The Clock Configuration & Set The System Clock To 8MHz

Configuration for the TIMER Mode:

  • In the Categories tab, select the TIM2 then  Enable Internal Clock &  PWM Generation Channel 2
  • In the Counter settings tab, set the (Prescaler = 0 & Counter Peroid = 65535
  • In the PWM Generation Channel set ( Mode : PWM mode 1 & Pulse = 0 )

Configuration for the GPIO Mode:

  • Configure The GPIO Pins [PA4 , PA5] as Input Pins
  • Configure The GPIO Pins [PB2] as Input Pins

Configuration for the UART Mode:

  • Enable USART1 Module (Asynchronous Mode)
  • Set the USART1 communication parameters (baud rate = 115200, parity=NON, stop bits =1, and word length = 8bits)
  • Generate The Initialization Code & Open The Project In CubeIDE

STM32CubeIDE Configuration:

Initialization and Motor Control Functions

This section of the code includes necessary headers and initializes global variables for the motor control system. It defines functions to handle the motor’s start/stop and duty cycle changes.

  • HandleStartStop: This function manages the start/stop functionality of the motor using a button. It employs debouncing to ensure stable button press detection and updates the motor state. If the motor is running, it starts PWM using Timer2; otherwise, it stops the motor.
  • HandleDutyCycle: This function adjusts the motor’s duty cycle based on button presses. It increments the duty cycle value each time the button is pressed and updates the PWM signal accordingly.
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
#include<stdio.h>
#include<string.h>

/* Private define ------------------------------------------------------------*/
#define DEBOUNCE_DELAY 50 // ms

/* Private variables ---------------------------------------------------------*/
TIM_HandleTypeDef htim2;
UART_HandleTypeDef huart1;

uint8_t motorRunning = 0;
uint8_t currentDutyCycle = 0; // 0: 25%, 1: 50%, 2: 75%, 3: 99%
uint32_t lastDebounceTime = 0;
static uint8_t lastButtonState;

/* Function prototypes -----------------------------------------------------*/
void HandleStartStop(void);
void HandleDutyCycle(void);
void UpdatePWM(void);
void SendUartMessage(char* message);

/* Function to handle start/stop button */
void HandleStartStop(void) {
    static GPIO_PinState lastStartStopState = GPIO_PIN_SET; // Track previous state
    GPIO_PinState currentStartStopState = HAL_GPIO_ReadPin(START_STOP_GPIO_Port, START_STOP_Pin);

    if (lastStartStopState == GPIO_PIN_SET && currentStartStopState == GPIO_PIN_RESET) {
        uint32_t currentTime = HAL_GetTick();
        if (currentTime - lastDebounceTime > DEBOUNCE_DELAY) {
            motorRunning = !motorRunning;
            SendUartMessage(motorRunning ? "Motor Started\n\r" : "Motor Stopped\n\r");
            UpdatePWM();
            lastDebounceTime = currentTime;
        }
    }

    if (motorRunning) {
        HAL_GPIO_WritePin(DIR_GPIO_Port, DIR_Pin, GPIO_PIN_RESET); // Ensure DIR pin is set
        HAL_TIM_Base_Start(&htim2); // Start Timer2
        HAL_TIM_OC_Start(&htim2, TIM_CHANNEL_2); // Start PWM
    } else {
        HAL_TIM_Base_Stop(&htim2); // Stop Timer2
        HAL_TIM_OC_Stop(&htim2, TIM_CHANNEL_2); // Stop PWM
    }

    lastStartStopState = currentStartStopState; // Update last button state
}

/* Function to handle duty cycle button */
void HandleDutyCycle(void) {
    uint8_t currentButtonState = HAL_GPIO_ReadPin(DUTY_CYCLE_GPIO_Port, DUTY_CYCLE_Pin);

    if (currentButtonState != lastButtonState) {
        uint32_t currentTime = HAL_GetTick();
        if (currentTime - lastDebounceTime > DEBOUNCE_DELAY) {
            if (currentButtonState == GPIO_PIN_RESET) { // Button is pressed
                currentDutyCycle = (currentDutyCycle + 1) % 4;
                UpdatePWM();
            }
            lastDebounceTime = currentTime;
        }
    }

    lastButtonState = currentButtonState; // Update last button state
}

PWM Update and UART Communication

This part of the code focuses on updating the PWM signal and sending UART messages.

  • UpdatePWM: This function calculates the PWM value based on the current duty cycle and computes the corresponding angular acceleration. It sets the PWM output using Timer2 and sends a message via UART to indicate the current duty cycle and acceleration.
  • SendUartMessage: This utility function transmits a string message over UART, allowing real-time monitoring of the motor’s status and parameters.
void UpdatePWM(void) {
    if (motorRunning) {
        uint32_t pwmValue;
        char msg[100];
        float tau_max = 1.0; // Maximum torque in Nm (example value)
        float moment_of_inertia = 0.002; // Effective mass in kg·m²
        float angular_acceleration;

        switch (currentDutyCycle) {
            case 0:
                pwmValue = 65535 * 0.25; // 25% of 65535
                angular_acceleration = (0.25 * tau_max) / moment_of_inertia;
                sprintf(msg, "Duty Cycle: 25%%, Acceleration: %.2f rad/s^2\n\r", angular_acceleration);
                break;
            case 1:
                pwmValue = 65535 * 0.50; // 50% of 65535
                angular_acceleration = (0.50 * tau_max) / moment_of_inertia;
                sprintf(msg, "Duty Cycle: 50%%, Acceleration: %.2f rad/s^2\n\r", angular_acceleration);
                break;
            case 2:
                pwmValue = 65535 * 0.75; // 75% of 65535
                angular_acceleration = (0.75 * tau_max) / moment_of_inertia;
                sprintf(msg, "Duty Cycle: 75%%, Acceleration: %.2f rad/s^2\n\r", angular_acceleration);
                break;
            case 3:
                pwmValue = 65535 * 0.99; // 99% of 65535
                angular_acceleration = (0.99 * tau_max) / moment_of_inertia;
                sprintf(msg, "Duty Cycle: 99%%, Acceleration: %.2f rad/s^2\n\r", angular_acceleration);
                break;
            default:
                pwmValue = 0;
                sprintf(msg, "Duty Cycle: Error\n\r");
                break;
        }

        __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, pwmValue);
        SendUartMessage(msg); // Send duty cycle and acceleration via UART
    } else {
        __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, 0); // Set PWM to 0 if motor is not running
    }
}

// Function to send UART message
void SendUartMessage(char* message) {
    HAL_UART_Transmit(&huart1, (uint8_t*)message, strlen(message), HAL_MAX_DELAY);
}

main Function

The main function initializes the Hardware Abstraction Layer (HAL) and configures the system clock. It sets up GPIO, Timer2 for PWM, and UART for communication. After sending an initialization message, it enters an infinite loop, monitoring button presses to control the motor and adjust the PWM duty cycle, facilitating real-time feedback and control through UART.

int main(void)
{
    /* MCU Configuration--------------------------------------------------------*/
    HAL_Init(); // Initialize the Hardware Abstraction Layer

    /* Configure the system clock */
    SystemClock_Config(); // Set up the system clock

    /* Initialize all configured peripherals */
    MX_GPIO_Init(); // Initialize GPIO pins
    MX_TIM2_Init(); // Initialize Timer2 for PWM
    MX_USART1_UART_Init(); // Initialize UART for communication

    SendUartMessage("Initialization Completed \n\r"); // Notify that initialization is complete

    /* Infinite loop */
    while (1)
    {
        HandleStartStop(); // Check and handle start/stop button
        HandleDutyCycle(); // Check and handle duty cycle button
    }
}

Proteus Configuration :

  • Open Proteus & Create New Project and click next
  • Click on Pick Device
  • Search for STM32F103C6 & 2CS25487 & MOTOR-DC & BUTTON & RES & TIP32 & TIP32  
  • Click on Virtual Instruments Mode then choose (VIRTUAL TERMINAL & OSCILLOSCOPE)
  • Click on Terminal Mode then choose (DEFAULT & POWER &GROUND)
  • finally make the circuit below and start the simulation
Circuit Diagram for STM32 H-Bridge Motor Driver in Proteus Simulation

That’s all!

If you have any questions or suggestions don’t hesitate to leave a comment below

You Might Also Like

1 comment

STM32 L298N Motor Driver: Dual H-Bridge for DC Motors - The Embedded Things October 9, 2024 - 3:12 pm

[…] L298N is a dual H-Bridge motor driver module designed to control two DC motors simultaneously. It can handle voltages between 5V and 35V, […]

Reply

Leave a Comment


Are you sure want to unlock this post?
Unlock left : 0
Are you sure want to cancel subscription?
-
00:00
00:00
Update Required Flash plugin
-
00:00
00:00