From 5d1b3a7a0390456407c17bfa7da8ced44a06f28b Mon Sep 17 00:00:00 2001 From: Ash-4 Date: Tue, 28 Jan 2025 16:06:08 -0600 Subject: [PATCH 1/5] DC mode timer sync STM32 and JL command --- DCCEXParser.cpp | 7 ++++++- DCCTimerSTM32.cpp | 46 +++++++++++++++++++++++++++++++++++++++++++++- IODeviceList.h | 6 ++++-- MotorDriver.cpp | 4 ++-- TrackManager.cpp | 30 +++++++++++++++++++++++++++++- TrackManager.h | 3 +++ version.h | 4 +++- 7 files changed, 92 insertions(+), 8 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index f48d07b..f4f3fa5 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -2,7 +2,7 @@ * © 2022 Paul M Antoine * © 2021 Neil McKechnie * © 2021 Mike S - * © 2021-2024 Herb Morton + * © 2021-2025 Herb Morton * © 2020-2023 Harald Barth * © 2020-2021 M Steve Todd * © 2020-2021 Fred Decker @@ -788,6 +788,11 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) TrackManager::reportCurrent(stream); // return; + case "L"_hk: // track state and mA value on display + if (params<3) break; + TrackManager::reportCurrentLCD(p[1], p[2]); // Track power status + return; + case "A"_hk: // intercepted by EXRAIL// returns automations/routes if (params!=1) break; // StringFormatter::send(stream, F("\n")); diff --git a/DCCTimerSTM32.cpp b/DCCTimerSTM32.cpp index df00df3..8092160 100644 --- a/DCCTimerSTM32.cpp +++ b/DCCTimerSTM32.cpp @@ -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,17 @@ #include "DIAG.h" #include +// Function prototypes +void refreshDCmodeTimers(); +void resetCounterDCmodeTimers(); + +HardwareTimer *Timer1 = new HardwareTimer(TIM1); +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 +302,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 @@ -329,6 +341,7 @@ void DCCTimer::DCCEXanalogWriteFrequencyInternal(uint8_t pin, uint32_t frequency { 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); + resetCounterDCmodeTimers(); } else DIAG(F("DCCEXanalogWriteFrequency::failed to allocate HardwareTimer instance!")); @@ -341,6 +354,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 +379,9 @@ 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 +676,31 @@ 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, 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(); + 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; + TIM4->CNT = 0; + TIM9->CNT = 0; + #if defined(TIM13) + TIM13->CNT = 0; + #endif +} + #endif diff --git a/IODeviceList.h b/IODeviceList.h index 2b82e1b..f02a0d6 100644 --- a/IODeviceList.h +++ b/IODeviceList.h @@ -1,5 +1,7 @@ /* - * © 2024, Chris Harlow. All rights reserved. + * © 2024, Chris Harlow. + * © 2025 Herb Morton + * All rights reserved. * * This file is part of CommandStation-EX * @@ -35,4 +37,4 @@ It has been moved here to be easier to maintain than editing IODevice.h #include "IO_EXSensorCAM.h" #include "IO_DS1307.h" #include "IO_I2CRailcom.h" - +#include "IO_HALDisplay.h" diff --git a/MotorDriver.cpp b/MotorDriver.cpp index 39ec08d..b7df4a1 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -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); diff --git a/TrackManager.cpp b/TrackManager.cpp index b793de1..e6001c1 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -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. * @@ -42,6 +42,7 @@ MotorDriver * TrackManager::track[MAX_TRACKS] = { NULL }; int16_t TrackManager::trackDCAddr[MAX_TRACKS] = { 0 }; +int16_t TrackManager::tPwr_mA[8]={0,0,0,0,0,0,0,0}; int8_t TrackManager::lastTrack=-1; bool TrackManager::progTrackSyncMain=false; @@ -646,6 +647,33 @@ void TrackManager::reportCurrent(Print* stream) { StringFormatter::send(stream,F(">\n")); } +void TrackManager::reportCurrentLCD(uint8_t display, byte row) { + FOR_EACH_TRACK(t) { + bool pstate = TrackManager::isPowerOn(t); // checks if power is on or off + TRACK_MODE tMode=(TrackManager::getMode(t)); // gets to current power mode + int16_t DCAddr=(TrackManager::returnDCAddr(t)); + + if (pstate) { // if power is on do this section + tPwr_mA[t]=(3*tPwr_mA[t]>>2) + ((track[t]->getPower()==POWERMODE::OVERLOAD) ? -1 : + track[t]->raw2mA(track[t]->getCurrentRaw(false))); + if (tMode & TRACK_MODE_DC) { // Test if track is in DC or DCX mode + SCREEN(display, row+t, F("%c: %S %d ON %dmA"), t+'A', (TrackManager::getModeName(tMode)),DCAddr, tPwr_mA[t]>>2); + } + else { // formats without DCAddress + SCREEN(display, row+t, F("%c: %S ON %dmA"), t+'A', (TrackManager::getModeName(tMode)), tPwr_mA[t]>>2); + } + } + /* else { // if power is off do this section + if (tMode & TRACK_MODE_DC) { // DC / DCX + SCREEN(display, row+t, F("Track %c: %S %d OFF"), t+'A', (TrackManager::getModeName(tMode)),DCAddr); + } + else { // Not DC or DCX + SCREEN(display, row+t, F("Track %c: %S OFF"), t+'A', (TrackManager::getModeName(tMode))); + } + } */ + } + } + void TrackManager::reportGauges(Print* stream) { StringFormatter::send(stream,F(" command private: static void addTrack(byte t, MotorDriver* driver); diff --git a/version.h b/version.h index ea1326d..8a58a90 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,9 @@ #include "StringFormatter.h" -#define VERSION "5.5.12" +#define VERSION "5.5.12 F439" +// - Nucleo-F4 DC mode timer sync +// - Track power status // 5.5.12 - Websocket support (wifi only) // 5.5.11 - (5.4.2) accessory command reverse // 5.5.10 - CamParser fix From da66469faa2019df173b1b026d832228688a74d0 Mon Sep 17 00:00:00 2001 From: Ash-4 Date: Wed, 29 Jan 2025 00:34:45 -0600 Subject: [PATCH 2/5] DC mode timer sync STM32 --- DCCTimerSTM32.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/DCCTimerSTM32.cpp b/DCCTimerSTM32.cpp index 8092160..a7976e7 100644 --- a/DCCTimerSTM32.cpp +++ b/DCCTimerSTM32.cpp @@ -37,11 +37,15 @@ #include "DIAG.h" #include +// 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) @@ -685,6 +689,8 @@ void ADCee::begin() { // 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) @@ -696,6 +702,8 @@ void refreshDCmodeTimers() { 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) From a3bd5ac86f8fc1ef32bc97486e18e069f9ba7edd Mon Sep 17 00:00:00 2001 From: Ash-4 Date: Wed, 29 Jan 2025 00:54:16 -0600 Subject: [PATCH 3/5] DC mode timer sync STM32 --- TrackManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TrackManager.cpp b/TrackManager.cpp index e6001c1..25004a5 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -663,14 +663,14 @@ void TrackManager::reportCurrentLCD(uint8_t display, byte row) { SCREEN(display, row+t, F("%c: %S ON %dmA"), t+'A', (TrackManager::getModeName(tMode)), tPwr_mA[t]>>2); } } - /* else { // if power is off do this section + else { // if power is off do this section if (tMode & TRACK_MODE_DC) { // DC / DCX SCREEN(display, row+t, F("Track %c: %S %d OFF"), t+'A', (TrackManager::getModeName(tMode)),DCAddr); } else { // Not DC or DCX SCREEN(display, row+t, F("Track %c: %S OFF"), t+'A', (TrackManager::getModeName(tMode))); } - } */ + } } } From 56a339a5985fc4c6a278bfff467110cae34c125c Mon Sep 17 00:00:00 2001 From: Ash-4 Date: Sat, 1 Feb 2025 17:28:50 -0600 Subject: [PATCH 4/5] Nucleo-F4 Timer Sync - speed fix --- DCCEXParser.cpp | 18 +++++++++++++++++- DCCTimerSTM32.cpp | 3 ++- MotorDriver.cpp | 2 ++ TrackManager.cpp | 35 +++++++++++++++++++++++++++++++++++ TrackManager.h | 1 + 5 files changed, 57 insertions(+), 2 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index f4f3fa5..1202506 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -631,7 +631,23 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) else break; // will reply } //TrackManager::streamTrackState(NULL,t); - + + TrackManager::setTrackPowerF439ZI(0); + TrackManager::setTrackPowerF439ZI(1); + TrackManager::setTrackPowerF439ZI(2); + TrackManager::setTrackPowerF439ZI(3); + TrackManager::setTrackPowerF439ZI(4); + TrackManager::setTrackPowerF439ZI(5); + TrackManager::setTrackPowerF439ZI(6); + TrackManager::setTrackPowerF439ZI(7); + TrackManager::setTrackPowerF439ZI(0); // repeated in case the 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 @@ -353,6 +353,7 @@ void DCCTimer::DCCEXanalogWriteFrequencyInternal(uint8_t pin, uint32_t frequency else { // Frequency change request + //DIAG(F("DCCEXanalogWriteFrequency_356::pin %d frequency %d"), pin, frequency); if (frequency != channel_frequency[pin]) { pinmap_pinout(digitalPinToPinName(pin), PinMap_TIM); // ensure the pin has been configured! diff --git a/MotorDriver.cpp b/MotorDriver.cpp index b7df4a1..550b874 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -371,7 +371,9 @@ void MotorDriver::setDCSignal(byte speedcode, uint8_t frequency /*default =0*/) } #endif //DIAG(F("Brake pin %d value %d freqency %d"), brakePin, brake, f); + //DIAG(F("MotorDriver_cpp_374_DCCEXanalogWriteFequency::Pin %d, frequency %d, tSpeed %d"), brakePin, f, tSpeed); DCCTimer::DCCEXanalogWriteFrequency(brakePin, f); // set DC PWM frequency + //DIAG(F("MotorDriver_cpp_375_DCCEXanalogWrite::brakePin %d, frequency %d, invertBrake"), brakePin, brake, invertBrake); DCCTimer::DCCEXanalogWrite(brakePin, brake, invertBrake); // line swapped to set frequency first #else // all AVR here DCCTimer::DCCEXanalogWriteFrequency(brakePin, frequency); // frequency steps diff --git a/TrackManager.cpp b/TrackManager.cpp index 25004a5..241ab90 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -738,3 +738,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(); +} \ No newline at end of file diff --git a/TrackManager.h b/TrackManager.h index 5d6b907..8009c38 100644 --- a/TrackManager.h +++ b/TrackManager.h @@ -67,6 +67,7 @@ class TrackManager { static void setPower(POWERMODE mode) {setMainPower(mode); setProgPower(mode);} static void setTrackPower(POWERMODE mode, byte t); + static void setTrackPowerF439ZI(byte t); 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);} From 7f488de06ebe4defb9f116dd5bb1800f910a1cbc Mon Sep 17 00:00:00 2001 From: Ash-4 Date: Tue, 11 Feb 2025 01:09:11 -0600 Subject: [PATCH 5/5] Nucleo-F4 Timer sync F439 build flag --- DCCEXParser.cpp | 26 +++++++++++--------------- platformio.ini | 20 ++++++++++---------- version.h | 3 ++- 3 files changed, 23 insertions(+), 26 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 1202506..4bb7fc3 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -632,21 +632,17 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) } //TrackManager::streamTrackState(NULL,t); - TrackManager::setTrackPowerF439ZI(0); - TrackManager::setTrackPowerF439ZI(1); - TrackManager::setTrackPowerF439ZI(2); - TrackManager::setTrackPowerF439ZI(3); - TrackManager::setTrackPowerF439ZI(4); - TrackManager::setTrackPowerF439ZI(5); - TrackManager::setTrackPowerF439ZI(6); - TrackManager::setTrackPowerF439ZI(7); - TrackManager::setTrackPowerF439ZI(0); // repeated in case the Track power status +// 5.5.13 - Update STM32duino core to v19.0.0. for updated PeripheralPins.c in preparation for F429/439ZI Ethernet support // 5.5.12 - Websocket support (wifi only) // 5.5.11 - (5.4.2) accessory command reverse // 5.5.10 - CamParser fix