HC-SR04
Cheap ultrasonic range finder
Hello World
Import programNucleo_UltrasonicHelloWorld
A hello world program for the HC-SR04
Library
Import libraryHC_SR04_Ultrasonic_Library
Works with interrupts
Datasheet
http://www.micropik.com/PDF/HCSR04.pdfNotes
The HC-SR04 is one of the lowest cost Sonar-based distance sensor options available (as low as $3 US). There seem to be several hardware versions and/or clones that work the same as far the functions of the two pins (trigger and echo). It operates off of 5V DC power at around 15 ma (not active is 2 ma). The detection range is 3-400 cm with around a 15 degree beam width. Like most Sonars, it more readily detects large hard objects that reflect sound, so it might not see something soft like a fluffy cat. It uses a short 40 kHz ultrasonic ping that humans can't hear and then listens for the ping signal to return after reflecting off an object. The time delay for the reflected signal to echo back is used to measure the distance using a simple scaling calculation based on the speed of sound in air.
There are two tranducers, one to transmit and one to receive. Two transducers turn out to be cheaper, since a higher voltage is needed to transmit and switching modes using only one transducer takes a lot of analog circuitry.
Small Sonar sensors are often used in robots to detect objects. Sometimes they are used along with an IR-based sensor to improve the likelihood of object detection. Some robots and other devices even add a mechanical bump or limit switch. To detect objects in different directions, some robots rotate sensors on a turret or use several sensors mounted facing out at different angles .
Electronics on the back of the Sparkfun HC-SR04 include an unmarked microprocessor? and a quad op amp. Other versions from other sources can have totally different hardware. The only HC-SR04 schematic available seems to have some different transmit parts and a different microcontroller.
Operation
As seen in the timing diagram above, only two signal pins are used. Trigger starts a measurement cycle and sends out a short ultrasonic pulse (eight cycles at 40Khz) and then listens for a reflected signal (echo). Several cycles at 40Khz are needed for the analog receiver circuit to detect the reflected signal. The width of the echo pulse output pin indicates distance. A hardware timer would typically be used to measure the echo pulse width. A simple divide operation can then scale the value to cm or inches, if needed.
The device should not be triggered again until waiting for the longest possible echo return time delay (maximum detection distance times speed of sound in air). This prevents any echos from the previous ultrasonic pulse interfering with the next pulse measurement or the new outgoing pulse being heard as the echo from the previous pulse. Something a bit over 10 measurements per second is typical on most small sonar sensors. The HC- SR04 data sheet suggests a min cycle time of 60 ms.
Hello World Demo
Wiring
mbed LPC1768 | HC-SR04 |
---|---|
Vu (5V) | Vcc |
Gnd | Gnd |
p6 | trig |
p7 | echo |
With my sensor and breadboard setup, an optional decoupling capacitor (10-100uf) across the power pins at the sensor helped reduce noise a tiny bit on measurements. This may also be a function of the noise on the host PC's 5V USB power supply.
Using the Timer
The simple approach is to use the mbed timer APIs to measure the width of the echo pulse using software polling to detect edges on the echo pulse. When echo goes high, start the timer and when echo goes low, stop the timer and read it. Software delay times to sample echo and start and stop the timer will be estimated using the timer and included in the timing calculations to obtain the distance measurement. On the LPC1768, the software delays of 3us did not have a significant effect (<1mm) on distance measurements.
#include "mbed.h" DigitalOut trigger(p6); DigitalOut myled(LED1); //monitor trigger DigitalOut myled2(LED2); //monitor echo DigitalIn echo(p7); int distance = 0; int correction = 0; Timer sonar; int main() { sonar.reset(); // measure actual software polling timer delays // delay used later in time correction // start timer sonar.start(); // min software polling delay to read echo pin while (echo==2) {}; myled2 = 0; // stop timer sonar.stop(); // read timer correction = sonar.read_us(); printf("Approximate software overhead timer delay is %d uS\n\r",correction); //Loop to read Sonar distance values, scale, and print while(1) { // trigger sonar to send a ping trigger = 1; myled = 1; myled2 = 0; sonar.reset(); wait_us(10.0); trigger = 0; myled = 0; //wait for echo high while (echo==0) {}; myled2=echo; //echo high, so start timer sonar.start(); //wait for echo low while (echo==1) {}; //stop timer and read value sonar.stop(); //subtract software overhead timer delay and scale to cm distance = (sonar.read_us()-correction)/58.0; myled2 = 0; printf(" %d cm \n\r",distance); //wait so that any echo(s) return before sending another ping wait(0.2); } }
This code works, but it eats up a lot of processor time taking measurements. Interrupts using the echo signal edges frees up processor time and would also provide a bit more accurate timing measurements. Last, everything should be setup in a class with a simple include file. Keeping the distance calculations in integer (and not float) mode would also speed up the code a bit.
Using the Timer and Interrrupts
The mbed API InterruptIn is often used in timer code. It can force a function to be called when an external I/O pin changes state from low-to-high (rise) and/or high-to-low (fall) using interrupts. This is a handy, efficient, and accurate way to start, stop and read timers using external signals.
Using interrupts to detect changes in the Sonar's echo pin along with the timer will free up processor time for other operations. There is an existing HC-SR04 library on the mbed component page for this device that uses timer interrupts to control the sensor. It sets up timers to measure the echo pulse with interrupts and uses another timer (with the mbed timeout API) to trigger continuous measurements automatically. A positive edge on echo interrupts and starts the timer, and a negative edge on echo interrupts and reads the timer. The main program is free to do other tasks and can periodically check the last sonar distance value and activate another function, if needed. Here is a demo version using the same pins (for the mbed LPC1768). Hopefully enough hardware timers are available on the mbed platform to use this approach in the application. Keep in mind the RTOS (if used) also needs one timer for time slice interrupts. There are other sonar sensors that do not require a timer, but they are a bit more expensive.
Import programLPC1768_HCSR04_HelloWorld
Sonar demo with timer interrupts
#include "mbed.h" #include "ultrasonic.h" void dist(int distance) { //put code here to execute when the distance has changed printf("Distance %d mm\r\n", distance); } ultrasonic mu(p6, p7, .1, 1, &dist); //Set the trigger pin to p6 and the echo pin to p7 //have updates every .1 seconds and a timeout after 1 //second, and call dist when the distance changes int main() { mu.startUpdates();//start measuring the distance while(1) { //Do something else here mu.checkDistance(); //call checkDistance() as much as possible, as this is where //the class checks if dist needs to be called. } }
This code puts the distance in mm (not cm). It divides by 6 (and not 5.8) to use faster integer operations. For most applications this would not matter, but a bit more accuracy on the absolute measurements in mm could be obtained by scaling the integer operation (and still avoiding slower float operations). So the line in the library file
_distance = (end - start)/6;
could be replaced by something like
_distance = (end-start);\\ _distance = (_distance)<<2+_distance)/29; // (distance*5)/29 is distance/5.8
You need to log in to post a discussion
Questions
5 years, 9 months ago
6 years, 5 months ago
7 years, 8 months ago