mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-11-27 01:56:14 +01:00
HAL writeAnalogue function change.
IODevice::writeAnalogue() has an additional optional parameter "duration", specifying the time taken for the animation in units of 100ms (max 3276 seconds, or about 54 minutes).
This commit is contained in:
parent
1dd574dc03
commit
7e601c38c4
30
IODevice.cpp
30
IODevice.cpp
|
@ -144,11 +144,12 @@ void IODevice::write(VPIN vpin, int value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 the same pin
|
||||||
// then only the first one found will be used.
|
// then only the first one found will be used. Duration is the time that the
|
||||||
void IODevice::writeAnalogue(VPIN vpin, int value, int profile) {
|
// 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) {
|
||||||
IODevice *dev = findDevice(vpin);
|
IODevice *dev = findDevice(vpin);
|
||||||
if (dev) {
|
if (dev) {
|
||||||
dev->_writeAnalogue(vpin, value, profile);
|
dev->_writeAnalogue(vpin, value, profile, duration);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#ifdef DIAG_IO
|
#ifdef DIAG_IO
|
||||||
|
@ -246,24 +247,13 @@ int IODevice::read(VPIN vpin) {
|
||||||
// Minimal implementations of public HAL interface, to support Arduino pin I/O and nothing more.
|
// Minimal implementations of public HAL interface, to support Arduino pin I/O and nothing more.
|
||||||
|
|
||||||
void IODevice::begin() { DIAG(F("NO HAL CONFIGURED!")); }
|
void IODevice::begin() { DIAG(F("NO HAL CONFIGURED!")); }
|
||||||
bool IODevice::configure(VPIN vpin, ConfigTypeEnum configType, int paramCount, int params[]) {
|
bool IODevice::configure(VPIN, ConfigTypeEnum, int, int []) { return true; }
|
||||||
(void)vpin; (void)paramCount; (void)params; // Avoid compiler warnings
|
|
||||||
if (configType == CONFIGURE_INPUT || configType == CONFIGURE_OUTPUT)
|
|
||||||
return true;
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
void IODevice::write(VPIN vpin, int value) {
|
void IODevice::write(VPIN vpin, int value) {
|
||||||
digitalWrite(vpin, value);
|
digitalWrite(vpin, value);
|
||||||
pinMode(vpin, OUTPUT);
|
pinMode(vpin, OUTPUT);
|
||||||
}
|
}
|
||||||
void IODevice::writeAnalogue(VPIN vpin, int value, int profile) {
|
void IODevice::writeAnalogue(VPIN, int, uint8_t, uint16_t) {}
|
||||||
(void)vpin; (void)value; (void)profile; // Avoid compiler warnings
|
bool IODevice::hasCallback(VPIN) { return false; }
|
||||||
}
|
|
||||||
bool IODevice::hasCallback(VPIN vpin) {
|
|
||||||
(void)vpin; // Avoid compiler warnings
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
int IODevice::read(VPIN vpin) {
|
int IODevice::read(VPIN vpin) {
|
||||||
pinMode(vpin, INPUT_PULLUP);
|
pinMode(vpin, INPUT_PULLUP);
|
||||||
return !digitalRead(vpin); // Return inverted state (5v=0, 0v=1)
|
return !digitalRead(vpin); // Return inverted state (5v=0, 0v=1)
|
||||||
|
@ -272,10 +262,8 @@ void IODevice::loop() {}
|
||||||
void IODevice::DumpAll() {
|
void IODevice::DumpAll() {
|
||||||
DIAG(F("NO HAL CONFIGURED!"));
|
DIAG(F("NO HAL CONFIGURED!"));
|
||||||
}
|
}
|
||||||
bool IODevice::exists(VPIN vpin) { return (vpin > 2 && vpin < 49); }
|
bool IODevice::exists(VPIN vpin) { return (vpin > 2 && vpin < NUM_DIGITAL_PINS); }
|
||||||
void IODevice::setGPIOInterruptPin(int16_t pinNumber) {
|
void IODevice::setGPIOInterruptPin(int16_t) {}
|
||||||
(void) pinNumber; // Avoid compiler warning
|
|
||||||
}
|
|
||||||
|
|
||||||
// Chain of callback blocks (identifying registered callback functions for state changes)
|
// Chain of callback blocks (identifying registered callback functions for state changes)
|
||||||
// Not used in IO_NO_HAL but must be declared.
|
// Not used in IO_NO_HAL but must be declared.
|
||||||
|
|
23
IODevice.h
23
IODevice.h
|
@ -42,6 +42,7 @@
|
||||||
#include "DIAG.h"
|
#include "DIAG.h"
|
||||||
#include "FSH.h"
|
#include "FSH.h"
|
||||||
#include "I2CManager.h"
|
#include "I2CManager.h"
|
||||||
|
#include "inttypes.h"
|
||||||
|
|
||||||
typedef uint16_t VPIN;
|
typedef uint16_t VPIN;
|
||||||
// Limit VPIN number to max 32767. Above this number, printing often gives negative values.
|
// Limit VPIN number to max 32767. Above this number, printing often gives negative values.
|
||||||
|
@ -128,7 +129,7 @@ public:
|
||||||
static void write(VPIN vpin, int value);
|
static void write(VPIN vpin, int value);
|
||||||
|
|
||||||
// write invokes the IODevice instance's _writeAnalogue method (not applicable for digital outputs)
|
// write invokes the IODevice instance's _writeAnalogue method (not applicable for digital outputs)
|
||||||
static void writeAnalogue(VPIN vpin, int value, int profile);
|
static void writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration=0);
|
||||||
|
|
||||||
// isBusy returns true if the device is currently in an animation of some sort, e.g. is changing
|
// isBusy returns true if the device is currently in an animation of some sort, e.g. is changing
|
||||||
// the output over a period of time.
|
// the output over a period of time.
|
||||||
|
@ -174,8 +175,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, int profile) {
|
virtual void _writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration) {
|
||||||
(void)vpin; (void)value; (void) profile;
|
(void)vpin; (void)value; (void) profile; (void)duration;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Function called to check whether callback notification is supported by this pin.
|
// Function called to check whether callback notification is supported by this pin.
|
||||||
|
@ -249,12 +250,14 @@ public:
|
||||||
static void create(VPIN vpin, int nPins, uint8_t I2CAddress);
|
static void create(VPIN vpin, int nPins, uint8_t I2CAddress);
|
||||||
// Constructor
|
// Constructor
|
||||||
PCA9685(VPIN vpin, int nPins, uint8_t I2CAddress);
|
PCA9685(VPIN vpin, int nPins, uint8_t I2CAddress);
|
||||||
enum ProfileType {
|
enum ProfileType : uint8_t {
|
||||||
Instant = 0, // Moves immediately between positions
|
Instant = 0, // Moves immediately between positions (if duration not specified)
|
||||||
|
UseDuration = 0, // Use specified duration
|
||||||
Fast = 1, // Takes around 500ms end-to-end
|
Fast = 1, // Takes around 500ms end-to-end
|
||||||
Medium = 2, // 1 second end-to-end
|
Medium = 2, // 1 second end-to-end
|
||||||
Slow = 3, // 2 seconds end-to-end
|
Slow = 3, // 2 seconds end-to-end
|
||||||
Bounce = 4 // For semaphores/turnouts with a bit of bounce!!
|
Bounce = 4, // For semaphores/turnouts with a bit of bounce!!
|
||||||
|
NoPowerOff = 0x80, // Flag to be ORed in to suppress power off after move.
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -263,7 +266,7 @@ private:
|
||||||
bool _configure(VPIN vpin, ConfigTypeEnum configType, int paramCount, int params[]) override;
|
bool _configure(VPIN vpin, ConfigTypeEnum configType, int paramCount, int params[]) override;
|
||||||
// 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, int profile) override;
|
void _writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration) override;
|
||||||
bool _isBusy(VPIN vpin) override;
|
bool _isBusy(VPIN vpin) override;
|
||||||
void _loop(unsigned long currentMicros) override;
|
void _loop(unsigned long currentMicros) override;
|
||||||
void updatePosition(uint8_t pin);
|
void updatePosition(uint8_t pin);
|
||||||
|
@ -279,10 +282,10 @@ private:
|
||||||
uint16_t fromPosition : 12;
|
uint16_t fromPosition : 12;
|
||||||
uint16_t toPosition : 12;
|
uint16_t toPosition : 12;
|
||||||
uint8_t profile; // Config parameter
|
uint8_t profile; // Config parameter
|
||||||
uint8_t stepNumber; // Index of current step (starting from 0)
|
uint16_t stepNumber; // Index of current step (starting from 0)
|
||||||
uint8_t numSteps; // Number of steps in animation, or 0 if none in progress.
|
uint16_t numSteps; // Number of steps in animation, or 0 if none in progress.
|
||||||
uint8_t currentProfile; // profile being used for current animation.
|
uint8_t currentProfile; // profile being used for current animation.
|
||||||
}; // 12 bytes per element, i.e. per pin in use
|
}; // 14 bytes per element, i.e. per pin in use
|
||||||
|
|
||||||
struct ServoData *_servoData [16];
|
struct ServoData *_servoData [16];
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,7 @@ bool PCA9685::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCount, i
|
||||||
int state = params[3];
|
int state = params[3];
|
||||||
if (state != -1) {
|
if (state != -1) {
|
||||||
// Position servo to initial state
|
// Position servo to initial state
|
||||||
_writeAnalogue(vpin, state ? s->activePosition : s->inactivePosition, Instant);
|
_writeAnalogue(vpin, state ? s->activePosition : s->inactivePosition, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -96,7 +96,6 @@ void PCA9685::_begin() {
|
||||||
|
|
||||||
// Initialise I/O module here.
|
// Initialise I/O module here.
|
||||||
if (I2CManager.exists(_I2CAddress)) {
|
if (I2CManager.exists(_I2CAddress)) {
|
||||||
DIAG(F("PCA9685 I2C:%x configured Vpins:%d-%d"), _I2CAddress, _firstVpin, _firstVpin+_nPins-1);
|
|
||||||
writeRegister(_I2CAddress, PCA9685_MODE1, MODE1_SLEEP | MODE1_AI);
|
writeRegister(_I2CAddress, PCA9685_MODE1, MODE1_SLEEP | MODE1_AI);
|
||||||
writeRegister(_I2CAddress, PCA9685_PRESCALE, PRESCALE_50HZ); // 50Hz clock, 20ms pulse period.
|
writeRegister(_I2CAddress, PCA9685_PRESCALE, PRESCALE_50HZ); // 50Hz clock, 20ms pulse period.
|
||||||
writeRegister(_I2CAddress, PCA9685_MODE1, MODE1_AI);
|
writeRegister(_I2CAddress, PCA9685_MODE1, MODE1_AI);
|
||||||
|
@ -104,10 +103,14 @@ void PCA9685::_begin() {
|
||||||
// In theory, we should wait 500us before sending any other commands to each device, to allow
|
// In theory, we should wait 500us before sending any other commands to each device, to allow
|
||||||
// the PWM oscillator to get running. However, we don't do any specific wait, as there's
|
// the PWM oscillator to get running. However, we don't do any specific wait, as there's
|
||||||
// plenty of other stuff to do before we will send a command.
|
// plenty of other stuff to do before we will send a command.
|
||||||
|
#if defined(DIAG_IO)
|
||||||
|
_display();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Device-specific write function, invoked from IODevice::write().
|
// Device-specific write function, invoked from IODevice::write().
|
||||||
|
// For this function, the configured profile is used.
|
||||||
void PCA9685::_write(VPIN vpin, int value) {
|
void PCA9685::_write(VPIN vpin, int value) {
|
||||||
#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);
|
||||||
|
@ -121,14 +124,24 @@ void PCA9685::_write(VPIN vpin, int value) {
|
||||||
writeDevice(pin, value ? _defaultActivePosition : _defaultInactivePosition);
|
writeDevice(pin, value ? _defaultActivePosition : _defaultInactivePosition);
|
||||||
} else {
|
} else {
|
||||||
// Use configured parameters for advanced transitions
|
// Use configured parameters for advanced transitions
|
||||||
_writeAnalogue(vpin, value ? s->activePosition : s->inactivePosition, s->profile);
|
_writeAnalogue(vpin, value ? s->activePosition : s->inactivePosition, s->profile, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Device-specific writeAnalogue function, invoked from IODevice::writeAnalogue().
|
// Device-specific writeAnalogue function, invoked from IODevice::writeAnalogue().
|
||||||
void PCA9685::_writeAnalogue(VPIN vpin, int value, int profile) {
|
// Profile is as follows:
|
||||||
|
// Bit 7: 0=Set PWM to 0% to power off servo motor when finished
|
||||||
|
// 1=Keep PWM pulses on (better when using PWM to drive an LED)
|
||||||
|
// Bits 6-0: 0 Use specified duration (defaults to 0 deciseconds)
|
||||||
|
// 1 (Fast) Move servo in 0.5 seconds
|
||||||
|
// 2 (Medium) Move servo in 1.0 seconds
|
||||||
|
// 3 (Slow) Move servo in 2.0 seconds
|
||||||
|
// 4 (Bounce) Servo 'bounces' at extremes.
|
||||||
|
//
|
||||||
|
void PCA9685::_writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration) {
|
||||||
#ifdef DIAG_IO
|
#ifdef DIAG_IO
|
||||||
DIAG(F("PCA9685 WriteAnalogue Vpin:%d Value:%d Profile:%d"), vpin, value, profile);
|
DIAG(F("PCA9685 WriteAnalogue Vpin:%d Value:%d Profile:%d Duration:%d"),
|
||||||
|
vpin, value, profile, duration);
|
||||||
#endif
|
#endif
|
||||||
int pin = vpin - _firstVpin;
|
int pin = vpin - _firstVpin;
|
||||||
if (value > 4095) value = 4095;
|
if (value > 4095) value = 4095;
|
||||||
|
@ -142,30 +155,32 @@ void PCA9685::_writeAnalogue(VPIN vpin, int value, int profile) {
|
||||||
s->activePosition = _defaultActivePosition;
|
s->activePosition = _defaultActivePosition;
|
||||||
s->inactivePosition = _defaultInactivePosition;
|
s->inactivePosition = _defaultInactivePosition;
|
||||||
s->currentPosition = value;
|
s->currentPosition = value;
|
||||||
s->profile = Instant;
|
s->profile = Instant; // Use instant profile (but not this time)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Animated profile. Initiate the appropriate action.
|
// Animated profile. Initiate the appropriate action.
|
||||||
s->currentProfile = profile;
|
s->currentProfile = profile;
|
||||||
s->numSteps = profile==Fast ? 10 :
|
uint8_t profileValue = profile & ~NoPowerOff; // Mask off 'don't-power-off' bit.
|
||||||
profile==Medium ? 20 :
|
s->numSteps = profileValue==Instant ? 1 :
|
||||||
profile==Slow ? 40 :
|
profileValue==Fast ? 10 : // 0.5 seconds
|
||||||
profile==Bounce ? sizeof(_bounceProfile)-1 :
|
profileValue==Medium ? 20 : // 1.0 seconds
|
||||||
1;
|
profileValue==Slow ? 40 : // 2.0 seconds
|
||||||
|
profileValue==Bounce ? sizeof(_bounceProfile)-1 : // ~ 1.5 seconds
|
||||||
|
duration * 2; // Convert from deciseconds (100ms) to refresh cycles (50ms)
|
||||||
s->stepNumber = 0;
|
s->stepNumber = 0;
|
||||||
s->toPosition = value;
|
s->toPosition = value;
|
||||||
s->fromPosition = s->currentPosition;
|
s->fromPosition = s->currentPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
// _isActive returns true if the device is currently in executing an animation,
|
// _isBusy 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.
|
||||||
bool PCA9685::_isActive(VPIN vpin) {
|
bool PCA9685::_isBusy(VPIN vpin) {
|
||||||
int pin = vpin - _firstVpin;
|
int pin = vpin - _firstVpin;
|
||||||
struct ServoData *s = _servoData[pin];
|
struct ServoData *s = _servoData[pin];
|
||||||
if (s == NULL)
|
if (s == NULL)
|
||||||
return false; // No structure means no animation!
|
return false; // No structure means no animation!
|
||||||
else
|
else
|
||||||
return (s->numSteps != 0);
|
return (s->stepNumber < s->numSteps);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PCA9685::_loop(unsigned long currentMicros) {
|
void PCA9685::_loop(unsigned long currentMicros) {
|
||||||
|
@ -194,7 +209,7 @@ void PCA9685::updatePosition(uint8_t pin) {
|
||||||
if (s->stepNumber < s->numSteps) {
|
if (s->stepNumber < s->numSteps) {
|
||||||
// Animation in progress, reposition servo
|
// Animation in progress, reposition servo
|
||||||
s->stepNumber++;
|
s->stepNumber++;
|
||||||
if (s->currentProfile == Bounce) {
|
if ((s->currentProfile & ~NoPowerOff) == Bounce) {
|
||||||
// Retrieve step positions from array in flash
|
// Retrieve step positions from array in flash
|
||||||
byte profileValue = GETFLASH(&_bounceProfile[s->stepNumber]);
|
byte profileValue = GETFLASH(&_bounceProfile[s->stepNumber]);
|
||||||
s->currentPosition = map(profileValue, 0, 100, s->fromPosition, s->toPosition);
|
s->currentPosition = map(profileValue, 0, 100, s->fromPosition, s->toPosition);
|
||||||
|
@ -208,10 +223,12 @@ void PCA9685::updatePosition(uint8_t pin) {
|
||||||
// We've finished animation, wait a little to allow servo to catch up
|
// We've finished animation, wait a little to allow servo to catch up
|
||||||
s->stepNumber++;
|
s->stepNumber++;
|
||||||
} else if (s->stepNumber == s->numSteps + _catchupSteps
|
} else if (s->stepNumber == s->numSteps + _catchupSteps
|
||||||
&& s->currentPosition != 4095 && s->currentPosition != 0) {
|
&& s->currentPosition != 0) {
|
||||||
#ifdef IO_SWITCH_OFF_SERVO
|
#ifdef IO_SWITCH_OFF_SERVO
|
||||||
|
if ((s->currentProfile & NoPowerOff) == 0) {
|
||||||
// Wait has finished, so switch off PWM to prevent annoying servo buzz
|
// Wait has finished, so switch off PWM to prevent annoying servo buzz
|
||||||
writeDevice(pin, 0);
|
writeDevice(pin, 0);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
s->numSteps = 0; // Done now.
|
s->numSteps = 0; // Done now.
|
||||||
}
|
}
|
||||||
|
@ -236,7 +253,7 @@ void PCA9685::writeDevice(uint8_t pin, int value) {
|
||||||
|
|
||||||
// Display details of this device.
|
// Display details of this device.
|
||||||
void PCA9685::_display() {
|
void PCA9685::_display() {
|
||||||
DIAG(F("PCA9685 I2C:x%x Vpins:%d-%d"), _I2CAddress, (int)_firstVpin,
|
DIAG(F("PCA9685 I2C:x%x Configured on Vpins:%d-%d"), _I2CAddress, (int)_firstVpin,
|
||||||
(int)_firstVpin+_nPins-1);
|
(int)_firstVpin+_nPins-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user