The schematic for the HC-SR04 ultrasonic ranging module with connections to the PIC18F248 is given in the Schematics Section below. The ancillary circuitry for the PIC18F248 also includes connection via RS232 to a PC (enables PC control of the HC-SR04).
Power Supply
A typical "wall-wart" power-supply is used (a surplus laptop charger in this case) in conjunction with a voltage regulator (LM317T) to provide the regulated 5V required by the PIC microcontroller.
The HC-SR04 ultrasonic ranging module also requires 5V which was taken from the microcontroller supply. During testing it was found that initiating a ultrasonic measurement on the HC-SR04 caused sufficient current spike to reset the microcontroller (brown-out). A 10uF decoupling capacitor adjacent to the HC-SR04 ultrasonic ranging module power pins solved this.
Firmware/Software
The HC-SR04 ultrasonic ranging module requires a pulse on the 'trigger pin' to initiate an ultrasonic ranging measurement, with the actual range data being supplied in the form of a pulse width duration on the 'echo pin'.
Initiating a measurement simply involves pulsing the trigger pin high (5V) for 10us. This will cause the HC-SR04 to transmit a series of ultrasonic pulses, after which the echo pin will be held high corresponding to the length of time that was required for the ultrasonic echo to be received. Therefore, interfacing with the HC-SR04 simply requires determining the length of the 'echo' pulse, which is readily done using a suitable timer onboard a PIC microcontroller. Once the length of the echo pulse is determined, in conjunction with the known value for the speed of sound, the distance can be calculated.
Code snippet 1 below shows the basic firmware required (coded using CSS C). Timer1 of the PIC18F248 is used as this a 16-bit timer. Once the ultrasonic measurement is inititated (pulse the trigger pin high for 10us), the code waits for the echo pin to go high and then starts the PIC Timer1. The value of Timer1 is read when the echo pin again goes low.
The HC-SR04 datasheet takes the speed of sound in air as 340m/s. Since the time measurement of the ultrasonic pulse is of the returned ECHO (i.e., the distance measured is actually to the object in question AND then back again) the value needs to be divided by two (2). The value provided by Timer1 is a number of 'counts' or 'increments' of the clock which needs to be converted to a time. This depends upon the PIC oscillator frequency and value of the Timer1 prescaler used. Therefore the equation becomes:
D =
4/Fosc
* Prescaler * Timer1 * C * 100
cm/m
*
1/1,000,000
s/us
* 1/2
where: (note the units)
- D = distance (cm)
- Fosc = PIC oscillator frequency (MHz)
- C = speed of sound in air (m/s)
- Prescaler = Timer1 prescalar value, either 1,2,4 or 8
- Timer1 = value from Timer1 (0 to 65535)
Simplifying the above equation by combining the 'constants' on the right then gives (for C=340m/s, Fosc=10MHz, prescaler=2):
D (cm) = 0.0136 * Timer1
A issue with the code in Snippet 1 below is that WHILE statements polling the input status of the HC-SR04 echo pin can hang the code if the echo pin does not change status for some reason. This can be addressed by using interrupts (e.g. the 'interrupt on change' available on PORT B of the PIC18F248) or by incorporating additional logical check of the value of Timer1, which if exceeds a specific value, indicates no echo pulse has been received. Code Snippet 2 below incorporates this additional logic check to avoid the possibility of hanging the code if the HC-SR04 echo pin does not change state.
The HC-SR04 datasheet, at least the one I managed to find, does not actually specify what happens if no ultrasonic echo is received by the HC-SR04. The only specification is a minimum distance of 2cm and a maximum distance of 4m (400cm). Assuming a speed of sound of 340m/s this distance range equates to a minimum 'echo' of approximately 117us and a maximum of approximately 23.5ms (remembering a distance of 2cm means a 'round trip' from the HC-SR04 transmitter to the receiver, i.e. 4cm).
One reference (1) indicates that the HC-SR04 has a 38ms response that indicates 'infinity' (i.e. no echo return). However, I found the HC-SR04 modules I used 'timed out' after 200ms (I blocked the ultrasonic receiver and used Timer1 on the PIC to measure the HC-SR04 maximum 'echo' pulse width). In actual operation (see the Testing/Experimental Results Section) the 200ms 'no echo return' is not actually observed, but due to spurious multiple reflections and other interferences, 'random' pulse widths are returned if no suitable target is in range of the HC-SR04.
Code Snippet 2 incorporates including checking the current value of Timer1 when polling the status of the HC-SR04 echo pin. This then allows setting the maximum distance (i.e. time) that the HC-SR04 is expected to reliably detect in the particular situation in which it is installed. Consequently, this also prevents the code from 'hanging' if the HC-SR04 echo pin does not change state.
Since the example code avoids using interrupts, the polling of the state of the HC-SR04 echo pin to mark the start of the echo pulse width could also result in the code 'hanging' if the HC-SR04 fails at this stage for some reason. Another reference (2) indicated that instead of measuring the width of the echo pulse, the time from triggering the HC-SR04 to the start of the echo pulse could be used instead. This with the incorporation of a suitable 'time out' check would then avoid the possibility of the code hanging while polling the HC-SR04 echo pin, and also be quicker (as the code would not need to wait for the echo pulse to finish).
However, I found that the time from initiating a reading on the HC-SR04 (10us pulse on the trigger pin) to the start of the echo pulse was fixed at 450us. I don't think this is a pecularity of the units I used for testing. Reference (2) refers to the datasheet which states two formula's indicating the alternate methods of operation. However, the two formula's are actually equivalent (the "uS/58 = centimeters" is just a re-arrangement and simplification assuming the speed of sound is 340m/s) and the confusion is just the poorly phrased English (and or poor translation) in the datasheet.
Therefore, the code in Snippet 2 below incorporates a further check of Timer1 when polling the state of the HC-SR04 echo pin waiting for the start of the echo pulse.
The values used in the code Snippet 2 for checking Timer1 while polling the HC-SR04 echo pin state will depend upon the PIC oscillator frequency and Timer1 prescaler value. This also shows why a 16-bit timer is required. An oscillator frequency of 10MHz with prescaler 1 equates to 0.4us/timer increment, therefore for a 8-bit timer gives a maximum of 256*0.4us = 102.4us, which at a speed of sound of 340m/s equates to ~1.74cm (below the minimum range of the HC-SR04). Although a 8-bit timer perhaps could be used if a prescaler of suitable value is available (e.g the PIC18F248 Timer0, which can be used as 8 or 16 bit, allows a prescaler value up to 256).
Speed of Sound
The HC-SR04 datasheet assumes a speed of sound in air of 340m/s. The speed of sound in air actually depends upon air temperature, a 'little' with humidity but not with atmospheric pressure (3).
The approximate speed of sound in dry (0% humidity) air, in meters per second, at temperatures near 0oC, can be calculated by (4):
Cair = 331.3 + (0.606 * T) m/s
where:
- Cair = speed of sound in air
- T = air temperature degrees Celsius
This formula shows the 340m/s is approximately the speed of sound in air at a temperature of ~15oC. A value of 346m/s at 25o is better for use at my locality. Correcting for air temperature (via the use of a suitable sensor for example) is probably not warranted, as even at a distance of 1m the error introduced is in the order of a few millimeters. If such accuracy and or precision is required, the HC-SR04 is likely not suitable in any case.
PIC Microcontroller Interface Code
The following code example shows the simple interface required to control the HC-SR04 ultrasonic ranging module. The code enables control of the HC-SR04 ultrasonic ranging module via a connected PC (RS232 connection).
Code Snippet 1:
#include
#define HCSR04_echo PIN_C1
#define HCSR04_trig PIN_C0
unsigned int16 timerCount; //raw count from Timer1
float distance; //calculated distance in cm
void main() {
char recChar; //received character from serial port
setup_timer_1(T1_INTERNAL | T1_DIV_BY_2); //start timer1, 10Mhz Fosc = 0.8us/timer inc.
output_low(HCSR04_trig); //initialise trigger pin
do {
if (kbhit()) {
printf("start\r\n");
//trigger measurement
output_high(HCSR04_trig);
delay_us(10);
output_low(HCSR04_trig);
while (input(HCSR04_echo)==0); //wait for return echo pulse to start
set_timer1(0); //reset timer1 register to 0
while (input(HCSR04_echo)==1); //wait for return echo pulse to finish
timerCount = get_timer1();
// range (m) = pulse high time (sec) * speed sound (m/s)/2
distance = timerCount * 0.0136f; // 0.0136 = 0.8/1000000*(340*100/2)
printf("raw count = %Lu, distance= %3.1g cm\r\n",timerCount,distance);
delay_ms(100);
}
} while (TRUE);
}
Code Snippet 2:
#include
#define HCSR04_echo PIN_C1
#define HCSR04_trig PIN_C0
unsigned int16 timerCount; //raw count from Timer1
float distance; //calculated distance in cm
void main() {
char recChar; //received character from serial port
setup_timer_1(T1_INTERNAL | T1_DIV_BY_2); //start timer1, 10Mhz Fosc = 0.8us/timer inc.
output_low(HCSR04_trig); //initialise trigger pin
do {
if (kbhit()) {
printf("start\r\n");
output_high(HCSR04_trig);
delay_us(10);
output_low(HCSR04_trig);
set_timer1(0); //reset timer1 register to 0
while (input(HCSR04_echo)==0 && (get_timer1() < 565)); //wait for return echo pulse to start, 450uS/0.8uS per timer inc = 563
if (input(HCSR04_echo)==0) {
printf("error: echo pulse not detected\n\r");
} else {
set_timer1(0); //reset timer1 register to 0
// range (m) = pulse high time (sec) * speed sound (m/s)/2 speed sound 345m/s @ 22.5 celsius
while (input(HCSR04_echo)==1 && ((timerCount[readingCounter] = get_timer1()) < 1450)); //wait for return echo pulse to finish
timerCount[readingCounter] = get_timer1();
if (timerCount[readingCounter]<1450) { //1450 * 0.8/1000000*17250 = 20cm
distance[readingCounter] = (float)timerCount[readingCounter] * 0.0138f; // 0.0138 = 0.8/1000000*17250
printf("raw count = %Lu, distance= %3.1g\r\n",timerCount[readingCounter],distance[readingCounter]);
} else {
//out of range
printf("Out of range - greater 20cm\r\n");
}
}
delay_ms(100);
}
} while (TRUE);
}
Only Logged-In Members can add comments