HCSR04 Ultrasonic sensor and STM32

Ultrasonic ranging module HC – SR04 provides 2cm – 400cm non-contact measurement function, the ranging accuracy can reach to 3mm.
The modules includes ultrasonic transmitters, receiver and control circuit.

Today in this tutorial we are going to learn How to interface HCSR04 Ultrasonic sensor module with STM32.

hcsr04
hcsr04

WORKING

  • Working of hcsr04 is pretty simple and straight.
  • The module emits an ultrasound at 40 KHz which, after reflecting from obstacle, bounces back to the module.
  • By using the travel time and the speed of the sound, we can calculate the distance between the sensor and the obstacle.

According to the datasheet of hc-sr04, the following is required to be done :-

  • Keep the Trig pin HIGH for at least 10us
  • The Module will now send 8 cycle burst of ultrasound at 40 kHz and detect whether there is a pulse signal back
  • IF the signal returns, module will output a HIGH PULSE whose width will be proportional to the range of the object.
  • Distance can be calculated by using the following formula :- range = high level time * velocity (340m/s) / 2
  • We can also use uS / 58 = Distance in cm or uS / 148 = distance in inch
  • It is recommended to wait for at least 60ms before starting the operation again.

Method to use

As mentioned above, the module sends a HIGH signal proportional to the distance measured. This signal remains high for few microseconds. To measure the width of the signal in microseconds, I will use the Input Capture.
You can check the tutorial on Input capture Input Capture in STM32

hcsr04 timing


CubeMX Setup

HCSR04 timer setup
  • As shown above, I have selected the Input Capture Direct Mode.
  • Prescalar is set to 72 -> This will divide the APB clock by 72, and bring the timer clock to 1 MHz. It is necessary because the HCSR04 sends the signal in microseconds.
  • ARR is set to 0xffff -> I have selected is as maximum as possible. This basically sets the limit, upto which we can count in microseconds.
  • Also make sure the TIMx Capture Compare interrupt is enabled. You can enable the Global interrupt also, if you don’t see this option
HCSR04 pin setup
  • I2C 1 is selected -> To connect the LCD1602
  • Pin PA8 -> is automatically selected as TIM1_CH1 pin. I will use it as the ECHO Pin
  • Pin PA9 -> will be use as TRIG Pin, so select it as the output.





Some Insight into the CODE

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;

#define TRIG_PIN GPIO_PIN_9
#define TRIG_PORT GPIOA

// Let's write the callback function

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
	if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)  // 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_1); // 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_1, TIM_INPUTCHANNELPOLARITY_FALLING);
		}

		else if (Is_First_Captured==1)   // if the first is already captured
		{
			IC_Val2 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);  // 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 * .034/2;
			Is_First_Captured = 0; // set it back to false

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

In the Function above:-

  • First Timestamp is captured, when the rising edge is detected. The polarity is now set for the falling edge
  • Second Timestamp will be captured on the falling edge
  • Difference between the Timestamps will be calculated. This Difference will be microseconds, as the timer is running at 1 MHz
  • Based on the Difference value, the distance is calculated using the formula given in the datasheet
  • Finally, the Interrupt will be disabled, so that we don’t capture any unwanted signals.

void HCSR04_Read (void)
{
	HAL_GPIO_WritePin(TRIG_PORT, TRIG_PIN, GPIO_PIN_SET);  // pull the TRIG pin HIGH
	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_CC1);
}

HCSR04_Read-> Triggers the sensor to start the measurement.

  • It will pull the TRIG Pin HIGH for 10 microseconds, and then pull it LOW.
  • This will force the sensor to start the measurement, and the sensor will pull the ECHO Pin HIGH for the respective amount of time
  • To measure this time, we will enable the Timer interrupt, so that we can capture this Rising and falling edges


RESULT

HCSR04 Connection
hcsr04 working
hcsr04 working

Check out the Video Below










Info

You can help with the development by DONATING
To download the code, click DOWNLOAD button and view the Ad. The project will download after the Ad is finished.

34 Comments. Leave new

  • is that ok the fact that I can’t measure distances less than 8 cm? I use a US-015 which has the measure range of 2-400cm?

    Reply
  • I’m doing it with uart but i cannot display it through UART can you help me?
    void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
    {
    if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) // 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_1); // 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_1, TIM_INPUTCHANNELPOLARITY_FALLING);
    }

    else if (Is_First_Captured==1)  // if the first is already captured
    {
    IC_Val2 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); // 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 * .034/2;
    HAL_UART_Transmit_DMA(&huart4, (uint8_t *)&c, sizeof(c));
    Is_First_Captured = 0; // set it back to false

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

    Reply
  • Hi
    can you help me with using 3 sensors with this code?
    thank you so much

    Reply
  • can you explain for me ?

    else if (IC_Val2<IC_Val1)
    {
    T=(0xffff-IC_Val1)+IC_Val2;

    }
    why when IC_Val2<IC_Val1 then we have T= oxffff-Ic_VAl1)+IC_Val2 ?

    Reply
  • Thanks for the useful information!
    However, in your codes there is some error.

    Trigger pin should be 9 instead of 8.

    #define TRIG_PIN GPIO_PIN_8
    #define TRIG_PORT GPIOA
    
    Reply
  • I think it will be easier

    void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
    {
    if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) // if the interrupt source is channel1
    {
    if (Is_First_Captured==0) // if the first value is not captured
    {
    TIM2->CNT = 0;
    Is_First_Captured = 1; // set the first captured as true
    // Now change the polarity to falling edge
    __HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_FALLING);
    }
    else if (Is_First_Captured==1)  // if the first is already captured
    {
    Difference = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); // read second value

    Distance = Difference * .034/2;
    Is_First_Captured = 0; // set it back to false

    // set polarity to rising edge
    __HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_RISING);

    }
    }

    }

    Reply
  • Im using your code but it is geting stuck at the while loop of: local_time++
    The code just keep adding up on the local_time value.
    I made the Local_time an integer. if i make it anything else it wont add up and when i just use the code it tells me: error: ‘local_time’ undeclared

    help is much appreciated, any ideas?

    Reply
  • tips: Check voltage before using HC-SR04. I use stm32f103c8t6 connecting st-link(3.3V). I thought that the 5V pin should output 5V(in fact <3V) and connect HC-SR04 VCC to it. The result is that the return value of function hcsr04_read() is always small values(2, 3 etc). Use microUSB as power of stm32f103c8t6 instead, which make the 5V pin 5V.
    spend lots of time checking my code but finally a voltmeter solved the problem QAQ

    Reply
  • * return __HAL_TIM_GET_COUNTER(&htim1);

    Reply
  • sorry for my google translate english. Your code contains bugs and incorrectly measures echo time. I did a little with the oscilloscope and found a problem.
    Of course, not using interrupts is a bad option, but since the poll is done right.

    uint32_t hcsr04_read(void) {

    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); // pull the TRIG pin HIGH
    delay(2); // wait for 2 us

    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET); // pull the TRIG pin HIGH
    delay(10); // wait for 10 us
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); // pull the TRIG pin low

    // read the time for which the pin is high

    while (!HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_4))
    ; // wait for the ECHO pin to go high
    __HAL_TIM_SET_COUNTER(&htim1, 0);
    while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_4))
    ; // wait for the ECHO pin to go high
    return __HAL_TIM_GET_COUNTER(&htim2);
    }

    Reply
    • I don’t know why but there are a lot of complaints recently on this. when I wrote this, I got the results pretty accurate.
      Seems like it needs the modifications. I will try to use Input capture to measure the pulse width and then calculate the distance.
      Thanks for informing. I will update this soon

      Reply
      • stm32f103c8t6
        the error is approximately the same 1000ms (oscilloscope control)> 400ms according to stm32

        while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_4))    // while the pin is high
        	 {
        		local_time++;   // measure time for which the pin is high
        		delay
        
        void delay (uint32_t us)
        {
        	__HAL_TIM_SET_COUNTER(&htim1, 0);
        	while ((__HAL_TIM_GET_COUNTER(&htim1))<us);
        }
        

        there is too much overhead.

        Reply
  • can u share circuit diagram on proteus ?

    Reply
  • Hi. Greetings from Panama. I have tried to replicate the code to read 8 hc-sr04 sensors but the distance measurements are very inaccurate. Instead of measuring 60 cm, it gives readings of 20 cm and so … What could it be?

    Reply
    • Timing could be the issue. Check the microsecond delay part properly.

      Reply
      • Thanks for answering. I already checked the timer part. I followed the steps in the tutorial you posted about timers (How to create delay in nano / micro seconds using timers in stm32) and nothing changed. Curiously, when modifying the equation to transform from microseconds to centimeters, thus leaving it “microseconds * 0.034”, the distances are calculated correctly, but I don’t know why it is no longer necessary to divide by 2.

        Another query, I want to send the information of these 8 HC-SR04 sensors, an MPU6050 and two encoders through I2C communication to a raspberry pi 3b +, What tutorial of those you have published can serve as a guide? thanks for your help.

        Reply
  • Thank you

    Reply
  • Hi, file “stm32f4xx_hal.h” in zipfile is encrypted, could you upload correct version ?

    Reply
    • The file is fine. There is no encryption. It have a Drivers.7z file, which you need to extract. I did that to reduce the size.
      Although, if you are creating project from scratch, you don’t need that. Your IDE will create that for you.

      Reply
  • the sensor seems to work fine but it keeps on getting the answer 2!
    (for “local time’)
    and I cant see why .. I doubled checked the pin config like in the video and moreover when I disconnect the sensor at mid_run the “local time” var is reached zero and halt – witch means that 2 is actually the same answer it gets in any time .. any ideas?

    Reply
  • I could compiled the download source code and appeared the undefined errors about lcd_init,lcd_send_cmd,lcd_send_data and DWT_Delay_Init

    Reply
  • thanks for this toturial i need it

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.

keyboard_arrow_up

Adblocker detected! Please consider reading this notice.

We've detected that you are using AdBlock Plus or some other adblocking software which is preventing the page from fully loading.

We don't have any banner, Flash, animation, obnoxious sound, or popup ad. We do not implement these annoying types of ads!

We need money to operate the site, and almost all of it comes from our online advertising.

Please add controllerstech.com to your ad blocking whitelist or disable your adblocking software.

×