The HC-SR04 Ultrasonic sensor is a very popular low cost ultrasonics sensor used for non-contact distance measurements. The module comes with a separate transmitter and receiver. To determine the distance of an obstacle in front of the module, the transmitter (TRIGGER) automatically transmits eight 40Hz ultrasonic bursts/signals. The bursts hits the obstacle and bounces back to the module. When/if the ultrasonic signal gets back, the receiver (ECHO) pin automatically turns HIGH for a duration of the sum of time taken to send and receive the ultrasonic burst. The time is then multiplied by a constant to get the distance.
PINOUT :
Vcc – +5 VDC
Trig – Digital output pin
Echo – Digital input pin
Gnd – GND
Programming the HC-SR04
- Set the Trig pin of the ultrasonic module HIGH
- Wait for signal to bounce back (ECHO)
- Start Timer when ECHO is detected i.e. Echo is HIGH
- Stop Timer when ECHO pin goes LOW
- Read Timer Value.
- Convert value to distance.
Programming the HC-SR04
- Set the Trig pin of the ultrasonic module HIGH
- Wait for signal to bounce back (ECHO)
- Start Timer when ECHO is detected i.e. Echo is HIGH
- Stop Timer when ECHO pin goes LOW
- Read Timer Value.
- Convert value to distance.
Specification | Rating |
Working Voltage | DC 5V |
Working Current | 15mA |
Maximum Range | 4m |
Minimum Range | 2cm |
Working Frequency | 40Hz |
Trigger Signal | at least 40us |
How do you detect the ECHO ?
Remember from the pinout above, we have set the Echo pin as an input pin, so to detect the ECHO we can simply check whether the state of the input pin is HIGH or not.
In the circuit diagram above, we have connected the Echo pin to PB6 of our microcontroller. To detect if PB6 is HIGH and start a Timer:
- We can check with a simple logical comparison and then keep incrementing a counter variable while the condition is true
as simple as:
//Pseudo code while(PB6!= 0){ counter++ }
distance = counter * CONSTANT
And then converting the value of counter to distance by multiplying it to a constant. This method is straight-forward but not very precise.
2. The more precise and efficient way, which I highly recommend is by setting a General Purpose Timer to detect the rising and falling edges of PB6. That way, you can detect exactly when PB6 gets HIGH and when it goes LOW.
Getting Your Timers Right
To write a more robust piece of code to interface with the ultrasonic sensor, we need to set two Timers. If you refer back to the specifications of our HC-SR04 Ultrasonic sensor in the table above, you will find that it says “Trigger Signal – at least 10us long “. This means that when we set our Trig pin HIGH, we have to wait for 10 microseconds and then turn it back to LOW. To achieve this 10 microseconds delay, we have to set a Timer to create this precise delay for us. Lets call this Timer TIMER1. As we mentioned earlier, we need to detect the rising and falling edges of the Echo pin, we shall need another timer for this. Lets call this TIMER0.
The first thing you should know about Timers is that, they are directly affected by the running frequency of the microcontroller.
Lets say we running on a TM4C123xx Tiva LanchPad which by default runs at 16MHz. To set the microsecond delay Timer, after performing the necessary initialization requirements such as enabling the clock for the Timer block , selecting the bit option and selecting the running mode whether periodic vs. one-shot and down vs. up counter, you will have to load the number 16 minus 1 into the “Timer Interval Load Value Register”.
This is how we arrive at 16 :
16MHz literally means 16,000,000 clock cycles per second.
Remember , 1e-6 microseconds = 1 second
1 second = 16,000,000 clock cycles
1 microsecond = 1e-6 microseconds x 16,000,000 cycles
= 16
Therefore at 16 MHz, our microcontroller runs 16 clock cycles in 1 microsecond.
To learn more about Timers please take a look at the lessons on Timers.
This is what the delay_Microsecond function looks like :
Written in CMSIS standard,Implemented on Texas Instrument TM4C123 Tiva C
void delay_Microsecond(uint32_t time) { int i; SYSCTL->RCGCTIMER |=(1U<<1); TIMER1->CTL=0; TIMER1->CFG=0x04; TIMER1->TAMR=0x02; TIMER1->TAILR= 16-1; TIMER1->ICR =0x1; TIMER1->CTL |=0x01; for(i=0;i<time;i++) { while((TIMER1->RIS & 0x1)==0); TIMER1->ICR = 0x1; } }
Now we are in a position to create microsecond delays. To create a 10 microseconds delay as required by the Trig. pin, we simply need to call our delay_Microsecond function like this
delay_Microsecond(10)
Now lets take a look at how we are going to use TIMER0 to get the rising and falling edges of our Echo pin. Again, using our circuit diagram above as an example, we can see the the Echo pin is connected pin PB6 of our microcontroller.
In our function for initializing this Timer,we have set PB6 Alternate Function Select Register(AFSEL), set the appropriate PCTL of the pin (this can be found in the datasheet), set the bit option and finally set the running mode as both edges input capture mode. Again for details on the meaning and functions of these things, please take a look at the lessons on Timers .
This is what the initialization function looks like:
void Timer0_init(void) { SYSCTL->RCGCTIMER |=(1U<<0); SYSCTL->RCGCGPIO |=(1U<<1); GPIOB->=~ECHO; GPIOB->DEN |=ECHO; GPIOB->AFSEL |=ECHO; GPIOB->PCTL &=~0x0F000000; GPIOB->PCTL |= 0x07000000; TIMER0->CTL &=~1; TIMER0->CFG =4; TIMER0->TAMR = 0x17; TIMER0->CTL |=0x0C; TIMER0->CTL |=1; }
Measuring the distance of an obstacle
To measure the distance of an obstacle in front of the ultrasonic module, we have to:
- Set the Trig pin LOW , to just make sure its low
- Wait for about 10 microseconds for it to take effect
- Set the Trig pin HIGH
- Wait for at least 10 microseconds
- Set the Trig pin LOW
- Start Timer0
- Capture the time stamp of the rising edge
- Capture the time stamp of the falling edge
- Get the difference of the two time stamps
- Calculate distance in centimeters by :
time_difference * 62.5e-9*5882
This is what this piece of code looks like
const double _16MHz_1clock = 62.5e-9; /*Value of 1clock cycle in nanoseconds*/ const uint32_t MULTIPLIER = 5882; /*Derived from speed of sound*/ uint32_t measureD(void){ GPIOA->DATA &=~TRIGGER; delay_Microsecond(12); GPIOA->DATA |= TRIGGER; delay_Microsecond(12); GPIOA->DATA &=~TRIGGER; /*Capture firstEgde i.e. rising edge*/ TIMER0->ICR =4; while((TIMER0->RIS &4)==0){}; //Wait till captured highEdge = TIMER0->TAR; /*Capture secondEdge i.e. falling edge */ TIMER0->ICR =4; //cleaer timer capture flag while((TIMER0->RIS &4) ==0){}; lowEdge = TIMER0->AR; ddistance = lowEdge -highEdge; ddistance = _16MHz_1clock *(double) MULTIPLIER *(double)ddistance; return ddistance; }
FULL CODE:
Two pieces of program source code is presented below. The first piece of code gives a highly precise result however requires the enabling of two General Purpose Timers; one for creating microseconds delay and the other for capturing the rising and falling edges of the Echo pin.
The second piece of code is more straightforward in understanding, however, less precise compared to the first. It requires enabling just one General Purpose Timer- for creating microseconds delays.
Program 1
//Program: Interfacing HC-SR04 with Tiva C ARM Cortex M4 //Remarks: Less precise version #include "TM4C123.h" // Device header #include <stdint.h> #define MAX_TIME 7500 float measureD(void); uint32_t counter =0; float distance=0; #define ECHO (1U<<2) //PA2(INput) #define TRIGGER (1U<<4) //PA4(OUTPUT) #define BLUE_LED (1U<<2)//PF3 onboard Blue LED void delay_Microsecond(uint32_t time); float measureD(void){ GPIOA->DATA &=~TRIGGER; delay_Microsecond(10); GPIOA->DATA |= TRIGGER; delay_Microsecond(10); GPIOA->DATA &=~TRIGGER; counter =0; while((GPIOA->DATA &ECHO)==0) {} while(((GPIOA->DATA &ECHO )!=0) &(counter < MAX_TIME)) { counter++; delay_Microsecond(1); } distance = (float)counter*(float)0.0170000; return distance; } int main(void) { SYSCTL->RCGCGPIO |=(1U<<0); //Enable clock for PORTA SYSCTL->RCGCGPIO |=(1U<<5); //Enable clock for PORTF GPIOA->DIR =TRIGGER; GPIOF->DIR =BLUE_LED; GPIOA->DEN |=(ECHO)|(TRIGGER); GPIOF->DEN |= BLUE_LED; while(1){ measureD(); if(measureD() < 10.0){ GPIOF->DATA |=BLUE_LED; } else{ GPIOF->DATA &=~BLUE_LED; } delay_Microsecond(10); } } void delay_Microsecond(uint32_t time) { int i; SYSCTL->RCGCTIMER |=(1U<<1); TIMER1->CTL=0; TIMER1->CFG=0x04; TIMER1->TAMR=0x02; TIMER1->TAILR= 16-1; TIMER1->ICR =0x1; TIMER1->CTL |=0x01; for(i=0;i<time;i++){ while((TIMER1->RIS & 0x1)==0); TIMER1->ICR = 0x1; } }
Add Comment