mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-11-23 08:06:13 +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
|
||||
// then only the first one found will be used.
|
||||
void IODevice::writeAnalogue(VPIN vpin, int value, int profile) {
|
||||
// then only the first one found will be used. Duration is the time that the
|
||||
// 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);
|
||||
if (dev) {
|
||||
dev->_writeAnalogue(vpin, value, profile);
|
||||
dev->_writeAnalogue(vpin, value, profile, duration);
|
||||
return;
|
||||
}
|
||||
#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.
|
||||
|
||||
void IODevice::begin() { DIAG(F("NO HAL CONFIGURED!")); }
|
||||
bool IODevice::configure(VPIN vpin, ConfigTypeEnum configType, int paramCount, int params[]) {
|
||||
(void)vpin; (void)paramCount; (void)params; // Avoid compiler warnings
|
||||
if (configType == CONFIGURE_INPUT || configType == CONFIGURE_OUTPUT)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
bool IODevice::configure(VPIN, ConfigTypeEnum, int, int []) { return true; }
|
||||
void IODevice::write(VPIN vpin, int value) {
|
||||
digitalWrite(vpin, value);
|
||||
pinMode(vpin, OUTPUT);
|
||||
}
|
||||
void IODevice::writeAnalogue(VPIN vpin, int value, int profile) {
|
||||
(void)vpin; (void)value; (void)profile; // Avoid compiler warnings
|
||||
}
|
||||
bool IODevice::hasCallback(VPIN vpin) {
|
||||
(void)vpin; // Avoid compiler warnings
|
||||
return false;
|
||||
}
|
||||
void IODevice::writeAnalogue(VPIN, int, uint8_t, uint16_t) {}
|
||||
bool IODevice::hasCallback(VPIN) { return false; }
|
||||
int IODevice::read(VPIN vpin) {
|
||||
pinMode(vpin, INPUT_PULLUP);
|
||||
return !digitalRead(vpin); // Return inverted state (5v=0, 0v=1)
|
||||
|
@ -272,10 +262,8 @@ void IODevice::loop() {}
|
|||
void IODevice::DumpAll() {
|
||||
DIAG(F("NO HAL CONFIGURED!"));
|
||||
}
|
||||
bool IODevice::exists(VPIN vpin) { return (vpin > 2 && vpin < 49); }
|
||||
void IODevice::setGPIOInterruptPin(int16_t pinNumber) {
|
||||
(void) pinNumber; // Avoid compiler warning
|
||||
}
|
||||
bool IODevice::exists(VPIN vpin) { return (vpin > 2 && vpin < NUM_DIGITAL_PINS); }
|
||||
void IODevice::setGPIOInterruptPin(int16_t) {}
|
||||
|
||||
// Chain of callback blocks (identifying registered callback functions for state changes)
|
||||
// Not used in IO_NO_HAL but must be declared.
|
||||
|
|
23
IODevice.h
23
IODevice.h
|
@ -42,6 +42,7 @@
|
|||
#include "DIAG.h"
|
||||
#include "FSH.h"
|
||||
#include "I2CManager.h"
|
||||
#include "inttypes.h"
|
||||
|
||||
typedef uint16_t VPIN;
|
||||
// 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);
|
||||
|
||||
// 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
|
||||
// the output over a period of time.
|
||||
|
@ -174,8 +175,8 @@ protected:
|
|||
};
|
||||
|
||||
// Method to write an 'analogue' value (optionally implemented within device class)
|
||||
virtual void _writeAnalogue(VPIN vpin, int value, int profile) {
|
||||
(void)vpin; (void)value; (void) profile;
|
||||
virtual void _writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration) {
|
||||
(void)vpin; (void)value; (void) profile; (void)duration;
|
||||
};
|
||||
|
||||
// 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);
|
||||
// Constructor
|
||||
PCA9685(VPIN vpin, int nPins, uint8_t I2CAddress);
|
||||
enum ProfileType {
|
||||
Instant = 0, // Moves immediately between positions
|
||||
enum ProfileType : uint8_t {
|
||||
Instant = 0, // Moves immediately between positions (if duration not specified)
|
||||
UseDuration = 0, // Use specified duration
|
||||
Fast = 1, // Takes around 500ms end-to-end
|
||||
Medium = 2, // 1 second 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:
|
||||
|
@ -263,7 +266,7 @@ private:
|
|||
bool _configure(VPIN vpin, ConfigTypeEnum configType, int paramCount, int params[]) override;
|
||||
// Device-specific write functions.
|
||||
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;
|
||||
void _loop(unsigned long currentMicros) override;
|
||||
void updatePosition(uint8_t pin);
|
||||
|
@ -279,10 +282,10 @@ private:
|
|||
uint16_t fromPosition : 12;
|
||||
uint16_t toPosition : 12;
|
||||
uint8_t profile; // Config parameter
|
||||
uint8_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 stepNumber; // Index of current step (starting from 0)
|
||||
uint16_t numSteps; // Number of steps in animation, or 0 if none in progress.
|
||||
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];
|
||||
|
||||
|
|
|
@ -65,7 +65,7 @@ bool PCA9685::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCount, i
|
|||
int state = params[3];
|
||||
if (state != -1) {
|
||||
// Position servo to initial state
|
||||
_writeAnalogue(vpin, state ? s->activePosition : s->inactivePosition, Instant);
|
||||
_writeAnalogue(vpin, state ? s->activePosition : s->inactivePosition, 0, 0);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -96,7 +96,6 @@ void PCA9685::_begin() {
|
|||
|
||||
// Initialise I/O module here.
|
||||
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_PRESCALE, PRESCALE_50HZ); // 50Hz clock, 20ms pulse period.
|
||||
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
|
||||
// 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.
|
||||
#if defined(DIAG_IO)
|
||||
_display();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Device-specific write function, invoked from IODevice::write().
|
||||
// For this function, the configured profile is used.
|
||||
void PCA9685::_write(VPIN vpin, int value) {
|
||||
#ifdef DIAG_IO
|
||||
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);
|
||||
} else {
|
||||
// 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().
|
||||
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
|
||||
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
|
||||
int pin = vpin - _firstVpin;
|
||||
if (value > 4095) value = 4095;
|
||||
|
@ -142,30 +155,32 @@ void PCA9685::_writeAnalogue(VPIN vpin, int value, int profile) {
|
|||
s->activePosition = _defaultActivePosition;
|
||||
s->inactivePosition = _defaultInactivePosition;
|
||||
s->currentPosition = value;
|
||||
s->profile = Instant;
|
||||
s->profile = Instant; // Use instant profile (but not this time)
|
||||
}
|
||||
|
||||
// Animated profile. Initiate the appropriate action.
|
||||
s->currentProfile = profile;
|
||||
s->numSteps = profile==Fast ? 10 :
|
||||
profile==Medium ? 20 :
|
||||
profile==Slow ? 40 :
|
||||
profile==Bounce ? sizeof(_bounceProfile)-1 :
|
||||
1;
|
||||
uint8_t profileValue = profile & ~NoPowerOff; // Mask off 'don't-power-off' bit.
|
||||
s->numSteps = profileValue==Instant ? 1 :
|
||||
profileValue==Fast ? 10 : // 0.5 seconds
|
||||
profileValue==Medium ? 20 : // 1.0 seconds
|
||||
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->toPosition = value;
|
||||
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.
|
||||
bool PCA9685::_isActive(VPIN vpin) {
|
||||
bool PCA9685::_isBusy(VPIN vpin) {
|
||||
int pin = vpin - _firstVpin;
|
||||
struct ServoData *s = _servoData[pin];
|
||||
if (s == NULL)
|
||||
return false; // No structure means no animation!
|
||||
else
|
||||
return (s->numSteps != 0);
|
||||
return (s->stepNumber < s->numSteps);
|
||||
}
|
||||
|
||||
void PCA9685::_loop(unsigned long currentMicros) {
|
||||
|
@ -194,7 +209,7 @@ void PCA9685::updatePosition(uint8_t pin) {
|
|||
if (s->stepNumber < s->numSteps) {
|
||||
// Animation in progress, reposition servo
|
||||
s->stepNumber++;
|
||||
if (s->currentProfile == Bounce) {
|
||||
if ((s->currentProfile & ~NoPowerOff) == Bounce) {
|
||||
// Retrieve step positions from array in flash
|
||||
byte profileValue = GETFLASH(&_bounceProfile[s->stepNumber]);
|
||||
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
|
||||
s->stepNumber++;
|
||||
} else if (s->stepNumber == s->numSteps + _catchupSteps
|
||||
&& s->currentPosition != 4095 && s->currentPosition != 0) {
|
||||
&& s->currentPosition != 0) {
|
||||
#ifdef IO_SWITCH_OFF_SERVO
|
||||
// Wait has finished, so switch off PWM to prevent annoying servo buzz
|
||||
writeDevice(pin, 0);
|
||||
if ((s->currentProfile & NoPowerOff) == 0) {
|
||||
// Wait has finished, so switch off PWM to prevent annoying servo buzz
|
||||
writeDevice(pin, 0);
|
||||
}
|
||||
#endif
|
||||
s->numSteps = 0; // Done now.
|
||||
}
|
||||
|
@ -236,7 +253,7 @@ void PCA9685::writeDevice(uint8_t pin, int value) {
|
|||
|
||||
// Display details of this device.
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user