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
45 /* HC-SR04 consists of ultrasonic transmitter, receiver, and control circuits.
46 * When triggered it sends out a series of 40KHz ultrasonic pulses and receives
47 * echo from an object. The distance between the unit and the object is calculated
48 * by measuring the traveling time of sound and output it as the width of a TTL pulse.
50 * *** Warning: HC-SR04 operates at +5V ***
54 static volatile timeDelta_t hcsr04SonarPulseTravelTime
= 0;
55 static volatile timeMs_t lastMeasurementReceivedAt
;
56 static int32_t lastCalculatedDistance
= RANGEFINDER_OUT_OF_RANGE
;
57 static timeMs_t lastMeasurementStartedAt
= 0;
59 static extiCallbackRec_t hcsr04_extiCallbackRec
;
62 static IO_t triggerIO
;
64 #if !defined(UNIT_TEST)
65 void hcsr04_extiHandler(extiCallbackRec_t
* cb
)
69 static timeUs_t timing_start
;
71 const timeUs_t currentTimeUs
= microsISR();
73 if (IORead(echoIO
) != 0) {
74 timing_start
= currentTimeUs
;
76 const timeUs_t timing_stop
= currentTimeUs
;
77 if (timing_stop
> timing_start
) {
78 lastMeasurementReceivedAt
= currentTimeUs
/ 1000;
79 hcsr04SonarPulseTravelTime
= timing_stop
- timing_start
;
85 void hcsr04_init(rangefinderDev_t
*dev
)
90 #define HCSR04_MINIMUM_FIRING_INTERVAL_MS 60
93 * Start a range reading
94 * Called periodically by the scheduler
95 * Measurement reading is done asynchronously, using interrupt
97 void hcsr04_start_reading(void)
99 #if !defined(UNIT_TEST)
101 delayMicroseconds(11);
106 void hcsr04_update(rangefinderDev_t
*dev
)
110 const timeMs_t timeNowMs
= millis();
112 // We should have a valid measurement within 60ms of trigger
113 if (lastMeasurementReceivedAt
> lastMeasurementStartedAt
&& (lastMeasurementReceivedAt
- lastMeasurementStartedAt
) <= HCSR04_MINIMUM_FIRING_INTERVAL_MS
) {
114 // The speed of sound is 340 m/s or approx. 29 microseconds per centimeter.
115 // The ping travels out and back, so to find the distance of the
116 // object we take half of the distance traveled.
117 // 340 m/s = 0.034 cm/microsecond = 29.41176471 *2 = 58.82352941 rounded to 59
119 lastCalculatedDistance
= hcsr04SonarPulseTravelTime
/ 59;
120 if (lastCalculatedDistance
> HCSR04_MAX_RANGE_CM
) {
121 lastCalculatedDistance
= RANGEFINDER_OUT_OF_RANGE
;
123 } else if ((lastMeasurementReceivedAt
- lastMeasurementStartedAt
) > HCSR04_MINIMUM_FIRING_INTERVAL_MS
) {
124 // No measurement within reasonable time - indicate failure
125 lastCalculatedDistance
= RANGEFINDER_HARDWARE_FAILURE
;
128 // the firing interval of the trigger signal should be greater than 60ms
129 // to avoid interference between consecutive measurements
130 if (timeNowMs
> lastMeasurementStartedAt
+ HCSR04_MINIMUM_FIRING_INTERVAL_MS
) {
131 // Trigger a new measurement
132 lastMeasurementStartedAt
= timeNowMs
;
133 hcsr04_start_reading();
138 * Get the distance that was measured by the last pulse, in centimeters.
140 int32_t hcsr04_get_distance(rangefinderDev_t
*dev
)
144 const int32_t result
= lastCalculatedDistance
;
146 lastCalculatedDistance
= RANGEFINDER_NO_NEW_DATA
;
151 bool hcsr04Detect(rangefinderDev_t
*dev
, const sonarConfig_t
* rangefinderHardwarePins
)
153 bool detected
= false;
155 triggerIO
= IOGetByTag(rangefinderHardwarePins
->triggerTag
);
156 echoIO
= IOGetByTag(rangefinderHardwarePins
->echoTag
);
158 if (IOGetOwner(triggerIO
) || IOGetOwner(echoIO
)) {
163 IOInit(triggerIO
, OWNER_SONAR_TRIGGER
, 0);
164 IOConfigGPIO(triggerIO
, IOCFG_OUT_PP
);
169 IOInit(echoIO
, OWNER_SONAR_ECHO
, 0);
170 IOConfigGPIO(echoIO
, IOCFG_IN_FLOATING
);
172 // HC-SR04 echo line should be low by default and should return a response pulse when triggered
173 if (IORead(echoIO
) == false) {
174 for (int i
= 0; i
< 5 && !detected
; i
++) {
175 timeMs_t requestTime
= millis();
176 hcsr04_start_reading();
178 while ((millis() - requestTime
) < HCSR04_MINIMUM_FIRING_INTERVAL_MS
) {
179 if (IORead(echoIO
) == true) {
188 // Hardware detected - configure the driver
189 EXTIHandlerInit(&hcsr04_extiCallbackRec
, hcsr04_extiHandler
);
190 EXTIConfig(echoIO
, &hcsr04_extiCallbackRec
, NVIC_PRIO_SONAR_EXTI
, IOCFG_IN_FLOATING
, BETAFLIGHT_EXTI_TRIGGER_BOTH
); // TODO - priority!
191 EXTIEnable(echoIO
, true);
194 dev
->maxRangeCm
= HCSR04_MAX_RANGE_CM
;
195 dev
->detectionConeDeciDegrees
= HCSR04_DETECTION_CONE_DECIDEGREES
;
196 dev
->detectionConeExtendedDeciDegrees
= HCSR04_DETECTION_CONE_EXTENDED_DECIDEGREES
;
198 dev
->init
= &hcsr04_init
;
199 dev
->update
= &hcsr04_update
;
200 dev
->read
= &hcsr04_get_distance
;
205 // Not detected - free resources
206 IORelease(triggerIO
);