mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-11-30 03:26:13 +01:00
Update IO_HCSR04.h
Modify mode of measurement so that the driver doesn't loop for long periods waiting for the incoming pulse to complete. Original loop behaviour can be reinstated by adding LOOP option in create call (see comment header in file).
This commit is contained in:
parent
d67b07fe46
commit
5dd2770442
200
IO_HCSR04.h
200
IO_HCSR04.h
|
@ -30,7 +30,7 @@
|
||||||
*
|
*
|
||||||
* This driver polls the HC-SR04 by sending the trigger pulse and then measuring
|
* This driver polls the HC-SR04 by sending the trigger pulse and then measuring
|
||||||
* the length of the received pulse. If the calculated distance is less than
|
* the length of the received pulse. If the calculated distance is less than
|
||||||
* the threshold, the output state returned by a read() call changes to 1. If
|
* the threshold, the output _state returned by a read() call changes to 1. If
|
||||||
* the distance is greater than the threshold plus a hysteresis margin, the
|
* the distance is greater than the threshold plus a hysteresis margin, the
|
||||||
* output changes to 0. The device also supports readAnalogue(), which returns
|
* output changes to 0. The device also supports readAnalogue(), which returns
|
||||||
* the measured distance in cm, or 32767 if the distance exceeds the
|
* the measured distance in cm, or 32767 if the distance exceeds the
|
||||||
|
@ -48,6 +48,20 @@
|
||||||
* Note: The timing accuracy required for measuring the pulse length means that
|
* Note: The timing accuracy required for measuring the pulse length means that
|
||||||
* the pins have to be direct Arduino pins; GPIO pins on an IO Extender cannot
|
* the pins have to be direct Arduino pins; GPIO pins on an IO Extender cannot
|
||||||
* provide the required accuracy.
|
* provide the required accuracy.
|
||||||
|
*
|
||||||
|
* Example configuration:
|
||||||
|
* HCSR04::create(23000, 32, 33, 80, 85);
|
||||||
|
*
|
||||||
|
* Where 23000 is the VPIN allocated,
|
||||||
|
* 32 is the pin connected to the HCSR04 trigger terminal,
|
||||||
|
* 33 is the pin connected to the HCSR04 echo terminal,
|
||||||
|
* 80 is the distance in cm below which pin 23000 will be active,
|
||||||
|
* and 85 is the distance in cm above which pin 23000 will be inactive.
|
||||||
|
*
|
||||||
|
* Alternative configuration, which hogs the processor until the measurement is complete
|
||||||
|
* (old behaviour, more accurate but higher impact on other CS tasks):
|
||||||
|
* HCSR04::create(23000, 32, 33, 80, 85, HCSR04::LOOP);
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef IO_HCSR04_H
|
#ifndef IO_HCSR04_H
|
||||||
|
@ -61,38 +75,52 @@ private:
|
||||||
// pins must be arduino GPIO pins, not extender pins or HAL pins.
|
// pins must be arduino GPIO pins, not extender pins or HAL pins.
|
||||||
int _trigPin = -1;
|
int _trigPin = -1;
|
||||||
int _echoPin = -1;
|
int _echoPin = -1;
|
||||||
// Thresholds for setting active state in cm.
|
// Thresholds for setting active _state in cm.
|
||||||
uint8_t _onThreshold; // cm
|
uint8_t _onThreshold; // cm
|
||||||
uint8_t _offThreshold; // cm
|
uint8_t _offThreshold; // cm
|
||||||
// Last measured distance in cm.
|
// Last measured distance in cm.
|
||||||
uint16_t _distance;
|
uint16_t _distance;
|
||||||
// Active=1/inactive=0 state
|
// Active=1/inactive=0 _state
|
||||||
uint8_t _value = 0;
|
uint8_t _value = 0;
|
||||||
// Factor for calculating the distance (cm) from echo time (ms).
|
// Factor for calculating the distance (cm) from echo time (us).
|
||||||
// Based on a speed of sound of 345 metres/second.
|
// Based on a speed of sound of 345 metres/second.
|
||||||
const uint16_t factor = 58; // ms/cm
|
const uint16_t factor = 58; // us/cm
|
||||||
|
// Limit the time spent looping by dropping out when the expected
|
||||||
|
// worst case threshold value is greater than an arbitrary value.
|
||||||
|
const uint16_t maxPermittedLoopTime = 10 * factor; // max in us
|
||||||
|
unsigned long _startTime = 0;
|
||||||
|
unsigned long _maxTime = 0;
|
||||||
|
enum {DORMANT, MEASURING}; // _state values
|
||||||
|
uint8_t _state = DORMANT;
|
||||||
|
uint8_t _counter = 0;
|
||||||
|
uint16_t _options = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
enum Options {
|
||||||
|
LOOP = 1, // Option HCSR04::LOOP reinstates old behaviour, i.e. complete measurement in one loop entry.
|
||||||
|
};
|
||||||
|
|
||||||
// Static create function provides alternative way to create object
|
// Static create function provides alternative way to create object
|
||||||
static void create(VPIN vpin, int trigPin, int echoPin, uint16_t onThreshold, uint16_t offThreshold) {
|
static void create(VPIN vpin, int trigPin, int echoPin, uint16_t onThreshold, uint16_t offThreshold, uint16_t options = 0) {
|
||||||
if (checkNoOverlap(vpin))
|
if (checkNoOverlap(vpin))
|
||||||
new HCSR04(vpin, trigPin, echoPin, onThreshold, offThreshold);
|
new HCSR04(vpin, trigPin, echoPin, onThreshold, offThreshold, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Constructor perfroms static initialisation of the device object
|
// Constructor performs static initialisation of the device object
|
||||||
HCSR04 (VPIN vpin, int trigPin, int echoPin, uint16_t onThreshold, uint16_t offThreshold) {
|
HCSR04 (VPIN vpin, int trigPin, int echoPin, uint16_t onThreshold, uint16_t offThreshold, uint16_t options) {
|
||||||
_firstVpin = vpin;
|
_firstVpin = vpin;
|
||||||
_nPins = 1;
|
_nPins = 1;
|
||||||
_trigPin = trigPin;
|
_trigPin = trigPin;
|
||||||
_echoPin = echoPin;
|
_echoPin = echoPin;
|
||||||
_onThreshold = onThreshold;
|
_onThreshold = onThreshold;
|
||||||
_offThreshold = offThreshold;
|
_offThreshold = offThreshold;
|
||||||
|
_options = options;
|
||||||
addDevice(this);
|
addDevice(this);
|
||||||
}
|
}
|
||||||
// _begin function called to perform dynamic initialisation of the device
|
// _begin function called to perform dynamic initialisation of the device
|
||||||
void _begin() override {
|
void _begin() override {
|
||||||
|
_state = 0;
|
||||||
pinMode(_trigPin, OUTPUT);
|
pinMode(_trigPin, OUTPUT);
|
||||||
pinMode(_echoPin, INPUT);
|
pinMode(_echoPin, INPUT);
|
||||||
ArduinoPins::fastWriteDigital(_trigPin, 0);
|
ArduinoPins::fastWriteDigital(_trigPin, 0);
|
||||||
|
@ -112,11 +140,97 @@ protected:
|
||||||
return _distance;
|
return _distance;
|
||||||
}
|
}
|
||||||
|
|
||||||
// _loop function - read HC-SR04 once every 50 milliseconds.
|
// _loop function - read HC-SR04 once every 100 milliseconds.
|
||||||
void _loop(unsigned long currentMicros) override {
|
void _loop(unsigned long currentMicros) override {
|
||||||
read_HCSR04device();
|
unsigned long waitTime;
|
||||||
// Delay next loop entry until 50ms have elapsed.
|
switch(_state) {
|
||||||
delayUntil(currentMicros + 50000UL);
|
case DORMANT: // Issue pulse
|
||||||
|
// If receive pin is still set on from previous call, do nothing till next entry.
|
||||||
|
if (ArduinoPins::fastReadDigital(_echoPin)) return;
|
||||||
|
|
||||||
|
// Send 10us pulse to trigger transmitter
|
||||||
|
ArduinoPins::fastWriteDigital(_trigPin, 1);
|
||||||
|
delayMicroseconds(10);
|
||||||
|
ArduinoPins::fastWriteDigital(_trigPin, 0);
|
||||||
|
|
||||||
|
// Wait, with timeout, for echo pin to become set.
|
||||||
|
// Measured time delay is just under 500us, so
|
||||||
|
// wait for max of 1000us.
|
||||||
|
_startTime = micros();
|
||||||
|
_maxTime = 1000;
|
||||||
|
|
||||||
|
while (!ArduinoPins::fastReadDigital(_echoPin)) {
|
||||||
|
// Not set yet, see if we've timed out.
|
||||||
|
waitTime = micros() - _startTime;
|
||||||
|
if (waitTime > _maxTime) {
|
||||||
|
// Timeout waiting for pulse start, abort the read and start again
|
||||||
|
_state = DORMANT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Echo pulse started, so wait for echo pin to reset, and measure length of pulse
|
||||||
|
_startTime = micros();
|
||||||
|
_maxTime = factor * _offThreshold;
|
||||||
|
_state = MEASURING;
|
||||||
|
// If maximum measurement time is high, then skip until next loop entry before
|
||||||
|
// starting to look for pulse end.
|
||||||
|
// This gives better accuracy at shorter distance thresholds but without extending
|
||||||
|
// loop execution time for longer thresholds. If LOOP option is set on, then
|
||||||
|
// the entire measurement will be done in one loop entry, i.e. the code will fall
|
||||||
|
// through into the measuring phase.
|
||||||
|
if (!(_options & LOOP) && _maxTime > maxPermittedLoopTime) break;
|
||||||
|
/* fallthrough */
|
||||||
|
|
||||||
|
case MEASURING: // Check if echo pulse has finished
|
||||||
|
do {
|
||||||
|
waitTime = micros() - _startTime;
|
||||||
|
if (!ArduinoPins::fastReadDigital(_echoPin)) {
|
||||||
|
// Echo pulse completed; check if pulse length is below threshold and if so set value.
|
||||||
|
if (waitTime <= factor * _onThreshold) {
|
||||||
|
// Measured time is within the onThreshold, so value is one.
|
||||||
|
_value = 1;
|
||||||
|
// If the new distance value is less than the current, use it immediately.
|
||||||
|
// But if the new distance value is longer, then it may be erroneously long
|
||||||
|
// (because of extended loop times delays), so apply a delay to distance increases.
|
||||||
|
uint16_t estimatedDistance = waitTime / factor;
|
||||||
|
if (estimatedDistance < _distance)
|
||||||
|
_distance = estimatedDistance;
|
||||||
|
else
|
||||||
|
_distance += 1; // Just increase distance slowly.
|
||||||
|
_counter = 0;
|
||||||
|
//DIAG(F("HCSR04: Pulse Len=%l Distance=%d"), waitTime, _distance);
|
||||||
|
}
|
||||||
|
_state = DORMANT;
|
||||||
|
} else {
|
||||||
|
// Echo pulse hasn't finished, so check if maximum time has elapsed
|
||||||
|
// If pulse is too long then set return value to zero,
|
||||||
|
// and finish without waiting for end of pulse.
|
||||||
|
if (waitTime > _maxTime) {
|
||||||
|
// Pulse length longer than maxTime, value is provisionally zero.
|
||||||
|
// But don't change _value unless provisional value is zero for 10 consecutive measurements
|
||||||
|
if (_value == 1) {
|
||||||
|
if (++_counter >= 10) {
|
||||||
|
_value = 0;
|
||||||
|
_distance = 32767;
|
||||||
|
_counter = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_state = DORMANT; // start again
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If there's lots of time remaining before the expected completion time,
|
||||||
|
// then exit and wait for next loop entry. Otherwise, loop until we finish.
|
||||||
|
// If option LOOP is set, then we loop until finished anyway.
|
||||||
|
uint32_t remainingTime = _maxTime - waitTime;
|
||||||
|
if (!(_options & LOOP) && remainingTime < maxPermittedLoopTime) return;
|
||||||
|
} while (_state == MEASURING) ;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Datasheet recommends a wait of at least 60ms between measurement cycles
|
||||||
|
if (_state == DORMANT)
|
||||||
|
delayUntil(currentMicros+60000UL); // wait 60ms till next measurement
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _display() override {
|
void _display() override {
|
||||||
|
@ -124,66 +238,6 @@ protected:
|
||||||
_firstVpin, _trigPin, _echoPin, _onThreshold, _offThreshold);
|
_firstVpin, _trigPin, _echoPin, _onThreshold, _offThreshold);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
// This polls the HC-SR04 device by sending a pulse and measuring the duration of
|
|
||||||
// the pulse observed on the receive pin. In order to be kind to the rest of the CS
|
|
||||||
// software, no interrupts are used and interrupts are not disabled. The pulse duration
|
|
||||||
// is measured in a loop, using the micros() function. Therefore, interrupts from other
|
|
||||||
// sources may affect the result. However, interrupts response code in CS typically takes
|
|
||||||
// much less than the 58us frequency for the DCC interrupt, and 58us corresponds to only 1cm
|
|
||||||
// in the HC-SR04.
|
|
||||||
// To reduce chatter on the output, hysteresis is applied on reset: the output is set to 1 when the
|
|
||||||
// measured distance is less than the onThreshold, and is set to 0 if the measured distance is
|
|
||||||
// greater than the offThreshold.
|
|
||||||
//
|
|
||||||
void read_HCSR04device() {
|
|
||||||
// uint16 enough to time up to 65ms
|
|
||||||
uint16_t startTime, waitTime = 0, currentTime, maxTime;
|
|
||||||
|
|
||||||
// If receive pin is still set on from previous call, abort the read.
|
|
||||||
if (ArduinoPins::fastReadDigital(_echoPin))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Send 10us pulse to trigger transmitter
|
|
||||||
ArduinoPins::fastWriteDigital(_trigPin, 1);
|
|
||||||
delayMicroseconds(10);
|
|
||||||
ArduinoPins::fastWriteDigital(_trigPin, 0);
|
|
||||||
|
|
||||||
// Wait for receive pin to be set
|
|
||||||
startTime = currentTime = micros();
|
|
||||||
maxTime = factor * _offThreshold * 2;
|
|
||||||
while (!ArduinoPins::fastReadDigital(_echoPin)) {
|
|
||||||
// lastTime = currentTime;
|
|
||||||
currentTime = micros();
|
|
||||||
waitTime = currentTime - startTime;
|
|
||||||
if (waitTime > maxTime) {
|
|
||||||
// Timeout waiting for pulse start, abort the read
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for receive pin to reset, and measure length of pulse
|
|
||||||
startTime = currentTime = micros();
|
|
||||||
maxTime = factor * _offThreshold;
|
|
||||||
while (ArduinoPins::fastReadDigital(_echoPin)) {
|
|
||||||
currentTime = micros();
|
|
||||||
waitTime = currentTime - startTime;
|
|
||||||
// If pulse is too long then set return value to zero,
|
|
||||||
// and finish without waiting for end of pulse.
|
|
||||||
if (waitTime > maxTime) {
|
|
||||||
// Pulse length longer than maxTime, reset value.
|
|
||||||
_value = 0;
|
|
||||||
_distance = 32767;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Check if pulse length is below threshold, if so set value.
|
|
||||||
//DIAG(F("HCSR04: Pulse Len=%l Distance=%d"), waitTime, distance);
|
|
||||||
_distance = waitTime / factor; // in centimetres
|
|
||||||
if (_distance < _onThreshold)
|
|
||||||
_value = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //IO_HCSR04_H
|
#endif //IO_HCSR04_H
|
||||||
|
|
Loading…
Reference in New Issue
Block a user