2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
26 #if defined(USE_RANGEFINDER_HCSR04)
28 #include "common/time.h"
30 #include "drivers/exti.h"
31 #include "drivers/io.h"
32 #include "drivers/nvic.h"
33 #include "drivers/rcc.h"
34 #include "drivers/time.h"
36 #include "drivers/rangefinder/rangefinder.h"
38 #include "rangefinder_hcsr04.h"
40 #define HCSR04_MAX_RANGE_CM 400 // 4m, from HC-SR04 spec sheet
41 #define HCSR04_DETECTION_CONE_DECIDEGREES 300 // recommended cone angle30 degrees, from HC-SR04 spec sheet
42 #define HCSR04_DETECTION_CONE_EXTENDED_DECIDEGREES 450 // in practice 45 degrees seems to work well
44 /* HC-SR04 consists of ultrasonic transmitter, receiver, and control circuits.
45 * When triggered it sends out a series of 40KHz ultrasonic pulses and receives
46 * echo from an object. The distance between the unit and the object is calculated
47 * by measuring the traveling time of sound and output it as the width of a TTL pulse.
49 * *** Warning: HC-SR04 operates at +5V ***
53 static volatile timeDelta_t hcsr04SonarPulseTravelTime
= 0;
54 static volatile timeMs_t lastMeasurementReceivedAt
;
55 static int32_t lastCalculatedDistance
= RANGEFINDER_OUT_OF_RANGE
;
56 static timeMs_t lastMeasurementStartedAt
= 0;
58 static extiCallbackRec_t hcsr04_extiCallbackRec
;
61 static IO_t triggerIO
;
63 #if !defined(UNIT_TEST)
64 static void hcsr04_extiHandler(extiCallbackRec_t
* cb
)
68 static timeUs_t timing_start
;
70 const timeUs_t currentTimeUs
= microsISR();
72 if (IORead(echoIO
) != 0) {
73 timing_start
= currentTimeUs
;
75 const timeUs_t timing_stop
= currentTimeUs
;
76 if (timing_stop
> timing_start
) {
77 lastMeasurementReceivedAt
= currentTimeUs
/ 1000;
78 hcsr04SonarPulseTravelTime
= timing_stop
- timing_start
;
84 static void hcsr04_init(rangefinderDev_t
*dev
)
89 #define HCSR04_MINIMUM_FIRING_INTERVAL_MS 60
92 * Start a range reading
93 * Called periodically by the scheduler
94 * Measurement reading is done asynchronously, using interrupt
96 static void hcsr04_start_reading(void)
98 #if !defined(UNIT_TEST)
100 delayMicroseconds(11);
105 static void hcsr04_update(rangefinderDev_t
*dev
)
109 const timeMs_t timeNowMs
= millis();
111 // We should have a valid measurement within 60ms of trigger
112 if (lastMeasurementReceivedAt
> lastMeasurementStartedAt
&& (lastMeasurementReceivedAt
- lastMeasurementStartedAt
) <= HCSR04_MINIMUM_FIRING_INTERVAL_MS
) {
113 // The speed of sound is 340 m/s or approx. 29 microseconds per centimeter.
114 // The ping travels out and back, so to find the distance of the
115 // object we take half of the distance traveled.
116 // 340 m/s = 0.034 cm/microsecond = 29.41176471 *2 = 58.82352941 rounded to 59
118 lastCalculatedDistance
= hcsr04SonarPulseTravelTime
/ 59;
119 if (lastCalculatedDistance
> HCSR04_MAX_RANGE_CM
) {
120 lastCalculatedDistance
= RANGEFINDER_OUT_OF_RANGE
;
122 } else if ((lastMeasurementReceivedAt
- lastMeasurementStartedAt
) > HCSR04_MINIMUM_FIRING_INTERVAL_MS
) {
123 // No measurement within reasonable time - indicate failure
124 lastCalculatedDistance
= RANGEFINDER_HARDWARE_FAILURE
;
127 // the firing interval of the trigger signal should be greater than 60ms
128 // to avoid interference between consecutive measurements
129 if (timeNowMs
> lastMeasurementStartedAt
+ HCSR04_MINIMUM_FIRING_INTERVAL_MS
) {
130 // Trigger a new measurement
131 lastMeasurementStartedAt
= timeNowMs
;
132 hcsr04_start_reading();
137 * Get the distance that was measured by the last pulse, in centimeters.
139 static int32_t hcsr04_get_distance(rangefinderDev_t
*dev
)
143 const int32_t result
= lastCalculatedDistance
;
145 lastCalculatedDistance
= RANGEFINDER_NO_NEW_DATA
;
150 bool hcsr04Detect(rangefinderDev_t
*dev
, const sonarConfig_t
* rangefinderHardwarePins
)
152 bool detected
= false;
154 triggerIO
= IOGetByTag(rangefinderHardwarePins
->triggerTag
);
155 echoIO
= IOGetByTag(rangefinderHardwarePins
->echoTag
);
157 if (IOGetOwner(triggerIO
) || IOGetOwner(echoIO
)) {
162 IOInit(triggerIO
, OWNER_SONAR_TRIGGER
, 0);
163 IOConfigGPIO(triggerIO
, IOCFG_OUT_PP
);
168 IOInit(echoIO
, OWNER_SONAR_ECHO
, 0);
169 IOConfigGPIO(echoIO
, IOCFG_IN_FLOATING
);
171 // HC-SR04 echo line should be low by default and should return a response pulse when triggered
172 if (IORead(echoIO
) == false) {
173 for (int i
= 0; i
< 5 && !detected
; i
++) {
174 timeMs_t requestTime
= millis();
175 hcsr04_start_reading();
177 while ((millis() - requestTime
) < HCSR04_MINIMUM_FIRING_INTERVAL_MS
) {
178 if (IORead(echoIO
) == true) {
187 // Hardware detected - configure the driver
188 EXTIHandlerInit(&hcsr04_extiCallbackRec
, hcsr04_extiHandler
);
189 EXTIConfig(echoIO
, &hcsr04_extiCallbackRec
, NVIC_PRIO_SONAR_EXTI
, IOCFG_IN_FLOATING
, BETAFLIGHT_EXTI_TRIGGER_BOTH
); // TODO - priority!
193 dev
->maxRangeCm
= HCSR04_MAX_RANGE_CM
;
194 dev
->detectionConeDeciDegrees
= HCSR04_DETECTION_CONE_DECIDEGREES
;
195 dev
->detectionConeExtendedDeciDegrees
= HCSR04_DETECTION_CONE_EXTENDED_DECIDEGREES
;
197 dev
->init
= &hcsr04_init
;
198 dev
->update
= &hcsr04_update
;
199 dev
->read
= &hcsr04_get_distance
;
204 // Not detected - free resources
205 IORelease(triggerIO
);