Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 53 additions & 2 deletions DCCTimerSTM32.cpp
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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!"));
Expand All @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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
4 changes: 2 additions & 2 deletions MotorDriver.cpp
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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);
Expand Down
65 changes: 64 additions & 1 deletion TrackManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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();
}
2 changes: 2 additions & 0 deletions TrackManager.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/*
* © 2022 Chris Harlow
* © 2022-2024 Harald Barth
* © 2025 Herb Morton
* © 2023 Colin Murdoch
*
* All rights reserved.
Expand Down Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions version.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "StringFormatter.h"

#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
Expand Down