1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2025-07-31 03:13:45 +02:00

Compare commits

..

1 Commits

Author SHA1 Message Date
Ash-4
81e95995b4 NucleoTimerSync for DC mode tracks 2025-04-29 17:32:51 -05:00
18 changed files with 148 additions and 322 deletions

View File

@@ -1,6 +1,7 @@
/*
* © 2023 Neil McKechnie
* © 2022-2024 Paul M. Antoine
* © 2025 Herb Morton
* © 2021 Mike S
* © 2021, 2023 Harald Barth
* © 2021 Fred Decker
@@ -36,6 +37,21 @@
#include "DIAG.h"
#include <wiring_private.h>
// DC mode timers enable the PWM signal on select pins.
// Code added to sync timers which have the same frequency.
// Function prototypes
void refreshDCmodeTimers();
void resetCounterDCmodeTimers();
HardwareTimer *Timer1 = new HardwareTimer(TIM1);
HardwareTimer *Timer2 = new HardwareTimer(TIM2);
HardwareTimer *Timer3 = new HardwareTimer(TIM3);
HardwareTimer *Timer4 = new HardwareTimer(TIM4);
HardwareTimer *Timer9 = new HardwareTimer(TIM9);
#if defined(TIM13)
HardwareTimer *Timer13 = new HardwareTimer(TIM13);
#endif
#if defined(ARDUINO_NUCLEO_F401RE)
// Nucleo-64 boards don't have additional serial ports defined by default
// Serial1 is available on the F401RE, but not hugely convenient.
@@ -290,7 +306,7 @@ void DCCTimer::DCCEXanalogWriteFrequency(uint8_t pin, uint32_t f) {
else if (f >= 3)
DCCTimer::DCCEXanalogWriteFrequencyInternal(pin, 16000);
else if (f >= 2)
DCCTimer::DCCEXanalogWriteFrequencyInternal(pin, 3400);
DCCTimer::DCCEXanalogWriteFrequencyInternal(pin, 3600);
else if (f == 1)
DCCTimer::DCCEXanalogWriteFrequencyInternal(pin, 480);
else
@@ -328,7 +344,8 @@ void DCCTimer::DCCEXanalogWriteFrequencyInternal(uint8_t pin, uint32_t frequency
if (pin_timer[pin] != NULL)
{
pin_timer[pin]->setPWM(pin_channel[pin], pin, frequency, 0); // set frequency in Hertz, 0% dutycycle
DIAG(F("DCCEXanalogWriteFrequency::Pin %d on Timer Channel %d, frequency %d"), pin, pin_channel[pin], frequency);
DIAG(F("DCCEXanalogWriteFrequency::Pin %d on Timer %d Channel %d, frequency %d"), pin, pin_timer[pin], pin_channel[pin], frequency);
resetCounterDCmodeTimers();
}
else
DIAG(F("DCCEXanalogWriteFrequency::failed to allocate HardwareTimer instance!"));
@@ -341,6 +358,7 @@ void DCCTimer::DCCEXanalogWriteFrequencyInternal(uint8_t pin, uint32_t frequency
pinmap_pinout(digitalPinToPinName(pin), PinMap_TIM); // ensure the pin has been configured!
pin_timer[pin]->setOverflow(frequency, HERTZ_FORMAT); // Just change the frequency if it's already running!
DIAG(F("DCCEXanalogWriteFrequency::setting frequency to %d"), frequency);
resetCounterDCmodeTimers();
}
}
channel_frequency[pin] = frequency;
@@ -365,6 +383,8 @@ void DCCTimer::DCCEXanalogWrite(uint8_t pin, int value, bool invert) {
pin_timer[pin]->setCaptureCompare(pin_channel[pin], duty_cycle, PERCENT_COMPARE_FORMAT); // DCC_EX_PWM_FREQ Hertz, duty_cycle% dutycycle
DIAG(F("DCCEXanalogWrite::Pin %d, value %d, duty cycle %d"), pin, value, duty_cycle);
// }
refreshDCmodeTimers();
resetCounterDCmodeTimers();
}
else
DIAG(F("DCCEXanalogWrite::Pin %d is not configured for PWM!"), pin);
@@ -659,4 +679,35 @@ void ADCee::begin() {
#endif
interrupts();
}
// NOTE: additional testing is needed to check the DCC signal
// where the DCC signal pin is a pwm pin on timers 1, 2, 3, 4, 9, 13
// or the brake pin is defined on a different timer.
// -- example: F411RE/F446RE - pin 10 on stacked EX8874
// lines added to sync timers --
// not exact sync, but timers with the same frequency should be in sync
void refreshDCmodeTimers() {
Timer1->refresh();
Timer2->refresh();
Timer3->refresh();
Timer4->refresh();
Timer9->refresh();
#if defined(TIM13)
Timer13->refresh();
#endif
}
// Function to synchronize timers - called every time there is powerON commmand for any DC track
void resetCounterDCmodeTimers() {
// Reset the counter for all DC mode timers
TIM1->CNT = 0;
TIM2->CNT = 0;
TIM3->CNT = 0;
TIM4->CNT = 0;
TIM9->CNT = 0;
#if defined(TIM13)
TIM13->CNT = 0;
#endif
}
#endif

View File

@@ -263,8 +263,6 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) {
case OPCODE_ATLT:
case OPCODE_IFGTE:
case OPCODE_IFLT:
case OPCODE_IFBITMAP_ALL:
case OPCODE_IFBITMAP_ANY:
case OPCODE_DRIVE: {
DIAG(F("EXRAIL analog input VPIN %u"),(VPIN)operand);
IODevice::configureAnalogIn((VPIN)operand);
@@ -275,10 +273,6 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) {
if (compileFeatures & FEATURE_SENSOR)
new EXRAILSensor(operand,progCounter+3,true );
break;
case OPCODE_ONBITMAP:
if (compileFeatures & FEATURE_SENSOR)
new EXRAILSensor(operand,progCounter+3,true, true );
break;
case OPCODE_ONBUTTON:
if (compileFeatures & FEATURE_SENSOR)
new EXRAILSensor(operand,progCounter+3,false );
@@ -768,14 +762,6 @@ void RMFT2::loop2() {
case OPCODE_IFLT: // do next operand if sensor< value
skipIf=IODevice::readAnalogue(operand)>=(int)(getOperand(1));
break;
case OPCODE_IFBITMAP_ALL: // do next operand if sensor & mask == mask
skipIf=(IODevice::readAnalogue(operand) & getOperand(1)) != getOperand(1);
break;
case OPCODE_IFBITMAP_ANY: // do next operand if sensor & mask !=0
skipIf=(IODevice::readAnalogue(operand) & getOperand(1)) == 0;
break;
case OPCODE_IFLOCO: // do if the loco is the active one
skipIf=loco!=(uint16_t)operand; // bad luck if someone enters negative loco numbers into EXRAIL
@@ -1108,27 +1094,7 @@ void RMFT2::loop2() {
case OPCODE_SEQUENCE:
//if (diag) DIAG(F("EXRAIL begin(%d)"),operand);
break;
case OPCODE_BITMAP_INC:
IODevice::writeAnalogue(operand,IODevice::readAnalogue(operand)+1);
break;
case OPCODE_BITMAP_DEC:
{ int newval=IODevice::readAnalogue(operand)-1;
if (newval<0) newval=0;
IODevice::writeAnalogue(operand,newval);
}
break;
case OPCODE_BITMAP_AND:
IODevice::writeAnalogue(operand,IODevice::readAnalogue(operand) & getOperand(1));
break;
case OPCODE_BITMAP_OR:
IODevice::writeAnalogue(operand,IODevice::readAnalogue(operand) | getOperand(1));
break;
case OPCODE_BITMAP_XOR:
IODevice::writeAnalogue(operand,IODevice::readAnalogue(operand) ^ getOperand(1));
break;
case OPCODE_AUTOSTART: // Handled only during begin process
case OPCODE_PAD: // Just a padding for previous opcode needing >1 operand byte.
case OPCODE_TURNOUT: // Turnout definition ignored at runtime
@@ -1148,7 +1114,6 @@ void RMFT2::loop2() {
case OPCODE_ONTIME:
case OPCODE_ONBUTTON:
case OPCODE_ONSENSOR:
case OPCODE_ONBITMAP:
#ifndef IO_NO_HAL
case OPCODE_DCCTURNTABLE: // Turntable definition ignored at runtime
case OPCODE_EXTTTURNTABLE: // Turntable definition ignored at runtime

View File

@@ -78,12 +78,11 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE,OPCODE_TOGGLE_TURNOUT,
OPCODE_ROUTE_DISABLED,
OPCODE_STASH,OPCODE_CLEAR_STASH,OPCODE_CLEAR_ALL_STASH,OPCODE_PICKUP_STASH,
OPCODE_CLEAR_ANY_STASH,
OPCODE_ONBUTTON,OPCODE_ONSENSOR,OPCODE_ONVP_SENSOR,
OPCODE_ONBUTTON,OPCODE_ONSENSOR,
OPCODE_NEOPIXEL,
OPCODE_ONBLOCKENTER,OPCODE_ONBLOCKEXIT,
OPCODE_ESTOPALL,OPCODE_XPOM,
OPCODE_BITMAP_AND,OPCODE_BITMAP_OR,OPCODE_BITMAP_XOR,OPCODE_BITMAP_INC,OPCODE_BITMAP_DEC,OPCODE_ONBITMAP,
// OPcodes below this point are skip-nesting IF operations
// OPcodes below this point are skip-nesting IF operations
// placed here so that they may be skipped as a group
// see skipIfBlock()
IF_TYPE_OPCODES, // do not move this...
@@ -97,7 +96,6 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE,OPCODE_TOGGLE_TURNOUT,
OPCODE_IFLOCO,
OPCODE_IFTTPOSITION,
OPCODE_IFSTASH,
OPCODE_IFBITMAP_ALL,OPCODE_IFBITMAP_ANY,
};
// Ensure thrunge_lcd is put last as there may be more than one display,

View File

@@ -94,8 +94,6 @@
#undef IFTIMEOUT
#undef IFTTPOSITION
#undef IFRE
#undef IFBITMAP_ALL
#undef IFBITMAP_ANY
#undef INVERT_DIRECTION
#undef JMRI_SENSOR
#undef JOIN
@@ -135,7 +133,6 @@
#undef ONBUTTON
#undef ONSENSOR
#undef ONTHROW
#undef ONBITMAP
#undef ONCHANGE
#undef PARSE
#undef PAUSE
@@ -198,11 +195,6 @@
#undef VIRTUAL_TURNOUT
#undef WAITFOR
#ifndef IO_NO_HAL
#undef BITMAP_AND
#undef BITMAP_OR
#undef BITMAP_XOR
#undef BITMAP_INC
#undef BITMAP_DEC
#undef WAITFORTT
#endif
#undef WITHROTTLE
@@ -659,22 +651,6 @@
* @param value
*/
#define IFRE(vpin,value)
/**
* @def IFBITMAP_ALL(vpin,mask)
* @briaf Checks if (vpin pseudo-analog value & mask) == mask.
* @see IF
* @param vpin
* @param mask Binary mask applied to vpin value
*/
#define IFBITMAP_ALL(vpin,mask)
/**
* @def IFBITMAP_ANY(vpin,mask)
* @briaf Checks if vpin pseudo-analog value & mask is non zero
* @see IF
* @param vpin
* @param mask Binary mask applied to vpin value
*/
#define IFBITMAP_ANY(vpin,mask)
/**
* @def INVERT_DIRECTION
* @brief Marks current task so that FWD and REV commands are inverted.
@@ -923,12 +899,6 @@
* @param vpin
*/
#define ONSENSOR(vpin)
/**
* @def ONBITMAP(vpin)
* @brief Start task here when bitmap sensor changes state (debounced)
* @param vpin
*/
#define ONBITMAP(vpin)
/**
* @def ONBUTTON(vpin)
* @brief Start task here when sensor changes HIGH to LOW.
@@ -1352,42 +1322,9 @@
* @param description... quoted text or HIDDEN
*/
#define VIRTUAL_TURNOUT(id,description...)
/**
* @def BITMAP_AND(vpin1,mask)
* @brief Performs a bitwise AND operation on the given vpin analog value and mask.
* @param vpin1
* @param mask Binary mask to be ANDed with vpin1 value
*/
#define BITMAP_AND(vpin1,mask)
/**
* @def BITMAP_INC(vpin)
* @brief Increments poesudo analog value by 1
* @param vpin
*/
#define BITMAP_INC(vpin)
/**
* @def BITMAP_DEC(vpin)
* @brief Decrements poesudo analog value by 1 (to zero)
* @param vpin
*/
#define BITMAP_DEC(vpin)
/**
* @def BITMAP_OR(vpin1,mask)
* @brief Performs a bitwise OR operation on the given vpin analog value and mask.
* @param vpin1
* @param mask Binary mask to be ORed with vpin1 value
*/
#define BITMAP_OR(vpin1,mask)
/**
* @def BITMAP_XOR(vpin1,mask)
* @brief Performs a bitwise XOR operation on the given vpin analog value and mask.
* @param vpin1
* @param mask Binary mask to be XORed with vpin1 value
*/
#define BITMAP_XOR(vpin1,mask)
/**
* @def WAITFOR(vpin)
* @brief Waits for completion of servo movement
* @brief WAits for completion of servo movement
* @param vpin
*/
#define WAITFOR(pin)

View File

@@ -160,8 +160,6 @@ bool exrailHalSetup() {
#define ONBUTTON(vpin) | FEATURE_SENSOR
#undef ONSENSOR
#define ONSENSOR(vpin) | FEATURE_SENSOR
#undef ONBITMAP
#define ONBITMAP(vpin) | FEATURE_SENSOR
#undef ONBLOCKENTER
#define ONBLOCKENTER(blockid) | FEATURE_BLOCK
#undef ONBLOCKEXIT
@@ -483,8 +481,6 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup];
#define IFTTPOSITION(id,position) OPCODE_IFTTPOSITION,V(id),OPCODE_PAD,V(position),
#endif
#define IFRE(sensor_id,value) OPCODE_IFRE,V(sensor_id),OPCODE_PAD,V(value),
#define IFBITMAP_ALL(vpin,mask) OPCODE_IFBITMAP_ALL,V(vpin),OPCODE_PAD,V(mask),
#define IFBITMAP_ANY(vpin,mask) OPCODE_IFBITMAP_ANY,V(vpin),OPCODE_PAD,V(mask),
#define INVERT_DIRECTION OPCODE_INVERT_DIRECTION,0,0,
#define JMRI_SENSOR(vpin,count...)
#define JOIN OPCODE_JOIN,0,0,
@@ -537,7 +533,6 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup];
#define ONTHROW(turnout_id) OPCODE_ONTHROW,V(turnout_id),
#define ONCHANGE(sensor_id) OPCODE_ONCHANGE,V(sensor_id),
#define ONSENSOR(sensor_id) OPCODE_ONSENSOR,V(sensor_id),
#define ONBITMAP(sensor_id) OPCODE_ONBITMAP,V(sensor_id),
#define ONBUTTON(sensor_id) OPCODE_ONBUTTON,V(sensor_id),
#define PAUSE OPCODE_PAUSE,0,0,
#define PICKUP_STASH(id) OPCODE_PICKUP_STASH,V(id),
@@ -600,11 +595,6 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup];
#define UNLATCH(sensor_id) OPCODE_UNLATCH,V(sensor_id),
#define VIRTUAL_SIGNAL(id)
#define VIRTUAL_TURNOUT(id,description...) OPCODE_PINTURNOUT,V(id),OPCODE_PAD,V(0),
#define BITMAP_AND(vpin,mask) OPCODE_BITMAP_AND,V(vpin),OPCODE_PAD,V(mask),
#define BITMAP_INC(vpin) OPCODE_BITMAP_INC,V(vpin),
#define BITMAP_DEC(vpin) OPCODE_BITMAP_DEC,V(vpin),
#define BITMAP_OR(vpin,mask) OPCODE_BITMAP_OR,V(vpin),OPCODE_PAD,V(mask),
#define BITMAP_XOR(vpin,mask) OPCODE_BITMAP_XOR,V(vpin),OPCODE_PAD,V(mask),
#define WITHROTTLE(msg) PRINT(msg)
#define WAITFOR(pin) OPCODE_WAITFOR,V(pin),
#ifndef IO_NO_HAL

View File

@@ -59,7 +59,7 @@ void EXRAILSensor::checkAll() {
bool EXRAILSensor::check() {
// check for debounced change in this sensor
inputState = useAnalog?IODevice::readAnalogue(pin):RMFT2::readSensor(pin);
inputState = RMFT2::readSensor(pin);
// Check if changed since last time, and process changes.
if (inputState == active) {// no change
@@ -83,18 +83,18 @@ bool EXRAILSensor::check() {
return false;
}
EXRAILSensor::EXRAILSensor(VPIN _pin, int _progCounter, bool _onChange, bool _useAnalog) {
EXRAILSensor::EXRAILSensor(VPIN _pin, int _progCounter, bool _onChange) {
// Add to the start of the list
//DIAG(F("ONthing vpin=%d at %d"), _pin, _progCounter);
nextSensor = firstSensor;
firstSensor = this;
pin=_pin;
progCounter=_progCounter;
onChange=_onChange;
useAnalog=_useAnalog;
IODevice::configureInput(pin, true);
active = useAnalog?IODevice::readAnalogue(pin): IODevice::read(pin);
active = IODevice::read(pin);
inputState = active;
latchDelay = minReadCount;
}

View File

@@ -29,8 +29,7 @@ class EXRAILSensor {
public:
static void checkAll();
EXRAILSensor(VPIN _pin, int _progCounter, bool _onChange, bool _useAnalog=false);
EXRAILSensor(VPIN _pin, int _progCounter, bool _onChange);
bool check();
private:
@@ -43,10 +42,9 @@ class EXRAILSensor {
EXRAILSensor* nextSensor;
VPIN pin;
int progCounter;
uint16_t active;
uint16_t inputState;
bool active;
bool inputState;
bool onChange;
bool useAnalog;
byte latchDelay;
};
#endif

View File

@@ -46,8 +46,6 @@
// Helper function for listing device types
static const FSH * guessI2CDeviceType(uint8_t address) {
if (address >= 0x10 && address <= 0x17)
return F("EX-SensorCAM");
if (address == 0x1A)
// 0x09-0x18 selectable, but for now handle the default
return F("Piicodev 865/915MHz Transceiver");

View File

@@ -20,31 +20,19 @@
This is the list of HAL drivers automatically included by IODevice.h
It has been moved here to be easier to maintain than editing IODevice.h
*/
#include "IO_AnalogueInputs.h"
#include "IO_DFPlayer.h"
#include "IO_DS1307.h"
#include "IO_duinoNodes.h"
#include "IO_EncoderThrottle.h"
#include "IO_EXFastclock.h"
#include "IO_EXIOExpander.h"
#include "IO_EXSensorCAM.h"
#include "IO_HALDisplay.h"
#include "IO_HCSR04.h"
#include "IO_I2CDFPlayer.h"
#include "IO_I2CRailcom.h"
#include "IO_MCP23008.h"
#include "IO_MCP23017.h"
#include "IO_NeoPixel.h"
#include "IO_PCA9555.h"
#include "IO_PCA9685pwm.h"
#include "IO_PCF8574.h"
#include "IO_PCF8575.h"
#include "IO_RotaryEncoder.h"
#include "IO_Servo.h"
#include "IO_TCA8418.h"
#include "IO_TM1638.h"
#include "IO_TouchKeypad.h"
#include "IO_PCA9555.h"
#include "IO_duinoNodes.h"
#include "IO_EXIOExpander.h"
#include "IO_trainbrains.h"
#include "IO_Bitmap.h"
#include "IO_VL53L0X.h"
#include "IO_EncoderThrottle.h"
#include "IO_TCA8418.h"
#include "IO_NeoPixel.h"
#include "IO_TM1638.h"
#include "IO_EXSensorCAM.h"
#include "IO_DS1307.h"
#include "IO_I2CRailcom.h"

View File

@@ -1,89 +0,0 @@
/*
* © 2025, Chris Harlow. All rights reserved.
*
* This file is part of DCC-EX API
*
* This is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* It is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef IO_Bitmap_h
#define IO_Bitmap_h
#include <Arduino.h>
#include "defines.h"
#include "IODevice.h"
/*
Bitmap provides a set of virtual pins with no hardware.
Bitmap pins are able to be output and input and may be set and tested
as digital or analogue values.
When writing a digital value, the analogue value is set to 0 or 1.
When reading a digital value, the return is LOW for value 0 or HIGH for any other value
or analogue.
Bitmap pins may be used for any purpose, this is easier to manage than LATCH in EXRAIL
as they can be explicitely set and tested without interfering with underlying hardware.
Bitmap pins may be set, reset and tested in the same way as any other pin.
They are not persistent across reboots, but are retained in the current session.
Bitmap pins may also be monitored by JMRI_SENSOR() and <S> as for any other pin.
*/
class Bitmap : public IODevice {
public:
static void create(VPIN firstVpin, int nPins) {
if (IODevice::checkNoOverlap(firstVpin,nPins))
new Bitmap( firstVpin, nPins);
}
Bitmap(VPIN firstVpin, int nPins) : IODevice(firstVpin, nPins) {
_pinValues=(int16_t *) calloc(nPins,sizeof(int16_t));
// Connect to HAL so my _write, _read and _loop will be called as required.
IODevice::addDevice(this);
}
// Called by HAL to start handling this device
void _begin() override {
_deviceState = DEVSTATE_NORMAL;
_display();
}
int _read(VPIN vpin) override {
int pin=vpin - _firstVpin;
return _pinValues[pin]?1:0;
}
void _write(VPIN vpin, int value) override {
int pin = vpin - _firstVpin;
_pinValues[pin]=value!=0; // this is digital write
}
int _readAnalogue(VPIN vpin) override {
int pin=vpin - _firstVpin;
return _pinValues[pin]; // this is analog read
}
void _writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration) override {
int pin=vpin - _firstVpin;
_pinValues[pin]=value; // this is analog write
}
void _display() override {
DIAG(F("Bitmap Configured on Vpins:%u-%u"),
(int)_firstVpin,
(int)_firstVpin+_nPins-1);
}
private:
int16_t* _pinValues;
};
#endif

View File

@@ -85,7 +85,6 @@ class EXSensorCAM : public IODevice {
void _begin() {
uint8_t status;
// Initialise EX-SensorCAM device
I2CManager.setClock(100000); // Set speed for I2C operations
I2CManager.begin();
if (!I2CManager.exists(_I2CAddress)) {
DIAG(F("EX-SensorCAM I2C:%s device not found"), _I2CAddress.toString());

View File

@@ -1,5 +1,4 @@
/*
* © 2025 Herb Morton
* © 2022 Paul M Antoine
* © 2021, Neil McKechnie. All rights reserved.
*
@@ -44,21 +43,15 @@
class PCF8574 : public GPIOBase<uint8_t> {
public:
static void create(VPIN firstVpin, uint8_t nPins, I2CAddress i2cAddress, int interruptPin=-1, int initPortState=-1) {
if (checkNoOverlap(firstVpin, nPins, i2cAddress)) new PCF8574(firstVpin, nPins, i2cAddress, interruptPin, initPortState);
static void create(VPIN firstVpin, uint8_t nPins, I2CAddress i2cAddress, int interruptPin=-1) {
if (checkNoOverlap(firstVpin, nPins, i2cAddress)) new PCF8574(firstVpin, nPins, i2cAddress, interruptPin);
}
private:
PCF8574(VPIN firstVpin, uint8_t nPins, I2CAddress i2cAddress, int interruptPin=-1, int initPortState=-1)
PCF8574(VPIN firstVpin, uint8_t nPins, I2CAddress i2cAddress, int interruptPin=-1)
: GPIOBase<uint8_t>((FSH *)F("PCF8574"), firstVpin, nPins, i2cAddress, interruptPin)
{
requestBlock.setReadParams(_I2CAddress, inputBuffer, 1);
if (initPortState>=0) {
_portMode = 255; // set all pins to output mode
_portInUse = 255; // 8 ports in use
_portOutputState = initPortState; // initialize pins low-high 0-255
I2CManager.write(_I2CAddress, 1, initPortState);
}
}
// The PCF8574 handles inputs by applying a weak pull-up when output is driven to '1'.

View File

@@ -1,6 +1,6 @@
/*
* © 2022-2024 Paul M Antoine
* © 2024 Herb Morton
* © 2024-2025 Herb Morton
* © 2021 Mike S
* © 2021 Fred Decker
* © 2020-2023 Harald Barth
@@ -371,8 +371,8 @@ void MotorDriver::setDCSignal(byte speedcode, uint8_t frequency /*default =0*/)
}
#endif
//DIAG(F("Brake pin %d value %d freqency %d"), brakePin, brake, f);
DCCTimer::DCCEXanalogWrite(brakePin, brake, invertBrake);
DCCTimer::DCCEXanalogWriteFrequency(brakePin, f); // set DC PWM frequency
DCCTimer::DCCEXanalogWrite(brakePin, brake, invertBrake); // line swapped to set frequency first
#else // all AVR here
DCCTimer::DCCEXanalogWriteFrequency(brakePin, frequency); // frequency steps
analogWrite(brakePin, invertBrake ? 255-brake : brake);

View File

@@ -1,58 +0,0 @@
Virtual Bitmap device pins.
a Bitmap device pin is a software representation of a virtual hardware device that has the ability to store a 16bit value.
This this is easier to manage than LATCH in EXRAIL as they can be explicitely set and tested without interfering with underlying hardware or breaching the 255 limit.
Virtual pins may be set, reset and tested in the same way as any other pin. Unlike sensors and leds, these device pins are both INPUT and OUTPUT These can be used in many ways:
As a simple digital flag to assist in inter-thread communication.
A flag or value that can be set from commands and tested in EXRAIL.(e.g. to stop a sequence)
As a counter for looping or occupancy counts such as trains passing over a multi track road crossing.
As a collection of 16 digital bits that can be set, reset, toggled, masked and tested.
Existing <> and exrail commands for vpins work on these pins.
Virtual pin creation:
HAL(Bitmap,firstpin,npins)
creates 1 or more virtual pins in software. (RAM requirement approximately 2 bytes per pin)
e.g. HAL(Bitmap,1000,20) creates pins 1000..1019
Simple use as flags:
This uses the traditional digital pin commands
SET(1013) RESET(1013) sets value 1 or 0
SET(1000,20) RESET(1000,20) sets/resets a range of pins
IF(1000) tests if pin value!=0
Commands can set 1/0 values using <z 1010> <z -1010> as for any digital output.
BLINK can be used to set them on/off on a time pattern.
In addition, Exrail sensor comands work as if these pins were sensors
ONBUTTON(1013) triggers when value changes from 0 to something.
ONSENSOR(1013) triggers when value changes to or from 0.
<S 1013 1013 1> and JMRI_SENSOR(1013) report <Q/q responses when changing to or from 0.
Use as analog values:
Analog values may be set into the virtual pins and tested using the existing analog value commands and exrail macros.
<z vpin value> <D ANIN vpin> etc.
Use as counters:
For loop counting, counters can be incremented by BITMAP_INC(1013) and decremented by BITMAP_DEC(1013) and tested with IF/IFNOT/IFGTE etc.
Counters be used to automate a multi track crossing where each train entering increments the counter and decrements it on clearing the crossing. Crossing gate automation can be started when the value changes from 0, and be stopped when the counter returns to 0. Detecting the first increment from 0 to 1 can be done with ONBUTTON(1013) and the automation can use IF(1013) or IFNOT(1013) to detect when it needs to reopen the road gates.
Use as binary flag groups:
Virtual pins (and others that respond to an analog read in order to provide bitmapped digital data, such as SensorCam) can be set and tested with new special EXRAIL commands
IFBITMAP_ALL(vpin,mask) Bitwise ANDs the the vpin value with the mask value and is true if ALL the 1 bits in the mask are also 1 bits in the value.
e.g. IFBITMAP_ALL(1013,0x0f) would be true if ALL the last 4 bits of the value are 1s.
IFBITMAP_ANY(1013,0x0f) would be true if ANY of the last 4 bits are 1s.
Modifying bitmap values:
BITMAP_AND(vpin,mask) performs a bitwise AND operation.
BITMAP_OR(vpin,mask) performa a bitwise OR operation
BITMAP_XOR(vpin,mask) performs a bitwise EXCLUSIVE OR (which is basically a toggle)

View File

@@ -2,7 +2,7 @@
* © 2022-2025 Chris Harlow
* © 2022-2024 Harald Barth
* © 2023-2024 Paul M. Antoine
* © 2024 Herb Morton
* © 2024-2025 Herb Morton
* © 2023 Colin Murdoch
* All rights reserved.
*
@@ -557,6 +557,20 @@ void TrackManager::setTrackPower(TRACK_MODE trackmodeToMatch, POWERMODE powermod
}
if (didChange)
CommandDistributor::broadcastPower();
// re-initialize DC mode timer settings following powerON
if (powermode == POWERMODE::ON) {
#ifdef ARDUINO_ARCH_STM32
// for (byte i=0;i<=lastTrack;i++) {
// setTrackPowerF439ZI(i);
// }
// repeated in case the <F29..31 was set on a later track than power
// Note: this retains power but prevents speed doubling
for (byte i=0;i<lastTrack;i++) {
setTrackPowerF439ZI(i);
}
#endif
}
}
// Set track power for this track, inependent of mode
@@ -587,6 +601,20 @@ void TrackManager::setTrackPower(POWERMODE powermode, byte t) {
driver->setPower(powermode);
if (oldpower != driver->getPower())
CommandDistributor::broadcastPower();
// re-initialize DC mode timer settings following powerON
if (powermode == POWERMODE::ON) {
#ifdef ARDUINO_ARCH_STM32
for (byte i=0;i<=lastTrack;i++) {
setTrackPowerF439ZI(i);
}
// repeated in case the <F29..31 was set on a later track than power
// Note: this retains power but prevents speed doubling
for (byte i=0;i<lastTrack;i++) {
setTrackPowerF439ZI(i);
}
#endif
}
}
// returns state of the one and only prog track
@@ -709,3 +737,38 @@ TRACK_MODE TrackManager::getMode(byte t) {
int16_t TrackManager::returnDCAddr(byte t) {
return (trackDCAddr[t]);
}
// Set track power for EACH track, independent of mode
// This updates the settings so that speed is correct
// following a frequency change - DC mode
void TrackManager::setTrackPowerF439ZI(byte t) {
MotorDriver *driver=track[t];
if (driver == NULL) { // track is not defined at all
// DIAG(F("Error: Track %c does not exist"), t+'A');
return;
}
TRACK_MODE trackmode = driver->getMode();
POWERMODE powermode = driver->getPower(); // line added to enable processing for DC mode tracks
POWERMODE oldpower = driver->getPower();
//if (trackmode & TRACK_MODE_NONE) {
// driver->setBrake(true); // Track is unused. Brake is good to have.
// powermode = POWERMODE::OFF; // Track is unused. Force it to OFF
//} else
if (trackmode & TRACK_MODE_DC) { // includes inverted DC (called DCX)
if (powermode == POWERMODE::ON) {
driver->setBrake(true); // DC starts with brake on
applyDCSpeed(t); // speed match DCC throttles
}
}
//else /* MAIN PROG EXT BOOST */ {
// if (powermode == POWERMODE::ON) {
// // toggle brake before turning power on - resets overcurrent error
// // on the Pololu board if brake is wired to ^D2.
// driver->setBrake(true);
// driver->setBrake(false); // DCC runs with brake off
// }
//}
driver->setPower(powermode);
if (oldpower != driver->getPower())
CommandDistributor::broadcastPower();
}

View File

@@ -1,6 +1,7 @@
/*
* © 2022 Chris Harlow
* © 2022-2024 Harald Barth
* © 2025 Herb Morton
* © 2023 Colin Murdoch
*
* All rights reserved.
@@ -69,6 +70,7 @@ class TrackManager {
static void setTrackPower(TRACK_MODE trackmode, POWERMODE powermode);
static void setMainPower(POWERMODE mode) {setTrackPower(TRACK_MODE_MAIN, mode);}
static void setProgPower(POWERMODE mode) {setTrackPower(TRACK_MODE_PROG, mode);}
static void setTrackPowerF439ZI(byte t);
static const int16_t MAX_TRACKS=8;
static bool setTrackMode(byte track, TRACK_MODE mode, int16_t DCaddr=0);

View File

@@ -146,13 +146,6 @@ void halSetup() {
//PCF8574::create(200, 8, 0x23, 40);
// Alternative form to initialize 8 pins as output
// INT pin -1, when INT pin is not used
//PCF8574::create(200, 8, 0x23, -1, 255); // 8 pins High
//PCF8574::create(200, 8, 0x23, -1, 0); // 8 pins Low
//PCF8574::create(200, 8, 0x23, -1, 0b11000010);
// pins listed sequentially from 7 to 0
//=======================================================================
// The following directive defines a PCF8575 16-port I2C GPIO Extender module.

View File

@@ -3,10 +3,8 @@
#include "StringFormatter.h"
#define VERSION "5.5.25"
// - PCF8574 output pin initialization parameter
// 5.2.25 - IO_Bitmap and assicated Exrail macros
// 5.5.24 - SensorCAM in I2C scan and automatically setClock
#define VERSION "5.5.23"
// 5.5.24 - Sync DC mode tracks - Nucleo-F4
// 5.5.23 - Reminder loop Idle packet optimization
// 5.5.22 - (5.4.9) Handle non-compliant decoders returning 255 for cv 20 and confusing <R> with bad consist addresses.
// - DCC 5mS gap to same loco DCC packet restriction