HC-SR04 Ultrasonic Sensor: Integrating with STM32 Microcontrollers Using TIMER

Things used in this project

Software apps and online services

1- STMicroelectronics STM32CubeMX

2- STMicroelectronics STM32CubeIDE

3- Proteus 8

STM32 Integration: Enhancing Ultrasonic Sensing with Timer Functionality

In this project, we delve into the seamless integration of the HC-SR04 ultrasonic sensor with STM32 microcontrollers, leveraging the advanced Timer functionality offered by the STM32 series. By harnessing the capabilities of STM32’s Timer peripherals, developers can significantly enhance the accuracy and efficiency of distance measurement applications. This article explores the comprehensive process of integrating the HC-SR04 sensor with STM32 microcontrollers, focusing on optimizing sensor performance through precise Timer control.

HC-SR04 Pinout

The pin configuration of the HC-SR04 sensor encompasses VCC, Ground, Trigger, and Echo pins. VCC and Ground serve to power the sensor, requiring a 5-volt supply for VCC and grounding for stable operation. The Trigger pin, functioning as an input, initiates distance measurement upon receiving a 10 µs pulse. Conversely, the Echo pin, an output, generates pulses whose width corresponds to the distance between the sensor and the detected obstacle.

How the HC-SR04 Ultrasonic Distance Sensor Works?

The HC-SR04 Ultrasonic Distance Sensor emits 40,000 Hz ultrasound waves, which bounce back if obstructed, allowing distance calculation based on the speed of sound.

To initiate the ultrasound burst, the Trig pin is set to a High State for 10 µs. Subsequently, the Echo pin detects reflections and remains High until the echo is received or times out after 38ms.

Distance calculation is achieved using the formula Distance = (Speed x Time) / 2, where Time represents the duration the Echo pin remains High. This value, when multiplied by the speed of sound and divided by 2, yields the distance from the sensor to the object, enabling efficient obstacle detection in various applications.

To commence this project, we’ll initially activate TIMER1 in input capture direct mode. Following this, we’ll configure PA10 as the output TRIGER_PIN. Finally, we’ll establish UART communication to transmit both the ultrasonic value readings and system status.

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 16MHz

Configuration for the GPIO Mode:

  • Configure The GPIO Pins PB10 as Output Pin

Configuration for the TIMER Mode:

  • In the Categories tab, select the TIM1
  • set  Channel 2  to Input Capture direct mode
  • In the Parameter settings tab, set the (Prescaler=16-1 & Counter Peroid 65535

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
  • In the DMA settings tab, enable the DMA for the USART1_TX peripheral
  • Set Mode to Circular & Increment Adress to Memory & data width Byte.

STM32CubeIDE Configuration:

  • Write The Application Layer Code
  • main.c

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "string.h"

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define TRIG_PIN GPIO_PIN_10
#define TRIG_PORT GPIOA
/* USER CODE END PD */

/* Private variables ---------------------------------------------------------*/
TIM_HandleTypeDef htim1;

UART_HandleTypeDef huart1;
DMA_HandleTypeDef hdma_usart1_tx;

/* USER CODE BEGIN PV */
uint32_t IC_Val1 = 0;
uint32_t IC_Val2 = 0;
uint32_t Difference = 0;
uint8_t Is_First_Captured = 0;  // is the first value captured ?
uint8_t Distance  = 0;
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_TIM1_Init(void);
static void MX_USART1_UART_Init(void);

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2)  // if the interrupt source is channel1
    {
        if (Is_First_Captured==0) // if the first value is not captured
        {
            IC_Val1 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2); // read the first value
            Is_First_Captured = 1;  // set the first captured as true
            // Now change the polarity to falling edge
            __HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_2, TIM_INPUTCHANNELPOLARITY_FALLING);
        }

        else if (Is_First_Captured==1)   // if the first is already captured
        {
            IC_Val2 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2);  // read second value
            __HAL_TIM_SET_COUNTER(htim, 0);  // reset the counter

            if (IC_Val2 > IC_Val1)
            {
                Difference = IC_Val2-IC_Val1;
            }

            else if (IC_Val1 > IC_Val2)
            {
                Difference = (0xffff - IC_Val1) + IC_Val2;
            }

            Distance = Difference * (0.034/2);
            Is_First_Captured = 0; // set it back to false

            // set polarity to rising edge
            __HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_2, TIM_INPUTCHANNELPOLARITY_RISING);
            __HAL_TIM_DISABLE_IT(&htim1, TIM_IT_CC2);
        }
    }
}

void HCSR04_Read (void)
{
    HAL_GPIO_WritePin(TRIG_PORT, TRIG_PIN, GPIO_PIN_SET);  // pull the TRIG pin HIGH
    HAL_Delay(10);  // wait for 10 us
    HAL_GPIO_WritePin(TRIG_PORT, TRIG_PIN, GPIO_PIN_RESET);  // pull the TRIG pin low

    __HAL_TIM_ENABLE_IT(&htim1, TIM_IT_CC2);
}
/* USER CODE END 0 */

int main(void)
{
  /* USER CODE BEGIN 1 */
 char MSG[40];
  /* USER CODE END 1 */

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

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

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_TIM1_Init();
  MX_USART1_UART_Init();
  
  /* USER CODE BEGIN 2 */
  HAL_TIM_IC_Start_IT(&htim1, TIM_CHANNEL_2);
  /* USER CODE END 2 */

  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
      HCSR04_Read();

      sprintf(MSG,"Distance : %d cm\n\r",Distance);
      HAL_UART_Transmit(&huart1,(uint8_t*)MSG, strlen(MSG),100);
      HAL_Delay(500);
  }
  /* USER CODE END 3 */
}

Proteus Configuration :

  • Open Proteus & Create New Project and click next

  • Click on Pick Device
  • Search for STM32F103C6 & SRF04
  • Click on Virtual Instruments Mode then choose VIRTUAL TERMINAL & OSCILLOSCOPE
  • finally make the circuit below and start the simulation

That’s all!

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

Related posts

STM32-Driven LED Bar: Integration with 74HC595 Shift Register

How to Interface STM32 Microcontrollers with ADC128S102 via SPI

STM32 K-Type Thermocouple Amplification: Using AD8495