1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2025-07-31 11:23:44 +02:00

Compare commits

..

3 Commits

Author SHA1 Message Date
pmantoine
ebfc59387f Add Mega definitions to allow compilation 2024-04-18 15:48:08 +08:00
pmantoine
c35a6aeedc Update WiFi_NINA with more debugged versions 2023-11-23 15:49:54 +08:00
pmantoine
d206cd537c Initial cut of WifiNINA support 2023-10-02 06:14:29 +08:00
34 changed files with 314 additions and 4477 deletions

2
.gitignore vendored
View File

@@ -13,5 +13,3 @@ myFilter.cpp
my*.h
!my*.example.h
compile_commands.json
newcode.txt.old
UserAddin.txt

View File

@@ -162,7 +162,7 @@ void CommandDistributor::broadcastTurnout(int16_t id, bool isClosed ) {
}
void CommandDistributor::broadcastTurntable(int16_t id, uint8_t position, bool moving) {
broadcastReply(COMMAND_TYPE, F("<I %d %d %d>\n"), id, position, moving);
broadcastReply(COMMAND_TYPE, F("<i %d %d %d>\n"), id, position, moving);
}
void CommandDistributor::broadcastClockTime(int16_t time, int8_t rate) {
@@ -269,6 +269,6 @@ void CommandDistributor::broadcastRaw(clientType type, char * msg) {
broadcastReply(type, F("%s"),msg);
}
void CommandDistributor::broadcastTrackState(const FSH* format,byte trackLetter, int16_t dcAddr) {
broadcastReply(COMMAND_TYPE, format,trackLetter, dcAddr);
void CommandDistributor::broadcastTrackState(const FSH* format,byte trackLetter,int16_t dcAddr) {
broadcastReply(COMMAND_TYPE, format,trackLetter,dcAddr);
}

View File

@@ -55,7 +55,7 @@ public :
static int16_t retClockTime();
static void broadcastPower();
static void broadcastRaw(clientType type,char * msg);
static void broadcastTrackState(const FSH* format,byte trackLetter, int16_t dcAddr);
static void broadcastTrackState(const FSH* format,byte trackLetter,int16_t dcAddr);
template<typename... Targs> static void broadcastReply(clientType type, Targs... msg);
static void forget(byte clientId);

View File

@@ -49,7 +49,7 @@ Once a new OPCODE is decided upon, update this list.
b, Write CV bit on main
B, Write CV bit
c, Request current command
C, configure the CS
C,
d,
D, Diagnostic commands
e, Erase EEPROM
@@ -60,14 +60,14 @@ Once a new OPCODE is decided upon, update this list.
G,
h,
H, Turnout state broadcast
i, Server details string
I, Turntable object command, control, and broadcast
i, Reserved for future use - Turntable object broadcast
I, Reserved for future use - Turntable object command and control
j, Throttle responses
J, Throttle queries
k, Reserved for future use - Potentially Railcom
K, Reserved for future use - Potentially Railcom
l, Loco speedbyte/function map broadcast
L, Reserved for LCC interface (implemented in EXRAIL)
L,
m,
M, Write DCC packet
n,
@@ -122,7 +122,7 @@ Once a new OPCODE is decided upon, update this list.
for (int16_t i=0;;i+=sizeof(flashList[0])) { \
int16_t value=GETHIGHFLASHW(flashList,i); \
if (value==INT16_MAX) break; \
StringFormatter::send(stream,F(" %d"),value); \
if (value != 0) StringFormatter::send(stream,F(" %d"),value); \
}
@@ -157,7 +157,6 @@ const int16_t HASH_KEYWORD_VPIN=-415;
const int16_t HASH_KEYWORD_A='A';
const int16_t HASH_KEYWORD_C='C';
const int16_t HASH_KEYWORD_G='G';
const int16_t HASH_KEYWORD_H='H';
const int16_t HASH_KEYWORD_I='I';
const int16_t HASH_KEYWORD_O='O';
const int16_t HASH_KEYWORD_P='P';
@@ -553,131 +552,69 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
case '1': // POWERON <1 [MAIN|PROG|JOIN]>
{
bool main=false;
bool prog=false;
bool join=false;
bool singletrack=false;
//byte t=0;
if (params > 1) break;
if (params==0) { // All
main=true;
prog=true;
}
if (params==1) {
if (p[0]==HASH_KEYWORD_MAIN) { // <1 MAIN>
main=true;
}
bool main=false;
bool prog=false;
bool join=false;
if (params > 1) break;
if (params==0) { // All
main=true;
prog=true;
}
if (params==1) {
if (p[0]==HASH_KEYWORD_MAIN) { // <1 MAIN>
main=true;
}
#ifndef DISABLE_PROG
else if (p[0] == HASH_KEYWORD_JOIN) { // <1 JOIN>
main=true;
prog=true;
join=true;
}
else if (p[0]==HASH_KEYWORD_PROG) { // <1 PROG>
prog=true;
}
else if (p[0] == HASH_KEYWORD_JOIN) { // <1 JOIN>
main=true;
prog=true;
join=true;
}
else if (p[0]==HASH_KEYWORD_PROG) { // <1 PROG>
prog=true;
}
#endif
//else if (p[0] >= 'A' && p[0] <= 'H') { // <1 A-H>
else if (p[0] >= HASH_KEYWORD_A && p[0] <= HASH_KEYWORD_H) { // <1 A-H>
byte t = (p[0] - 'A');
//DIAG(F("Processing track - %d "), t);
if (TrackManager::isProg(t)) {
main = false;
prog = true;
}
else
{
main=true;
prog=false;
}
singletrack=true;
if (main) TrackManager::setTrackPower(false, false, POWERMODE::ON, t);
if (prog) TrackManager::setTrackPower(true, false, POWERMODE::ON, t);
StringFormatter::send(stream, F("<1 %c>\n"), t+'A');
//CommandDistributor::broadcastPower();
//TrackManager::streamTrackState(NULL,t);
return;
}
else break; // will reply <X>
}
TrackManager::setJoin(join);
if (main) TrackManager::setMainPower(POWERMODE::ON);
if (prog) TrackManager::setProgPower(POWERMODE::ON);
else break; // will reply <X>
}
if (!singletrack) {
TrackManager::setJoin(join);
if (join) TrackManager::setJoinPower(POWERMODE::ON);
else {
if (main) TrackManager::setMainPower(POWERMODE::ON);
if (prog) TrackManager::setProgPower(POWERMODE::ON);
}
CommandDistributor::broadcastPower();
return;
}
CommandDistributor::broadcastPower();
return;
}
case '0': // POWEROFF <0 [MAIN | PROG] >
{
bool main=false;
bool prog=false;
bool singletrack=false;
//byte t=0;
if (params > 1) break;
if (params==0) { // All
main=true;
prog=true;
}
if (params==1) {
if (p[0]==HASH_KEYWORD_MAIN) { // <0 MAIN>
main=true;
}
#ifndef DISABLE_PROG
else if (p[0]==HASH_KEYWORD_PROG) { // <0 PROG>
prog=true;
}
#endif
//else if (p[0] >= 'A' && p[0] <= 'H') { // <1 A-H>
else if (p[0] >= HASH_KEYWORD_A && p[0] <= HASH_KEYWORD_H) { // <1 A-H>
byte t = (p[0] - 'A');
//DIAG(F("Processing track - %d "), t);
if (TrackManager::isProg(t)) {
main = false;
prog = true;
}
else
{
main=true;
prog=false;
}
singletrack=true;
TrackManager::setJoin(false);
if (main) TrackManager::setTrackPower(false, false, POWERMODE::OFF, t);
if (prog) {
TrackManager::progTrackBoosted=false; // Prog track boost mode will not outlive prog track off
TrackManager::setTrackPower(true, false, POWERMODE::OFF, t);
}
StringFormatter::send(stream, F("<0 %c>\n"), t+'A');
//CommandDistributor::broadcastPower();
//TrackManager::streamTrackState(NULL, t);
return;
}
else break; // will reply <X>
}
if (!singletrack) {
TrackManager::setJoin(false);
if (main) TrackManager::setMainPower(POWERMODE::OFF);
if (prog) {
TrackManager::progTrackBoosted=false; // Prog track boost mode will not outlive prog track off
TrackManager::setProgPower(POWERMODE::OFF);
}
CommandDistributor::broadcastPower();
return;
bool main=false;
bool prog=false;
if (params > 1) break;
if (params==0) { // All
main=true;
prog=true;
}
if (params==1) {
if (p[0]==HASH_KEYWORD_MAIN) { // <0 MAIN>
main=true;
}
#ifndef DISABLE_PROG
else if (p[0]==HASH_KEYWORD_PROG) { // <0 PROG>
prog=true;
}
#endif
else break; // will reply <X>
}
TrackManager::setJoin(false);
if (main) TrackManager::setMainPower(POWERMODE::OFF);
if (prog) {
TrackManager::progTrackBoosted=false; // Prog track boost mode will not outlive prog track off
TrackManager::setProgPower(POWERMODE::OFF);
}
CommandDistributor::broadcastPower();
return;
}
}
case '!': // ESTOP ALL <!>
DCC::setThrottle(0,1,1); // this broadcasts speed 1(estop) and sets all reminders to speed 1.
@@ -693,7 +630,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
Sensor::printAll(stream);
return;
case 's': // STATUS <s>
case 's': // <s>
StringFormatter::send(stream, F("<iDCC-EX V-%S / %S / %S G-%S>\n"), F(VERSION), F(ARDUINO_TYPE), DCC::getMotorShieldName(), F(GITHUB_SHA));
CommandDistributor::broadcastPower(); // <s> is the only "get power status" command we have
Turnout::printAll(stream); //send all Turnout states
@@ -714,17 +651,13 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
case ' ': // < >
StringFormatter::send(stream, F("\n"));
return;
case 'C': // CONFIG <C [params]>
if (parseC(stream, params, p))
return;
break;
#ifndef DISABLE_DIAG
case 'D': // DIAG <D [params]>
case 'D': // < >
if (parseD(stream, params, p))
return;
break;
#endif
case '=': // TACK MANAGER CONTROL <= [params]>
case '=': // <= Track manager control >
if (TrackManager::parseJ(stream, params, p))
return;
break;
@@ -809,15 +742,11 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
SENDFLASHLIST(stream,RMFT2::rosterIdList)
}
else {
auto rosterName= RMFT2::getRosterName(id);
if (!rosterName) rosterName=F("");
auto functionNames= RMFT2::getRosterFunctions(id);
if (!functionNames) functionNames=RMFT2::getRosterFunctions(0);
if (!functionNames) functionNames=F("");
StringFormatter::send(stream,F(" %d \"%S\" \"%S\""),
id, rosterName, functionNames);
}
const FSH * functionNames= RMFT2::getRosterFunctions(id);
StringFormatter::send(stream,F(" %d \"%S\" \"%S\""),
id, RMFT2::getRosterName(id),
functionNames == NULL ? RMFT2::getRosterFunctions(0) : functionNames);
}
#endif
StringFormatter::send(stream, F(">\n"));
return;
@@ -910,9 +839,6 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
break;
#endif
case 'L': // LCC interface implemented in EXRAIL parser
break; // Will <X> if not intercepted by EXRAIL
default: //anything else will diagnose and drop out to <X>
DIAG(F("Opcode=%c params=%d"), opcode, params);
for (int i = 0; i < params; i++)
@@ -1118,28 +1044,19 @@ bool DCCEXParser::parseS(Print *stream, int16_t params, int16_t p[])
return false;
}
bool DCCEXParser::parseC(Print *stream, int16_t params, int16_t p[]) {
bool DCCEXParser::parseD(Print *stream, int16_t params, int16_t p[])
{
if (params == 0)
return false;
bool onOff = (params > 0) && (p[1] == 1 || p[1] == HASH_KEYWORD_ON); // dont care if other stuff or missing... just means off
switch (p[0])
{
#ifndef DISABLE_PROG
case HASH_KEYWORD_PROGBOOST:
TrackManager::progTrackBoosted=true;
return true;
#endif
case HASH_KEYWORD_RESET:
DCCTimer::reset();
break; // and <X> if we didnt restart
case HASH_KEYWORD_SPEED28:
DCC::setGlobalSpeedsteps(28);
DIAG(F("28 Speedsteps"));
case HASH_KEYWORD_CABS: // <D CABS>
DCC::displayCabList(stream);
return true;
case HASH_KEYWORD_SPEED128:
DCC::setGlobalSpeedsteps(128);
DIAG(F("128 Speedsteps"));
case HASH_KEYWORD_RAM: // <D RAM>
StringFormatter::send(stream, F("Free memory=%d\n"), DCCTimer::getMinimumFreeMemory());
return true;
#ifndef DISABLE_PROG
@@ -1159,33 +1076,12 @@ bool DCCEXParser::parseC(Print *stream, int16_t params, int16_t p[]) {
LCD(0, F("Ack Retry=%d Sum=%d"), p[2], DCCACK::setAckRetry(p[2])); // <D ACK RETRY 2>
}
} else {
DIAG(F("Ack diag %S"), onOff ? F("on") : F("off"));
StringFormatter::send(stream, F("Ack diag %S\n"), onOff ? F("on") : F("off"));
Diag::ACK = onOff;
}
return true;
#endif
default: // invalid/unknown
break;
}
return false;
}
bool DCCEXParser::parseD(Print *stream, int16_t params, int16_t p[])
{
if (params == 0)
return false;
bool onOff = (params > 0) && (p[1] == 1 || p[1] == HASH_KEYWORD_ON); // dont care if other stuff or missing... just means off
switch (p[0])
{
case HASH_KEYWORD_CABS: // <D CABS>
DCC::displayCabList(stream);
return true;
case HASH_KEYWORD_RAM: // <D RAM>
DIAG(F("Free memory=%d"), DCCTimer::getMinimumFreeMemory());
return true;
case HASH_KEYWORD_CMD: // <D CMD ON/OFF>
Diag::CMD = onOff;
return true;
@@ -1207,14 +1103,34 @@ bool DCCEXParser::parseD(Print *stream, int16_t params, int16_t p[])
Diag::LCN = onOff;
return true;
#endif
#ifndef DISABLE_PROG
case HASH_KEYWORD_PROGBOOST:
TrackManager::progTrackBoosted=true;
return true;
#endif
case HASH_KEYWORD_RESET:
DCCTimer::reset();
break; // and <X> if we didnt restart
#ifndef DISABLE_EEPROM
case HASH_KEYWORD_EEPROM: // <D EEPROM NumEntries>
if (params >= 2)
EEStore::dump(p[1]);
return true;
#endif
case HASH_KEYWORD_SERVO: // <D SERVO vpin position [profile]>
case HASH_KEYWORD_SPEED28:
DCC::setGlobalSpeedsteps(28);
StringFormatter::send(stream, F("28 Speedsteps"));
return true;
case HASH_KEYWORD_SPEED128:
DCC::setGlobalSpeedsteps(128);
StringFormatter::send(stream, F("128 Speedsteps"));
return true;
case HASH_KEYWORD_SERVO: // <D SERVO vpin position [profile]>
case HASH_KEYWORD_ANOUT: // <D ANOUT vpin position [profile]>
IODevice::writeAnalogue(p[1], p[2], params>3 ? p[3] : 0);
break;
@@ -1237,7 +1153,7 @@ bool DCCEXParser::parseD(Print *stream, int16_t params, int16_t p[])
break;
default: // invalid/unknown
return parseC(stream, params, p);
break;
}
return false;
}
@@ -1265,7 +1181,7 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[])
if (tto) {
bool type = tto->isEXTT();
uint8_t position = tto->getPosition();
StringFormatter::send(stream, F("<I %d %d>\n"), type, position);
StringFormatter::send(stream, F("<i %d %d>\n"), type, position);
} else {
return false;
}
@@ -1291,7 +1207,7 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[])
if (!DCCTurntable::create(p[0])) return false;
Turntable *tto = Turntable::get(p[0]);
tto->addPosition(0, 0, p[2]);
StringFormatter::send(stream, F("<I>\n"));
StringFormatter::send(stream, F("<i>\n"));
} else {
if (!tto) return false;
if (!tto->isEXTT()) return false;
@@ -1308,7 +1224,7 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[])
if (!EXTTTurntable::create(p[0], (VPIN)p[2])) return false;
Turntable *tto = Turntable::get(p[0]);
tto->addPosition(0, 0, p[3]);
StringFormatter::send(stream, F("<I>\n"));
StringFormatter::send(stream, F("<i>\n"));
} else {
return false;
}
@@ -1322,7 +1238,7 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[])
// tto must exist, no more than 48 positions, angle 0 - 3600
if (!tto || p[2] > 48 || p[4] < 0 || p[4] > 3600) return false;
tto->addPosition(p[2], p[3], p[4]);
StringFormatter::send(stream, F("<I>\n"));
StringFormatter::send(stream, F("<i>\n"));
} else {
return false;
}

View File

@@ -49,7 +49,6 @@ struct DCCEXParser
static bool parseZ(Print * stream, int16_t params, int16_t p[]);
static bool parseS(Print * stream, int16_t params, int16_t p[]);
static bool parsef(Print * stream, int16_t params, int16_t p[]);
static bool parseC(Print * stream, int16_t params, int16_t p[]);
static bool parseD(Print * stream, int16_t params, int16_t p[]);
#ifndef IO_NO_HAL
static bool parseI(Print * stream, int16_t params, int16_t p[]);

View File

@@ -3,7 +3,6 @@
* © 2021 Mike S
* © 2021-2023 Harald Barth
* © 2021 Fred Decker
* © 2023 Travis Farmer
* All rights reserved.
*
* This file is part of CommandStation-EX
@@ -91,8 +90,6 @@ private:
static const int DCC_SIGNAL_TIME=58; // this is the 58uS DCC 1-bit waveform half-cycle
#if defined(ARDUINO_ARCH_STM32) // TODO: PMA temporary hack - assumes 100Mhz F_CPU as STM32 can change frequency
static const long CLOCK_CYCLES=(100000000L / 1000000 * DCC_SIGNAL_TIME) >>1;
#elif defined(ARDUINO_GIGA)
static const long CLOCK_CYCLES=(480000000L / 1000000 * DCC_SIGNAL_TIME) >>1;
#else
static const long CLOCK_CYCLES=(F_CPU / 1000000 * DCC_SIGNAL_TIME) >>1;
#endif

View File

@@ -1,206 +0,0 @@
/*
* © 2023 Travis Farmer
* © 2023 Neil McKechnie
* © 2022-2023 Paul M. Antoine
* © 2021 Mike S
* © 2021, 2023 Harald Barth
* © 2021 Fred Decker
* © 2021 Chris Harlow
* © 2021 David Cutting
* All rights reserved.
*
* This file is part of Asbelos DCC 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 <https://www.gnu.org/licenses/>.
*/
// ATTENTION: this file only compiles on a STM32 based boards
// Please refer to DCCTimer.h for general comments about how this class works
// This is to avoid repetition and duplication.
#if defined(ARDUINO_GIGA)
#include "DCCTimer.h"
#include "DIAG.h"
#include "GigaHardwareTimer.h"
#include <Arduino_AdvancedAnalog.h>
//#include "config.h"
///////////////////////////////////////////////////////////////////////////////////////////////
// Experimental code for High Accuracy (HA) DCC Signal mode
// Warning - use of TIM2 and TIM3 can affect the use of analogWrite() function on certain pins,
// which is used by the DC motor types.
///////////////////////////////////////////////////////////////////////////////////////////////
INTERRUPT_CALLBACK interruptHandler=0;
//HardwareTimer* timer = NULL;
//HardwareTimer* timerAux = NULL;
HardwareTimer timer(TIM2);
HardwareTimer timerAux(TIM3);
static bool tim2ModeHA = false;
static bool tim3ModeHA = false;
void DCCTimer_Handler() __attribute__((interrupt));
void DCCTimer_Handler() {
interruptHandler();
}
void DCCTimer::begin(INTERRUPT_CALLBACK callback) {
interruptHandler=callback;
noInterrupts();
// adc_set_sample_rate(ADC_SAMPLETIME_480CYCLES);
timer.pause();
timerAux.pause();
timer.setPrescaleFactor(1);
timer.setOverflow(DCC_SIGNAL_TIME, MICROSEC_FORMAT);
timer.attachInterrupt(DCCTimer_Handler);
timer.refresh();
timerAux.setPrescaleFactor(1);
timerAux.setOverflow(DCC_SIGNAL_TIME, MICROSEC_FORMAT);
timerAux.refresh();
timer.resume();
timerAux.resume();
interrupts();
}
bool DCCTimer::isPWMPin(byte pin) {
switch (pin) {
case 12:
return true;
case 13:
return true;
default:
return false;
}
}
void DCCTimer::setPWM(byte pin, bool high) {
switch (pin) {
case 12:
if (!tim3ModeHA) {
timerAux.setMode(1, TIMER_OUTPUT_COMPARE_INACTIVE, 12);
tim3ModeHA = true;
}
if (high)
TIM2->CCMR1 = (TIM2->CCMR1 & ~TIM_CCMR1_OC1M_Msk) | TIM_CCMR1_OC1M_0;
else
TIM2->CCMR1 = (TIM2->CCMR1 & ~TIM_CCMR1_OC1M_Msk) | TIM_CCMR1_OC1M_1;
break;
case 13:
if (!tim2ModeHA) {
timer.setMode(1, TIMER_OUTPUT_COMPARE_INACTIVE, 13);
tim2ModeHA = true;
}
if (high)
TIM3->CCMR1 = (TIM3->CCMR1 & ~TIM_CCMR1_OC1M_Msk) | TIM_CCMR1_OC1M_0;
else
TIM3->CCMR1 = (TIM3->CCMR1 & ~TIM_CCMR1_OC1M_Msk) | TIM_CCMR1_OC1M_1;
break;
}
}
void DCCTimer::clearPWM() {
timer.setMode(1, TIMER_OUTPUT_COMPARE_INACTIVE, NC);
tim2ModeHA = false;
timerAux.setMode(1, TIMER_OUTPUT_COMPARE_INACTIVE, NC);
tim3ModeHA = false;
}
void DCCTimer::getSimulatedMacAddress(byte mac[6]) {
volatile uint32_t *serno1 = (volatile uint32_t *)UID_BASE;
volatile uint32_t *serno2 = (volatile uint32_t *)UID_BASE+4;
volatile uint32_t *serno3 = (volatile uint32_t *)UID_BASE+8;
volatile uint32_t m1 = *serno1;
volatile uint32_t m2 = *serno2;
volatile uint32_t m3 = *serno3;
mac[0] = 0xBE;
mac[1] = 0xEF;
mac[2] = m1 ^ m3 >> 24;
mac[3] = m1 ^ m3 >> 16;
mac[4] = m1 ^ m3 >> 8;
mac[5] = m1 ^ m3 >> 0;
//DIAG(F("MAC: %P:%P:%P:%P:%P:%P"),mac[0],mac[1],mac[2],mac[3],mac[4],mac[5]);
}
volatile int DCCTimer::minimum_free_memory=__INT_MAX__;
// Return low memory value...
int DCCTimer::getMinimumFreeMemory() {
noInterrupts(); // Disable interrupts to get volatile value
int retval = freeMemory();
interrupts();
return retval;
}
extern "C" char* sbrk(int incr);
int DCCTimer::freeMemory() {
char top;
unsigned int tmp = (unsigned int)(&top - reinterpret_cast<char*>(sbrk(0)));
return (int)(tmp / 1000);
}
void DCCTimer::reset() {
//Watchdog &watchdog = Watchdog::get_instance();
//Watchdog::stop();
//Watchdog::start(500);
//while(true) {};
}
int * ADCee::analogvals = NULL;
int16_t ADCee::ADCmax()
{
return 1023;
}
AdvancedADC adc(A0, A1);
int ADCee::init(uint8_t pin) {
adc.begin(AN_RESOLUTION_10, 16000, 1, 512);
return 123;
}
/*
* Read function ADCee::read(pin) to get value instead of analogRead(pin)
*/
int ADCee::read(uint8_t pin, bool fromISR) {
static SampleBuffer buf = adc.read();
int retVal = -123;
if (adc.available()) {
buf.release();
buf = adc.read();
}
return (buf[pin - A0]);
}
/*
* Scan function that is called from interrupt
*/
#pragma GCC push_options
#pragma GCC optimize ("-O3")
void ADCee::scan() {
}
#pragma GCC pop_options
void ADCee::begin() {
noInterrupts();
interrupts();
}
#endif

View File

@@ -306,18 +306,18 @@ void DCCTimer::DCCEXanalogWrite(uint8_t pin, int value) {
// Calculate percentage duty cycle from value given
uint32_t duty_cycle = (value * 100 / 256) + 1;
if (pin_timer[pin] != NULL) {
// if (duty_cycle == 100)
// {
// pin_timer[pin]->pauseChannel(pin_channel[pin]);
// DIAG(F("DCCEXanalogWrite::Pausing timer channel on pin %d"), pin);
// }
// else
// {
if (duty_cycle == 100)
{
pin_timer[pin]->pauseChannel(pin_channel[pin]);
DIAG(F("DCCEXanalogWrite::Pausing timer channel on pin %d"), pin);
}
else
{
pinmap_pinout(digitalPinToPinName(pin), PinMap_TIM); // ensure the pin has been configured!
// pin_timer[pin]->resumeChannel(pin_channel[pin]);
pin_timer[pin]->resumeChannel(pin_channel[pin]);
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);
// }
}
}
else
DIAG(F("DCCEXanalogWrite::Pin %d is not configured for PWM!"), pin);

View File

@@ -31,12 +31,12 @@
#include "Sensors.h"
#include "Turnouts.h"
#if defined(ARDUINO_ARCH_SAMC) || defined(ARDUINO_GIGA)
#if defined(ARDUINO_ARCH_SAMC)
ExternalEEPROM EEPROM;
#endif
void EEStore::init() {
#if defined(ARDUINO_ARCH_SAMC) || defined(ARDUINO_GIGA)
#if defined(ARDUINO_ARCH_SAMC)
EEPROM.begin(0x50); // Address for Microchip 24-series EEPROM with all three
// A pins grounded (0b1010000 = 0x50)
#endif

View File

@@ -26,7 +26,7 @@
#include <Arduino.h>
#if defined(ARDUINO_ARCH_SAMC) || defined(ARDUINO_GIGA)
#if defined(ARDUINO_ARCH_SAMC)
#include <SparkFun_External_EEPROM.h>
extern ExternalEEPROM EEPROM;
#else

View File

@@ -85,7 +85,7 @@ RMFT2 * RMFT2::pausingTask=NULL; // Task causing a PAUSE.
// when pausingTask is set, that is the ONLY task that gets any service,
// and all others will have their locos stopped, then resumed after the pausing task resumes.
byte RMFT2::flags[MAX_FLAGS];
Print * RMFT2::LCCSerial=0;
LookList * RMFT2::sequenceLookup=NULL;
LookList * RMFT2::onThrowLookup=NULL;
LookList * RMFT2::onCloseLookup=NULL;
@@ -176,26 +176,23 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) {
onCloseLookup=LookListLoader(OPCODE_ONCLOSE);
onActivateLookup=LookListLoader(OPCODE_ONACTIVATE);
onDeactivateLookup=LookListLoader(OPCODE_ONDEACTIVATE);
onRedLookup=LookListLoader(OPCODE_ONRED);
onAmberLookup=LookListLoader(OPCODE_ONAMBER);
onGreenLookup=LookListLoader(OPCODE_ONGREEN);
onChangeLookup=LookListLoader(OPCODE_ONCHANGE);
onClockLookup=LookListLoader(OPCODE_ONTIME);
#ifndef IO_NO_HAL
onRotateLookup=LookListLoader(OPCODE_ONROTATE);
#endif
onOverloadLookup=LookListLoader(OPCODE_ONOVERLOAD);
// onLCCLookup is not the same so not loaded here.
// Second pass startup, define any turnouts or servos, set signals red
// add sequences onRoutines to the lookups
if (compileFeatures & FEATURE_SIGNAL) {
onRedLookup=LookListLoader(OPCODE_ONRED);
onAmberLookup=LookListLoader(OPCODE_ONAMBER);
onGreenLookup=LookListLoader(OPCODE_ONGREEN);
for (int sigslot=0;;sigslot++) {
VPIN sigid=GETHIGHFLASHW(RMFT2::SignalDefinitions,sigslot*8);
if (sigid==0) break; // end of signal list
doSignal(sigid & SIGNAL_ID_MASK, SIGNAL_RED);
}
}
int progCounter;
for (progCounter=0;; SKIPOP){
@@ -346,65 +343,13 @@ void RMFT2::ComandFilter(Print * stream, byte & opcode, byte & paramCount, int16
reject=!parseSlash(stream,paramCount,p);
opcode=0;
break;
case 'L':
if (compileFeatures & FEATURE_LCC) {
// This entire code block is compiled out if LLC macros not used
if (paramCount==0) { //<L> LCC adapter introducing self
LCCSerial=stream; // now we know where to send events we raise
// loop through all possible sent events
for (int progCounter=0;; SKIPOP) {
byte opcode=GET_OPCODE;
if (opcode==OPCODE_ENDEXRAIL) break;
if (opcode==OPCODE_LCC) StringFormatter::send(stream,F("<LS x%h>\n"),getOperand(progCounter,0));
if (opcode==OPCODE_LCCX) { // long form LCC
StringFormatter::send(stream,F("<LS x%h%h%h%h>\n"),
getOperand(progCounter,1),
getOperand(progCounter,2),
getOperand(progCounter,3),
getOperand(progCounter,0)
);
}}
// we stream the hex events we wish to listen to
// and at the same time build the event index looku.
int eventIndex=0;
for (int progCounter=0;; SKIPOP) {
byte opcode=GET_OPCODE;
if (opcode==OPCODE_ENDEXRAIL) break;
if (opcode==OPCODE_ONLCC) {
onLCCLookup[eventIndex]=progCounter; // TODO skip...
StringFormatter::send(stream,F("<LL %d x%h%h%h:%h>\n"),
eventIndex,
getOperand(progCounter,1),
getOperand(progCounter,2),
getOperand(progCounter,3),
getOperand(progCounter,0)
);
eventIndex++;
}
}
StringFormatter::send(stream,F("<LR>\n")); // Ready to rumble
opcode=0;
break;
}
if (paramCount==1) { // <L eventid> LCC event arrived from adapter
int16_t eventid=p[0];
reject=eventid<0 || eventid>=countLCCLookup;
if (!reject) startNonRecursiveTask(F("LCC"),eventid,onLCCLookup[eventid]);
opcode=0;
}
}
break;
default: // other commands pass through
break;
}
if (reject) {
opcode=0;
StringFormatter::send(stream,F("<X>\n"));
StringFormatter::send(stream,F("<X>"));
}
}
@@ -432,19 +377,17 @@ bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) {
if (flag & LATCH_FLAG) StringFormatter::send(stream,F(" LATCHED"));
}
}
if (compileFeatures & FEATURE_SIGNAL) {
// do the signals
// flags[n] represents the state of the nth signal in the table
for (int sigslot=0;;sigslot++) {
VPIN sigid=GETHIGHFLASHW(RMFT2::SignalDefinitions,sigslot*8);
if (sigid==0) break; // end of signal list
byte flag=flags[sigslot] & SIGNAL_MASK; // obtain signal flags for this id
StringFormatter::send(stream,F("\n%S[%d]"),
(flag == SIGNAL_RED)? F("RED") : (flag==SIGNAL_GREEN) ? F("GREEN") : F("AMBER"),
sigid & SIGNAL_ID_MASK);
}
}
// do the signals
// flags[n] represents the state of the nth signal in the table
for (int sigslot=0;;sigslot++) {
VPIN sigid=GETHIGHFLASHW(RMFT2::SignalDefinitions,sigslot*8);
if (sigid==0) break; // end of signal list
byte flag=flags[sigslot] & SIGNAL_MASK; // obtain signal flags for this id
StringFormatter::send(stream,F("\n%S[%d]"),
(flag == SIGNAL_RED)? F("RED") : (flag==SIGNAL_GREEN) ? F("GREEN") : F("AMBER"),
sigid & SIGNAL_ID_MASK);
}
StringFormatter::send(stream,F(" *>\n"));
return true;
}
@@ -837,20 +780,6 @@ void RMFT2::loop2() {
TrackManager::setJoin(false);
CommandDistributor::broadcastPower();
break;
case OPCODE_SET_POWER:
// operand is TRACK_POWER , trackid
//byte thistrack=getOperand(1);
switch (operand) {
case TRACK_POWER_0:
TrackManager::setTrackPower(TrackManager::isProg(getOperand(1)), false, POWERMODE::OFF, getOperand(1));
break;
case TRACK_POWER_1:
TrackManager::setTrackPower(TrackManager::isProg(getOperand(1)), false, POWERMODE::ON, getOperand(1));
break;
}
break;
case OPCODE_SET_TRACK:
// operand is trackmode<<8 | track id
@@ -1091,21 +1020,7 @@ void RMFT2::loop2() {
invert=false;
}
break;
case OPCODE_LCC: // short form LCC
if ((compileFeatures & FEATURE_LCC) && LCCSerial)
StringFormatter::send(LCCSerial,F("<L x%h>"),(uint16_t)operand);
break;
case OPCODE_LCCX: // long form LCC
if ((compileFeatures & FEATURE_LCC) && LCCSerial)
StringFormatter::send(LCCSerial,F("<L x%h%h%h%h>\n"),
getOperand(progCounter,1),
getOperand(progCounter,2),
getOperand(progCounter,3),
getOperand(progCounter,0)
);
break;
case OPCODE_SERVO: // OPCODE_SERVO,V(vpin),OPCODE_PAD,V(position),OPCODE_PAD,V(profile),OPCODE_PAD,V(duration)
IODevice::writeAnalogue(operand,getOperand(1),getOperand(2),getOperand(3));
@@ -1143,7 +1058,6 @@ void RMFT2::loop2() {
case OPCODE_SERVOTURNOUT: // Turnout definition ignored at runtime
case OPCODE_PINTURNOUT: // Turnout definition ignored at runtime
case OPCODE_ONCLOSE: // Turnout event catchers ignored here
case OPCODE_ONLCC: // LCC event catchers ignored here
case OPCODE_ONTHROW:
case OPCODE_ONACTIVATE: // Activate event catchers ignored here
case OPCODE_ONDEACTIVATE:
@@ -1213,7 +1127,6 @@ int16_t RMFT2::getSignalSlot(int16_t id) {
}
/* static */ void RMFT2::doSignal(int16_t id,char rag) {
if (!(compileFeatures & FEATURE_SIGNAL)) return; // dont compile code below
if (diag) DIAG(F(" doSignal %d %x"),id,rag);
// Schedule any event handler for this signal change.
@@ -1281,7 +1194,6 @@ int16_t RMFT2::getSignalSlot(int16_t id) {
}
/* static */ bool RMFT2::isSignal(int16_t id,char rag) {
if (!(compileFeatures & FEATURE_SIGNAL)) return false;
int16_t sigslot=getSignalSlot(id);
if (sigslot<0) return false;
return (flags[sigslot] & SIGNAL_MASK) == rag;
@@ -1334,10 +1246,8 @@ void RMFT2::powerEvent(int16_t track, bool overload) {
void RMFT2::handleEvent(const FSH* reason,LookList* handlers, int16_t id) {
int pc= handlers->find(id);
if (pc>=0) startNonRecursiveTask(reason,id,pc);
}
void RMFT2::startNonRecursiveTask(const FSH* reason, int16_t id,int pc) {
if (pc<0) return;
// Check we dont already have a task running this handler
RMFT2 * task=loopTask;
while(task) {

View File

@@ -59,14 +59,15 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE,
OPCODE_ROSTER,OPCODE_KILLALL,
OPCODE_ROUTE,OPCODE_AUTOMATION,OPCODE_SEQUENCE,
OPCODE_ENDTASK,OPCODE_ENDEXRAIL,
OPCODE_SET_TRACK,OPCODE_SET_POWER,
OPCODE_SET_TRACK,
OPCODE_ONRED,OPCODE_ONAMBER,OPCODE_ONGREEN,
OPCODE_ONCHANGE,
OPCODE_ONCLOCKTIME,
OPCODE_ONTIME,
#ifndef IO_NO_HAL
OPCODE_TTADDPOSITION,OPCODE_DCCTURNTABLE,OPCODE_EXTTTURNTABLE,
OPCODE_ONROTATE,OPCODE_ROTATE,OPCODE_WAITFORTT,
OPCODE_LCC,OPCODE_LCCX,OPCODE_ONLCC,
OPCODE_ONROTATE,OPCODE_ROTATE,OPCODE_IFTTPOSITION,OPCODE_WAITFORTT,
#endif
OPCODE_ONOVERLOAD,
// OPcodes below this point are skip-nesting IF operations
@@ -80,8 +81,7 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE,
OPCODE_IFRANDOM,OPCODE_IFRESERVE,
OPCODE_IFCLOSED,OPCODE_IFTHROWN,
OPCODE_IFRE,
OPCODE_IFLOCO,
OPCODE_IFTTPOSITION
OPCODE_IFLOCO
};
// Ensure thrunge_lcd is put last as there may be more than one display,
@@ -95,11 +95,7 @@ enum thrunger: byte {
thrunge_lcd, // Must be last!!
};
// Flag bits for compile time features.
static const byte FEATURE_SIGNAL= 0x80;
static const byte FEATURE_LCC = 0x40;
static const byte FEATURE_ROSTER= 0x20;
// Flag bits for status of hardware and TPL
static const byte SECTION_FLAG = 0x80;
@@ -178,7 +174,6 @@ private:
OPCODE op2=OPCODE_ENDEXRAIL,OPCODE op3=OPCODE_ENDEXRAIL);
static void handleEvent(const FSH* reason,LookList* handlers, int16_t id);
static uint16_t getOperand(int progCounter,byte n);
static void startNonRecursiveTask(const FSH* reason, int16_t id,int pc);
static RMFT2 * loopTask;
static RMFT2 * pausingTask;
void delayMe(long millisecs);
@@ -197,7 +192,6 @@ private:
static const HIGHFLASH byte RouteCode[];
static const HIGHFLASH int16_t SignalDefinitions[];
static byte flags[MAX_FLAGS];
static Print * LCCSerial;
static LookList * sequenceLookup;
static LookList * onThrowLookup;
static LookList * onCloseLookup;
@@ -212,10 +206,6 @@ private:
static LookList * onRotateLookup;
#endif
static LookList * onOverloadLookup;
static const int countLCCLookup;
static int onLCCLookup[];
static const byte compileFeatures;
// Local variables - exist for each instance/task
RMFT2 *next; // loop chain

View File

@@ -86,8 +86,6 @@
#undef LATCH
#undef LCD
#undef SCREEN
#undef LCC
#undef LCCX
#undef LCN
#undef MOVETT
#undef ONACTIVATE
@@ -96,7 +94,6 @@
#undef ONDEACTIVATE
#undef ONDEACTIVATEL
#undef ONCLOSE
#undef ONLCC
#undef ONTIME
#undef ONCLOCKTIME
#undef ONCLOCKMINS
@@ -141,7 +138,6 @@
#undef SERVO_SIGNAL
#undef SET
#undef SET_TRACK
#undef SET_POWER
#undef SETLOCO
#undef SIGNAL
#undef SIGNALH
@@ -196,7 +192,7 @@
#define ENDTASK
#define ESTOP
#define EXRAIL
#define EXTT_TURNTABLE(id,vpin,home,description)
#define EXTT_TURNTABLE(id,vpin,i2c_address,home,description)
#define FADE(pin,value,ms)
#define FOFF(func)
#define FOLLOW(route)
@@ -224,9 +220,7 @@
#define INVERT_DIRECTION
#define JOIN
#define KILLALL
#define LATCH(sensor_id)
#define LCC(eventid)
#define LCCX(senderid,eventid)
#define LATCH(sensor_id)
#define LCD(row,msg)
#define SCREEN(display,row,msg)
#define LCN(msg)
@@ -241,7 +235,6 @@
#define ONDEACTIVATE(addr,subaddr)
#define ONDEACTIVATEL(linear)
#define ONCLOSE(turnout_id)
#define ONLCC(sender,event)
#define ONGREEN(signal_id)
#define ONRED(signal_id)
#define ONROTATE(turntable_id)
@@ -282,7 +275,6 @@
#define SERVO_TURNOUT(id,pin,activeAngle,inactiveAngle,profile,description...)
#define SET(pin)
#define SET_TRACK(track,mode)
#define SET_POWER(track,onoff)
#define SETLOCO(loco)
#define SIGNAL(redpin,amberpin,greenpin)
#define SIGNALH(redpin,amberpin,greenpin)

View File

@@ -63,11 +63,6 @@
// (10#mins)%100)
#define STRIP_ZERO(value) 10##value%100
// These constants help EXRAIL macros convert Track Power e.g. SET_POWER(A ON|OFF).
//const byte TRACK_POWER_0=0, TRACK_POWER_OFF=0;
//const byte TRACK_POWER_1=1, TRACK_POWER_ON=1;
// Pass 1 Implements aliases
#include "EXRAIL2MacroReset.h"
#undef ALIAS
@@ -79,34 +74,12 @@
#include "EXRAIL2MacroReset.h"
#undef HAL
#define HAL(haltype,params...) haltype::create(params);
#undef EXTT_TURNTABLE
#define EXTT_TURNTABLE(id,vpin,i2c_address,home,description...) EXTurntable::create(vpin,1,i2c_address);
void exrailHalSetup() {
#include "myAutomation.h"
}
// Pass 1c detect compile time featurtes
#include "EXRAIL2MacroReset.h"
#undef SIGNAL
#define SIGNAL(redpin,amberpin,greenpin) | FEATURE_SIGNAL
#undef SIGNALH
#define SIGNALH(redpin,amberpin,greenpin) | FEATURE_SIGNAL
#undef SERVO_SIGNAL
#define SERVO_SIGNAL(vpin,redval,amberval,greenval) | FEATURE_SIGNAL
#undef DCC_SIGNAL
#define DCC_SIGNAL(id,addr,subaddr) | FEATURE_SIGNAL
#undef VIRTUAL_SIGNAL
#define VIRTUAL_SIGNAL(id) | FEATURE_SIGNAL
#undef LCC
#define LCC(eventid) | FEATURE_LCC
#undef LCCX
#define LCCX(senderid,eventid) | FEATURE_LCC
#undef ONLCC
#define ONLCC(senderid,eventid) | FEATURE_LCC
const byte RMFT2::compileFeatures = 0
#include "myAutomation.h"
;
// Pass 2 create throttle route list
#include "EXRAIL2MacroReset.h"
#undef ROUTE
@@ -224,7 +197,7 @@ const FSH * RMFT2::getTurnoutDescription(int16_t turnoutid) {
#undef DCC_TURNTABLE
#define DCC_TURNTABLE(id,home,description...) O_DESC(id,description)
#undef EXTT_TURNTABLE
#define EXTT_TURNTABLE(id,vpin,home,description...) O_DESC(id,description)
#define EXTT_TURNTABLE(id,vpin,i2c_address,home,description...) O_DESC(id,description)
const FSH * RMFT2::getTurntableDescription(int16_t turntableId) {
switch (turntableId) {
@@ -300,16 +273,6 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = {
#include "myAutomation.h"
0,0,0,0 };
// Pass 9 ONLCC counter and lookup array
#include "EXRAIL2MacroReset.h"
#undef ONLCC
#define ONLCC(sender,event) +1
const int RMFT2::countLCCLookup=0
#include "myAutomation.h"
;
int RMFT2::onLCCLookup[RMFT2::countLCCLookup];
// Last Pass : create main routes table
// Only undef the macros, not dummy them.
#define RMFT2_UNDEF_ONLY
@@ -354,7 +317,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup];
#define ESTOP OPCODE_SPEED,V(1),
#define EXRAIL
#ifndef IO_NO_HAL
#define EXTT_TURNTABLE(id,vpin,home,description...) OPCODE_EXTTTURNTABLE,V(id),OPCODE_PAD,V(vpin),OPCODE_PAD,V(home),
#define EXTT_TURNTABLE(id,vpin,i2c_address,home,description...) OPCODE_EXTTTURNTABLE,V(id),OPCODE_PAD,V(vpin),OPCODE_PAD,V(i2c_address),OPCODE_PAD,V(home),
#endif
#define FADE(pin,value,ms) OPCODE_SERVO,V(pin),OPCODE_PAD,V(value),OPCODE_PAD,V(PCA9685::ProfileType::UseDuration|PCA9685::NoPowerOff),OPCODE_PAD,V(ms/100L),
#define FOFF(func) OPCODE_FOFF,V(func),
@@ -386,11 +349,6 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup];
#define JOIN OPCODE_JOIN,0,0,
#define KILLALL OPCODE_KILLALL,0,0,
#define LATCH(sensor_id) OPCODE_LATCH,V(sensor_id),
#define LCC(eventid) OPCODE_LCC,V(eventid),
#define LCCX(sender,event) OPCODE_LCCX,V(event),\
OPCODE_PAD,V((((uint64_t)sender)>>32)&0xFFFF),\
OPCODE_PAD,V((((uint64_t)sender)>>16)&0xFFFF),\
OPCODE_PAD,V((((uint64_t)sender)>>0)&0xFFFF),
#define LCD(id,msg) PRINT(msg)
#define SCREEN(display,id,msg) PRINT(msg)
#define LCN(msg) PRINT(msg)
@@ -399,10 +357,6 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup];
#define ONACTIVATEL(linear) OPCODE_ONACTIVATE,V(linear+3),
#define ONAMBER(signal_id) OPCODE_ONAMBER,V(signal_id),
#define ONCLOSE(turnout_id) OPCODE_ONCLOSE,V(turnout_id),
#define ONLCC(sender,event) OPCODE_ONLCC,V(event),\
OPCODE_PAD,V((((uint64_t)sender)>>32)&0xFFFF),\
OPCODE_PAD,V((((uint64_t)sender)>>16)&0xFFFF),\
OPCODE_PAD,V((((uint64_t)sender)>>0)&0xFFFF),
#define ONTIME(value) OPCODE_ONTIME,V(value),
#define ONCLOCKTIME(hours,mins) OPCODE_ONTIME,V((STRIP_ZERO(hours)*60)+STRIP_ZERO(mins)),
#define ONCLOCKMINS(mins) ONCLOCKTIME(25,mins)
@@ -453,12 +407,11 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup];
#define SERVO_TURNOUT(id,pin,activeAngle,inactiveAngle,profile,description...) OPCODE_SERVOTURNOUT,V(id),OPCODE_PAD,V(pin),OPCODE_PAD,V(activeAngle),OPCODE_PAD,V(inactiveAngle),OPCODE_PAD,V(PCA9685::ProfileType::profile),
#define SET(pin) OPCODE_SET,V(pin),
#define SET_TRACK(track,mode) OPCODE_SET_TRACK,V(TRACK_MODE_##mode <<8 | TRACK_NUMBER_##track),
#define SET_POWER(track,onoff) OPCODE_SET_POWER,V(TRACK_POWER_##onoff),OPCODE_PAD, V(TRACK_NUMBER_##track),
#define SETLOCO(loco) OPCODE_SETLOCO,V(loco),
#define SIGNAL(redpin,amberpin,greenpin)
#define SIGNALH(redpin,amberpin,greenpin)
#define SPEED(speed) OPCODE_SPEED,V(speed),
#define START(route) OPCODE_START,V(route),
#define START(route) OPCODE_START,V(route),
#define STOP OPCODE_SPEED,V(0),
#define THROW(id) OPCODE_THROW,V(id),
#ifndef IO_NO_HAL

View File

@@ -1 +1 @@
#define GITHUB_SHA "devel-202310230944Z"
#define GITHUB_SHA "devel-202309241855Z"

File diff suppressed because it is too large Load Diff

View File

@@ -1,220 +0,0 @@
/****************************************************************************************************************************
HardwareTimer.h
For Portenta_H7 boards
Written by Khoi Hoang
Built by Khoi Hoang https://github.com/khoih-prog/Portenta_H7_TimerInterrupt
Licensed under MIT license
Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by
unsigned long miliseconds), you just consume only one Portenta_H7 STM32 timer and avoid conflicting with other cores' tasks.
The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers
Therefore, their executions are not blocked by bad-behaving functions / tasks.
This important feature is absolutely necessary for mission-critical tasks.
Version: 1.4.0
Version Modified By Date Comments
------- ----------- ---------- -----------
1.2.1 K.Hoang 15/09/2021 Initial coding for Portenta_H7
1.3.0 K.Hoang 17/09/2021 Add PWM features and examples
1.3.1 K.Hoang 21/09/2021 Fix warnings in PWM examples
1.4.0 K.Hoang 22/01/2022 Fix `multiple-definitions` linker error. Fix bug
*****************************************************************************************************************************/
// Modified from stm32 core v2.0.0
/*
Copyright (c) 2017 Daniel Fekete
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Copyright (c) 2019 STMicroelectronics
Modified to support Arduino_Core_STM32
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef GIGAHARDWARETIMER_H_
#define GIGAHARDWARETIMER_H_
#if defined(ARDUINO_GIGA)
/* Includes ------------------------------------------------------------------*/
#include "Gigatimer.h"
#if defined(HAL_TIM_MODULE_ENABLED) && !defined(HAL_TIM_MODULE_ONLY)
#define TIMER_CHANNELS 4 // channel5 and channel 6 are not considered here has they don't have gpio output and they don't have interrupt
typedef enum
{
TIMER_DISABLED, // == TIM_OCMODE_TIMING no output, useful for only-interrupt
// Output Compare
TIMER_OUTPUT_COMPARE, // == Obsolete, use TIMER_DISABLED instead. Kept for compatibility reason
TIMER_OUTPUT_COMPARE_ACTIVE, // == TIM_OCMODE_ACTIVE pin is set high when counter == channel compare
TIMER_OUTPUT_COMPARE_INACTIVE, // == TIM_OCMODE_INACTIVE pin is set low when counter == channel compare
TIMER_OUTPUT_COMPARE_TOGGLE, // == TIM_OCMODE_TOGGLE pin toggles when counter == channel compare
TIMER_OUTPUT_COMPARE_PWM1, // == TIM_OCMODE_PWM1 pin high when counter < channel compare, low otherwise
TIMER_OUTPUT_COMPARE_PWM2, // == TIM_OCMODE_PWM2 pin low when counter < channel compare, high otherwise
TIMER_OUTPUT_COMPARE_FORCED_ACTIVE, // == TIM_OCMODE_FORCED_ACTIVE pin always high
TIMER_OUTPUT_COMPARE_FORCED_INACTIVE, // == TIM_OCMODE_FORCED_INACTIVE pin always low
//Input capture
TIMER_INPUT_CAPTURE_RISING, // == TIM_INPUTCHANNELPOLARITY_RISING
TIMER_INPUT_CAPTURE_FALLING, // == TIM_INPUTCHANNELPOLARITY_FALLING
TIMER_INPUT_CAPTURE_BOTHEDGE, // == TIM_INPUTCHANNELPOLARITY_BOTHEDGE
// Used 2 channels for a single pin. One channel in TIM_INPUTCHANNELPOLARITY_RISING another channel in TIM_INPUTCHANNELPOLARITY_FALLING.
// Channels must be used by pair: CH1 with CH2, or CH3 with CH4
// This mode is very useful for Frequency and Dutycycle measurement
TIMER_INPUT_FREQ_DUTY_MEASUREMENT,
TIMER_NOT_USED = 0xFFFF // This must be the last item of this enum
} TimerModes_t;
typedef enum
{
TICK_FORMAT, // default
MICROSEC_FORMAT,
HERTZ_FORMAT,
} TimerFormat_t;
typedef enum
{
RESOLUTION_1B_COMPARE_FORMAT = 1, // used for Dutycycle: [0 .. 1]
RESOLUTION_2B_COMPARE_FORMAT, // used for Dutycycle: [0 .. 3]
RESOLUTION_3B_COMPARE_FORMAT, // used for Dutycycle: [0 .. 7]
RESOLUTION_4B_COMPARE_FORMAT, // used for Dutycycle: [0 .. 15]
RESOLUTION_5B_COMPARE_FORMAT, // used for Dutycycle: [0 .. 31]
RESOLUTION_6B_COMPARE_FORMAT, // used for Dutycycle: [0 .. 63]
RESOLUTION_7B_COMPARE_FORMAT, // used for Dutycycle: [0 .. 127]
RESOLUTION_8B_COMPARE_FORMAT, // used for Dutycycle: [0 .. 255]
RESOLUTION_9B_COMPARE_FORMAT, // used for Dutycycle: [0 .. 511]
RESOLUTION_10B_COMPARE_FORMAT, // used for Dutycycle: [0 .. 1023]
RESOLUTION_11B_COMPARE_FORMAT, // used for Dutycycle: [0 .. 2047]
RESOLUTION_12B_COMPARE_FORMAT, // used for Dutycycle: [0 .. 4095]
RESOLUTION_13B_COMPARE_FORMAT, // used for Dutycycle: [0 .. 8191]
RESOLUTION_14B_COMPARE_FORMAT, // used for Dutycycle: [0 .. 16383]
RESOLUTION_15B_COMPARE_FORMAT, // used for Dutycycle: [0 .. 32767]
RESOLUTION_16B_COMPARE_FORMAT, // used for Dutycycle: [0 .. 65535]
TICK_COMPARE_FORMAT = 0x80, // default
MICROSEC_COMPARE_FORMAT,
HERTZ_COMPARE_FORMAT,
PERCENT_COMPARE_FORMAT, // used for Dutycycle
} TimerCompareFormat_t;
#ifdef __cplusplus
#include <functional>
using callback_function_t = std::function<void(void)>;
/* Class --------------------------------------------------------*/
class HardwareTimer
{
public:
HardwareTimer(TIM_TypeDef *instance);
~HardwareTimer(); // destructor
void pause(void); // Pause counter and all output channels
void pauseChannel(uint32_t channel); // Timer is still running but channel (output and interrupt) is disabled
void resume(void); // Resume counter and all output channels
void resumeChannel(uint32_t channel); // Resume only one channel
void setPrescaleFactor(uint32_t prescaler); // set prescaler register (which is factor value - 1)
uint32_t getPrescaleFactor();
void setOverflow(uint32_t val, TimerFormat_t format =
TICK_FORMAT); // set AutoReload register depending on format provided
uint32_t getOverflow(TimerFormat_t format = TICK_FORMAT); // return overflow depending on format provided
void setPWM(uint32_t channel, PinName pin, uint32_t frequency, uint32_t dutycycle,
callback_function_t PeriodCallback = nullptr,
callback_function_t CompareCallback = nullptr); // Set all in one command freq in HZ, Duty in percentage. Including both interrup.
void setPWM(uint32_t channel, uint32_t pin, uint32_t frequency, uint32_t dutycycle,
callback_function_t PeriodCallback = nullptr, callback_function_t CompareCallback = nullptr);
void setCount(uint32_t val, TimerFormat_t format =
TICK_FORMAT); // set timer counter to value 'val' depending on format provided
uint32_t getCount(TimerFormat_t format =
TICK_FORMAT); // return current counter value of timer depending on format provided
void setMode(uint32_t channel, TimerModes_t mode,
PinName pin = NC); // Configure timer channel with specified mode on specified pin if available
void setMode(uint32_t channel, TimerModes_t mode, uint32_t pin);
TimerModes_t getMode(uint32_t channel); // Retrieve configured mode
void setPreloadEnable(bool value); // Configure overflow preload enable setting
uint32_t getCaptureCompare(uint32_t channel,
TimerCompareFormat_t format = TICK_COMPARE_FORMAT); // return Capture/Compare register value of specified channel depending on format provided
void setCaptureCompare(uint32_t channel, uint32_t compare,
TimerCompareFormat_t format = TICK_COMPARE_FORMAT); // set Compare register value of specified channel depending on format provided
void setInterruptPriority(uint32_t preemptPriority, uint32_t subPriority); // set interrupt priority
//Add interrupt to period update
void attachInterrupt(callback_function_t
callback); // Attach interrupt callback which will be called upon update event (timer rollover)
void detachInterrupt(); // remove interrupt callback which was attached to update event
bool hasInterrupt(); //returns true if a timer rollover interrupt has already been set
//Add interrupt to capture/compare channel
void attachInterrupt(uint32_t channel,
callback_function_t callback); // Attach interrupt callback which will be called upon compare match event of specified channel
void detachInterrupt(uint32_t
channel); // remove interrupt callback which was attached to compare match event of specified channel
bool hasInterrupt(uint32_t channel); //returns true if an interrupt has already been set on the channel compare match
void timerHandleDeinit(); // Timer deinitialization
// Refresh() is usefull while timer is running after some registers update
void refresh(
void); // Generate update event to force all registers (Autoreload, prescaler, compare) to be taken into account
uint32_t getTimerClkFreq(); // return timer clock frequency in Hz.
static void captureCompareCallback(TIM_HandleTypeDef
*htim); // Generic Caputre and Compare callback which will call user callback
static void updateCallback(TIM_HandleTypeDef
*htim); // Generic Update (rollover) callback which will call user callback
// The following function(s) are available for more advanced timer options
TIM_HandleTypeDef *getHandle(); // return the handle address for HAL related configuration
int getChannel(uint32_t channel);
int getLLChannel(uint32_t channel);
int getIT(uint32_t channel);
int getAssociatedChannel(uint32_t channel);
#if defined(TIM_CCER_CC1NE)
bool isComplementaryChannel[TIMER_CHANNELS];
#endif
private:
TimerModes_t _ChannelMode[TIMER_CHANNELS];
timerObj_t _timerObj;
callback_function_t callbacks[1 +
TIMER_CHANNELS]; //Callbacks: 0 for update, 1-4 for channels. (channel5/channel6, if any, doesn't have interrupt)
};
extern timerObj_t *HardwareTimer_Handle[TIMER_NUM];
extern timer_index_t get_timer_index(TIM_TypeDef *htim);
#endif /* __cplusplus */
#endif // HAL_TIM_MODULE_ENABLED && !HAL_TIM_MODULE_ONLY
#endif
#endif // GIGAHARDWARETIMER_H_

View File

@@ -1,950 +0,0 @@
/****************************************************************************************************************************
timer.c
For Portenta_H7 boards
Written by Khoi Hoang
Built by Khoi Hoang https://github.com/khoih-prog/Portenta_H7_TimerInterrupt
Licensed under MIT license
Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by
unsigned long miliseconds), you just consume only one Portenta_H7 STM32 timer and avoid conflicting with other cores' tasks.
The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers
Therefore, their executions are not blocked by bad-behaving functions / tasks.
This important feature is absolutely necessary for mission-critical tasks.
Version: 1.4.0
Version Modified By Date Comments
------- ----------- ---------- -----------
1.2.1 K.Hoang 15/09/2021 Initial coding for Portenta_H7
1.3.0 K.Hoang 17/09/2021 Add PWM features and examples
1.3.1 K.Hoang 21/09/2021 Fix warnings in PWM examples
1.4.0 K.Hoang 22/01/2022 Fix `multiple-definitions` linker error. Fix bug
*****************************************************************************************************************************/
// Modified from stm32 core v2.0.0
/*
*******************************************************************************
Copyright (c) 2019, STMicroelectronics
All rights reserved.
This software component is licensed by ST under BSD 3-Clause license,
the "License"; You may not use this file except in compliance with the
License. You may obtain a copy of the License at:
opensource.org/licenses/BSD-3-Clause
*******************************************************************************
*/
#if defined(ARDUINO_GIGA)
#include "Gigatimer.h"
#ifdef __cplusplus
extern "C" {
#endif
#if defined(HAL_TIM_MODULE_ENABLED) && !defined(HAL_TIM_MODULE_ONLY)
/* Private Functions */
/* Aim of the function is to get _timerObj pointer using htim pointer */
/* Highly inspired from magical linux kernel's "container_of" */
/* (which was not directly used since not compatible with IAR toolchain) */
timerObj_t *get_timer_obj(TIM_HandleTypeDef *htim)
{
timerObj_t *obj;
obj = (timerObj_t *)((char *)htim - offsetof(timerObj_t, handle));
return (obj);
}
/**
@brief TIMER Initialization - clock init and nvic init
@param htim_base: TIM handle
@retval None
*/
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim_base)
{
timerObj_t *obj = get_timer_obj(htim_base);
enableTimerClock(htim_base);
// configure Update interrupt
HAL_NVIC_SetPriority(getTimerUpIrq(htim_base->Instance), obj->preemptPriority, obj->subPriority);
HAL_NVIC_EnableIRQ(getTimerUpIrq(htim_base->Instance));
if (getTimerCCIrq(htim_base->Instance) != getTimerUpIrq(htim_base->Instance))
{
// configure Capture Compare interrupt
HAL_NVIC_SetPriority(getTimerCCIrq(htim_base->Instance), obj->preemptPriority, obj->subPriority);
HAL_NVIC_EnableIRQ(getTimerCCIrq(htim_base->Instance));
}
}
/**
@brief TIMER Deinitialization - clock and nvic
@param htim_base: TIM handle
@retval None
*/
void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef *htim_base)
{
disableTimerClock(htim_base);
HAL_NVIC_DisableIRQ(getTimerUpIrq(htim_base->Instance));
HAL_NVIC_DisableIRQ(getTimerCCIrq(htim_base->Instance));
}
/**
@brief Initializes the TIM Output Compare MSP.
@param htim: TIM handle
@retval None
*/
void HAL_TIM_OC_MspInit(TIM_HandleTypeDef *htim)
{
timerObj_t *obj = get_timer_obj(htim);
enableTimerClock(htim);
// configure Update interrupt
HAL_NVIC_SetPriority(getTimerUpIrq(htim->Instance), obj->preemptPriority, obj->subPriority);
HAL_NVIC_EnableIRQ(getTimerUpIrq(htim->Instance));
if (getTimerCCIrq(htim->Instance) != getTimerUpIrq(htim->Instance))
{
// configure Capture Compare interrupt
HAL_NVIC_SetPriority(getTimerCCIrq(htim->Instance), obj->preemptPriority, obj->subPriority);
HAL_NVIC_EnableIRQ(getTimerCCIrq(htim->Instance));
}
}
/**
@brief DeInitialize TIM Output Compare MSP.
@param htim: TIM handle
@retval None
*/
void HAL_TIM_OC_MspDeInit(TIM_HandleTypeDef *htim)
{
disableTimerClock(htim);
HAL_NVIC_DisableIRQ(getTimerUpIrq(htim->Instance));
HAL_NVIC_DisableIRQ(getTimerCCIrq(htim->Instance));
}
/**
@brief Initializes the TIM Input Capture MSP.
@param htim: TIM handle
@retval None
*/
void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
{
enableTimerClock(htim);
}
/**
@brief DeInitialize TIM Input Capture MSP.
@param htim: TIM handle
@retval None
*/
void HAL_TIM_IC_MspDeInit(TIM_HandleTypeDef *htim)
{
disableTimerClock(htim);
}
/* Exported functions */
/**
@brief Enable the timer clock
@param htim: TIM handle
@retval None
*/
void enableTimerClock(TIM_HandleTypeDef *htim)
{
// Enable TIM clock
#if defined(TIM1_BASE)
if (htim->Instance == TIM1)
{
__HAL_RCC_TIM1_CLK_ENABLE();
}
#endif
#if defined(TIM2_BASE)
if (htim->Instance == TIM2)
{
__HAL_RCC_TIM2_CLK_ENABLE();
}
#endif
#if defined(TIM3_BASE)
if (htim->Instance == TIM3)
{
__HAL_RCC_TIM3_CLK_ENABLE();
}
#endif
#if defined(TIM4_BASE)
if (htim->Instance == TIM4)
{
__HAL_RCC_TIM4_CLK_ENABLE();
}
#endif
#if defined(TIM5_BASE)
if (htim->Instance == TIM5)
{
__HAL_RCC_TIM5_CLK_ENABLE();
}
#endif
#if defined(TIM6_BASE)
if (htim->Instance == TIM6)
{
__HAL_RCC_TIM6_CLK_ENABLE();
}
#endif
#if defined(TIM7_BASE)
if (htim->Instance == TIM7)
{
__HAL_RCC_TIM7_CLK_ENABLE();
}
#endif
#if defined(TIM8_BASE)
if (htim->Instance == TIM8)
{
__HAL_RCC_TIM8_CLK_ENABLE();
}
#endif
#if defined(TIM9_BASE)
if (htim->Instance == TIM9)
{
__HAL_RCC_TIM9_CLK_ENABLE();
}
#endif
#if defined(TIM10_BASE)
if (htim->Instance == TIM10)
{
__HAL_RCC_TIM10_CLK_ENABLE();
}
#endif
#if defined(TIM11_BASE)
if (htim->Instance == TIM11)
{
__HAL_RCC_TIM11_CLK_ENABLE();
}
#endif
#if defined(TIM12_BASE)
if (htim->Instance == TIM12)
{
__HAL_RCC_TIM12_CLK_ENABLE();
}
#endif
#if defined(TIM13_BASE)
if (htim->Instance == TIM13)
{
__HAL_RCC_TIM13_CLK_ENABLE();
}
#endif
#if defined(TIM14_BASE)
if (htim->Instance == TIM14)
{
__HAL_RCC_TIM14_CLK_ENABLE();
}
#endif
#if defined(TIM15_BASE)
if (htim->Instance == TIM15)
{
__HAL_RCC_TIM15_CLK_ENABLE();
}
#endif
#if defined(TIM16_BASE)
if (htim->Instance == TIM16)
{
__HAL_RCC_TIM16_CLK_ENABLE();
}
#endif
#if defined(TIM17_BASE)
if (htim->Instance == TIM17)
{
__HAL_RCC_TIM17_CLK_ENABLE();
}
#endif
#if defined(TIM18_BASE)
if (htim->Instance == TIM18)
{
__HAL_RCC_TIM18_CLK_ENABLE();
}
#endif
#if defined(TIM19_BASE)
if (htim->Instance == TIM19)
{
__HAL_RCC_TIM19_CLK_ENABLE();
}
#endif
#if defined(TIM20_BASE)
if (htim->Instance == TIM20)
{
__HAL_RCC_TIM20_CLK_ENABLE();
}
#endif
#if defined(TIM21_BASE)
if (htim->Instance == TIM21)
{
__HAL_RCC_TIM21_CLK_ENABLE();
}
#endif
#if defined(TIM22_BASE)
if (htim->Instance == TIM22)
{
__HAL_RCC_TIM22_CLK_ENABLE();
}
#endif
}
/**
@brief Disable the timer clock
@param htim: TIM handle
@retval None
*/
void disableTimerClock(TIM_HandleTypeDef *htim)
{
// Enable TIM clock
#if defined(TIM1_BASE)
if (htim->Instance == TIM1)
{
__HAL_RCC_TIM1_CLK_DISABLE();
}
#endif
#if defined(TIM2_BASE)
if (htim->Instance == TIM2)
{
__HAL_RCC_TIM2_CLK_DISABLE();
}
#endif
#if defined(TIM3_BASE)
if (htim->Instance == TIM3)
{
__HAL_RCC_TIM3_CLK_DISABLE();
}
#endif
#if defined(TIM4_BASE)
if (htim->Instance == TIM4)
{
__HAL_RCC_TIM4_CLK_DISABLE();
}
#endif
#if defined(TIM5_BASE)
if (htim->Instance == TIM5)
{
__HAL_RCC_TIM5_CLK_DISABLE();
}
#endif
#if defined(TIM6_BASE)
if (htim->Instance == TIM6)
{
__HAL_RCC_TIM6_CLK_DISABLE();
}
#endif
#if defined(TIM7_BASE)
if (htim->Instance == TIM7)
{
__HAL_RCC_TIM7_CLK_DISABLE();
}
#endif
#if defined(TIM8_BASE)
if (htim->Instance == TIM8)
{
__HAL_RCC_TIM8_CLK_DISABLE();
}
#endif
#if defined(TIM9_BASE)
if (htim->Instance == TIM9)
{
__HAL_RCC_TIM9_CLK_DISABLE();
}
#endif
#if defined(TIM10_BASE)
if (htim->Instance == TIM10)
{
__HAL_RCC_TIM10_CLK_DISABLE();
}
#endif
#if defined(TIM11_BASE)
if (htim->Instance == TIM11)
{
__HAL_RCC_TIM11_CLK_DISABLE();
}
#endif
#if defined(TIM12_BASE)
if (htim->Instance == TIM12)
{
__HAL_RCC_TIM12_CLK_DISABLE();
}
#endif
#if defined(TIM13_BASE)
if (htim->Instance == TIM13)
{
__HAL_RCC_TIM13_CLK_DISABLE();
}
#endif
#if defined(TIM14_BASE)
if (htim->Instance == TIM14)
{
__HAL_RCC_TIM14_CLK_DISABLE();
}
#endif
#if defined(TIM15_BASE)
if (htim->Instance == TIM15)
{
__HAL_RCC_TIM15_CLK_DISABLE();
}
#endif
#if defined(TIM16_BASE)
if (htim->Instance == TIM16)
{
__HAL_RCC_TIM16_CLK_DISABLE();
}
#endif
#if defined(TIM17_BASE)
if (htim->Instance == TIM17)
{
__HAL_RCC_TIM17_CLK_DISABLE();
}
#endif
#if defined(TIM18_BASE)
if (htim->Instance == TIM18)
{
__HAL_RCC_TIM18_CLK_DISABLE();
}
#endif
#if defined(TIM19_BASE)
if (htim->Instance == TIM19)
{
__HAL_RCC_TIM19_CLK_DISABLE();
}
#endif
#if defined(TIM20_BASE)
if (htim->Instance == TIM20)
{
__HAL_RCC_TIM20_CLK_DISABLE();
}
#endif
#if defined(TIM21_BASE)
if (htim->Instance == TIM21)
{
__HAL_RCC_TIM21_CLK_DISABLE();
}
#endif
#if defined(TIM22_BASE)
if (htim->Instance == TIM22)
{
__HAL_RCC_TIM22_CLK_DISABLE();
}
#endif
}
/**
@brief This function return IRQ number corresponding to update interrupt event of timer instance.
@param tim: timer instance
@retval IRQ number
*/
IRQn_Type getTimerUpIrq(TIM_TypeDef *tim)
{
IRQn_Type IRQn = NonMaskableInt_IRQn;
if (tim != (TIM_TypeDef *)NC)
{
/* Get IRQn depending on TIM instance */
switch ((uint32_t)tim)
{
#if defined(TIM1_BASE)
case (uint32_t)TIM1_BASE:
IRQn = TIM1_IRQn;
break;
#endif
#if defined(TIM2_BASE)
case (uint32_t)TIM2_BASE:
IRQn = TIM2_IRQn;
break;
#endif
#if defined(TIM3_BASE)
case (uint32_t)TIM3_BASE:
IRQn = TIM3_IRQn;
break;
#endif
#if defined(TIM4_BASE)
case (uint32_t)TIM4_BASE:
IRQn = TIM4_IRQn;
break;
#endif
#if defined(TIM5_BASE)
case (uint32_t)TIM5_BASE:
IRQn = TIM5_IRQn;
break;
#endif
// KH
#if 0
#if defined(TIM6_BASE)
case (uint32_t)TIM6_BASE:
IRQn = TIM6_IRQn;
break;
#endif
#endif
//////
#if defined(TIM7_BASE)
case (uint32_t)TIM7_BASE:
IRQn = TIM7_IRQn;
break;
#endif
#if defined(TIM8_BASE)
case (uint32_t)TIM8_BASE:
IRQn = TIM8_IRQn;
break;
#endif
#if defined(TIM9_BASE)
case (uint32_t)TIM9_BASE:
IRQn = TIM9_IRQn;
break;
#endif
#if defined(TIM10_BASE)
case (uint32_t)TIM10_BASE:
IRQn = TIM10_IRQn;
break;
#endif
#if defined(TIM11_BASE)
case (uint32_t)TIM11_BASE:
IRQn = TIM11_IRQn;
break;
#endif
#if defined(TIM12_BASE)
case (uint32_t)TIM12_BASE:
IRQn = TIM12_IRQn;
break;
#endif
#if defined(TIM13_BASE)
case (uint32_t)TIM13_BASE:
IRQn = TIM13_IRQn;
break;
#endif
#if defined(TIM14_BASE)
case (uint32_t)TIM14_BASE:
IRQn = TIM14_IRQn;
break;
#endif
#if defined(TIM15_BASE)
case (uint32_t)TIM15_BASE:
IRQn = TIM15_IRQn;
break;
#endif
#if defined(TIM16_BASE)
case (uint32_t)TIM16_BASE:
IRQn = TIM16_IRQn;
break;
#endif
#if defined(TIM17_BASE)
case (uint32_t)TIM17_BASE:
IRQn = TIM17_IRQn;
break;
#endif
#if defined(TIM18_BASE)
case (uint32_t)TIM18_BASE:
IRQn = TIM18_IRQn;
break;
#endif
#if defined(TIM19_BASE)
case (uint32_t)TIM19_BASE:
IRQn = TIM19_IRQn;
break;
#endif
#if defined(TIM20_BASE)
case (uint32_t)TIM20_BASE:
IRQn = TIM20_IRQn;
break;
#endif
#if defined(TIM21_BASE)
case (uint32_t)TIM21_BASE:
IRQn = TIM21_IRQn;
break;
#endif
#if defined(TIM22_BASE)
case (uint32_t)TIM22_BASE:
IRQn = TIM22_IRQn;
break;
#endif
default:
//_Error_Handler("TIM: Unknown timer IRQn", (int)tim);
break;
}
}
return IRQn;
}
/**
@brief This function return IRQ number corresponding to Capture or Compare interrupt event of timer instance.
@param tim: timer instance
@retval IRQ number
*/
IRQn_Type getTimerCCIrq(TIM_TypeDef *tim)
{
IRQn_Type IRQn = NonMaskableInt_IRQn;
if (tim != (TIM_TypeDef *)NC)
{
/* Get IRQn depending on TIM instance */
switch ((uint32_t)tim)
{
#if defined(TIM1_BASE)
case (uint32_t)TIM1_BASE:
IRQn = TIM1_CC_IRQn;
break;
#endif
#if defined(TIM2_BASE)
case (uint32_t)TIM2_BASE:
IRQn = TIM2_IRQn;
break;
#endif
#if defined(TIM3_BASE)
case (uint32_t)TIM3_BASE:
IRQn = TIM3_IRQn;
break;
#endif
#if defined(TIM4_BASE)
case (uint32_t)TIM4_BASE:
IRQn = TIM4_IRQn;
break;
#endif
#if defined(TIM5_BASE)
case (uint32_t)TIM5_BASE:
IRQn = TIM5_IRQn;
break;
#endif
#if 0
// KH
#if defined(TIM6_BASE)
case (uint32_t)TIM6_BASE:
IRQn = TIM6_IRQn;
break;
#endif
#endif
//////
#if defined(TIM7_BASE)
case (uint32_t)TIM7_BASE:
IRQn = TIM7_IRQn;
break;
#endif
#if defined(TIM8_BASE)
case (uint32_t)TIM8_BASE:
IRQn = TIM8_CC_IRQn;
break;
#endif
#if defined(TIM9_BASE)
case (uint32_t)TIM9_BASE:
IRQn = TIM9_IRQn;
break;
#endif
#if defined(TIM10_BASE)
case (uint32_t)TIM10_BASE:
IRQn = TIM10_IRQn;
break;
#endif
#if defined(TIM11_BASE)
case (uint32_t)TIM11_BASE:
IRQn = TIM11_IRQn;
break;
#endif
#if defined(TIM12_BASE)
case (uint32_t)TIM12_BASE:
IRQn = TIM12_IRQn;
break;
#endif
#if defined(TIM13_BASE)
case (uint32_t)TIM13_BASE:
IRQn = TIM13_IRQn;
break;
#endif
#if defined(TIM14_BASE)
case (uint32_t)TIM14_BASE:
IRQn = TIM14_IRQn;
break;
#endif
#if defined(TIM15_BASE)
case (uint32_t)TIM15_BASE:
IRQn = TIM15_IRQn;
break;
#endif
#if defined(TIM16_BASE)
case (uint32_t)TIM16_BASE:
IRQn = TIM16_IRQn;
break;
#endif
#if defined(TIM17_BASE)
case (uint32_t)TIM17_BASE:
IRQn = TIM17_IRQn;
break;
#endif
#if defined(TIM18_BASE)
case (uint32_t)TIM18_BASE:
IRQn = TIM18_IRQn;
break;
#endif
#if defined(TIM19_BASE)
case (uint32_t)TIM19_BASE:
IRQn = TIM19_IRQn;
break;
#endif
#if defined(TIM20_BASE)
case (uint32_t)TIM20_BASE:
IRQn = TIM20_CC_IRQn;
break;
#endif
#if defined(TIM21_BASE)
case (uint32_t)TIM21_BASE:
IRQn = TIM21_IRQn;
break;
#endif
#if defined(TIM22_BASE)
case (uint32_t)TIM22_BASE:
IRQn = TIM22_IRQn;
break;
#endif
break;
default:
//_Error_Handler("TIM: Unknown timer IRQn", (int)tim);
break;
}
}
return IRQn;
}
/**
@brief This function return the timer clock source.
@param tim: timer instance
@retval 1 = PCLK1 or 2 = PCLK2
*/
uint8_t getTimerClkSrc(TIM_TypeDef *tim)
{
uint8_t clkSrc = 0;
if (tim != (TIM_TypeDef *)NC)
#if defined(STM32F0xx) || defined(STM32G0xx)
/* TIMx source CLK is PCKL1 */
clkSrc = 1;
#else
{
/* Get source clock depending on TIM instance */
switch ((uint32_t)tim)
{
#if defined(TIM2_BASE)
case (uint32_t)TIM2:
#endif
#if defined(TIM3_BASE)
case (uint32_t)TIM3:
#endif
#if defined(TIM4_BASE)
case (uint32_t)TIM4:
#endif
#if defined(TIM5_BASE)
case (uint32_t)TIM5:
#endif
#if defined(TIM6_BASE)
case (uint32_t)TIM6:
#endif
#if defined(TIM7_BASE)
case (uint32_t)TIM7:
#endif
#if defined(TIM12_BASE)
case (uint32_t)TIM12:
#endif
#if defined(TIM13_BASE)
case (uint32_t)TIM13:
#endif
#if defined(TIM14_BASE)
case (uint32_t)TIM14:
#endif
#if defined(TIM18_BASE)
case (uint32_t)TIM18:
#endif
clkSrc = 1;
break;
#if defined(TIM1_BASE)
case (uint32_t)TIM1:
#endif
#if defined(TIM8_BASE)
case (uint32_t)TIM8:
#endif
#if defined(TIM9_BASE)
case (uint32_t)TIM9:
#endif
#if defined(TIM10_BASE)
case (uint32_t)TIM10:
#endif
#if defined(TIM11_BASE)
case (uint32_t)TIM11:
#endif
#if defined(TIM15_BASE)
case (uint32_t)TIM15:
#endif
#if defined(TIM16_BASE)
case (uint32_t)TIM16:
#endif
#if defined(TIM17_BASE)
case (uint32_t)TIM17:
#endif
#if defined(TIM19_BASE)
case (uint32_t)TIM19:
#endif
#if defined(TIM20_BASE)
case (uint32_t)TIM20:
#endif
#if defined(TIM21_BASE)
case (uint32_t)TIM21:
#endif
#if defined(TIM22_BASE)
case (uint32_t)TIM22:
#endif
clkSrc = 2;
break;
default:
////_Error_Handler("TIM: Unknown timer instance", (int)tim);
break;
}
}
#endif
return clkSrc;
}
#endif /* HAL_TIM_MODULE_ENABLED && !HAL_TIM_MODULE_ONLY */
#ifdef __cplusplus
}
#endif
#endif
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

View File

@@ -1,198 +0,0 @@
/****************************************************************************************************************************
timer.h
For Portenta_H7 boards
Written by Khoi Hoang
Built by Khoi Hoang https://github.com/khoih-prog/Portenta_H7_TimerInterrupt
Licensed under MIT license
Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by
unsigned long miliseconds), you just consume only one Portenta_H7 STM32 timer and avoid conflicting with other cores' tasks.
The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers
Therefore, their executions are not blocked by bad-behaving functions / tasks.
This important feature is absolutely necessary for mission-critical tasks.
Version: 1.4.0
Version Modified By Date Comments
------- ----------- ---------- -----------
1.2.1 K.Hoang 15/09/2021 Initial coding for Portenta_H7
1.3.0 K.Hoang 17/09/2021 Add PWM features and examples
1.3.1 K.Hoang 21/09/2021 Fix warnings in PWM examples
1.4.0 K.Hoang 22/01/2022 Fix `multiple-definitions` linker error. Fix bug
*****************************************************************************************************************************/
// Modified from stm32 core v2.0.0
/*
*******************************************************************************
Copyright (c) 2019, STMicroelectronics
All rights reserved.
This software component is licensed by ST under BSD 3-Clause license,
the "License"; You may not use this file except in compliance with the
License. You may obtain a copy of the License at:
opensource.org/licenses/BSD-3-Clause
*******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __GIGATIMER_H
#define __GIGATIMER_H
#if defined(ARDUINO_GIGA)
/* Includes ------------------------------------------------------------------*/
#include "PinNames.h"
#ifdef __cplusplus
extern "C" {
#endif
#if defined(HAL_TIM_MODULE_ENABLED) && !defined(HAL_TIM_MODULE_ONLY)
/* Exported constants --------------------------------------------------------*/
#ifndef TIM_IRQ_PRIO
#if (__CORTEX_M == 0x00U)
#define TIM_IRQ_PRIO 3
#else
#define TIM_IRQ_PRIO 14
#endif /* __CORTEX_M */
#endif /* TIM_IRQ_PRIO */
#ifndef TIM_IRQ_SUBPRIO
#define TIM_IRQ_SUBPRIO 0
#endif
#if defined(TIM1_BASE) && !defined(TIM1_IRQn)
#define TIM1_IRQn TIM1_UP_IRQn
#define TIM1_IRQHandler TIM1_UP_IRQHandler
#endif
#if defined(TIM8_BASE) && !defined(TIM8_IRQn)
#define TIM8_IRQn TIM8_UP_TIM13_IRQn
#define TIM8_IRQHandler TIM8_UP_TIM13_IRQHandler
#endif
#if defined(TIM12_BASE) && !defined(TIM12_IRQn)
#define TIM12_IRQn TIM8_BRK_TIM12_IRQn
#define TIM12_IRQHandler TIM8_BRK_TIM12_IRQHandler
#endif
#if defined(TIM13_BASE) && !defined(TIM13_IRQn)
#define TIM13_IRQn TIM8_UP_TIM13_IRQn
#endif
#if defined(TIM14_BASE) && !defined(TIM14_IRQn)
#define TIM14_IRQn TIM8_TRG_COM_TIM14_IRQn
#define TIM14_IRQHandler TIM8_TRG_COM_TIM14_IRQHandler
#endif
typedef enum
{
#if defined(TIM1_BASE)
TIMER1_INDEX,
#endif
#if defined(TIM2_BASE)
TIMER2_INDEX,
#endif
#if defined(TIM3_BASE)
TIMER3_INDEX,
#endif
#if defined(TIM4_BASE)
TIMER4_INDEX,
#endif
#if defined(TIM5_BASE)
TIMER5_INDEX,
#endif
#if defined(TIM6_BASE)
TIMER6_INDEX,
#endif
#if defined(TIM7_BASE)
TIMER7_INDEX,
#endif
#if defined(TIM8_BASE)
TIMER8_INDEX,
#endif
#if defined(TIM9_BASE)
TIMER9_INDEX,
#endif
#if defined(TIM10_BASE)
TIMER10_INDEX,
#endif
#if defined(TIM11_BASE)
TIMER11_INDEX,
#endif
#if defined(TIM12_BASE)
TIMER12_INDEX,
#endif
#if defined(TIM13_BASE)
TIMER13_INDEX,
#endif
#if defined(TIM14_BASE)
TIMER14_INDEX,
#endif
#if defined(TIM15_BASE)
TIMER15_INDEX,
#endif
#if defined(TIM16_BASE)
TIMER16_INDEX,
#endif
#if defined(TIM17_BASE)
TIMER17_INDEX,
#endif
#if defined(TIM18_BASE)
TIMER18_INDEX,
#endif
#if defined(TIM19_BASE)
TIMER19_INDEX,
#endif
#if defined(TIM20_BASE)
TIMER20_INDEX,
#endif
#if defined(TIM21_BASE)
TIMER21_INDEX,
#endif
#if defined(TIM22_BASE)
TIMER22_INDEX,
#endif
TIMER_NUM,
UNKNOWN_TIMER = 0XFFFF
} timer_index_t;
// This structure is used to be able to get HardwareTimer instance (C++ class)
// from handler (C structure) specially for interrupt management
typedef struct
{
// Those 2 first fields must remain in this order at the beginning of the structure
void *__this;
TIM_HandleTypeDef handle;
uint32_t preemptPriority;
uint32_t subPriority;
} timerObj_t;
/* Exported functions ------------------------------------------------------- */
timerObj_t *get_timer_obj(TIM_HandleTypeDef *htim);
void enableTimerClock(TIM_HandleTypeDef *htim);
void disableTimerClock(TIM_HandleTypeDef *htim);
uint32_t getTimerIrq(TIM_TypeDef *tim);
uint8_t getTimerClkSrc(TIM_TypeDef *tim);
IRQn_Type getTimerUpIrq(TIM_TypeDef *tim);
IRQn_Type getTimerCCIrq(TIM_TypeDef *tim);
#endif /* HAL_TIM_MODULE_ENABLED && !HAL_TIM_MODULE_ONLY */
#ifdef __cplusplus
}
#endif
#endif
#endif /* __GIGATIMER_H */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

View File

@@ -117,46 +117,35 @@ void I2CManagerClass::I2C_setClock(uint32_t i2cClockSpeed) {
// Disable the I2C device, as TRISE can only be programmed whilst disabled
s->CR1 &= ~(I2C_CR1_PE); // Disable I2C
s->CR1 |= I2C_CR1_SWRST; // reset the I2C
asm("nop"); // wait a bit... suggestion from online!
s->CR1 &= ~(I2C_CR1_SWRST); // Normal operation
if (i2cClockSpeed > 100000UL)
if (i2cClockSpeed > 100000L)
{
// if (i2cClockSpeed > 400000L)
// i2cClockSpeed = 400000L;
if (i2cClockSpeed > 400000L)
i2cClockSpeed = 400000L;
t_rise = 300; // nanoseconds
}
else
{
// i2cClockSpeed = 100000L;
i2cClockSpeed = 100000L;
t_rise = 1000; // nanoseconds
}
// Configure the rise time register - max allowed tRISE is 1000ns,
// so value = 1000ns * I2C_PERIPH_CLK MHz / 1000 + 1.
s->TRISE = (t_rise * i2c_MHz / 1000) + 1;
// Configure the rise time register
s->TRISE = (t_rise / (1000 / i2c_MHz)) + 1;
// Bit 15: I2C Master mode, 0=standard, 1=Fast Mode
// Bit 14: Duty, fast mode duty cycle (use 2:1)
// Bit 11-0: FREQR
// if (i2cClockSpeed > 400000UL) {
// // In fast mode plus, I2C period is 3 * CCR * TPCLK1.
// // s->CCR &= ~(0x3000); // Clear all bits except 12 and 13 which must remain per reset value
// s->CCR = APB1clk1 / 3 / i2cClockSpeed; // Set I2C clockspeed to start!
// s->CCR |= 0xC000; // We need Fast Mode AND DUTY bits set
// } else {
// In standard and fast mode, I2C period is 2 * CCR * TPCLK1
s->CCR &= ~(0x3000); // Clear all bits except 12 and 13 which must remain per reset value
s->CCR |= (APB1clk1 / 2 / i2cClockSpeed); // Set I2C clockspeed to start!
// s->CCR |= (i2c_MHz * 500 / (i2cClockSpeed / 1000)); // Set I2C clockspeed to start!
// if (i2cClockSpeed > 100000UL)
// s->CCR |= 0xC000; // We need Fast Mode bits set as well
// }
// DIAG(F("I2C_init() peripheral clock is now: %d, full reg is %x"), (s->CR2 & 0xFF), s->CR2);
// DIAG(F("I2C_init() peripheral CCR is now: %d"), s->CCR);
// DIAG(F("I2C_init() peripheral TRISE is now: %d"), s->TRISE);
if (i2cClockSpeed > 100000L) {
// In fast mode, I2C period is 3 * CCR * TPCLK1.
//APB1clk1 / 3 / i2cClockSpeed = 38, but that results in 306KHz not 400!
ccr_freq = 30; // So 30 gives 396KHz or so!
s->CCR = (uint16_t)(ccr_freq | 0x8000); // We need Fast Mode set
} else {
// In standard mode, I2C period is 2 * CCR * TPCLK1
ccr_freq = (APB1clk1 / 2 / i2cClockSpeed); // Should be 225 for 45Mhz APB1 clock
s->CCR |= (uint16_t)ccr_freq;
}
// Enable the I2C master mode
s->CR1 |= I2C_CR1_PE; // Enable I2C
@@ -170,7 +159,6 @@ void I2CManagerClass::I2C_init()
// Query the clockspeed from the STM32 HAL layer
APB1clk1 = HAL_RCC_GetPCLK1Freq();
i2c_MHz = APB1clk1 / 1000000UL;
// DIAG(F("I2C_init() peripheral clock speed is: %d"), i2c_MHz);
// Enable clocks
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;//(1 << 21); // Enable I2C CLOCK
// Reset the I2C1 peripheral to initial state
@@ -193,7 +181,6 @@ void I2CManagerClass::I2C_init()
GPIOB->AFR[1] |= (4<<0) | (4<<4); // PB8 on low nibble, PB9 on next nibble up
// Software reset the I2C peripheral
I2C1->CR1 &= ~I2C_CR1_PE; // Disable I2C1 peripheral
s->CR1 |= I2C_CR1_SWRST; // reset the I2C
asm("nop"); // wait a bit... suggestion from online!
s->CR1 &= ~(I2C_CR1_SWRST); // Normal operation
@@ -204,7 +191,6 @@ void I2CManagerClass::I2C_init()
// Set I2C peripheral clock frequency
// s->CR2 |= I2C_PERIPH_CLK;
s->CR2 |= i2c_MHz;
// DIAG(F("I2C_init() peripheral clock is now: %d"), s->CR2);
// set own address to 00 - not used in master mode
I2C1->OAR1 = (1 << 14); // bit 14 should be kept at 1 according to the datasheet
@@ -228,7 +214,6 @@ void I2CManagerClass::I2C_init()
s->CR2 |= (I2C_CR2_ITBUFEN | I2C_CR2_ITEVTEN | I2C_CR2_ITERREN); // Enable Buffer, Event and Error interrupts
#endif
// DIAG(F("I2C_init() setting initial I2C clock to 100KHz"));
// Calculate baudrate and set default rate for now
// Configure the Clock Control Register for 100KHz SCL frequency
// Bit 15: I2C Master mode, 0=standard, 1=Fast Mode
@@ -236,14 +221,12 @@ void I2CManagerClass::I2C_init()
// Bit 11-0: so CCR divisor would be clk / 2 / 100000 (where clk is in Hz)
// s->CCR = I2C_PERIPH_CLK * 5;
s->CCR &= ~(0x3000); // Clear all bits except 12 and 13 which must remain per reset value
s->CCR |= (APB1clk1 / 2 / 100000UL); // Set a default of 100KHz I2C clockspeed to start!
s->CCR |= (APB1clk1 / 2 / 100000UL); // i2c_MHz * 5;
// s->CCR = i2c_MHz * 5;
// Configure the rise time register - max allowed is 1000ns, so value = 1000ns * I2C_PERIPH_CLK MHz / 1000 + 1.
s->TRISE = (1000 * i2c_MHz / 1000) + 1;
// DIAG(F("I2C_init() peripheral clock is now: %d, full reg is %x"), (s->CR2 & 0xFF), s->CR2);
// DIAG(F("I2C_init() peripheral CCR is now: %d"), s->CCR);
// DIAG(F("I2C_init() peripheral TRISE is now: %d"), s->TRISE);
// s->TRISE = I2C_PERIPH_CLK + 1; // 1000 ns / 50 ns = 20 + 1 = 21
s->TRISE = i2c_MHz + 1;
// Enable the I2C master mode
s->CR1 |= I2C_CR1_PE; // Enable I2C

View File

@@ -35,21 +35,13 @@
#define WIRE_HAS_TIMEOUT
#endif
#if defined(GIGA_I2C_1)
#define DCCEX_WIRE Wire1
#else
#define DCCEX_WIRE Wire
#endif
/***************************************************************************
* Initialise I2C interface software
***************************************************************************/
void I2CManagerClass::_initialise() {
DCCEX_WIRE.begin();
Wire.begin();
#if defined(WIRE_HAS_TIMEOUT)
DCCEX_WIRE.setWireTimeout(_timeout, true);
Wire.setWireTimeout(_timeout, true);
#endif
}
@@ -58,7 +50,7 @@ void I2CManagerClass::_initialise() {
* on Arduino. Mega4809 supports 1000000 (Fast+) too.
***************************************************************************/
void I2CManagerClass::_setClock(unsigned long i2cClockSpeed) {
DCCEX_WIRE.setClock(i2cClockSpeed);
Wire.setClock(i2cClockSpeed);
}
/***************************************************************************
@@ -69,7 +61,7 @@ void I2CManagerClass::_setClock(unsigned long i2cClockSpeed) {
void I2CManagerClass::setTimeout(unsigned long value) {
_timeout = value;
#if defined(WIRE_HAS_TIMEOUT)
DCCEX_WIRE.setWireTimeout(value, true);
Wire.setWireTimeout(value, true);
#endif
}
@@ -82,7 +74,7 @@ static uint8_t muxSelect(I2CAddress address) {
I2CMux muxNo = address.muxNumber();
I2CSubBus subBus = address.subBus();
if (muxNo != I2CMux_None) {
DCCEX_WIRE.beginTransmission(I2C_MUX_BASE_ADDRESS+muxNo);
Wire.beginTransmission(I2C_MUX_BASE_ADDRESS+muxNo);
uint8_t data = (subBus == SubBus_All) ? 0xff :
(subBus == SubBus_None) ? 0x00 :
#if defined(I2CMUX_PCA9547)
@@ -94,8 +86,8 @@ static uint8_t muxSelect(I2CAddress address) {
// with a bit set for the subBus to be enabled
1 << subBus;
#endif
DCCEX_WIRE.write(&data, 1);
return DCCEX_WIRE.endTransmission(true); // have to release I2C bus for it to work
Wire.write(&data, 1);
return Wire.endTransmission(true); // have to release I2C bus for it to work
}
return I2C_STATUS_OK;
}
@@ -118,9 +110,9 @@ uint8_t I2CManagerClass::write(I2CAddress address, const uint8_t buffer[], uint8
#endif
// Only send new transaction if address is non-zero.
if (muxStatus == I2C_STATUS_OK && address != 0) {
DCCEX_WIRE.beginTransmission(address);
if (size > 0) DCCEX_WIRE.write(buffer, size);
status = DCCEX_WIRE.endTransmission();
Wire.beginTransmission(address);
if (size > 0) Wire.write(buffer, size);
status = Wire.endTransmission();
}
#ifdef I2C_EXTENDED_ADDRESS
// Deselect MUX if there's more than one MUX present, to avoid having multiple ones selected
@@ -169,25 +161,25 @@ uint8_t I2CManagerClass::read(I2CAddress address, uint8_t readBuffer[], uint8_t
// Only start new transaction if address is non-zero.
if (muxStatus == I2C_STATUS_OK && address != 0) {
if (writeSize > 0) {
DCCEX_WIRE.beginTransmission(address);
DCCEX_WIRE.write(writeBuffer, writeSize);
status = DCCEX_WIRE.endTransmission(false); // Don't free bus yet
Wire.beginTransmission(address);
Wire.write(writeBuffer, writeSize);
status = Wire.endTransmission(false); // Don't free bus yet
}
if (status == I2C_STATUS_OK) {
#ifdef WIRE_HAS_TIMEOUT
DCCEX_WIRE.clearWireTimeoutFlag();
DCCEX_WIRE.requestFrom(address, (size_t)readSize);
if (!DCCEX_WIRE.getWireTimeoutFlag()) {
while (DCCEX_WIRE.available() && nBytes < readSize)
readBuffer[nBytes++] = DCCEX_WIRE.read();
Wire.clearWireTimeoutFlag();
Wire.requestFrom(address, (size_t)readSize);
if (!Wire.getWireTimeoutFlag()) {
while (Wire.available() && nBytes < readSize)
readBuffer[nBytes++] = Wire.read();
if (nBytes < readSize) status = I2C_STATUS_TRUNCATED;
} else {
status = I2C_STATUS_TIMEOUT;
}
#else
DCCEX_WIRE.requestFrom(address, (size_t)readSize);
while (DCCEX_WIRE.available() && nBytes < readSize)
readBuffer[nBytes++] = DCCEX_WIRE.read();
Wire.requestFrom(address, (size_t)readSize);
while (Wire.available() && nBytes < readSize)
readBuffer[nBytes++] = Wire.read();
if (nBytes < readSize) status = I2C_STATUS_TRUNCATED;
#endif
}

View File

@@ -5,7 +5,6 @@
* © 2020-2023 Harald Barth
* © 2020-2021 Chris Harlow
* © 2023 Colin Murdoch
* © 2023 Travis Farmer
* All rights reserved.
*
* This file is part of CommandStation-EX
@@ -58,7 +57,6 @@ MotorDriver::MotorDriver(int16_t power_pin, byte signal_pin, byte signal_pin2, i
getFastPin(F("SIG"),signalPin,fastSignalPin);
pinMode(signalPin, OUTPUT);
#ifndef ARDUINO_GIGA // no giga
fastSignalPin.shadowinout = NULL;
if (HAVE_PORTA(fastSignalPin.inout == &PORTA)) {
DIAG(F("Found PORTA pin %d"),signalPin);
@@ -90,14 +88,13 @@ MotorDriver::MotorDriver(int16_t power_pin, byte signal_pin, byte signal_pin2, i
fastSignalPin.shadowinout = fastSignalPin.inout;
fastSignalPin.inout = &shadowPORTF;
}
#endif // giga
signalPin2=signal_pin2;
if (signalPin2!=UNUSED_PIN) {
dualSignal=true;
getFastPin(F("SIG2"),signalPin2,fastSignalPin2);
pinMode(signalPin2, OUTPUT);
#ifndef ARDUINO_GIGA // no giga
fastSignalPin2.shadowinout = NULL;
if (HAVE_PORTA(fastSignalPin2.inout == &PORTA)) {
DIAG(F("Found PORTA pin %d"),signalPin2);
@@ -129,7 +126,6 @@ MotorDriver::MotorDriver(int16_t power_pin, byte signal_pin, byte signal_pin2, i
fastSignalPin2.shadowinout = fastSignalPin2.inout;
fastSignalPin2.inout = &shadowPORTF;
}
#endif // giga
}
else dualSignal=false;
@@ -505,16 +501,8 @@ unsigned int MotorDriver::mA2raw( unsigned int mA) {
return (int32_t)mA * senseScale / senseFactorInternal;
}
void MotorDriver::getFastPin(const FSH* type,int pin, bool input, FASTPIN & result) {
// DIAG(F("MotorDriver %S Pin=%d,"),type,pin);
#if defined(ARDUINO_GIGA) // yes giga
(void)type;
(void)input; // no warnings please
result = pin;
#else // no giga
(void) type; // avoid compiler warning if diag not used above.
#if defined(ARDUINO_ARCH_SAMD)
PortGroup *port = digitalPinToPort(pin);
@@ -529,7 +517,6 @@ void MotorDriver::getFastPin(const FSH* type,int pin, bool input, FASTPIN & res
result.inout = portOutputRegister(port);
result.maskHIGH = digitalPinToBitMask(pin);
result.maskLOW = ~result.maskHIGH;
#endif // giga
// DIAG(F(" port=0x%x, inoutpin=0x%x, isinput=%d, mask=0x%x"),port, result.inout,input,result.maskHIGH);
}

View File

@@ -4,7 +4,6 @@
* © 2021 Fred Decker
* © 2020 Chris Harlow
* © 2022 Harald Barth
* © 2023 Travis Farmer
* All rights reserved.
*
* This file is part of CommandStation-EX
@@ -31,21 +30,12 @@
// use powers of two so we can do logical and/or on the track modes in if clauses.
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};
#if defined(ARDUINO_GIGA) // yes giga
#define setHIGH(fastpin) digitalWrite(fastpin,1)
#define setLOW(fastpin) digitalWrite(fastpin,0)
#else // no giga
#define setHIGH(fastpin) *fastpin.inout |= fastpin.maskHIGH
#define setLOW(fastpin) *fastpin.inout &= fastpin.maskLOW
#endif // giga
#if defined(ARDUINO_GIGA) // yes giga
#define isHIGH(fastpin) ((PinStatus)digitalRead(fastpin)==1)
#define isLOW(fastpin) ((PinStatus)digitalRead(fastpin)==0)
#else // no giga
#define isHIGH(fastpin) (*fastpin.inout & fastpin.maskHIGH)
#define isLOW(fastpin) (!isHIGH(fastpin))
#endif // giga
#define TOKENPASTE(x, y) x ## y
#define TOKENPASTE2(x, y) TOKENPASTE(x, y)
@@ -127,19 +117,12 @@ typedef uint32_t portreg_t;
typedef uint8_t portreg_t;
#endif
#if defined(ARDUINO_GIGA) // yes giga
typedef int FASTPIN;
#else // no giga
struct FASTPIN {
volatile portreg_t *inout;
portreg_t maskHIGH;
portreg_t maskLOW;
volatile portreg_t *shadowinout;
};
#endif // giga
// The port registers that are shadowing
// the real port registers. These are
// defined in Motordriver.cpp
@@ -165,12 +148,6 @@ class MotorDriver {
// otherwise the call from interrupt context can undo whatever we do
// from outside interrupt
void setBrake( bool on, bool interruptContext=false);
#if defined(ARDUINO_GIGA) // yes giga
__attribute__((always_inline)) inline void setSignal( bool high) {
digitalWrite(signalPin, high);
if (dualSignal) digitalWrite(signalPin2, !high);
};
#else // no giga
__attribute__((always_inline)) inline void setSignal( bool high) {
if (trackPWM) {
DCCTimer::setPWM(signalPin,high);
@@ -186,7 +163,6 @@ class MotorDriver {
}
}
};
#endif // giga
inline void enableSignal(bool on) {
if (on)
pinMode(signalPin, OUTPUT);
@@ -208,12 +184,6 @@ class MotorDriver {
int getCurrentRaw(bool fromISR=false);
unsigned int raw2mA( int raw);
unsigned int mA2raw( unsigned int mA);
#if defined(ARDUINO_GIGA) // yes giga
inline bool digitalPinHasPWM(int pin) {
if (pin!=UNUSED_PIN && pin>=2 && pin<=13) return true;
else return false;
}
#endif // giga
inline bool brakeCanPWM() {
#if defined(ARDUINO_ARCH_ESP32)
return (brakePin != UNUSED_PIN); // This was just (true) but we probably do need to check for UNUSED_PIN!

View File

@@ -117,7 +117,6 @@ void StringFormatter::send2(Print * stream,const FSH* format, va_list args) {
case 'o': stream->print(va_arg(args, int), OCT); break;
case 'x': stream->print((unsigned int)va_arg(args, unsigned int), HEX); break;
case 'X': stream->print((unsigned long)va_arg(args, unsigned long), HEX); break;
case 'h': printHex(stream,(unsigned int)va_arg(args, unsigned int)); break;
case 'M':
{ // this prints a unsigned long microseconds time in readable format
unsigned long time = va_arg(args, long);
@@ -219,15 +218,4 @@ void StringFormatter::printPadded(Print* stream, long value, byte width, bool fo
if (!formatLeft) stream->print(value, DEC);
}
// printHex prints the full 2 byte hex with leading zeros, unlike print(value,HEX)
const char FLASH hexchars[]="0123456789ABCDEF";
void StringFormatter::printHex(Print * stream,uint16_t value) {
char result[5];
for (int i=3;i>=0;i--) {
result[i]=GETFLASH(hexchars+(value & 0x0F));
value>>=4;
}
result[4]='\0';
stream->print(result);
}

View File

@@ -49,7 +49,6 @@ class StringFormatter
static void lcd2(uint8_t display, byte row, const FSH* input...);
static void printEscapes(char * input);
static void printEscape( char c);
static void printHex(Print * stream,uint16_t value);
private:
static void send2(Print * serial, const FSH* input,va_list args);

View File

@@ -26,8 +26,7 @@
#include "MotorDriver.h"
#include "DCCTimer.h"
#include "DIAG.h"
#include "CommandDistributor.h"
#include "DCCEXParser.h"
#include"CommandDistributor.h"
// Virtualised Motor shield multi-track hardware Interface
#define FOR_EACH_TRACK(t) for (byte t=0;t<=lastTrack;t++)
@@ -332,7 +331,6 @@ bool TrackManager::parseJ(Print *stream, int16_t params, int16_t p[])
FOR_EACH_TRACK(t)
streamTrackState(stream,t);
return true;
}
p[0]-=HASH_KEYWORD_A; // convert A... to 0....
@@ -367,36 +365,32 @@ void TrackManager::streamTrackState(Print* stream, byte t) {
// null stream means send to commandDistributor for broadcast
if (track[t]==NULL) return;
auto format=F("");
bool pstate = TrackManager::isPowerOn(t);
switch(track[t]->getMode()) {
case TRACK_MODE_MAIN:
if (pstate) {format=F("<= %c MAIN ON>\n");} else {format = F("<= %c MAIN OFF>\n");}
format=F("<= %c MAIN>\n");
break;
#ifndef DISABLE_PROG
case TRACK_MODE_PROG:
if (pstate) {format=F("<= %c PROG ON>\n");} else {format=F("<= %c PROG OFF>\n");}
format=F("<= %c PROG>\n");
break;
#endif
case TRACK_MODE_NONE:
if (pstate) {format=F("<= %c NONE ON>\n");} else {format=F("<= %c NONE OFF>\n");}
format=F("<= %c NONE>\n");
break;
case TRACK_MODE_EXT:
if (pstate) {format=F("<= %c EXT ON>\n");} else {format=F("<= %c EXT OFF>\n");}
format=F("<= %c EXT>\n");
break;
case TRACK_MODE_DC:
if (pstate) {format=F("<= %c DC %d ON>\n");} else {format=F("<= %c DC %d OFF>\n");}
format=F("<= %c DC %d>\n");
break;
case TRACK_MODE_DCX:
if (pstate) {format=F("<= %c DCX %d ON>\n");} else {format=F("<= %c DCX %d OFF>\n");}
format=F("<= %c DCX %d>\n");
break;
default:
break; // unknown, dont care
}
if (stream) StringFormatter::send(stream,format,'A'+t, trackDCAddr[t]);
else CommandDistributor::broadcastTrackState(format,'A'+t, trackDCAddr[t]);
if (stream) StringFormatter::send(stream,format,'A'+t,trackDCAddr[t]);
else CommandDistributor::broadcastTrackState(format,'A'+t,trackDCAddr[t]);
}
byte TrackManager::nextCycleTrack=MAX_TRACKS;
@@ -430,70 +424,49 @@ std::vector<MotorDriver *>TrackManager::getMainDrivers() {
}
#endif
void TrackManager::setPower2(bool setProg,bool setJoin, POWERMODE mode) {
void TrackManager::setPower2(bool setProg,POWERMODE mode) {
if (!setProg) mainPowerGuess=mode;
FOR_EACH_TRACK(t) {
TrackManager::setTrackPower(setProg, setJoin, mode, t);
}
return;
}
void TrackManager::setTrackPower(bool setProg, bool setJoin, POWERMODE mode, byte thistrack) {
//DIAG(F("SetTrackPower Processing Track %d"), thistrack);
MotorDriver * driver=track[thistrack];
if (!driver) return;
switch (track[thistrack]->getMode()) {
case TRACK_MODE_MAIN:
if (setProg) break;
// toggle brake before turning power on - resets overcurrent error
// on the Pololu board if brake is wired to ^D2.
// XXX see if we can make this conditional
driver->setBrake(true);
driver->setBrake(false); // DCC runs with brake off
driver->setPower(mode);
break;
case TRACK_MODE_DC:
case TRACK_MODE_DCX:
//DIAG(F("Processing track - %d setProg %d"), thistrack, setProg);
if (setProg || setJoin) break;
driver->setBrake(true); // DC starts with brake on
applyDCSpeed(thistrack); // speed match DCC throttles
driver->setPower(mode);
break;
case TRACK_MODE_PROG:
if (!setProg && !setJoin) break;
driver->setBrake(true);
driver->setBrake(false);
driver->setPower(mode);
break;
case TRACK_MODE_EXT:
driver->setBrake(true);
driver->setBrake(false);
driver->setPower(mode);
break;
case TRACK_MODE_NONE:
break;
MotorDriver * driver=track[t];
if (!driver) continue;
switch (track[t]->getMode()) {
case TRACK_MODE_MAIN:
if (setProg) break;
// toggle brake before turning power on - resets overcurrent error
// on the Pololu board if brake is wired to ^D2.
// XXX see if we can make this conditional
driver->setBrake(true);
driver->setBrake(false); // DCC runs with brake off
driver->setPower(mode);
break;
case TRACK_MODE_DC:
case TRACK_MODE_DCX:
if (setProg) break;
driver->setBrake(true); // DC starts with brake on
applyDCSpeed(t); // speed match DCC throttles
driver->setPower(mode);
break;
case TRACK_MODE_PROG:
if (!setProg) break;
driver->setBrake(true);
driver->setBrake(false);
driver->setPower(mode);
break;
case TRACK_MODE_EXT:
driver->setBrake(true);
driver->setBrake(false);
driver->setPower(mode);
break;
case TRACK_MODE_NONE:
break;
}
}
void TrackManager::reportPowerChange(Print* stream, byte thistrack) {
// This function is for backward JMRI compatibility only
// It reports the first track only, as main, regardless of track settings.
// <c MeterName value C/V unit min max res warn>
int maxCurrent=track[0]->raw2mA(track[0]->getRawCurrentTripValue());
StringFormatter::send(stream, F("<c CurrentMAIN %d C Milli 0 %d 1 %d>\n"),
track[0]->raw2mA(track[0]->getCurrentRaw(false)), maxCurrent, maxCurrent);
}
}
POWERMODE TrackManager::getProgPower() {
FOR_EACH_TRACK(t)
if (track[t]->getMode()==TRACK_MODE_PROG)
return track[t]->getPower();
return track[t]->getPower();
return POWERMODE::OFF;
}
@@ -565,32 +538,3 @@ bool TrackManager::isPowerOn(byte t) {
return true;
}
bool TrackManager::isProg(byte t) {
if (track[t]->getMode()==TRACK_MODE_PROG)
return true;
return false;
}
byte TrackManager::returnMode(byte t) {
return (track[t]->getMode());
}
int16_t TrackManager::returnDCAddr(byte t) {
return (trackDCAddr[t]);
}
const char* TrackManager::getModeName(byte Mode) {
//DIAG(F("PowerMode %d"), Mode);
switch (Mode)
{
case 1: return "NONE";
case 2: return "MAIN";
case 4: return "PROG";
case 8: return "DC";
case 16: return "DCX";
case 32: return "EXT";
default: return "----";
}
}

View File

@@ -39,10 +39,6 @@ const byte TRACK_NUMBER_5=5, TRACK_NUMBER_F=5;
const byte TRACK_NUMBER_6=6, TRACK_NUMBER_G=6;
const byte TRACK_NUMBER_7=7, TRACK_NUMBER_H=7;
// These constants help EXRAIL macros convert Track Power e.g. SET_POWER(A ON|OFF).
const byte TRACK_POWER_0=0, TRACK_POWER_OFF=0;
const byte TRACK_POWER_1=1, TRACK_POWER_ON=1;
class TrackManager {
public:
static void Setup(const FSH * shieldName,
@@ -64,14 +60,10 @@ class TrackManager {
#ifdef ARDUINO_ARCH_ESP32
static std::vector<MotorDriver *>getMainDrivers();
#endif
static void setPower2(bool progTrack,bool joinTrack,POWERMODE mode);
static void setPower2(bool progTrack,POWERMODE mode);
static void setPower(POWERMODE mode) {setMainPower(mode); setProgPower(mode);}
static void setMainPower(POWERMODE mode) {setPower2(false,false,mode);}
static void setProgPower(POWERMODE mode) {setPower2(true,false,mode);}
static void setJoinPower(POWERMODE mode) {setPower2(false,true,mode);}
static void setTrackPower(bool setProg, bool setJoin, POWERMODE mode, byte thistrack);
static void setMainPower(POWERMODE mode) {setPower2(false,mode);}
static void setProgPower(POWERMODE mode) {setPower2(true,mode);}
static const int16_t MAX_TRACKS=8;
static bool setTrackMode(byte track, TRACK_MODE mode, int16_t DCaddr=0);
@@ -85,14 +77,9 @@ class TrackManager {
static void sampleCurrent();
static void reportGauges(Print* stream);
static void reportCurrent(Print* stream);
static void reportPowerChange(Print* stream, byte thistrack);
static void reportObsoleteCurrent(Print* stream);
static void streamTrackState(Print* stream, byte t);
static bool isPowerOn(byte t);
static bool isProg(byte t);
static byte returnMode(byte t);
static int16_t returnDCAddr(byte t);
static const char* getModeName(byte Mode);
static int16_t joinRelay;
static bool progTrackSyncMain; // true when prog track is a siding switched to main

View File

@@ -185,7 +185,7 @@ public:
for (Turntable *tto = _firstTurntable; tto != 0; tto = tto->_nextTurntable)
if (!tto->isHidden()) {
gotOne = true;
StringFormatter::send(stream, F("<I %d %d>\n"), tto->getId(), tto->getPosition());
StringFormatter::send(stream, F("<i %d %d>\n"), tto->getId(), tto->getPosition());
}
return gotOne;
}

View File

@@ -3,7 +3,6 @@
* © 2020-2022 Harald Barth
* © 2020-2022 Chris Harlow
* © 2023 Nathan Kellenicki
* © 2023 Travis Farmer
* All rights reserved.
*
* This file is part of CommandStation-EX
@@ -58,14 +57,6 @@ Stream * WifiInterface::wifiStream;
#define SERIAL3 Serial3
#endif
#if defined(ARDUINO_GIGA) // yes giga
#define NUM_SERIAL 5
#define SERIAL1 Serial1
#define SERIAL2 Serial2
#define SERIAL3 Serial3
#define SERIAL4 Serial4
#endif // giga
#if defined(ARDUINO_ARCH_STM32)
// Handle serial ports availability on STM32 for variants!
// #undef NUM_SERIAL
@@ -215,13 +206,12 @@ wifiSerialState WifiInterface::setup2(const FSH* SSid, const FSH* password,
while(!wifiStream->available());
version[i]=wifiStream->read();
StringFormatter::printEscape(version[i]);
}
if ((version[0] == '0') ||
(version[0] == '2' && version[2] == '0') ||
(version[0] == '2' && version[2] == '2' && version[4] == '0' && version[6] == '0')) {
DIAG(F("You need to up/downgrade the ESP firmware"));
SSid = F("UPDATE_ESP_FIRMWARE");
forceAP = true;
if ((version[0] == '0') ||
(version[0] == '2' && version[2] == '0') ||
(version[0] == '2' && version[2] == '2' && version[4] == '0' && version[6] == '0')) {
SSid = F("DCCEX_SAYS_BROKEN_FIRMWARE");
forceAP = true;
}
}
}
checkForOK(2000, true, false);

View File

@@ -1,8 +1,10 @@
/*
© 2023 Paul M. Antoine
© 2021 Harald Barth
© 2021-23 Harald Barth
© 2023 Nathan Kellenicki
© 2023 Travis Farmer
© 2023 Chris Harlow
This file is part of CommandStation-EX
This is free software: you can redistribute it and/or modify
@@ -20,20 +22,19 @@
*/
#include "defines.h"
#ifdef WIFI_NINA
#if defined(WIFI_NINA) || defined(GIGA_WIFI)
//#include <vector>
#include <SPI.h>
#ifndef ARDUINO_GIGA
#include <WifiNINA.h>
#else
#if defined(GIGA_WIFI)
#include <WiFi.h>
#else
#include <WiFiNINA.h>
#endif
#endif
#include "Wifi_NINA.h"
// #include "ESPmDNS.h"
// #include <WiFi.h>
// #include "esp_wifi.h"
// #include "WifiESP32.h"
// #include <SPI.h>
#include "DIAG.h"
#include "RingStream.h"
#include "CommandDistributor.h"
@@ -46,47 +47,27 @@
#define ESP32_RESETN PA10 // Reset pin
#define SPIWIFI_ACK PB3 // a.k.a BUSY or READY pin
#define ESP32_GPIO0 -1
#elif defined(ARDUINO_GIGA)
#define SPIWIFI SPI
#define SPIWIFI_SS 10 // Chip select pin
#define SPIWIFI_ACK 7 // a.k.a BUSY or READY pin
#define ESP32_RESETN 5 // Reset pin
#define ESP32_GPIO0 -1 // Not connected
#elif defined(ARDUINO_AVR_MEGA2560)
#define SPIWIFI SPI // The SPI port
#define SPIWIFI_SS 53 // Chip select pin
#define ESP32_RESETN 22 // Reset pin
#define SPIWIFI_ACK 23 // a.k.a BUSY or READY pin
#define ESP32_GPIO0 -1
#else
#warning "WiFiNINA has no SPI port or pin allocations for this archiecture yet!"
#endif
#define MAX_CLIENTS 10
/*class NetworkClient {
public:
NetworkClient(WiFiClient c) {
wifi = c;
};
bool ok() {
return (inUse && wifi.connected());
};
bool recycle(WiFiClient c) {
if (inUse == true) return false;
// return false here until we have
// implemented a LRU timer
// if (LRU too recent) return false;
//return false;
wifi = c;
inUse = true;
return true;
};
WiFiClient wifi;
bool inUse = true;
};*/
//static std::vector<NetworkClient> clients; // a list to hold all clients
static WiFiServer *server = NULL;
static RingStream *outboundRing = new RingStream(10240);
static bool APmode = false;
static IPAddress ip;
// #ifdef WIFI_TASK_ON_CORE0
// void wifiLoop(void *){
// for(;;){
// WifiNINA::loop();
// }
// }
// #endif
char asciitolower(char in) {
if (in <= 'Z' && in >= 'A')
@@ -106,28 +87,21 @@ bool WifiNINA::setup(const char *SSid,
uint8_t tries = 40;
// Set up the pins!
#ifndef ARDUINO_GIGA
#if !defined(GIGA_WIFI)
WiFi.setPins(SPIWIFI_SS, SPIWIFI_ACK, ESP32_RESETN, ESP32_GPIO0, &SPIWIFI);
#endif
// check for the WiFi module:
if (WiFi.status() == WL_NO_MODULE) {
DIAG(F("Communication with WiFi module failed!"));
// don't continue for now!
while (true);
// while (true);
return;
}
// Print firmware version on the module
String fv = WiFi.firmwareVersion();
DIAG(F("WifiNINA Firmware version found:%s"), fv.c_str());
// clean start
// WiFi.mode(WIFI_STA);
// WiFi.disconnect(true);
// differnet settings that did not improve for haba
// WiFi.useStaticBuffers(true);
// WiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN);
// WiFi.setSortMethod(WIFI_CONNECT_AP_BY_SECURITY);
const char *yourNetwork = "Your network ";
if (strncmp(yourNetwork, SSid, 13) == 0 || strncmp("", SSid, 13) == 0)
haveSSID = false;
@@ -147,27 +121,29 @@ bool WifiNINA::setup(const char *SSid,
delay(500);
}
if (WiFi.status() == WL_CONNECTED) {
// String ip_str = sprintf("%xl", WiFi.localIP());
DIAG(F("Wifi STA IP %d.%d.%d.%d"), WiFi.localIP()[0], WiFi.localIP()[1],WiFi.localIP()[2],WiFi.localIP()[3]);
IPAddress ip = WiFi.localIP();
DIAG(F("Wifi STA IP %d.%d.%d.%d"), ip[0], ip[1], ip[2], ip[3]);
wifiUp = true;
} else {
DIAG(F("Could not connect to Wifi SSID %s"),SSid);
DIAG(F("Forcing one more Wifi restart"));
// esp_wifi_start();
// esp_wifi_connect();
WiFi.end();
WiFi.begin(SSid, password);
tries=40;
while (WiFi.status() != WL_CONNECTED && tries) {
Serial.print('.');
tries--;
delay(500);
Serial.print('.');
tries--;
delay(500);
}
if (WiFi.status() == WL_CONNECTED) {
ip = WiFi.localIP();
DIAG(F("Wifi STA IP 2nd try %d.%d.%d.%d"), ip[0], ip[1], ip[2], ip[3]);
wifiUp = true;
} else {
DIAG(F("Wifi STA mode FAIL. Will revert to AP mode"));
haveSSID=false;
ip = WiFi.localIP();
DIAG(F("Wifi STA IP 2nd try %d.%d.%d.%d"), ip[0], ip[1], ip[2], ip[3]);
wifiUp = true;
} else {
DIAG(F("Wifi STA mode FAIL. Will revert to AP mode"));
haveSSID=false;
}
}
}
@@ -189,7 +165,7 @@ bool WifiNINA::setup(const char *SSid,
strMac.replace(":","");
strMac.replace(":","");
// convert mac addr hex chars to lower case to be compatible with AT software
std::transform(strMac.begin(), strMac.end(), strMac.begin(), asciitolower);
//std::transform(strMac.begin(), strMac.end(), strMac.begin(), asciitolower); ///TJF: why does this fail compile with WiFiNINA, but not giga WiFi???
strSSID.concat(strMac);
strPass.concat(strMac);
}
@@ -226,27 +202,8 @@ bool WifiNINA::setup(const char *SSid,
server = new WiFiServer(port); // start listening on tcp port
server->begin();
// server started here
// #ifdef WIFI_TASK_ON_CORE0
// //start loop task
// if (pdPASS != xTaskCreatePinnedToCore(
// wifiLoop, /* Task function. */
// "wifiLoop",/* name of task. */
// 10000, /* Stack size of task */
// NULL, /* parameter of the task */
// 1, /* priority of the task */
// NULL, /* Task handle to keep track of created task */
// 0)) { /* pin task to core 0 */
// DIAG(F("Could not create wifiLoop task"));
// return false;
// }
// // report server started after wifiLoop creation
// // when everything looks good
// DIAG(F("Server starting (core 0) port %d"),port);
// #else
DIAG(F("Server will be started on port %d"),port);
// #endif
ip = WiFi.localIP();
LCD(4,F("IP: %d.%d.%d.%d"), ip[0], ip[1], ip[2], ip[3]);
LCD(5,F("Port:%d"), port);
@@ -263,112 +220,6 @@ const char *wlerror[] = {
"WL_DISCONNECTED"
};
/*void WifiNINA::loop() {
int clientId; //tmp loop var
// really no good way to check for LISTEN especially in AP mode?
wl_status_t wlStatus;
if (APmode || (wlStatus = (wl_status_t)WiFi.status()) == WL_CONNECTED) {
// loop over all clients and remove inactive
for (clientId=0; clientId<clients.size(); clientId++){
// check if client is there and alive
if(clients[clientId].inUse && !clients[clientId].wifi.connected()) {
DIAG(F("Remove client %d"), clientId);
CommandDistributor::forget(clientId);
clients[clientId].wifi.stop();
clients[clientId].inUse = false;
//Do NOT clients.erase(clients.begin()+clientId) as
//that would mix up clientIds for later.
}
}
WiFiClient client = server->available();
if (client) {
///while (client.available() == true) {
for (clientId=0; clientId<clients.size(); clientId++){
if (clients[clientId].recycle(client)) {
ip = client.remoteIP();
DIAG(F("Recycle client %d %d.%d.%d.%d"), clientId, ip[0], ip[1], ip[2], ip[3]);
break;
}
}
if (clientId>=clients.size()) {
NetworkClient* nc=new NetworkClient(client);
clients.push_back(*nc);
//delete nc;
ip = client.remoteIP();
DIAG(F("New client %d, %d.%d.%d.%d"), clientId, ip[0], ip[1], ip[2], ip[3]);
}
///}
}
// loop over all connected clients
for (clientId=0; clientId<clients.size(); clientId++){
if(clients[clientId].ok()) {
int len;
if ((len = clients[clientId].wifi.available()) > 0) {
// read data from client
byte cmd[len+1];
for(int i=0; i<len; i++) {
cmd[i]=clients[clientId].wifi.read();
}
cmd[len]=0;
CommandDistributor::parse(clientId,cmd,outboundRing);
}
}
} // all clients
WiThrottle::loop(outboundRing);
// something to write out?
clientId=outboundRing->read();
if (clientId >= 0) {
// We have data to send in outboundRing
// and we have a valid clientId.
// First read it out to buffer
// and then look if it can be sent because
// we can not leave it in the ring for ever
int count=outboundRing->count();
{
char buffer[count+1]; // one extra for '\0'
for(int i=0;i<count;i++) {
int c = outboundRing->read();
if (c >= 0) // Panic check, should never be false
buffer[i] = (char)c;
else {
DIAG(F("Ringread fail at %d"),i);
break;
}
}
// buffer filled, end with '\0' so we can use it as C string
buffer[count]='\0';
if((unsigned int)clientId <= clients.size() && clients[clientId].ok()) {
if (Diag::CMD || Diag::WITHROTTLE)
DIAG(F("SEND %d:%s"), clientId, buffer);
clients[clientId].wifi.write(buffer,count);
} else {
DIAG(F("Unsent(%d): %s"), clientId, buffer);
}
}
}
} else if (!APmode) { // in STA mode but not connected any more
// kick it again
if (wlStatus <= 6) {
DIAG(F("Wifi aborted with error %s. Kicking Wifi!"), wlerror[wlStatus]);
// esp_wifi_start();
// esp_wifi_connect();
uint8_t tries=40;
while (WiFi.status() != WL_CONNECTED && tries) {
Serial.print('.');
tries--;
delay(500);
}
} else {
// all well, probably
//DIAG(F("Running BT"));
}
}
}*/
WiFiClient * clients[MAX_CLIENTS]; // nulled in setup
void WifiNINA::checkForNewClient() {
@@ -377,7 +228,7 @@ void WifiNINA::checkForNewClient() {
for (byte clientId=0; clientId<MAX_CLIENTS; clientId++){
if (!clients[clientId]) {
clients[clientId]= new WiFiClient(newClient); // use this slot
DIAG(F("New client connected to slot %d"),clientId); //TJF: brought in for debugging.
//DIAG(F("New client connected to slot %d"),clientId); //TJF: brought in for debugging.
return;
}
}
@@ -388,10 +239,9 @@ void WifiNINA::checkForLostClients() {
auto c=clients[clientId];
if(c && !c->connected()) {
clients[clientId]->stop();
DIAG(F("Remove client %d"), clientId);
//DIAG(F("Remove client %d"), clientId);
CommandDistributor::forget(clientId);
//delete c; //TJF: this causes a crash when client drops.. commenting out for now.
clients[clientId]=nullptr; // TJF: what to do... what to do...
clients[clientId]=nullptr;
}
}
}
@@ -406,7 +256,7 @@ void WifiNINA::checkForClientInput() {
// read data from client
byte cmd[len+1];
for(int i=0; i<len; i++) cmd[i]=c->read();
cmd[len]=0;
cmd[len]=0x00;
CommandDistributor::parse(clientId,cmd,outboundRing);
}
}
@@ -423,14 +273,10 @@ void WifiNINA::checkForClientOutput() {
if (!c) {
// client is gone, throw away msg
for (int i=0;i<replySize;i++) outboundRing->read();
DIAG(F("gone, drop message.")); //TJF: only for diag
//DIAG(F("gone, drop message.")); //TJF: only for diag
return;
}
// emit data to the client object
// This should work in theory, the
DIAG(F("send message")); //TJF: only for diag
//TJF: the old code had to add a 0x00 byte to the end to terminate the
//TJF: c string, before sending it. i take it this is not needed?
for (int i=0;i<replySize;i++) c->write(outboundRing->read());
}

View File

@@ -5,7 +5,6 @@
* © 2021 Fred Decker
* © 2020-2022 Harald Barth
* © 2020-2021 Chris Harlow
* © 2023 Travis Farmer
*
* This file is part of CommandStation-EX
*
@@ -148,28 +147,7 @@
// #ifndef I2C_USE_WIRE
// #define I2C_USE_WIRE
// #endif
#elif defined(ARDUINO_GIGA)
#define ARDUINO_TYPE "Giga"
#ifndef GIGA_EXT_EEPROM
#define DISABLE_EEPROM
#endif
#if defined(ENABLE_WIFI) && !defined(WIFI_NINA)
#define WIFI_NINA
#endif
//#if !defined(I2C_USE_WIRE)
//#define I2C_USE_WIRE
//#endif
#define SDA I2C_SDA
#define SCL I2C_SCL
#define DCC_EX_TIMER
// these don't work...
//extern const uint16_t PROGMEM port_to_input_PGM[];
//extern const uint16_t PROGMEM port_to_output_PGM[];
//extern const uint8_t PROGMEM digital_pin_to_bit_mask_PGM[];
//#define digitalPinToBitMask(P) ( pgm_read_byte( digital_pin_to_bit_mask_PGM + (P) ) )
//#define portOutputRegister(P) ( (volatile uint8_t *)( pgm_read_word( port_to_output_PGM + (P))) )
//#define portInputRegister(P) ( (volatile uint8_t *)( pgm_read_word( port_to_input_PGM + (P))) )
/* TODO when ready
#elif defined(ARDUINO_ARCH_RP2040)
#define ARDUINO_TYPE "RP2040"

View File

@@ -24,7 +24,6 @@
//#include "IO_TouchKeypad.h // Touch keypad with 16 keys
//#include "IO_EXTurntable.h" // Turntable-EX turntable controller
//#include "IO_EXFastClock.h" // FastClock driver
//#include "IO_PCA9555.h" // 16-bit I/O expander (NXP & Texas Instruments).
//==========================================================================
// The function halSetup() is invoked from CS if it exists within the build.

View File

@@ -105,6 +105,7 @@ lib_deps =
${env.lib_deps}
arduino-libraries/Ethernet
SPI
https://github.com/adafruit/WiFiNINA
monitor_speed = 115200
monitor_echo = yes
build_flags =
@@ -179,7 +180,10 @@ monitor_echo = yes
platform = ststm32
board = nucleo_f411re
framework = arduino
lib_deps = ${env.lib_deps}
lib_deps =
${env.lib_deps}
SPI
https://github.com/adafruit/WiFiNINA
build_flags = -std=c++17 -Os -g2 -Wunused-variable
monitor_speed = 115200
monitor_echo = yes
@@ -200,7 +204,10 @@ monitor_echo = yes
platform = ststm32
board = nucleo_f401re
framework = arduino
lib_deps = ${env.lib_deps}
lib_deps =
${env.lib_deps}
SPI
https://github.com/adafruit/WiFiNINA
build_flags = -std=c++17 -Os -g2 -Wunused-variable
monitor_speed = 115200
monitor_echo = yes
@@ -221,14 +228,14 @@ monitor_echo = yes
; Commented out by default as the F446ZE needs variant files
; installed before you can let PlatformIO see this
;
; [env:Nucleo-F446ZE]
; platform = ststm32
; board = nucleo_f446ze
; framework = arduino
; lib_deps = ${env.lib_deps}
; build_flags = -std=c++17 -Os -g2 -Wunused-variable
; monitor_speed = 115200
; monitor_echo = yes
[env:Nucleo-F446ZE]
platform = ststm32
board = nucleo_f446ze
framework = arduino
lib_deps = ${env.lib_deps}
build_flags = -std=c++17 -Os -g2 -Wunused-variable
monitor_speed = 115200
monitor_echo = yes
; Commented out by default as the F412ZG needs variant files
; installed before you can let PlatformIO see this

View File

@@ -3,19 +3,8 @@
#include "StringFormatter.h"
#define VERSION "5.1.17gw"
// 5.1.17gw - Giga support by Travis Farmer, with WifiNINA integrated to see if it works
// 5.1.17 - Divide out C for config and D for diag commands
// 5.1.16 - Remove I2C address from EXTT_TURNTABLE macro to work with MUX, requires separate HAL macro to create
// 5.1.15 - LCC/Adapter support and Exrail feature-compile-out.
// 5.1.14 - Fixed IFTTPOSITION
// 5.1.13 - Changed turntable broadcast from i to I due to server string conflict
// 5.1.12 - Added Power commands <0 A> & <1 A> etc. and update to <=>
// Added EXRAIL SET_POWER(track, ON/OFF)
// Fixed a problem whereby <1 MAIN> also powered on PROG track
// Added functions to TrackManager.cpp to allow UserAddin code for power display on OLED/LCD
// Added - returnMode(byte t), returnDCAddr(byte t) & getModeName(byte Mode)
// 5.1.11 - STM32F4xx revised I2C clock setup, no correctly sets clock and has fully variable frequency selection
#define VERSION "5.1.10-nina"
// 5.1.10-nina - Initial cut of a WifiNINA driver for Wifi for STM32
// 5.1.10 - STM32F4xx DCCEXanalogWrite to handle PWM generation for TrackManager DC/DCX
// - STM32F4xx DCC 58uS timer now using non-PWM output timers where possible
// - ESP32 brakeCanPWM check now detects UNUSED_PIN