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