diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index aa635ae..aaf733c 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -219,6 +219,9 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream *ringStream) { void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) { +#ifdef DISABLE_PROG + (void)ringStream; +#endif #ifndef DISABLE_EEPROM (void)EEPROM; // tell compiler not to warn this is unused #endif diff --git a/DCCTimerSTM32.cpp b/DCCTimerSTM32.cpp index cc60547..cffae40 100644 --- a/DCCTimerSTM32.cpp +++ b/DCCTimerSTM32.cpp @@ -35,7 +35,7 @@ #endif #include "DIAG.h" -#if defined(ARDUINO_NUCLEO_F411RE) +#if defined(ARDUINO_NUCLEO_F401RE) || defined(ARDUINO_NUCLEO_F411RE) // Nucleo-64 boards don't have additional serial ports defined by default HardwareSerial Serial1(PB7, PA15); // Rx=PB7, Tx=PA15 -- CN7 pins 17 and 21 - F411RE // Serial2 is defined to use USART2 by default, but is in fact used as the diag console @@ -52,7 +52,7 @@ HardwareSerial Serial6(PA12, PA11); // Rx=PA12, Tx=PA11 -- CN10 pins 12 and 14 HardwareSerial Serial3(PC11, PC10); // Rx=PC11, Tx=PC10 -- USART3 - F446RE HardwareSerial Serial5(PD2, PC12); // Rx=PC7, Tx=PC6 -- UART5 - F446RE // On the F446RE, Serial4 and Serial6 also use pins we can't readily map while using the Arduino pins -#elif defined(ARDUINO_NUCLEO_F412ZG) || defined(ARDUINO_NUCLEO_F429ZI) || defined(ARDUINO_NUCLEO_F446ZE) +#elif defined(ARDUINO_NUCLEO_F413ZH) || defined(ARDUINO_NUCLEO_F429ZI) || defined(ARDUINO_NUCLEO_F446ZE)|| defined(ARDUINO_NUCLEO_F412ZG) // Nucleo-144 boards don't have Serial1 defined by default HardwareSerial Serial6(PG9, PG14); // Rx=PG9, Tx=PG14 -- USART6 // Serial3 is defined to use USART3 by default, but is in fact used as the diag console diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 76dcf44..ae005b5 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202307250927Z" +#define GITHUB_SHA "3bddf4d" diff --git a/IO_PCA9555.h b/IO_PCA9555.h new file mode 100644 index 0000000..137e287 --- /dev/null +++ b/IO_PCA9555.h @@ -0,0 +1,112 @@ +/* + * © 2021, Neil McKechnie. 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 . + */ + +#ifndef io_pca9555_h +#define io_pca9555_h + +#include "IO_GPIOBase.h" +#include "FSH.h" + +///////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * IODevice subclass for PCA9555 16-bit I/O expander (NXP & Texas Instruments). + */ + +class PCA9555 : public GPIOBase { +public: + static void create(VPIN vpin, int nPins, uint8_t I2CAddress, int interruptPin=-1) { + new PCA9555(vpin, min(nPins,16), I2CAddress, interruptPin); + } + + // Constructor + PCA9555(VPIN vpin, int nPins, uint8_t I2CAddress, int interruptPin=-1) + : GPIOBase((FSH *)F("PCA9555"), vpin, nPins, I2CAddress, interruptPin) + { + requestBlock.setRequestParams(_I2CAddress, inputBuffer, sizeof(inputBuffer), + outputBuffer, sizeof(outputBuffer)); + outputBuffer[0] = REG_INPUT_P0; + } + +private: + void _writeGpioPort() override { + I2CManager.write(_I2CAddress, 3, REG_OUTPUT_P0, _portOutputState, _portOutputState>>8); + } + void _writePullups() override { + // Do nothing, pull-ups are always in place for input ports + // This function is here for HAL GPIOBase API compatibilitiy + + } + void _writePortModes() override { + // Write 0 to REG_CONF_P0 & REG_CONF_P1 for in-use pins that are outputs, 1 for others. + // PCA9555 & TCA9555, Interrupt is always enabled for raising and falling edge + uint16_t temp = ~(_portMode & _portInUse); + I2CManager.write(_I2CAddress, 3, REG_CONF_P0, temp, temp>>8); + } + void _readGpioPort(bool immediate) override { + if (immediate) { + uint8_t buffer[2]; + I2CManager.read(_I2CAddress, buffer, 2, 1, REG_INPUT_P0); + _portInputState = ((uint16_t)buffer[1]<<8) | buffer[0]; + /* PCA9555 Int bug fix, from PCA9555 datasheet: "must change command byte to something besides 00h + * after a Read operation to the PCA9555 device or before reading from + * another device" + * Recommended solution, read from REG_OUTPUT_P0, then do nothing with the received data + * Issue not seen during testing, uncomment if needed + */ + //I2CManager.read(_I2CAddress, buffer, 2, 1, REG_OUTPUT_P0); + } else { + // Queue new request + requestBlock.wait(); // Wait for preceding operation to complete + // Issue new request to read GPIO register + I2CManager.queueRequest(&requestBlock); + } + } + // This function is invoked when an I/O operation on the requestBlock completes. + void _processCompletion(uint8_t status) override { + if (status == I2C_STATUS_OK) + _portInputState = ((uint16_t)inputBuffer[1]<<8) | inputBuffer[0]; + else + _portInputState = 0xffff; + } + + void _setupDevice() override { + // HAL API calls + _writePortModes(); + _writePullups(); + _writeGpioPort(); + } + + uint8_t inputBuffer[2]; + uint8_t outputBuffer[1]; + + + enum { + REG_INPUT_P0 = 0x00, + REG_INPUT_P1 = 0x01, + REG_OUTPUT_P0 = 0x02, + REG_OUTPUT_P1 = 0x03, + REG_POL_INV_P0 = 0x04, + REG_POL_INV_P1 = 0x05, + REG_CONF_P0 = 0x06, + REG_CONF_P1 = 0x07, + }; + +}; + +#endif diff --git a/MotorDriver.cpp b/MotorDriver.cpp index 5b51778..d5dca13 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -291,14 +291,39 @@ uint16_t taurustones[28] = { 165, 175, 196, 220, void MotorDriver::setDCSignal(byte speedcode) { if (brakePin == UNUSED_PIN) return; + switch(brakePin) { #if defined(ARDUINO_AVR_UNO) - TCCR2B = (TCCR2B & B11111000) | B00000110; // set divisor on timer 2 to result in (approx) 122.55Hz + // Not worth doin something here as: + // If we are on pin 9 or 10 we are on Timer1 and we can not touch Timer1 as that is our DCC source. + // If we are on pin 5 or 6 we are on Timer 0 ad we can not touch Timer0 as that is millis() etc. + // We are most likely not on pin 3 or 11 as no known motor shield has that as brake. #endif #if defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560) - TCCR2B = (TCCR2B & B11111000) | B00000110; // set divisor on timer 2 to result in (approx) 122.55Hz - TCCR4B = (TCCR4B & B11111000) | B00000100; // same for timer 4 but maxcount and thus divisor differs - TCCR5B = (TCCR5B & B11111000) | B00000100; // same for timer 5 which is like timer 4 + case 9: + case 10: + // Timer2 (is differnet) + TCCR2A = (TCCR2A & B11111100) | B00000001; // set WGM1=0 and WGM0=1 phase correct PWM + TCCR2B = (TCCR2B & B11110000) | B00000110; // set WGM2=0 ; set divisor on timer 2 to 1/256 for 122.55Hz + //DIAG(F("2 A=%x B=%x"), TCCR2A, TCCR2B); + break; + case 6: + case 7: + case 8: + // Timer4 + TCCR4A = (TCCR4A & B11111100) | B00000001; // set WGM0=1 and WGM1=0 for normal PWM 8-bit + TCCR4B = (TCCR4B & B11100000) | B00000100; // set WGM2=0 and WGM3=0 for normal PWM 8 bit and div 1/256 for 122.55Hz + break; + case 46: + case 45: + case 44: + // Timer5 + TCCR5A = (TCCR5A & B11111100) | B00000001; // set WGM0=1 and WGM1=0 for normal PWM 8-bit + TCCR5B = (TCCR5B & B11100000) | B00000100; // set WGM2=0 and WGM3=0 for normal PWM 8 bit and div 1/256 for 122.55Hz + break; #endif + default: + break; + } // spedcoode is a dcc speed & direction byte tSpeed=speedcode & 0x7F; // DCC Speed with 0,1 stop and speed steps 2 to 127 byte tDir=speedcode & 0x80; @@ -368,14 +393,39 @@ void MotorDriver::throttleInrush(bool on) { } #else if(on){ + switch(brakePin) { #if defined(ARDUINO_AVR_UNO) - TCCR2B = (TCCR2B & B11111000) | B00000001; // div 1 is max + // Not worth doin something here as: + // If we are on pin 9 or 10 we are on Timer1 and we can not touch Timer1 as that is our DCC source. + // If we are on pin 5 or 6 we are on Timer 0 ad we can not touch Timer0 as that is millis() etc. + // We are most likely not on pin 3 or 11 as no known motor shield has that as brake. #endif #if defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560) - TCCR2B = (TCCR2B & B11111000) | B00000001; // div 1 is max - TCCR4B = (TCCR4B & B11111000) | B00000001; // div 1 is max - TCCR5B = (TCCR5B & B11111000) | B00000001; // div 1 is max + case 9: + case 10: + // Timer2 (is different) + TCCR2A = (TCCR2A & B11111100) | B00000011; // set WGM0=1 and WGM1=1 for fast PWM + TCCR2B = (TCCR2B & B11110000) | B00000001; // set WGM2=0 and prescaler div=1 (max) + DIAG(F("2 A=%x B=%x"), TCCR2A, TCCR2B); + break; + case 6: + case 7: + case 8: + // Timer4 + TCCR4A = (TCCR4A & B11111100) | B00000001; // set WGM0=1 and WGM1=0 for fast PWM 8-bit + TCCR4B = (TCCR4B & B11100000) | B00001001; // set WGM2=1 and WGM3=0 for fast PWM 8 bit and div=1 (max) + break; + case 46: + case 45: + case 44: + // Timer5 + TCCR5A = (TCCR5A & B11111100) | B00000001; // set WGM0=1 and WGM1=0 for fast PWM 8-bit + TCCR5B = (TCCR5B & B11100000) | B00001001; // set WGM2=1 and WGM3=0 for fast PWM 8 bit and div=1 (max) + break; #endif + default: + break; + } } analogWrite(brakePin,duty); #endif diff --git a/MotorDriver.h b/MotorDriver.h index b8de0b0..21bceb6 100644 --- a/MotorDriver.h +++ b/MotorDriver.h @@ -28,7 +28,7 @@ #include "DCCTimer.h" // use powers of two so we can do logical and/or on the track modes in if clauses. -enum TRACK_MODE : byte {TRACK_MODE_OFF = 1, TRACK_MODE_MAIN = 2, TRACK_MODE_PROG = 4, +enum TRACK_MODE : byte {TRACK_MODE_NONE = 1, TRACK_MODE_MAIN = 2, TRACK_MODE_PROG = 4, TRACK_MODE_DC = 8, TRACK_MODE_DCX = 16, TRACK_MODE_EXT = 32}; #define setHIGH(fastpin) *fastpin.inout |= fastpin.maskHIGH @@ -290,7 +290,7 @@ class MotorDriver { static const int TRIP_CURRENT_PROG=250; unsigned long power_sample_overload_wait = POWER_SAMPLE_OVERLOAD_WAIT; unsigned int power_good_counter = 0; - TRACK_MODE trackMode = TRACK_MODE_OFF; // we assume off at startup + TRACK_MODE trackMode = TRACK_MODE_NONE; // we assume track not assigned at startup }; #endif diff --git a/TrackManager.cpp b/TrackManager.cpp index 8588383..0f69235 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -38,6 +38,7 @@ const int16_t HASH_KEYWORD_PROG = -29718; #endif const int16_t HASH_KEYWORD_MAIN = 11339; const int16_t HASH_KEYWORD_OFF = 22479; +const int16_t HASH_KEYWORD_NONE = -26550; const int16_t HASH_KEYWORD_DC = 2183; const int16_t HASH_KEYWORD_DCX = 6463; // DC reversed polarity const int16_t HASH_KEYWORD_EXT = 8201; // External DCC signal @@ -140,7 +141,7 @@ void TrackManager::addTrack(byte t, MotorDriver* driver) { track[t]=driver; if (driver) { track[t]->setPower(POWERMODE::OFF); - track[t]->setMode(TRACK_MODE_OFF); + track[t]->setMode(TRACK_MODE_NONE); track[t]->setTrackLetter('A'+t); lastTrack=t; } @@ -181,7 +182,7 @@ void TrackManager::setPROGSignal( bool on) { // with interrupts turned off around the critical section void TrackManager::setDCSignal(int16_t cab, byte speedbyte) { FOR_EACH_TRACK(t) { - if (trackDCAddr[t]!=cab) continue; + if (trackDCAddr[t]!=cab && cab != 0) continue; if (track[t]->getMode()==TRACK_MODE_DC) track[t]->setDCSignal(speedbyte); else if (track[t]->getMode()==TRACK_MODE_DCX) track[t]->setDCSignal(speedbyte ^ 128); } @@ -192,11 +193,16 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr //DIAG(F("Track=%c Mode=%d"),trackToSet+'A', mode); // DC tracks require a motorDriver that can set brake! - if ((mode==TRACK_MODE_DC || mode==TRACK_MODE_DCX) - && !track[trackToSet]->brakeCanPWM()) { - DIAG(F("Brake pin can't PWM: No DC")); - return false; - } + if (mode==TRACK_MODE_DC || mode==TRACK_MODE_DCX) { +#if defined(ARDUINO_AVR_UNO) + DIAG(F("Uno has no PWM timers available for DC")); + return false; +#endif + if (!track[trackToSet]->brakeCanPWM()) { + DIAG(F("Brake pin can't PWM: No DC")); + return false; + } + } #ifdef ARDUINO_ARCH_ESP32 // remove pin from MUX matrix and turn it off @@ -219,7 +225,7 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr FOR_EACH_TRACK(t) if (track[t]->getMode()==TRACK_MODE_PROG && t != trackToSet) { track[t]->setPower(POWERMODE::OFF); - track[t]->setMode(TRACK_MODE_OFF); + track[t]->setMode(TRACK_MODE_NONE); track[t]->makeProgTrack(false); // revoke prog track special handling streamTrackState(NULL,t); } @@ -327,8 +333,8 @@ bool TrackManager::parseJ(Print *stream, int16_t params, int16_t p[]) return setTrackMode(p[0],TRACK_MODE_PROG); #endif - if (params==2 && p[1]==HASH_KEYWORD_OFF) // <= id OFF> - return setTrackMode(p[0],TRACK_MODE_OFF); + if (params==2 && (p[1]==HASH_KEYWORD_OFF || p[1]==HASH_KEYWORD_NONE)) // <= id OFF> <= id NONE> + return setTrackMode(p[0],TRACK_MODE_NONE); if (params==2 && p[1]==HASH_KEYWORD_EXT) // <= id EXT> return setTrackMode(p[0],TRACK_MODE_EXT); @@ -355,8 +361,8 @@ void TrackManager::streamTrackState(Print* stream, byte t) { format=F("<= %c PROG>\n"); break; #endif - case TRACK_MODE_OFF: - format=F("<= %c OFF>\n"); + case TRACK_MODE_NONE: + format=F("<= %c NONE>\n"); break; case TRACK_MODE_EXT: format=F("<= %c EXT>\n"); @@ -438,7 +444,7 @@ void TrackManager::setPower2(bool setProg,POWERMODE mode) { driver->setBrake(false); driver->setPower(mode); break; - case TRACK_MODE_OFF: + case TRACK_MODE_NONE: break; } } diff --git a/WifiInterface.cpp b/WifiInterface.cpp index 20dc235..7511af6 100644 --- a/WifiInterface.cpp +++ b/WifiInterface.cpp @@ -60,7 +60,7 @@ Stream * WifiInterface::wifiStream; #if defined(ARDUINO_ARCH_STM32) // Handle serial ports availability on STM32 for variants! // #undef NUM_SERIAL -#if defined(ARDUINO_NUCLEO_F411RE) +#if defined(ARDUINO_NUCLEO_F401RE) || defined(ARDUINO_NUCLEO_F411RE) #define NUM_SERIAL 3 #define SERIAL1 Serial1 #define SERIAL3 Serial6 @@ -68,9 +68,11 @@ Stream * WifiInterface::wifiStream; #define NUM_SERIAL 3 #define SERIAL1 Serial3 #define SERIAL3 Serial5 -#elif defined(ARDUINO_NUCLEO_F412ZG) || defined(ARDUINO_NUCLEO_F429ZI) || defined(ARDUINO_NUCLEO_F446ZE) +#elif defined(ARDUINO_NUCLEO_F413ZH) || defined(ARDUINO_NUCLEO_F429ZI) || defined(ARDUINO_NUCLEO_F446ZE) || defined(ARDUINO_NUCLEO_F412ZG) #define NUM_SERIAL 2 #define SERIAL1 Serial6 +#else +#warning This variant of Nucleo not yet explicitly supported #endif #endif diff --git a/version.h b/version.h index e6cb735..449b18e 100644 --- a/version.h +++ b/version.h @@ -4,6 +4,13 @@ #include "StringFormatter.h" #define VERSION "5.0.1LCC" +// 5.0.0 - Make 4.2.69 the 5.0.0 release +// 4.2.69 - Bugfix: Make work in DC mode +// 4.2.68 - Rename track mode OFF to NONE +// 4.2.67 - AVR: Pin specific timer register seting +// - Protect Uno user from choosing DC(X) +// - More Nucleo variant defines +// - GPIO PCA9555 / TCA9555 support // 4.2.66 - Throttle inrush current by applying PWM to brake pin when // fault pin goes active // 4.2.65 - new config WIFI_FORCE_AP option