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

Compare commits

..

16 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
pmantoine
ed0cfee091 STM32 DCCEXanalogWrite for TrackManager PWM 2023-09-28 17:43:22 +08:00
Harald Barth
624656ebc9 date tag 2023-09-24 20:56:27 +02:00
Harald Barth
5a7f278b1e correct return when requesting D RAM 2023-09-24 20:55:16 +02:00
kempe63
11b9fd4ef5 Fixed IO_PCA9555.h to work with PCA9548 mux 2023-09-23 20:25:10 +01:00
kempe63
d07718be8c Revert "Update version.h to 5.1.7"
This reverts commit d57b5ba537.
2023-09-23 20:18:59 +01:00
kempe63
d57b5ba537 Update version.h to 5.1.7 2023-09-23 20:09:01 +01:00
kempe63
dab02ec659 Update IO_PCA9555.h
Now compiles and works on PCA9548 Mux. Tested with MCP23017 and PCA9555 on the same Subbus
2023-09-23 16:10:16 +01:00
kempe63
6ad5326f1d PCA9555 compiles and tested on PCA9548 mux
IO_PCA9555.h added changes that had been applied to IO_MCP23017 for support on PCA9548 Mux. Constructor now also private and type casting of variables made the same for IO_PCA9555. Tested with MCP23017 and PCA9555 simultaneous on the same Mux Subbus
2023-09-23 16:06:14 +01:00
kempe63
39e1363ce0 Merge branch 'devel' of https://github.com/DCC-EX/CommandStation-EX into devel 2023-09-23 16:04:26 +01:00
kempe63
c9d4f5e94d Update IO_PCA9555.h 2023-09-23 15:56:09 +01:00
pmantoine
dfe3e9d42c STM32 ADCee extensions 2023-09-21 16:09:04 +08:00
pmantoine
5d810a620b STM32 variant support extension and tidy 2023-09-21 16:05:35 +08:00
pmantoine
550ad58c4d STM32 ADCee extended to support ADC2 and ADC3 2023-09-21 14:39:25 +08:00
15 changed files with 804 additions and 83 deletions

View File

@@ -96,7 +96,11 @@ void setup()
// Start Ethernet if it exists
#ifndef ARDUINO_ARCH_ESP32
#if WIFI_ON
#ifndef WIFI_NINA
WifiInterface::setup(WIFI_SERIAL_LINK_SPEED, F(WIFI_SSID), F(WIFI_PASSWORD), F(WIFI_HOSTNAME), IP_PORT, WIFI_CHANNEL, WIFI_FORCE_AP);
#else
WifiNINA::setup(WIFI_SSID, WIFI_PASSWORD, WIFI_HOSTNAME, IP_PORT, WIFI_CHANNEL, WIFI_FORCE_AP);
#endif // WIFI_NINA
#endif // WIFI_ON
#else
// ESP32 needs wifi on always
@@ -144,7 +148,11 @@ void loop()
// Responsibility 3: Optionally handle any incoming WiFi traffic
#ifndef ARDUINO_ARCH_ESP32
#if WIFI_ON
#ifndef WIFI_NINA
WifiInterface::loop();
#else
WifiNINA::loop();
#endif //WIFI_NINA
#endif //WIFI_ON
#else //ARDUINO_ARCH_ESP32
#ifndef WIFI_TASK_ON_CORE0

View File

@@ -1,4 +1,5 @@
/*
* © 2023 Paul M. Antoine
* © 2021 Fred Decker
* © 2020-2021 Harald Barth
* © 2020-2021 Chris Harlow
@@ -33,8 +34,13 @@
#include "SerialManager.h"
#include "version.h"
#ifndef ARDUINO_ARCH_ESP32
#include "WifiInterface.h"
#ifdef WIFI_NINA
#include "Wifi_NINA.h"
#else
#include "WifiInterface.h"
#endif // WIFI_NINA
#else
#undef WIFI_NINA
#include "WifiESP32.h"
#endif
#if ETHERNET_ON == true

View File

@@ -1057,7 +1057,7 @@ bool DCCEXParser::parseD(Print *stream, int16_t params, int16_t p[])
case HASH_KEYWORD_RAM: // <D RAM>
StringFormatter::send(stream, F("Free memory=%d\n"), DCCTimer::getMinimumFreeMemory());
break;
return true;
#ifndef DISABLE_PROG
case HASH_KEYWORD_ACK: // <D ACK ON/OFF> <D ACK [LIMIT|MIN|MAX|RETRY] Value>

View File

@@ -125,8 +125,13 @@ private:
// On platforms that scan, it is called from waveform ISR
// only on a regular basis.
static void scan();
#if defined (ARDUINO_ARCH_STM32)
// bit array of used pins (max 32)
static uint32_t usedpins;
#else
// bit array of used pins (max 16)
static uint16_t usedpins;
#endif
static uint8_t highestPin;
// cached analog values (malloc:ed to actual number of ADC channels)
static int *analogvals;

View File

@@ -1,6 +1,6 @@
/*
* © 2023 Neil McKechnie
* © 2022-23 Paul M. Antoine
* © 2022-2023 Paul M. Antoine
* © 2021 Mike S
* © 2021, 2023 Harald Barth
* © 2021 Fred Decker
@@ -52,7 +52,7 @@ HardwareSerial Serial6(PA12, PA11); // Rx=PA12, Tx=PA11 -- CN10 pins 12 and 14
HardwareSerial Serial3(PC11, PC10); // Rx=PC11, Tx=PC10 -- USART3 - F446RE
HardwareSerial Serial5(PD2, PC12); // Rx=PC7, Tx=PC6 -- UART5 - F446RE
// On the F446RE, Serial4 and Serial6 also use pins we can't readily map while using the Arduino pins
#elif defined(ARDUINO_NUCLEO_F413ZH) || defined(ARDUINO_NUCLEO_F429ZI) || defined(ARDUINO_NUCLEO_F446ZE)|| defined(ARDUINO_NUCLEO_F412ZG)
#elif defined(ARDUINO_NUCLEO_F412ZG) || defined(ARDUINO_NUCLEO_F413ZH) || defined(ARDUINO_NUCLEO_F429ZI) || defined(ARDUINO_NUCLEO_F446ZE)
// Nucleo-144 boards don't have Serial1 defined by default
HardwareSerial Serial6(PG9, PG14); // Rx=PG9, Tx=PG14 -- USART6
// Serial3 is defined to use USART3 by default, but is in fact used as the diag console
@@ -154,13 +154,28 @@ HardwareSerial Serial6(PG9, PG14); // Rx=PG9, Tx=PG14 -- USART6
///////////////////////////////////////////////////////////////////////////////////////////////
INTERRUPT_CALLBACK interruptHandler=0;
// Let's use STM32's timer #11 until disabused of this notion
// Timer #11 is used for "servo" library, but as DCC-EX is not using
// this libary, we should be free and clear.
HardwareTimer timer(TIM11);
// On STM32F4xx models that have them, Timers 6 and 7 have no PWM output capability,
// so are good choices for general timer duties - they are used for tone and servo
// in stm32duino so we shall usurp those as DCC-EX doesn't use tone or servo libs.
// NB: the F401, F410 and F411 do **not** have Timer 6 or 7, so we use Timer 11
#ifndef DCC_EX_TIMER
#if defined(TIM6)
#define DCC_EX_TIMER TIM6
#elif defined(TIM7)
#define DCC_EX_TIMER TIM7
#elif defined(TIM11)
#define DCC_EX_TIMER TIM11
#else
#warning This STM32F4XX variant does not have Timers 6,7 or 11!!
#endif
#endif // ifndef DCC_EX_TIMER
HardwareTimer dcctimer(DCC_EX_TIMER);
void DCCTimer_Handler() __attribute__((interrupt));
// Timer IRQ handler
void Timer11_Handler() {
void DCCTimer_Handler() {
interruptHandler();
}
@@ -168,22 +183,24 @@ void DCCTimer::begin(INTERRUPT_CALLBACK callback) {
interruptHandler=callback;
noInterrupts();
// adc_set_sample_rate(ADC_SAMPLETIME_480CYCLES);
timer.pause();
timer.setPrescaleFactor(1);
dcctimer.pause();
dcctimer.setPrescaleFactor(1);
// timer.setOverflow(CLOCK_CYCLES * 2);
timer.setOverflow(DCC_SIGNAL_TIME, MICROSEC_FORMAT);
timer.attachInterrupt(Timer11_Handler);
timer.refresh();
timer.resume();
dcctimer.setOverflow(DCC_SIGNAL_TIME, MICROSEC_FORMAT);
// dcctimer.attachInterrupt(Timer11_Handler);
dcctimer.attachInterrupt(DCCTimer_Handler);
dcctimer.setInterruptPriority(0, 0); // Set highest preemptive priority!
dcctimer.refresh();
dcctimer.resume();
interrupts();
}
bool DCCTimer::isPWMPin(byte pin) {
//TODO: SAMD whilst this call to digitalPinHasPWM will reveal which pins can do PWM,
//TODO: STM32 whilst this call to digitalPinHasPWM will reveal which pins can do PWM,
// there's no support yet for High Accuracy, so for now return false
// return digitalPinHasPWM(pin);
(void) pin;
return false;
}
@@ -235,22 +252,91 @@ void DCCTimer::reset() {
while(true) {};
}
// TODO: may need to use uint32_t on STMF4xx variants with > 16 analog inputs!
#if defined(ARDUINO_NUCLEO_F446RE) || defined(ARDUINO_NUCLEO_F429ZI) || defined(ARDUINO_NUCLEO_F446ZE)
#warning STM32 board selected not fully supported - only use ADC1 inputs 0-15 for current sensing!
#endif
// For now, define the max of 16 ports - some variants have more, but this not **yet** supported
#define NUM_ADC_INPUTS 16
// #define NUM_ADC_INPUTS NUM_ANALOG_INPUTS
// TODO: rationalise the size of these... could really use sparse arrays etc.
static HardwareTimer * pin_timer[100] = {0};
static uint32_t channel_frequency[100] = {0};
static uint32_t pin_channel[100] = {0};
uint16_t ADCee::usedpins = 0;
uint8_t ADCee::highestPin = 0;
int * ADCee::analogvals = NULL;
uint32_t * analogchans = NULL;
bool adc1configured = false;
// Using the HardwareTimer library API included in stm32duino core to handle PWM duties
// TODO: in order to use the HA code above which Neil kindly wrote, we may have to do something more
// sophisticated about detecting any clash between the timer we'd like to use for PWM and the ones
// currently used for HA so they don't interfere with one another. For now we'll just make PWM
// work well... then work backwards to integrate with HA mode if we can.
void DCCTimer::DCCEXanalogWriteFrequency(uint8_t pin, uint32_t frequency)
{
if (pin_timer[pin] == NULL) {
// Automatically retrieve TIM instance and channel associated to pin
// This is used to be compatible with all STM32 series automatically.
TIM_TypeDef *Instance = (TIM_TypeDef *)pinmap_peripheral(digitalPinToPinName(pin), PinMap_PWM);
if (Instance == NULL) {
// We shouldn't get here (famous last words) as it ought to have been caught by brakeCanPWM()!
DIAG(F("DCCEXanalogWriteFrequency::Pin %d has no PWM function!"), pin);
return;
}
pin_channel[pin] = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(pin), PinMap_PWM));
int16_t ADCee::ADCmax() {
return 4095;
// Instantiate HardwareTimer object. Thanks to 'new' instantiation,
// HardwareTimer is not destructed when setup function is finished.
pin_timer[pin] = new HardwareTimer(Instance);
// Configure and start PWM
// MyTim->setPWM(channel, pin, 5, 10, NULL, NULL); // No callback required, we can simplify the function call
if (pin_timer[pin] != NULL)
{
pin_timer[pin]->setPWM(pin_channel[pin], pin, frequency, 0); // set frequency in Hertz, 0% dutycycle
DIAG(F("DCCEXanalogWriteFrequency::Pin %d on Timer %d, frequency %d"), pin, pin_channel[pin], frequency);
}
else
DIAG(F("DCCEXanalogWriteFrequency::failed to allocate HardwareTimer instance!"));
}
else
{
// Frequency change request
if (frequency != channel_frequency[pin])
{
pinmap_pinout(digitalPinToPinName(pin), PinMap_TIM); // ensure the pin has been configured!
pin_timer[pin]->setOverflow(frequency, HERTZ_FORMAT); // Just change the frequency if it's already running!
DIAG(F("DCCEXanalogWriteFrequency::setting frequency to %d"), frequency);
}
}
channel_frequency[pin] = frequency;
return;
}
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
{
pinmap_pinout(digitalPinToPinName(pin), PinMap_TIM); // ensure the pin has been configured!
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);
}
// Now we can handle more ADCs, maybe this works!
#define NUM_ADC_INPUTS NUM_ANALOG_INPUTS
uint32_t ADCee::usedpins = 0; // Max of 32 ADC input channels!
uint8_t ADCee::highestPin = 0; // Highest pin to scan
int * ADCee::analogvals = NULL; // Array of analog values last captured
uint32_t * analogchans = NULL; // Array of channel numbers to be scanned
// bool adc1configured = false;
ADC_TypeDef * * adcchans = NULL; // Array to capture which ADC is each input channel on
int16_t ADCee::ADCmax()
{
return 4095;
}
int ADCee::init(uint8_t pin) {
@@ -261,11 +347,33 @@ int ADCee::init(uint8_t pin) {
return -1024; // some silly value as error
uint32_t stmgpio = STM_PORT(stmpin); // converts to the GPIO port (16-bits per port group on STM32)
uint32_t adcchan = STM_PIN_CHANNEL(pinmap_function(stmpin, PinMap_ADC)); // find ADC channel (only valid for ADC1!)
GPIO_TypeDef * gpioBase;
uint32_t adcchan = STM_PIN_CHANNEL(pinmap_function(stmpin, PinMap_ADC)); // find ADC input channel
ADC_TypeDef *adc = (ADC_TypeDef *)pinmap_find_peripheral(stmpin, PinMap_ADC); // find which ADC this pin is on ADC1/2/3 etc.
int adcnum = 1;
if (adc == ADC1)
DIAG(F("ADCee::init(): found pin %d on ADC1"), pin);
// Checking for ADC2 and ADC3 being defined helps cater for more variants later
#if defined(ADC2)
else if (adc == ADC2)
{
DIAG(F("ADCee::init(): found pin %d on ADC2"), pin);
adcnum = 2;
}
#endif
#if defined(ADC3)
else if (adc == ADC3)
{
DIAG(F("ADCee::init(): found pin %d on ADC3"), pin);
adcnum = 3;
}
#endif
else DIAG(F("ADCee::init(): found pin %d on unknown ADC!"), pin);
// Port config - find which port we're on and power it up
switch(stmgpio) {
// Port config - find which port we're on and power it up
GPIO_TypeDef *gpioBase;
switch (stmgpio)
{
case 0x00:
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; //Power up PORTA
gpioBase = GPIOA;
@@ -278,6 +386,20 @@ int ADCee::init(uint8_t pin) {
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN; //Power up PORTC
gpioBase = GPIOC;
break;
case 0x03:
RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; //Power up PORTD
gpioBase = GPIOD;
break;
case 0x04:
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOEEN; //Power up PORTE
gpioBase = GPIOE;
break;
#if defined(GPIOF)
case 0x05:
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOFEN; //Power up PORTF
gpioBase = GPIOF;
break;
#endif
default:
return -1023; // some silly value as error
}
@@ -293,31 +415,33 @@ int ADCee::init(uint8_t pin) {
if (adcchan > 18)
return -1022; // silly value as error
if (adcchan < 10)
ADC1->SMPR2 |= (0b111 << (adcchan * 3)); // Channel sampling rate 480 cycles
adc->SMPR2 |= (0b111 << (adcchan * 3)); // Channel sampling rate 480 cycles
else
ADC1->SMPR1 |= (0b111 << ((adcchan - 10) * 3)); // Channel sampling rate 480 cycles
adc->SMPR1 |= (0b111 << ((adcchan - 10) * 3)); // Channel sampling rate 480 cycles
// Read the inital ADC value for this analog input
ADC1->SQR3 = adcchan; // 1st conversion in regular sequence
ADC1->CR2 |= (1 << 30); // Start 1st conversion SWSTART
while(!(ADC1->SR & (1 << 1))); // Wait until conversion is complete
value = ADC1->DR; // Read value from register
adc->SQR3 = adcchan; // 1st conversion in regular sequence
adc->CR2 |= ADC_CR2_SWSTART; //(1 << 30); // Start 1st conversion SWSTART
while(!(adc->SR & (1 << 1))); // Wait until conversion is complete
value = adc->DR; // Read value from register
uint8_t id = pin - PNUM_ANALOG_BASE;
if (id > 15) { // today we have not enough bits in the mask to support more
return -1021;
}
// if (id > 15) { // today we have not enough bits in the mask to support more
// return -1021;
// }
if (analogvals == NULL) { // allocate analogvals and analogchans if this is the first invocation of init.
if (analogvals == NULL) { // allocate analogvals, analogchans and adcchans if this is the first invocation of init
analogvals = (int *)calloc(NUM_ADC_INPUTS+1, sizeof(int));
analogchans = (uint32_t *)calloc(NUM_ADC_INPUTS+1, sizeof(uint32_t));
adcchans = (ADC_TypeDef **)calloc(NUM_ADC_INPUTS+1, sizeof(ADC_TypeDef));
}
analogvals[id] = value; // Store sampled value
analogchans[id] = adcchan; // Keep track of which ADC channel is used for reading this pin
usedpins |= (1 << id); // This pin is now ready
adcchans[id] = adc; // Keep track of which ADC this channel is on
usedpins |= (1 << id); // This pin is now ready
if (id > highestPin) highestPin = id; // Store our highest pin in use
DIAG(F("ADCee::init(): value=%d, channel=%d, id=%d"), value, adcchan, id);
DIAG(F("ADCee::init(): value=%d, ADC%d: channel=%d, id=%d"), value, adcnum, adcchan, id);
return value;
}
@@ -344,13 +468,16 @@ void ADCee::scan() {
static uint8_t id = 0; // id and mask are the same thing but it is faster to
static uint16_t mask = 1; // increment and shift instead to calculate mask from id
static bool waiting = false;
static ADC_TypeDef *adc;
if (waiting) {
adc = adcchans[id];
if (waiting)
{
// look if we have a result
if (!(ADC1->SR & (1 << 1)))
if (!(adc->SR & (1 << 1)))
return; // no result, continue to wait
// found value
analogvals[id] = ADC1->DR;
analogvals[id] = adc->DR;
// advance at least one track
#ifdef DEBUG_ADC
if (id == 1) TrackManager::track[1]->setBrake(0);
@@ -369,9 +496,10 @@ void ADCee::scan() {
// look for a valid track to sample or until we are around
while (true) {
if (mask & usedpins) {
// start new ADC aquire on id
ADC1->SQR3 = analogchans[id]; //1st conversion in regular sequence
ADC1->CR2 |= (1 << 30); //Start 1st conversion SWSTART
// start new ADC aquire on id
adc = adcchans[id];
adc->SQR3 = analogchans[id]; // 1st conversion in regular sequence
adc->CR2 |= (1 << 30); // Start 1st conversion SWSTART
#ifdef DEBUG_ADC
if (id == 1) TrackManager::track[1]->setBrake(1);
#endif
@@ -392,19 +520,83 @@ void ADCee::scan() {
void ADCee::begin() {
noInterrupts();
//ADC1 config sequence
// TODO: currently defaults to ADC1, may need more to handle other members of STM32F4xx family
RCC->APB2ENR |= (1 << 8); //Enable ADC1 clock (Bit8)
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; // Enable ADC1 clock
// Set ADC prescaler - DIV8 ~ 40ms, DIV6 ~ 30ms, DIV4 ~ 20ms, DIV2 ~ 11ms
ADC->CCR = (0 << 16); // Set prescaler 0=DIV2, 1=DIV4, 2=DIV6, 3=DIV8
ADC1->CR1 &= ~(1 << 8); //SCAN mode disabled (Bit8)
ADC1->CR1 &= ~(3 << 24); //12bit resolution (Bit24,25 0b00)
ADC1->SQR1 = (1 << 20); //Set number of conversions projected (L[3:0] 0b0001) -> 1 conversion
// Disable the DMA controller for ADC1
ADC1->CR2 &= ~ADC_CR2_DMA;
ADC1->CR2 &= ~(1 << 1); //Single conversion
ADC1->CR2 &= ~(1 << 11); //Right alignment of data bits bit12....bit0
ADC1->SQR1 &= ~(0x3FFFFFFF); //Clear whole 1st 30bits in register
ADC1->SQR2 &= ~(0x3FFFFFFF); //Clear whole 1st 30bits in register
ADC1->SQR3 &= ~(0x3FFFFFFF); //Clear whole 1st 30bits in register
ADC1->CR2 |= (1 << 0); // Switch on ADC1
// Wait for ADC1 to become ready (calibration complete)
while (!(ADC1->CR2 & ADC_CR2_ADON)) {
}
#if defined(ADC2)
// Enable the ADC2 clock
RCC->APB2ENR |= RCC_APB2ENR_ADC2EN;
// Initialize ADC2
ADC2->CR1 = 0; // Disable all channels
ADC2->CR2 = 0; // Clear CR2 register
ADC2->CR1 &= ~(1 << 8); //SCAN mode disabled (Bit8)
ADC2->CR1 &= ~(3 << 24); //12bit resolution (Bit24,25 0b00)
ADC2->SQR1 = (1 << 20); //Set number of conversions projected (L[3:0] 0b0001) -> 1 conversion
ADC2->CR2 &= ~ADC_CR2_DMA; // Disable the DMA controller for ADC3
ADC2->CR2 &= ~(1 << 1); //Single conversion
ADC2->CR2 &= ~(1 << 11); //Right alignment of data bits bit12....bit0
ADC2->SQR1 &= ~(0x3FFFFFFF); //Clear whole 1st 30bits in register
ADC2->SQR2 &= ~(0x3FFFFFFF); //Clear whole 1st 30bits in register
ADC2->SQR3 &= ~(0x3FFFFFFF); //Clear whole 1st 30bits in register
// Enable the ADC
ADC2->CR2 |= ADC_CR2_ADON;
// Wait for ADC2 to become ready (calibration complete)
while (!(ADC2->CR2 & ADC_CR2_ADON)) {
}
// Perform ADC3 calibration (optional)
// ADC3->CR2 |= ADC_CR2_CAL;
// while (ADC3->CR2 & ADC_CR2_CAL) {
// }
#endif
#if defined(ADC3)
// Enable the ADC3 clock
RCC->APB2ENR |= RCC_APB2ENR_ADC3EN;
// Initialize ADC3
ADC3->CR1 = 0; // Disable all channels
ADC3->CR2 = 0; // Clear CR2 register
ADC3->CR1 &= ~(1 << 8); //SCAN mode disabled (Bit8)
ADC3->CR1 &= ~(3 << 24); //12bit resolution (Bit24,25 0b00)
ADC3->SQR1 = (1 << 20); //Set number of conversions projected (L[3:0] 0b0001) -> 1 conversion
ADC3->CR2 &= ~ADC_CR2_DMA; // Disable the DMA controller for ADC3
ADC3->CR2 &= ~(1 << 1); //Single conversion
ADC3->CR2 &= ~(1 << 11); //Right alignment of data bits bit12....bit0
ADC3->SQR1 &= ~(0x3FFFFFFF); //Clear whole 1st 30bits in register
ADC3->SQR2 &= ~(0x3FFFFFFF); //Clear whole 1st 30bits in register
ADC3->SQR3 &= ~(0x3FFFFFFF); //Clear whole 1st 30bits in register
// Enable the ADC
ADC3->CR2 |= ADC_CR2_ADON;
// Wait for ADC3 to become ready (calibration complete)
while (!(ADC3->CR2 & ADC_CR2_ADON)) {
}
// Perform ADC3 calibration (optional)
// ADC3->CR2 |= ADC_CR2_CAL;
// while (ADC3->CR2 & ADC_CR2_CAL) {
// }
#endif
interrupts();
}
#endif

View File

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

View File

@@ -37,9 +37,11 @@
* I2C bus, or more than one I2C bus on the STM32 architecture
*****************************************************************************/
#if defined(I2C_USE_INTERRUPTS) && defined(ARDUINO_ARCH_STM32)
#if defined(ARDUINO_NUCLEO_F411RE) || defined(ARDUINO_NUCLEO_F446RE) || defined(ARDUINO_NUCLEO_F412ZG) || defined(ARDUINO_NUCLEO_F429ZI) || defined(ARDUINO_NUCLEO_F446ZE)
#if defined(ARDUINO_NUCLEO_F401RE) || defined(ARDUINO_NUCLEO_F411RE) || defined(ARDUINO_NUCLEO_F446RE) \
|| defined(ARDUINO_NUCLEO_F412ZG) || defined(ARDUINO_NUCLEO_F413ZH) \
|| defined(ARDUINO_NUCLEO_F429ZI) || defined(ARDUINO_NUCLEO_F446ZE)
// Assume I2C1 for now - default I2C bus on Nucleo-F411RE and likely all Nucleo-64
// and Nucleo-144variants
// and Nucleo-144 variants
I2C_TypeDef *s = I2C1;
// In init we will ask the STM32 HAL layer for the configured APB1 clock frequency in Hz

View File

@@ -33,17 +33,16 @@ public:
static void create(VPIN vpin, uint8_t nPins, I2CAddress i2cAddress, int interruptPin=-1) {
if (checkNoOverlap(vpin, nPins, i2cAddress)) new PCA9555(vpin,nPins, i2cAddress, interruptPin);
}
private:
// Constructor
PCA9555(VPIN vpin, int nPins, uint8_t I2CAddress, int interruptPin=-1)
PCA9555(VPIN vpin, uint8_t nPins, I2CAddress I2CAddress, int interruptPin=-1)
: GPIOBase<uint16_t>((FSH *)F("PCA9555"), vpin, nPins, I2CAddress, interruptPin)
{
requestBlock.setRequestParams(_I2CAddress, inputBuffer, sizeof(inputBuffer),
outputBuffer, sizeof(outputBuffer));
outputBuffer[0] = REG_INPUT_P0;
}
private:
void _writeGpioPort() override {
I2CManager.write(_I2CAddress, 3, REG_OUTPUT_P0, _portOutputState, _portOutputState>>8);
}

View File

@@ -34,6 +34,11 @@ unsigned long MotorDriver::globalOverloadStart = 0;
volatile portreg_t shadowPORTA;
volatile portreg_t shadowPORTB;
volatile portreg_t shadowPORTC;
#if defined(ARDUINO_ARCH_STM32)
volatile portreg_t shadowPORTD;
volatile portreg_t shadowPORTE;
volatile portreg_t shadowPORTF;
#endif
MotorDriver::MotorDriver(int16_t power_pin, byte signal_pin, byte signal_pin2, int16_t brake_pin,
byte current_pin, float sense_factor, unsigned int trip_milliamps, int16_t fault_pin) {
@@ -68,6 +73,21 @@ MotorDriver::MotorDriver(int16_t power_pin, byte signal_pin, byte signal_pin2, i
fastSignalPin.shadowinout = fastSignalPin.inout;
fastSignalPin.inout = &shadowPORTC;
}
if (HAVE_PORTD(fastSignalPin.inout == &PORTD)) {
DIAG(F("Found PORTD pin %d"),signalPin);
fastSignalPin.shadowinout = fastSignalPin.inout;
fastSignalPin.inout = &shadowPORTD;
}
if (HAVE_PORTE(fastSignalPin.inout == &PORTE)) {
DIAG(F("Found PORTE pin %d"),signalPin);
fastSignalPin.shadowinout = fastSignalPin.inout;
fastSignalPin.inout = &shadowPORTE;
}
if (HAVE_PORTF(fastSignalPin.inout == &PORTF)) {
DIAG(F("Found PORTF pin %d"),signalPin);
fastSignalPin.shadowinout = fastSignalPin.inout;
fastSignalPin.inout = &shadowPORTF;
}
signalPin2=signal_pin2;
if (signalPin2!=UNUSED_PIN) {
@@ -91,6 +111,21 @@ MotorDriver::MotorDriver(int16_t power_pin, byte signal_pin, byte signal_pin2, i
fastSignalPin2.shadowinout = fastSignalPin2.inout;
fastSignalPin2.inout = &shadowPORTC;
}
if (HAVE_PORTD(fastSignalPin2.inout == &PORTD)) {
DIAG(F("Found PORTD pin %d"),signalPin2);
fastSignalPin2.shadowinout = fastSignalPin2.inout;
fastSignalPin2.inout = &shadowPORTD;
}
if (HAVE_PORTE(fastSignalPin2.inout == &PORTE)) {
DIAG(F("Found PORTE pin %d"),signalPin2);
fastSignalPin2.shadowinout = fastSignalPin2.inout;
fastSignalPin2.inout = &shadowPORTE;
}
if (HAVE_PORTF(fastSignalPin2.inout == &PORTF)) {
DIAG(F("Found PORTF pin %d"),signalPin2);
fastSignalPin2.shadowinout = fastSignalPin2.inout;
fastSignalPin2.inout = &shadowPORTF;
}
}
else dualSignal=false;
@@ -279,7 +314,7 @@ void MotorDriver::startCurrentFromHW() {
#pragma GCC pop_options
#endif //ANALOG_READ_INTERRUPT
#if defined(ARDUINO_ARCH_ESP32)
#if defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_STM32)
#ifdef VARIABLE_TONES
uint16_t taurustones[28] = { 165, 175, 196, 220,
247, 262, 294, 330,
@@ -330,7 +365,7 @@ void MotorDriver::setDCSignal(byte speedcode) {
byte tSpeed=speedcode & 0x7F; // DCC Speed with 0,1 stop and speed steps 2 to 127
byte tDir=speedcode & 0x80;
byte brake;
#if defined(ARDUINO_ARCH_ESP32)
#if defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_STM32)
{
int f = 131;
#ifdef VARIABLE_TONES
@@ -348,7 +383,7 @@ void MotorDriver::setDCSignal(byte speedcode) {
else brake = 2 * (128-tSpeed);
if (invertBrake)
brake=255-brake;
#if defined(ARDUINO_ARCH_ESP32)
#if defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_STM32)
DCCTimer::DCCEXanalogWrite(brakePin,brake);
#else
analogWrite(brakePin,brake);
@@ -372,6 +407,24 @@ void MotorDriver::setDCSignal(byte speedcode) {
setSignal(tDir);
HAVE_PORTC(PORTC=shadowPORTC);
interrupts();
} else if (HAVE_PORTD(fastSignalPin.shadowinout == &PORTD)) {
noInterrupts();
HAVE_PORTD(shadowPORTD=PORTD);
setSignal(tDir);
HAVE_PORTD(PORTD=shadowPORTD);
interrupts();
} else if (HAVE_PORTE(fastSignalPin.shadowinout == &PORTE)) {
noInterrupts();
HAVE_PORTE(shadowPORTE=PORTE);
setSignal(tDir);
HAVE_PORTE(PORTE=shadowPORTE);
interrupts();
} else if (HAVE_PORTF(fastSignalPin.shadowinout == &PORTF)) {
noInterrupts();
HAVE_PORTF(shadowPORTF=PORTF);
setSignal(tDir);
HAVE_PORTF(PORTF=shadowPORTF);
interrupts();
} else {
noInterrupts();
setSignal(tDir);
@@ -393,6 +446,13 @@ void MotorDriver::throttleInrush(bool on) {
} else {
ledcDetachPin(brakePin);
}
#elif defined(ARDUINO_ARCH_STM32)
if(on) {
DCCTimer::DCCEXanalogWriteFrequency(brakePin, 62500);
DCCTimer::DCCEXanalogWrite(brakePin,duty);
} else {
pinMode(brakePin, OUTPUT);
}
#else
if(on){
switch(brakePin) {

View File

@@ -1,5 +1,5 @@
/*
* © 2022 Paul M Antoine
* © 2022-2023 Paul M. Antoine
* © 2021 Mike S
* © 2021 Fred Decker
* © 2020 Chris Harlow
@@ -60,6 +60,16 @@ enum TRACK_MODE : byte {TRACK_MODE_NONE = 1, TRACK_MODE_MAIN = 2, TRACK_MODE_PRO
#define HAVE_PORTB(X) X
#define PORTC GPIOC->ODR
#define HAVE_PORTC(X) X
#define PORTD GPIOD->ODR
#define HAVE_PORTD(X) X
#if defined(GPIOE)
#define PORTE GPIOE->ODR
#define HAVE_PORTE(X) X
#endif
#if defined(GPIOF)
#define PORTF GPIOF->ODR
#define HAVE_PORTF(X) X
#endif
#endif
// if macros not defined as pass-through we define
@@ -74,6 +84,15 @@ enum TRACK_MODE : byte {TRACK_MODE_NONE = 1, TRACK_MODE_MAIN = 2, TRACK_MODE_PRO
#ifndef HAVE_PORTC
#define HAVE_PORTC(X) byte TOKENPASTE2(Unique_, __LINE__) __attribute__((unused)) =0
#endif
#ifndef HAVE_PORTD
#define HAVE_PORTD(X) byte TOKENPASTE2(Unique_, __LINE__) __attribute__((unused)) =0
#endif
#ifndef HAVE_PORTE
#define HAVE_PORTE(X) byte TOKENPASTE2(Unique_, __LINE__) __attribute__((unused)) =0
#endif
#ifndef HAVE_PORTF
#define HAVE_PORTF(X) byte TOKENPASTE2(Unique_, __LINE__) __attribute__((unused)) =0
#endif
// Virtualised Motor shield 1-track hardware Interface
@@ -110,6 +129,9 @@ struct FASTPIN {
extern volatile portreg_t shadowPORTA;
extern volatile portreg_t shadowPORTB;
extern volatile portreg_t shadowPORTC;
extern volatile portreg_t shadowPORTD;
extern volatile portreg_t shadowPORTE;
extern volatile portreg_t shadowPORTF;
enum class POWERMODE : byte { OFF, ON, OVERLOAD, ALERT };
@@ -163,16 +185,16 @@ class MotorDriver {
unsigned int raw2mA( int raw);
unsigned int mA2raw( unsigned int mA);
inline bool brakeCanPWM() {
#if defined(ARDUINO_ARCH_ESP32) || defined(__arm__)
// TODO: on ARM we can use digitalPinHasPWM, and may wish/need to
return true;
#else
#ifdef digitalPinToTimer
#if defined(ARDUINO_ARCH_ESP32)
return (brakePin != UNUSED_PIN); // This was just (true) but we probably do need to check for UNUSED_PIN!
#elif defined(__arm__)
// On ARM we can use digitalPinHasPWM
return ((brakePin!=UNUSED_PIN) && (digitalPinHasPWM(brakePin)));
#elif defined(digitalPinToTimer)
return ((brakePin!=UNUSED_PIN) && (digitalPinToTimer(brakePin)));
#else
return (brakePin<14 && brakePin >1);
#endif //digitalPinToTimer
#endif //ESP32/ARM
#endif
}
inline int getRawCurrentTripValue() {
return rawCurrentTripValue;

View File

@@ -154,10 +154,16 @@ void TrackManager::setDCCSignal( bool on) {
HAVE_PORTA(shadowPORTA=PORTA);
HAVE_PORTB(shadowPORTB=PORTB);
HAVE_PORTC(shadowPORTC=PORTC);
HAVE_PORTD(shadowPORTD=PORTD);
HAVE_PORTE(shadowPORTE=PORTE);
HAVE_PORTF(shadowPORTF=PORTF);
APPLY_BY_MODE(TRACK_MODE_MAIN,setSignal(on));
HAVE_PORTA(PORTA=shadowPORTA);
HAVE_PORTB(PORTB=shadowPORTB);
HAVE_PORTC(PORTC=shadowPORTC);
HAVE_PORTD(PORTD=shadowPORTD);
HAVE_PORTE(PORTE=shadowPORTE);
HAVE_PORTF(PORTF=shadowPORTF);
}
void TrackManager::setCutout( bool on) {
@@ -172,10 +178,16 @@ void TrackManager::setPROGSignal( bool on) {
HAVE_PORTA(shadowPORTA=PORTA);
HAVE_PORTB(shadowPORTB=PORTB);
HAVE_PORTC(shadowPORTC=PORTC);
HAVE_PORTD(shadowPORTD=PORTD);
HAVE_PORTE(shadowPORTE=PORTE);
HAVE_PORTF(shadowPORTF=PORTF);
APPLY_BY_MODE(TRACK_MODE_PROG,setSignal(on));
HAVE_PORTA(PORTA=shadowPORTA);
HAVE_PORTB(PORTB=shadowPORTB);
HAVE_PORTC(PORTC=shadowPORTC);
HAVE_PORTD(PORTD=shadowPORTD);
HAVE_PORTE(PORTE=shadowPORTE);
HAVE_PORTF(PORTF=shadowPORTF);
}
// setDCSignal(), called from normal context

291
Wifi_NINA.cpp Normal file
View File

@@ -0,0 +1,291 @@
/*
© 2023 Paul M. Antoine
© 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
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/>.
*/
#include "defines.h"
#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 "DIAG.h"
#include "RingStream.h"
#include "CommandDistributor.h"
#include "WiThrottle.h"
// Configure the pins used for the ESP32 connection
#if !defined(ARDUINO_GIGA) && defined(ARDUINO_ARCH_STM32) // Here my STM32 configuration
#define SPIWIFI SPI // The SPI port
#define SPIWIFI_SS PA4 // Chip select pin
#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
static WiFiServer *server = NULL;
static RingStream *outboundRing = new RingStream(10240);
static bool APmode = false;
static IPAddress ip;
char asciitolower(char in) {
if (in <= 'Z' && in >= 'A')
return in - ('Z' - 'z');
return in;
}
bool WifiNINA::setup(const char *SSid,
const char *password,
const char *hostname,
int port,
const byte channel,
const bool forceAP) {
bool havePassword = true;
bool haveSSID = true;
bool wifiUp = false;
uint8_t tries = 40;
// Set up the pins!
#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);
return;
}
// Print firmware version on the module
String fv = WiFi.firmwareVersion();
DIAG(F("WifiNINA Firmware version found:%s"), fv.c_str());
const char *yourNetwork = "Your network ";
if (strncmp(yourNetwork, SSid, 13) == 0 || strncmp("", SSid, 13) == 0)
haveSSID = false;
if (strncmp(yourNetwork, password, 13) == 0 || strncmp("", password, 13) == 0)
havePassword = false;
if (haveSSID && havePassword && !forceAP) {
#ifndef ARDUINO_GIGA
WiFi.setHostname(hostname); // Strangely does not work unless we do it HERE!
#endif
// WiFi.mode(WIFI_STA);
// WiFi.setAutoReconnect(true);
WiFi.begin(SSid, password);
while (WiFi.status() != WL_CONNECTED && tries) {
Serial.print('.');
tries--;
delay(500);
}
if (WiFi.status() == WL_CONNECTED) {
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);
}
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;
}
}
}
if (!haveSSID || forceAP) {
// prepare all strings
String strSSID(forceAP ? SSid : "DCCEX_");
String strPass(forceAP ? password : "PASS_");
if (!forceAP) {
byte mac[6];
WiFi.macAddress(mac);
String strMac;
for (int i = 0; i++; i < 6) {
strMac += String(mac[i], HEX);
}
DIAG(F("MAC address: %x:%x:%x:%x:%x:%x"), mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
strMac.remove(0,9);
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); ///TJF: why does this fail compile with WiFiNINA, but not giga WiFi???
strSSID.concat(strMac);
strPass.concat(strMac);
}
if (WiFi.beginAP(strSSID.c_str(),
havePassword ? password : strPass.c_str(),
channel) == WL_AP_LISTENING) {
DIAG(F("Wifi AP SSID %s PASS %s"),strSSID.c_str(),havePassword ? password : strPass.c_str());
ip = WiFi.localIP();
DIAG(F("Wifi AP IP %d.%d.%d.%d"),ip[0], ip[1], ip[2], ip[3]);
wifiUp = true;
APmode = true;
} else {
DIAG(F("Could not set up AP with Wifi SSID %s"),strSSID.c_str());
}
}
if (!wifiUp) {
DIAG(F("Wifi setup all fail (STA and AP mode)"));
// no idea to go on
return false;
}
// TODO: we need to run the MDNS_Generic server I suspect
// // Now Wifi is up, register the mDNS service
// if(!MDNS.begin(hostname)) {
// DIAG(F("Wifi setup failed to start mDNS"));
// }
// if(!MDNS.addService("withrottle", "tcp", 2560)) {
// DIAG(F("Wifi setup failed to add withrottle service to mDNS"));
// }
server = new WiFiServer(port); // start listening on tcp port
server->begin();
// server started here
DIAG(F("Server will be started on port %d"),port);
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);
return true;
}
const char *wlerror[] = {
"WL_IDLE_STATUS",
"WL_NO_SSID_AVAIL",
"WL_SCAN_COMPLETED",
"WL_CONNECTED",
"WL_CONNECT_FAILED",
"WL_CONNECTION_LOST",
"WL_DISCONNECTED"
};
WiFiClient * clients[MAX_CLIENTS]; // nulled in setup
void WifiNINA::checkForNewClient() {
auto newClient=server->available();
if (!newClient) return;
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.
return;
}
}
}
void WifiNINA::checkForLostClients() {
for (byte clientId=0; clientId<MAX_CLIENTS; clientId++){
auto c=clients[clientId];
if(c && !c->connected()) {
clients[clientId]->stop();
//DIAG(F("Remove client %d"), clientId);
CommandDistributor::forget(clientId);
clients[clientId]=nullptr;
}
}
}
void WifiNINA::checkForClientInput() {
// Find a client providing input
for (byte clientId=0; clientId<MAX_CLIENTS; clientId++){
auto c=clients[clientId];
if(c) {
auto len=c->available();
if (len) {
// read data from client
byte cmd[len+1];
for(int i=0; i<len; i++) cmd[i]=c->read();
cmd[len]=0x00;
CommandDistributor::parse(clientId,cmd,outboundRing);
}
}
}
}
void WifiNINA::checkForClientOutput() {
// something to write out?
auto clientId=outboundRing->read();
if (clientId < 0) return;
auto replySize=outboundRing->count();
if (replySize==0) return; // nothing to send
auto c=clients[clientId];
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
return;
}
// emit data to the client object
for (int i=0;i<replySize;i++) c->write(outboundRing->read());
}
void WifiNINA::loop() {
checkForLostClients(); // ***
checkForNewClient();
checkForClientInput(); // ***
WiThrottle::loop(outboundRing); // allow withrottle to broadcast if needed
checkForClientOutput();
}
#endif // WIFI_NINA

46
Wifi_NINA.h Normal file
View File

@@ -0,0 +1,46 @@
/*
* © 2023 Paul M. Antoine
* © 2021 Harald Barth
* © 2023 Nathan Kellenicki
*
* This file is part of CommandStation-EX
*
* 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/>.
*/
#ifndef WifiNINA_h
#define WifiNINA_h
// #include "FSH.h"
#include <Arduino.h>
// #include <SPI.h>
// #include <WifiNINA.h>
class WifiNINA
{
public:
static bool setup(const char *wifiESSID,
const char *wifiPassword,
const char *hostname,
const int port,
const byte channel,
const bool forceAP);
static void loop();
private:
static void checkForNewClient();
static void checkForLostClients();
static void checkForClientInput();
static void checkForClientOutput();
};
#endif //WifiNINA_h

View File

@@ -31,7 +31,6 @@ include_dir = .
[env]
build_flags = -Wall -Wextra
; monitor_filters = time
; lib_deps = adafruit/Adafruit ST7735 and ST7789 Library @ ^1.10.0
[env:samd21-dev-usb]
platform = atmelsam
@@ -60,7 +59,7 @@ framework = arduino
lib_deps = ${env.lib_deps}
monitor_speed = 115200
monitor_echo = yes
build_flags = -std=c++17 ; -DI2C_USE_WIRE -DDIAG_LOOPTIMES -DDIAG_IO
build_flags = -std=c++17
[env:mega2560-debug]
platform = atmelavr
@@ -72,7 +71,7 @@ lib_deps =
SPI
monitor_speed = 115200
monitor_echo = yes
build_flags = -DDIAG_IO=2 -DDIAG_LOOPTIMES
build_flags = -DDIAG_IO=2 -DDIAG_LOOPTIMES
[env:mega2560-no-HAL]
platform = atmelavr
@@ -84,7 +83,7 @@ lib_deps =
SPI
monitor_speed = 115200
monitor_echo = yes
build_flags = -DIO_NO_HAL
build_flags = -DIO_NO_HAL
[env:mega2560-I2C-wire]
platform = atmelavr
@@ -106,9 +105,10 @@ lib_deps =
${env.lib_deps}
arduino-libraries/Ethernet
SPI
https://github.com/adafruit/WiFiNINA
monitor_speed = 115200
monitor_echo = yes
build_flags = ; -DDIAG_LOOPTIMES
build_flags =
[env:mega328]
platform = atmelavr
@@ -180,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
@@ -190,10 +193,78 @@ platform = ststm32
board = nucleo_f446re
framework = arduino
lib_deps = ${env.lib_deps}
build_flags = -std=c++17 -Os -g2 -Wunused-variable ; -DDIAG_LOOPTIMES ; -DDIAG_IO
build_flags = -std=c++17 -Os -g2 -Wunused-variable
monitor_speed = 115200
monitor_echo = yes
; Experimental - no reason this should not work, but not
; tested as yet
;
[env:Nucleo-F401RE]
platform = ststm32
board = nucleo_f401re
framework = arduino
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
; Commented out by default as the F13ZH has variant files
; but NOT the nucleo_f413zh.json file which needs to be
; installed before you can let PlatformIO see this
;
; [env:Nucleo-F413ZH]
; platform = ststm32
; board = nucleo_f413zh
; 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 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
; Commented out by default as the F412ZG needs variant files
; installed before you can let PlatformIO see this
;
; [env:Nucleo-F412ZG]
; platform = ststm32
; board = blah_f412zg
; framework = arduino
; lib_deps = ${env.lib_deps}
; build_flags = -std=c++17 -Os -g2 -Wunused-variable
; monitor_speed = 115200
; monitor_echo = yes
; upload_protocol = stlink
; Experimental - Ethernet work still in progress
;
; [env:Nucleo-F429ZI]
; platform = ststm32
; board = nucleo_f429zi
; framework = arduino
; lib_deps = ${env.lib_deps}
; arduino-libraries/Ethernet @ ^2.0.1
; stm32duino/STM32Ethernet @ ^1.3.0
; stm32duino/STM32duino LwIP @ ^2.1.2
; build_flags = -std=c++17 -Os -g2 -Wunused-variable
; monitor_speed = 115200
; monitor_echo = yes
; upload_protocol = stlink
[env:Teensy3_2]
platform = teensy
board = teensy31
@@ -232,5 +303,4 @@ board = teensy41
framework = arduino
build_flags = -std=c++17 -Os -g2
lib_deps = ${env.lib_deps}
lib_ignore =
lib_ignore =

View File

@@ -3,7 +3,15 @@
#include "StringFormatter.h"
#define VERSION "5.1.7"
#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
// - ARM architecture brakeCanPWM now uses digitalPinHasPWM()
// - STM32F4xx shadowpin extensions to handle pins on ports D, E and F
// 5.1.9 - Fixed IO_PCA9555'h to work with PCA9548 mux, tested OK
// 5.1.8 - STM32Fxx ADCee extension to support ADCs #2 and #3
// 5.1.7 - Fix turntable broadcasts for non-movement activities and <JP> result
// 5.1.6 - STM32F4xx native I2C driver added
// 5.1.5 - Added turntable object and EXRAIL commands