1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2024-11-30 03:26:13 +01:00

HAL driver enhancements

Performance enhancements in IODevice::loop() function.
Improved error handling, device is placed off line if not responding.
Improved error reporting, device shown as offline if not operational (faulty or not present).
This commit is contained in:
Neil McKechnie 2021-09-21 11:02:23 +01:00
parent 08835e25c6
commit 302b16547e
8 changed files with 177 additions and 121 deletions

View File

@ -53,9 +53,7 @@ void IODevice::begin() {
MCP23017::create(180, 16, 0x21); MCP23017::create(180, 16, 0x21);
// Call the begin() methods of each configured device in turn // Call the begin() methods of each configured device in turn
unsigned long currentMicros = micros();
for (IODevice *dev=_firstDevice; dev!=NULL; dev = dev->_nextDevice) { for (IODevice *dev=_firstDevice; dev!=NULL; dev = dev->_nextDevice) {
dev->_nextEntryTime = currentMicros;
dev->_begin(); dev->_begin();
} }
_initPhase = false; _initPhase = false;
@ -69,18 +67,24 @@ void IODevice::begin() {
// doesn't need to invoke it. // doesn't need to invoke it.
void IODevice::loop() { void IODevice::loop() {
unsigned long currentMicros = micros(); unsigned long currentMicros = micros();
// Call every device's loop function in turn, one per entry.
if (!_nextLoopDevice) _nextLoopDevice = _firstDevice; IODevice *lastLoopDevice = _nextLoopDevice; // So we know when to stop...
// Check if device exists, and is due to run // Loop through devices until we find one ready to be serviced.
if (_nextLoopDevice /* && ((long)(currentMicros-_nextLoopDevice->_nextEntryTime) >= 0) */ ) { do {
// Move _nextEntryTime on, so that we can guarantee that the device will continue to if (!_nextLoopDevice) _nextLoopDevice = _firstDevice;
// be serviced if it doesn't update _nextEntryTime. if (_nextLoopDevice) {
_nextLoopDevice->_nextEntryTime = currentMicros; if (_nextLoopDevice->_deviceState != DEVSTATE_FAILED
// Invoke device's _loop function && ((long)(currentMicros - _nextLoopDevice->_nextEntryTime)) >= 0) {
_nextLoopDevice->_loop(currentMicros); // Found one ready to run, so invoke its _loop method.
// Move to next device. _nextLoopDevice->_nextEntryTime = currentMicros;
_nextLoopDevice = _nextLoopDevice->_nextDevice; _nextLoopDevice->_loop(currentMicros);
} _nextLoopDevice = _nextLoopDevice->_nextDevice;
break;
}
// Not this one, move to next one
_nextLoopDevice = _nextLoopDevice->_nextDevice;
}
} while (_nextLoopDevice != lastLoopDevice); // Stop looking when we've done all.
// Report loop time if diags enabled // Report loop time if diags enabled
#if defined(DIAG_LOOPTIMES) #if defined(DIAG_LOOPTIMES)
@ -127,7 +131,8 @@ bool IODevice::hasCallback(VPIN vpin) {
// Display (to diagnostics) details of the device. // Display (to diagnostics) details of the device.
void IODevice::_display() { void IODevice::_display() {
DIAG(F("Unknown device Vpins:%d-%d"), (int)_firstVpin, (int)_firstVpin+_nPins-1); DIAG(F("Unknown device Vpins:%d-%d %S"),
(int)_firstVpin, (int)_firstVpin+_nPins-1, _deviceState==DEVSTATE_FAILED ? F("OFFLINE") : F(""));
} }
// Find device associated with nominated Vpin and pass configuration values on to it. // Find device associated with nominated Vpin and pass configuration values on to it.
@ -151,13 +156,18 @@ void IODevice::write(VPIN vpin, int value) {
#endif #endif
} }
// Write analogue value to virtual pin(s). If multiple devices are allocated the same pin // Write analogue value to virtual pin(s). If multiple devices are allocated
// then only the first one found will be used. Duration is the time that the // the same pin then only the first one found will be used.
// operation is to be performed over (e.g. as an animation) in deciseconds (0-3276 sec) //
void IODevice::writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration) { // The significance of param1 and param2 may vary from device to device.
// For servo controllers, param1 is the profile of the transition and param2
// the duration, i.e. the time that the operation is to be animated over
// in deciseconds (0-3276 sec)
//
void IODevice::writeAnalogue(VPIN vpin, int value, uint8_t param1, uint16_t param2) {
IODevice *dev = findDevice(vpin); IODevice *dev = findDevice(vpin);
if (dev) { if (dev) {
dev->_writeAnalogue(vpin, value, profile, duration); dev->_writeAnalogue(vpin, value, param1, param2);
return; return;
} }
#ifdef DIAG_IO #ifdef DIAG_IO
@ -257,7 +267,7 @@ int IODevice::readAnalogue(VPIN vpin) {
return dev->_readAnalogue(vpin); return dev->_readAnalogue(vpin);
} }
#ifdef DIAG_IO #ifdef DIAG_IO
//DIAG(F("IODevice::readAnalogue(): Vpin %d not found!"), (int)vpin); DIAG(F("IODevice::readAnalogue(): Vpin %d not found!"), (int)vpin);
#endif #endif
return false; return false;
} }

View File

@ -163,7 +163,14 @@ public:
protected: protected:
// Method to perform initialisation of the device (optionally implemented within device class) // Constructor
IODevice(VPIN firstVpin=0, int nPins=0) {
_firstVpin = firstVpin;
_nPins = nPins;
_nextEntryTime = 0;
}
// Method to perform initialisation of the device (optionally implemented within device class)
virtual void _begin() {} virtual void _begin() {}
// Method to configure device (optionally implemented within device class) // Method to configure device (optionally implemented within device class)
@ -178,8 +185,8 @@ protected:
}; };
// Method to write an 'analogue' value (optionally implemented within device class) // Method to write an 'analogue' value (optionally implemented within device class)
virtual void _writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration) { virtual void _writeAnalogue(VPIN vpin, int value, uint8_t param1, uint16_t param2) {
(void)vpin; (void)value; (void) profile; (void)duration; (void)vpin; (void)value; (void) param1; (void)param2;
}; };
// Function called to check whether callback notification is supported by this pin. // Function called to check whether callback notification is supported by this pin.
@ -275,7 +282,7 @@ private:
// Device-specific write functions. // Device-specific write functions.
void _write(VPIN vpin, int value) override; void _write(VPIN vpin, int value) override;
void _writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration) override; void _writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration) override;
int _read(VPIN vpin) override; // returns the busy status of the device int _read(VPIN vpin) override; // returns the digital state or busy status of the device
void _loop(unsigned long currentMicros) override; void _loop(unsigned long currentMicros) override;
void updatePosition(uint8_t pin); void updatePosition(uint8_t pin);
void writeDevice(uint8_t pin, int value); void writeDevice(uint8_t pin, int value);
@ -302,7 +309,6 @@ private:
static const byte FLASH _bounceProfile[30]; static const byte FLASH _bounceProfile[30];
const unsigned int refreshInterval = 50; // refresh every 50ms const unsigned int refreshInterval = 50; // refresh every 50ms
unsigned long _lastRefreshTime; // last seen value of micros() count
// structures for setting up non-blocking writes to servo controller // structures for setting up non-blocking writes to servo controller
I2CRB requestBlock; I2CRB requestBlock;

View File

@ -78,48 +78,52 @@ private:
#endif #endif
} else { } else {
DIAG(F("ADS111x device not found, I2C:%x"), _i2cAddress); DIAG(F("ADS111x device not found, I2C:%x"), _i2cAddress);
_deviceState = DEVSTATE_FAILED;
} }
} }
void _loop(unsigned long currentMicros) override { void _loop(unsigned long currentMicros) override {
if (currentMicros - _lastMicros >= scanInterval) { // Check that previous non-blocking write has completed, if not then wait
// Check that previous non-blocking write has completed, if not then wait uint8_t status = _i2crb.wait();
uint8_t status = _i2crb.wait(); if (status == I2C_STATUS_OK) {
if (status == I2C_STATUS_OK) { // If _currentPin is in the valid range, continue reading the pin values
// If _currentPin is in the valid range, continue reading the pin values if (_currentPin < _nPins) {
if (_currentPin < _nPins) { _outBuffer[0] = 0x00; // Conversion register address
_outBuffer[0] = 0x00; // Conversion register address uint8_t status = I2CManager.read(_i2cAddress, _inBuffer, 2, _outBuffer, 1); // Read register
uint8_t status = I2CManager.read(_i2cAddress, _inBuffer, 2, _outBuffer, 1); // Read register if (status == I2C_STATUS_OK) {
if (status == I2C_STATUS_OK) { _value[_currentPin] = ((uint16_t)_inBuffer[0] << 8) + (uint16_t)_inBuffer[1];
_value[_currentPin] = ((uint16_t)_inBuffer[0] << 8) + (uint16_t)_inBuffer[1]; #ifdef IO_ANALOGUE_SLOW
#ifdef IO_ANALOGUE_SLOW DIAG(F("ADS111x pin:%d value:%d"), _currentPin, _value[_currentPin]);
DIAG(F("ADS111x pin:%d value:%d"), _currentPin, _value[_currentPin]); #endif
#endif
}
} }
if (status != I2C_STATUS_OK)
DIAG(F("ADS111x I2C:x%d Error:%d"), _i2cAddress, status);
} }
// Move to next pin
if (++_currentPin >= _nPins) _currentPin = 0;
// Configure ADC and multiplexer for next scan. See ADS111x datasheet for details
// of configuration register settings.
_outBuffer[0] = 0x01; // Config register address
_outBuffer[1] = 0xC0 + (_currentPin << 4); // Trigger single-shot, channel n
_outBuffer[2] = 0xA3; // 250 samples/sec, comparator off
// Write command, without waiting for completion.
I2CManager.write(_i2cAddress, _outBuffer, 3, &_i2crb);
_lastMicros = currentMicros;
} }
if (status != I2C_STATUS_OK) {
DIAG(F("ADS111x I2C:x%d Error:%d %S"), _i2cAddress, status, I2CManager.getErrorMessage(status));
_deviceState = DEVSTATE_FAILED;
}
// Move to next pin
if (++_currentPin >= _nPins) _currentPin = 0;
// Configure ADC and multiplexer for next scan. See ADS111x datasheet for details
// of configuration register settings.
_outBuffer[0] = 0x01; // Config register address
_outBuffer[1] = 0xC0 + (_currentPin << 4); // Trigger single-shot, channel n
_outBuffer[2] = 0xA3; // 250 samples/sec, comparator off
// Write command, without waiting for completion.
I2CManager.write(_i2cAddress, _outBuffer, 3, &_i2crb);
delayUntil(currentMicros + scanInterval);
} }
int _readAnalogue(VPIN vpin) override { int _readAnalogue(VPIN vpin) override {
int pin = vpin - _firstVpin; int pin = vpin - _firstVpin;
return _value[pin]; return _value[pin];
} }
void _display() override { void _display() override {
DIAG(F("ADS111x I2C:x%x Configured on Vpins:%d-%d"), _i2cAddress, _firstVpin, _firstVpin+_nPins-1); DIAG(F("ADS111x I2C:x%x Configured on Vpins:%d-%d %S"), _i2cAddress, _firstVpin, _firstVpin+_nPins-1,
_deviceState == DEVSTATE_FAILED ? F("OFFLINE") : F(""));
} }
// ADC conversion rate is 250SPS, or 4ms per conversion. Set the period between updates to 10ms. // ADC conversion rate is 250SPS, or 4ms per conversion. Set the period between updates to 10ms.
@ -134,7 +138,6 @@ private:
uint8_t _outBuffer[3]; uint8_t _outBuffer[3];
uint8_t _inBuffer[2]; uint8_t _inBuffer[2];
uint8_t _currentPin; // ADC pin currently being scanned uint8_t _currentPin; // ADC pin currently being scanned
unsigned long _lastMicros = 0;
I2CRB _i2crb; I2CRB _i2crb;
}; };

View File

@ -66,6 +66,7 @@ private:
HardwareSerial *_serial; HardwareSerial *_serial;
bool _playing = false; bool _playing = false;
uint8_t _inputIndex = 0; uint8_t _inputIndex = 0;
unsigned long _commandSendTime; // Allows timeout processing
public: public:
DFPlayer(VPIN firstVpin, int nPins, HardwareSerial &serial) { DFPlayer(VPIN firstVpin, int nPins, HardwareSerial &serial) {
@ -81,21 +82,32 @@ public:
protected: protected:
void _begin() override { void _begin() override {
_serial->begin(9600); _serial->begin(9600);
_display(); _deviceState = DEVSTATE_INITIALISING;
// Send a query to the device to see if it responds
sendPacket(0x42);
_commandSendTime = micros();
} }
void _loop(unsigned long) override { void _loop(unsigned long currentMicros) override {
// Check for incoming data on _serial, and update busy flag accordingly. // Check for incoming data on _serial, and update busy flag accordingly.
// Expected message is in the form "7F FF 06 3D xx xx xx xx xx EF" // Expected message is in the form "7F FF 06 3D xx xx xx xx xx EF"
while (_serial->available()) { while (_serial->available()) {
int c = _serial->read(); int c = _serial->read();
// DIAG(F("Received: %x"), c);
if (c == 0x7E) if (c == 0x7E)
_inputIndex = 1; _inputIndex = 1;
else if ((c==0xFF && _inputIndex==1) || (c==0x06 && _inputIndex==2) else if ((c==0xFF && _inputIndex==1)
|| (c==0x3D && _inputIndex==3) || (_inputIndex >=4 && _inputIndex <= 8)) || (c==0x3D && _inputIndex==3)
|| (_inputIndex >=4 && _inputIndex <= 8))
_inputIndex++; _inputIndex++;
else if (c==0xEF && _inputIndex==9) { else if (c==0x06 && _inputIndex==2) {
// Valid command prefix, so consider the device online.
_deviceState = DEVSTATE_NORMAL;
#ifdef DIAG_IO
_display();
#endif
_inputIndex++;
} else if (c==0xEF && _inputIndex==9) {
// End of play // End of play
#ifdef DIAG_IO #ifdef DIAG_IO
DIAG(F("DFPlayer: Finished")); DIAG(F("DFPlayer: Finished"));
@ -104,6 +116,12 @@ protected:
_inputIndex = 0; _inputIndex = 0;
} }
} }
// Check if the initial prompt to device has timed out. Allow 1 second
if (_deviceState == DEVSTATE_INITIALISING && currentMicros - _commandSendTime > 1000000UL) {
DIAG(F("DFPlayer device not responding on serial port"));
_deviceState = DEVSTATE_FAILED;
}
delayUntil(currentMicros + 10000); // Only enter every 10ms
} }
// Write with value 1 starts playing a song. The relative pin number is the file number. // Write with value 1 starts playing a song. The relative pin number is the file number.
@ -175,7 +193,8 @@ protected:
} }
void _display() override { void _display() override {
DIAG(F("DFPlayer Configured on Vpins:%d-%d"), _firstVpin, _firstVpin+_nPins-1); DIAG(F("DFPlayer Configured on Vpins:%d-%d %S"), _firstVpin, _firstVpin+_nPins-1,
(_deviceState==DEVSTATE_FAILED) ? F("OFFLINE") : F(""));
} }
private: private:

View File

@ -59,7 +59,6 @@ protected:
T _portPullup; T _portPullup;
// Interval between refreshes of each input port // Interval between refreshes of each input port
static const int _portTickTime = 4000; static const int _portTickTime = 4000;
unsigned long _lastLoopEntry = 0;
// Virtual functions for interfacing with I2C GPIO Device // Virtual functions for interfacing with I2C GPIO Device
virtual void _writeGpioPort() = 0; virtual void _writeGpioPort() = 0;
@ -105,10 +104,12 @@ void GPIOBase<T>::_begin() {
_portMode = 0; // default to input mode _portMode = 0; // default to input mode
_portPullup = -1; // default to pullup enabled _portPullup = -1; // default to pullup enabled
_portInputState = -1; _portInputState = -1;
_setupDevice();
_deviceState = DEVSTATE_NORMAL;
} else {
DIAG(F("%S I2C:x%x Device not detected"), _deviceName, _I2CAddress);
_deviceState = DEVSTATE_FAILED;
} }
_setupDevice();
_deviceState = DEVSTATE_NORMAL;
_lastLoopEntry = micros();
} }
// Configuration parameters for inputs: // Configuration parameters for inputs:
@ -172,27 +173,25 @@ void GPIOBase<T>::_loop(unsigned long currentMicros) {
#endif #endif
} }
// Check if interrupt configured. If so, and pin is not pulled down, finish. // Check if interrupt configured. If not, or if it is active (pulled down), then
if (_gpioInterruptPin >= 0) { // initiate a scan.
if (digitalRead(_gpioInterruptPin)) return; if (_gpioInterruptPin < 0 || !digitalRead(_gpioInterruptPin)) {
} else // TODO: Could suppress reads if there are no pins configured as inputs!
// No interrupt pin. Check if tick has elapsed. If not, finish.
if (currentMicros - _lastLoopEntry < (unsigned long)_portTickTime) return;
// TODO: Could suppress reads if there are no pins configured as inputs! // Read input
if (_deviceState == DEVSTATE_NORMAL) {
// Read input _readGpioPort(false); // Initiate non-blocking read
_lastLoopEntry = currentMicros; _deviceState= DEVSTATE_SCANNING;
if (_deviceState == DEVSTATE_NORMAL) { }
_readGpioPort(false); // Initiate non-blocking read
_deviceState= DEVSTATE_SCANNING;
} }
// Delay next entry until tick elapsed.
delayUntil(currentMicros + _portTickTime);
} }
template <class T> template <class T>
void GPIOBase<T>::_display() { void GPIOBase<T>::_display() {
DIAG(F("%S I2C:x%x Configured on Vpins:%d-%d"), _deviceName, _I2CAddress, DIAG(F("%S I2C:x%x Configured on Vpins:%d-%d %S"), _deviceName, _I2CAddress,
_firstVpin, _firstVpin+_nPins-1); _firstVpin, _firstVpin+_nPins-1, (_deviceState==DEVSTATE_FAILED) ? F("OFFLINE") : F(""));
} }
template <class T> template <class T>

View File

@ -68,8 +68,6 @@ private:
uint16_t _distance; uint16_t _distance;
// Active=1/inactive=0 state // Active=1/inactive=0 state
uint8_t _value = 0; uint8_t _value = 0;
// Time of last loop execution
unsigned long _lastExecutionTime;
// Factor for calculating the distance (cm) from echo time (ms). // Factor for calculating the distance (cm) from echo time (ms).
// 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; // ms/cm
@ -97,7 +95,6 @@ protected:
pinMode(_trigPin, OUTPUT); pinMode(_trigPin, OUTPUT);
pinMode(_echoPin, INPUT); pinMode(_echoPin, INPUT);
ArduinoPins::fastWriteDigital(_trigPin, 0); ArduinoPins::fastWriteDigital(_trigPin, 0);
_lastExecutionTime = micros();
#if defined(DIAG_IO) #if defined(DIAG_IO)
_display(); _display();
#endif #endif
@ -116,13 +113,9 @@ protected:
// _loop function - read HC-SR04 once every 50 milliseconds. // _loop function - read HC-SR04 once every 50 milliseconds.
void _loop(unsigned long currentMicros) override { void _loop(unsigned long currentMicros) override {
if (currentMicros - _lastExecutionTime > 50000UL) { read_HCSR04device();
_lastExecutionTime = currentMicros; // Delay next loop entry until 50ms have elapsed.
delayUntil(currentMicros + 50000UL);
read_HCSR04device();
// Delay next loop entry until 50ms have elapsed.
//delayUntil(currentMicros + 50000UL);
}
} }
void _display() override { void _display() override {

View File

@ -107,12 +107,14 @@ void PCA9685::_begin() {
#if defined(DIAG_IO) #if defined(DIAG_IO)
_display(); _display();
#endif #endif
} } else
_deviceState = DEVSTATE_FAILED;
} }
// Device-specific write function, invoked from IODevice::write(). // Device-specific write function, invoked from IODevice::write().
// For this function, the configured profile is used. // For this function, the configured profile is used.
void PCA9685::_write(VPIN vpin, int value) { void PCA9685::_write(VPIN vpin, int value) {
if (_deviceState == DEVSTATE_FAILED) return;
#ifdef DIAG_IO #ifdef DIAG_IO
DIAG(F("PCA9685 Write Vpin:%d Value:%d"), vpin, value); DIAG(F("PCA9685 Write Vpin:%d Value:%d"), vpin, value);
#endif #endif
@ -137,6 +139,7 @@ void PCA9685::_write(VPIN vpin, int value) {
// 4 (Bounce) Servo 'bounces' at extremes. // 4 (Bounce) Servo 'bounces' at extremes.
// //
void PCA9685::_writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration) { void PCA9685::_writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration) {
if (_deviceState == DEVSTATE_FAILED) return;
#ifdef DIAG_IO #ifdef DIAG_IO
DIAG(F("PCA9685 WriteAnalogue Vpin:%d Value:%d Profile:%d Duration:%d"), DIAG(F("PCA9685 WriteAnalogue Vpin:%d Value:%d Profile:%d Duration:%d"),
vpin, value, profile, duration); vpin, value, profile, duration);
@ -172,6 +175,7 @@ void PCA9685::_writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t dur
// _read returns true if the device is currently in executing an animation, // _read returns true if the device is currently in executing an animation,
// changing the output over a period of time. // changing the output over a period of time.
int PCA9685::_read(VPIN vpin) { int PCA9685::_read(VPIN vpin) {
if (_deviceState == DEVSTATE_FAILED) return 0;
int pin = vpin - _firstVpin; int pin = vpin - _firstVpin;
struct ServoData *s = _servoData[pin]; struct ServoData *s = _servoData[pin];
if (s == NULL) if (s == NULL)
@ -181,12 +185,10 @@ int PCA9685::_read(VPIN vpin) {
} }
void PCA9685::_loop(unsigned long currentMicros) { void PCA9685::_loop(unsigned long currentMicros) {
if (currentMicros - _lastRefreshTime >= refreshInterval * 1000) { for (int pin=0; pin<_nPins; pin++) {
for (int pin=0; pin<_nPins; pin++) { updatePosition(pin);
updatePosition(pin);
}
_lastRefreshTime = currentMicros;
} }
delayUntil(currentMicros + refreshInterval * 1000UL);
} }
// Private function to reposition servo // Private function to reposition servo
@ -238,20 +240,25 @@ void PCA9685::writeDevice(uint8_t pin, int value) {
DIAG(F("PCA9685 I2C:x%x WriteDevice Pin:%d Value:%d"), _I2CAddress, pin, value); DIAG(F("PCA9685 I2C:x%x WriteDevice Pin:%d Value:%d"), _I2CAddress, pin, value);
#endif #endif
// Wait for previous request to complete // Wait for previous request to complete
requestBlock.wait(); uint8_t status = requestBlock.wait();
// Set up new request. if (status != I2C_STATUS_OK) {
outputBuffer[0] = PCA9685_FIRST_SERVO + 4 * pin; _deviceState = DEVSTATE_FAILED;
outputBuffer[1] = 0; DIAG(F("PCA9685 I2C:x%x failed %S"), _I2CAddress, I2CManager.getErrorMessage(status));
outputBuffer[2] = (value == 4095 ? 0x10 : 0); // 4095=full on } else {
outputBuffer[3] = value & 0xff; // Set up new request.
outputBuffer[4] = value >> 8; outputBuffer[0] = PCA9685_FIRST_SERVO + 4 * pin;
I2CManager.queueRequest(&requestBlock); outputBuffer[1] = 0;
outputBuffer[2] = (value == 4095 ? 0x10 : 0); // 4095=full on
outputBuffer[3] = value & 0xff;
outputBuffer[4] = value >> 8;
I2CManager.queueRequest(&requestBlock);
}
} }
// Display details of this device. // Display details of this device.
void PCA9685::_display() { void PCA9685::_display() {
DIAG(F("PCA9685 I2C:x%x Configured on Vpins:%d-%d"), _I2CAddress, (int)_firstVpin, DIAG(F("PCA9685 I2C:x%x Configured on Vpins:%d-%d %S"), _I2CAddress, (int)_firstVpin,
(int)_firstVpin+_nPins-1); (int)_firstVpin+_nPins-1, (_deviceState==DEVSTATE_FAILED) ? F("OFFLINE") : F(""));
} }
// Internal helper function for this device // Internal helper function for this device

View File

@ -44,7 +44,11 @@
* all VL53L0X modules are turned off, the driver works through each module in turn by * all VL53L0X modules are turned off, the driver works through each module in turn by
* setting XSHUT to HIGH to turn the module on,, then writing the module's desired I2C address. * setting XSHUT to HIGH to turn the module on,, then writing the module's desired I2C address.
* In this way, many VL53L0X modules can be connected to the one I2C bus, each one * In this way, many VL53L0X modules can be connected to the one I2C bus, each one
* using with a distinct I2C address. * using a distinct I2C address.
*
* WARNING: If the device's XSHUT pin is not connected, then it is very prone to noise,
* and the device may even reset when handled. If you're not using XSHUT, then it's
* best to tie it to +5V.
* *
* The driver is configured as follows: * The driver is configured as follows:
* *
@ -98,7 +102,6 @@ private:
bool _value; bool _value;
bool _initialising = true; bool _initialising = true;
uint8_t _entryCount = 0; uint8_t _entryCount = 0;
unsigned long _lastEntryTime = 0;
bool _scanInProgress = false; bool _scanInProgress = false;
// Register addresses // Register addresses
enum : uint8_t { enum : uint8_t {
@ -135,7 +138,8 @@ protected:
} else { } else {
_entryCount = 0; _entryCount = 0;
} }
} }
void _loop(unsigned long currentMicros) override { void _loop(unsigned long currentMicros) override {
if (_initialising) { if (_initialising) {
switch (_entryCount++) { switch (_entryCount++) {
@ -156,11 +160,17 @@ protected:
I2CManager.write(VL53L0X_I2C_DEFAULT_ADDRESS, 2, VL53L0X_REG_I2C_SLAVE_DEVICE_ADDRESS, _i2cAddress); I2CManager.write(VL53L0X_I2C_DEFAULT_ADDRESS, 2, VL53L0X_REG_I2C_SLAVE_DEVICE_ADDRESS, _i2cAddress);
break; break;
case 3: case 3:
// After two more loops, check if device has been configured.
if (I2CManager.exists(_i2cAddress)) { if (I2CManager.exists(_i2cAddress)) {
#ifdef DIAG_IO
_display(); _display();
#endif
// Set 2.8V mode // Set 2.8V mode
write_reg(VL53L0X_CONFIG_PAD_SCL_SDA__EXTSUP_HV, write_reg(VL53L0X_CONFIG_PAD_SCL_SDA__EXTSUP_HV,
read_reg(VL53L0X_CONFIG_PAD_SCL_SDA__EXTSUP_HV) | 0x01); read_reg(VL53L0X_CONFIG_PAD_SCL_SDA__EXTSUP_HV) | 0x01);
} else {
DIAG(F("VL53L0X I2C:x%x device not responding"), _i2cAddress);
_deviceState = DEVSTATE_FAILED;
} }
_initialising = false; _initialising = false;
_entryCount = 0; _entryCount = 0;
@ -168,14 +178,17 @@ protected:
default: default:
break; break;
} }
} else if (_lastEntryTime - currentMicros > 10000UL) { } else {
// Service device every 10ms
_lastEntryTime = currentMicros;
if (!_scanInProgress) { if (!_scanInProgress) {
// Not scanning, so initiate a scan // Not scanning, so initiate a scan
write_reg(VL53L0X_REG_SYSRANGE_START, 0x01); uint8_t status = write_reg(VL53L0X_REG_SYSRANGE_START, 0x01);
_scanInProgress = true; if (status != I2C_STATUS_OK) {
DIAG(F("VL53L0X I2C:x%x Error:%d %S"), _i2cAddress, status, I2CManager.getErrorMessage(status));
_deviceState = DEVSTATE_FAILED;
_value = false;
} else
_scanInProgress = true;
} else { } else {
// Scan in progress, so check for completion. // Scan in progress, so check for completion.
@ -198,8 +211,11 @@ protected:
_scanInProgress = false; _scanInProgress = false;
} }
} }
// Next entry in 10 milliseconds.
delayUntil(currentMicros + 10000UL);
} }
} }
// For analogue read, first pin returns distance, second pin is signal strength, and third is ambient level. // For analogue read, first pin returns distance, second pin is signal strength, and third is ambient level.
int _readAnalogue(VPIN vpin) override { int _readAnalogue(VPIN vpin) override {
int pin = vpin - _firstVpin; int pin = vpin - _firstVpin;
@ -214,13 +230,16 @@ protected:
return -1; return -1;
} }
} }
// For digital read, return the same value for all pins. // For digital read, return the same value for all pins.
int _read(VPIN) override { int _read(VPIN) override {
return _value; return _value;
} }
void _display() override { void _display() override {
DIAG(F("VL53L0X I2C:x%x Configured on Vpins:%d-%d On:%dmm Off:%dmm"), DIAG(F("VL53L0X I2C:x%x Configured on Vpins:%d-%d On:%dmm Off:%dmm %S"),
_i2cAddress, _firstVpin, _firstVpin+_nPins-1, _onThreshold, _offThreshold); _i2cAddress, _firstVpin, _firstVpin+_nPins-1, _onThreshold, _offThreshold,
(_deviceState==DEVSTATE_FAILED) ? F("OFFLINE") : F(""));
} }
@ -228,12 +247,12 @@ private:
inline uint16_t makeuint16(byte lsb, byte msb) { inline uint16_t makeuint16(byte lsb, byte msb) {
return (((uint16_t)msb) << 8) | lsb; return (((uint16_t)msb) << 8) | lsb;
} }
void write_reg(uint8_t reg, uint8_t data) { uint8_t write_reg(uint8_t reg, uint8_t data) {
// write byte to register // write byte to register
uint8_t outBuffer[2]; uint8_t outBuffer[2];
outBuffer[0] = reg; outBuffer[0] = reg;
outBuffer[1] = data; outBuffer[1] = data;
I2CManager.write(_i2cAddress, outBuffer, 2); return I2CManager.write(_i2cAddress, outBuffer, 2);
} }
uint8_t read_reg(uint8_t reg) { uint8_t read_reg(uint8_t reg) {
// read byte from register register // read byte from register register