From fdc956576b3478291fe33ad0d072af77f3de5865 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Fri, 5 Apr 2024 01:02:49 +0200 Subject: [PATCH] ESP32 rewrite PWM LEDC to use pin mux --- DCCTimer.h | 4 +++- DCCTimerESP.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++++---- MotorDriver.cpp | 6 +++--- MotorDriver.h | 3 ++- TrackManager.cpp | 23 +++++++++++++++++++-- 5 files changed, 79 insertions(+), 11 deletions(-) diff --git a/DCCTimer.h b/DCCTimer.h index 5cf1026..984d6eb 100644 --- a/DCCTimer.h +++ b/DCCTimer.h @@ -66,7 +66,9 @@ class DCCTimer { static void ackRailcomTimer(); static void DCCEXanalogWriteFrequency(uint8_t pin, uint32_t frequency); static void DCCEXanalogWrite(uint8_t pin, int value); - + static void DCCEXledcDetachPin(uint8_t pin); + static void DCCEXanalogCopyChannel(uint8_t frompin, uint8_t topin); + static void DCCEXInrushControlOn(uint8_t pin); // Update low ram level. Allow for extra bytes to be specified // by estimation or inspection, that may be used by other // called subroutines. Must be called with interrupts disabled. diff --git a/DCCTimerESP.cpp b/DCCTimerESP.cpp index ae81c74..3e8d49e 100644 --- a/DCCTimerESP.cpp +++ b/DCCTimerESP.cpp @@ -78,6 +78,7 @@ int DCCTimer::freeMemory() { //////////////////////////////////////////////////////////////////////// #ifdef ARDUINO_ARCH_ESP32 +#include "DIAG.h" #include #include #include @@ -154,8 +155,10 @@ void DCCTimer::reset() { void DCCTimer::DCCEXanalogWriteFrequency(uint8_t pin, uint32_t f) { if (f >= 16) DCCTimer::DCCEXanalogWriteFrequencyInternal(pin, f); - else if (f == 7) +/* + else if (f == 7) // not used on ESP32 DCCTimer::DCCEXanalogWriteFrequencyInternal(pin, 62500); +*/ else if (f >= 4) DCCTimer::DCCEXanalogWriteFrequencyInternal(pin, 32000); else if (f >= 3) @@ -188,22 +191,65 @@ void DCCTimer::DCCEXanalogWriteFrequencyInternal(uint8_t pin, uint32_t frequency } } +void DCCTimer::DCCEXledcDetachPin(uint8_t pin) { + DIAG(F("Clear pin %d channel"), pin); + pin_to_channel[pin] = 0; + pinMatrixOutDetach(pin, false, false); +} + + +void DCCTimer::DCCEXanalogCopyChannel(uint8_t frompin, uint8_t topin) { + DIAG(F("Pin %d copied to %d channel %d"), frompin, topin, pin_to_channel[frompin]); + pin_to_channel[topin] = pin_to_channel[frompin]; + ledcAttachPin(topin, pin_to_channel[topin]); +} void DCCTimer::DCCEXanalogWrite(uint8_t pin, int value) { + // This allocates channels 15, 13, 11, .... + // so each channel gets its own timer. if (pin < SOC_GPIO_PIN_COUNT) { if (pin_to_channel[pin] == 0) { + int search_channel; + int n; if (!cnt_channel) { log_e("No more PWM channels available! All %u already used", LEDC_CHANNELS); return; } - pin_to_channel[pin] = --cnt_channel; - ledcSetup(cnt_channel, 1000, 8); - ledcAttachPin(pin, cnt_channel); + // search for free channels top down + for (search_channel=LEDC_CHANNELS-1; search_channel >=cnt_channel; search_channel -= 2) { + bool chanused = false; + for (n=0; n < SOC_GPIO_PIN_COUNT; n++) { + if (pin_to_channel[n] == search_channel) { // current search_channel used + chanused = true; + break; + } + } + if (chanused) + continue; + if (n == SOC_GPIO_PIN_COUNT) // current search_channel unused + break; + } + if (search_channel >= cnt_channel) { + pin_to_channel[pin] = search_channel; + DIAG(F("Pin %d assigned to search channel %d"), pin, search_channel); + } else { + pin_to_channel[pin] = --cnt_channel; // This sets 15, 13, ... + DIAG(F("Pin %d assigned to new channel %d"), pin, cnt_channel); + --cnt_channel; // Now we are at 14, 12, ... + } + ledcSetup(pin_to_channel[pin], 1000, 8); + ledcAttachPin(pin, pin_to_channel[pin]); } else { + //DIAG(F("Pin %d assigned to old channel %d"), pin, pin_to_channel[pin]); ledcAttachPin(pin, pin_to_channel[pin]); } ledcWrite(pin_to_channel[pin], value); } } +void DCCTimer::DCCEXInrushControlOn(uint8_t pin) { + ledcSetup(0, 62500, 8); + ledcAttachPin(pin, 0); + ledcWrite(0, 207); +} int ADCee::init(uint8_t pin) { pinMode(pin, ANALOG); diff --git a/MotorDriver.cpp b/MotorDriver.cpp index c233c22..afd8e6e 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -411,10 +411,10 @@ void MotorDriver::throttleInrush(bool on) { duty = 255-duty; #if defined(ARDUINO_ARCH_ESP32) if(on) { - DCCTimer::DCCEXanalogWrite(brakePin,duty); - DCCTimer::DCCEXanalogWriteFrequency(brakePin, 7); // 7 means max + DCCTimer::DCCEXInrushControlOn(brakePin); } else { - ledcDetachPin(brakePin); + ledcDetachPin(brakePin); // not DCCTimer::DCCEXledcDetachPin() as we have not + // registered the pin in the pin to channel array } #elif defined(ARDUINO_ARCH_STM32) if(on) { diff --git a/MotorDriver.h b/MotorDriver.h index 4491164..945e4ee 100644 --- a/MotorDriver.h +++ b/MotorDriver.h @@ -193,13 +193,14 @@ class MotorDriver { } }; inline pinpair getSignalPin() { return pinpair(signalPin,signalPin2); }; + inline byte getBrakePin() { return brakePin; }; void setDCSignal(byte speedByte, uint8_t frequency=0); void throttleInrush(bool on); inline void detachDCSignal() { #if defined(__arm__) pinMode(brakePin, OUTPUT); #elif defined(ARDUINO_ARCH_ESP32) - ledcDetachPin(brakePin); + DCCTimer::DCCEXledcDetachPin(brakePin); #else setDCSignal(128); #endif diff --git a/TrackManager.cpp b/TrackManager.cpp index 338b11c..21fe44a 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -252,13 +252,32 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr track[trackToSet]->makeProgTrack(false); // only the prog track knows it's type } track[trackToSet]->setMode(mode); - trackDCAddr[trackToSet]=dcAddr; // When a track is switched, we must clear any side effects of its previous // state, otherwise trains run away or just dont move. // This can be done BEFORE the PWM-Timer evaluation (methinks) - if (!(mode & TRACK_MODE_DC)) { + if (mode & TRACK_MODE_DC) { + if (trackDCAddr[trackToSet] != dcAddr) { + // if we change dcAddr, detach first old signal + track[trackToSet]->detachDCSignal(); +#ifdef ARDUINO_ARCH_ESP32 + int trackfound = -1; + FOR_EACH_TRACK(t) { + if ((track[t]->getMode() & TRACK_MODE_DC) && trackDCAddr[t] == dcAddr) { + trackfound = t; + break; + } + } + if (trackfound > -1) { + DCCTimer::DCCEXanalogCopyChannel(track[trackfound]->getBrakePin(), + track[trackToSet]->getBrakePin()); + } +#endif + } + // set future DC Addr; + trackDCAddr[trackToSet]=dcAddr; + } else { // DCC tracks need to have set the PWM to zero or they will not work. track[trackToSet]->detachDCSignal(); track[trackToSet]->setBrake(false);