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

Compare commits

..

71 Commits

Author SHA1 Message Date
Harald Barth
2ada89f918 LCN bugfix 2023-02-12 20:35:57 +01:00
peteGSX
0b0aba7aef Merge pull request #305 from DCC-EX:41-feature-request-enable-servo-animations-in-ex-ioexpander
Fix myHal example for EX-IOExpander
2023-02-12 19:14:59 +10:00
peteGSX
9c95eb6905 Servo animation moved to EX-IO 2023-02-12 19:06:46 +10:00
peteGSX
47cda83210 Disabled servo animations 2023-02-12 10:36:26 +10:00
peteGSX
d8d785877e Fix myHal example for EX-IOExpander 2023-02-09 13:38:06 +10:00
peteGSX
3b82a94d83 Merge pull request #304 from DCC-EX:ex-io-28-feature-request-enable-pwm-support
Ex-io-28-feature-request-enable-pwm-support
2023-02-09 13:20:46 +10:00
peteGSX
acadf241e6 Update version 2023-02-09 13:15:04 +10:00
peteGSX
8cc5f7ddf4 Merge branch 'ex-io-28-feature-request-enable-pwm-support' of https://github.com/DCC-EX/CommandStation-EX into ex-io-28-feature-request-enable-pwm-support 2023-02-09 13:08:34 +10:00
peteGSX
f1c17c3606 Add more state checking 2023-02-09 13:03:00 +10:00
peteGSX
d36ac7dcfd Revert IODevice.h change 2023-02-09 12:58:48 +10:00
peteGSX
6b67760db1 Fix dynamic RAM allocation 2023-02-09 12:58:48 +10:00
peteGSX
6874ddca9b Servo functional 2023-02-09 12:58:48 +10:00
peteGSX
06827a42b7 Remove excess drivers 2023-02-09 12:58:48 +10:00
peteGSX
f59fe6e83b Some success 2023-02-09 12:58:48 +10:00
peteGSX
c768bdc361 Start adding servo to EX-IO 2023-02-09 12:58:48 +10:00
peteGSX
ad97260055 Add extra error checking 2023-02-09 12:58:48 +10:00
peteGSX
938b4cfbd6 Update version 2023-02-09 12:58:48 +10:00
peteGSX
2a3d48dc00 Fix digital read bug 2023-02-09 12:58:48 +10:00
peteGSX
5efb0c5013 Basic PWM working 2023-02-09 12:58:48 +10:00
peteGSX
e53ed7b46d Brief start on PWM 2023-02-09 12:58:48 +10:00
peteGSX
4d31cd64a5 Add new drivers 2023-02-09 12:58:48 +10:00
peteGSX
6031a0fb7f Fix mess after rebase and conflicts 2023-02-09 12:58:48 +10:00
peteGSX
d375723a13 Cleaned up PWM start 2023-02-09 12:57:30 +10:00
peteGSX
fa38583772 Brief PWM start 2023-02-09 12:57:30 +10:00
peteGSX
984ef6fead Refactored, analogue tested 2023-02-09 12:57:29 +10:00
peteGSX
cf2817d7c4 Brief PWM start 2023-02-09 12:54:05 +10:00
peteGSX
0c2f8428df Refactored, analogue tested 2023-02-09 12:54:05 +10:00
peteGSX
53215b496e Refactored, analogue tested 2023-02-09 12:54:05 +10:00
peteGSX
d41b5e0938 Brief PWM start 2023-02-09 12:54:05 +10:00
peteGSX
d8cbdb24e1 Refactored, analogue tested 2023-02-09 12:54:05 +10:00
peteGSX
93ac1b6d61 Revert IODevice.h change 2023-02-09 12:45:34 +10:00
peteGSX
ad4a0a9592 Merge pull request #303 from DCC-EX:exio-test-servo-included
Exio-test-servo-included
2023-02-09 09:43:03 +10:00
peteGSX
deb49f2943 Fix dynamic RAM allocation 2023-02-09 09:31:09 +10:00
peteGSX
5cb216dd79 Servo functional 2023-02-09 08:41:50 +10:00
peteGSX
afc94a75bb Remove excess drivers 2023-02-09 07:39:58 +10:00
peteGSX
2848ba616b Some success 2023-02-09 07:38:00 +10:00
peteGSX
3d480ee9ef Start adding servo to EX-IO 2023-02-09 05:32:27 +10:00
Harald Barth
57292c2250 installer.sh script bug fix and enhancements 2023-02-07 20:44:03 +01:00
peteGSX
c870940dde Add extra error checking 2023-02-07 07:32:16 +10:00
peteGSX
754639c7e3 Update version 2023-02-06 19:39:25 +10:00
peteGSX
a478ad7112 Merge pull request #302 from DCC-EX:separate-server-from-pca9685
Separate-server-from-pca9685
2023-02-06 19:34:00 +10:00
peteGSX
abe79b854e Fix digital read bug 2023-02-04 09:19:32 +10:00
peteGSX
ec83a345dc Basic PWM working 2023-02-01 19:46:08 +10:00
peteGSX
4e32c707b9 Brief start on PWM 2023-02-01 14:53:46 +10:00
peteGSX
73e1dfc192 Remove duplicate comment 2023-02-01 08:13:23 +10:00
peteGSX
1ae74d9487 Merge branch 'separate-server-from-pca9685' of https://github.com/DCC-EX/CommandStation-EX into separate-server-from-pca9685 2023-02-01 07:51:45 +10:00
peteGSX
a7366b42c1 Add new drivers 2023-02-01 07:51:38 +10:00
peteGSX
84431d1841 Fix mess after rebase and conflicts 2023-02-01 07:49:31 +10:00
peteGSX
e12c5292fa Merge branch 'ex-io-28-feature-request-enable-pwm-support' of https://github.com/DCC-EX/CommandStation-EX into ex-io-28-feature-request-enable-pwm-support 2023-02-01 07:29:17 +10:00
peteGSX
e76197faa9 Brief PWM start 2023-02-01 07:26:03 +10:00
peteGSX
bdc8aec9a6 Refactored, analogue tested 2023-02-01 07:19:52 +10:00
peteGSX
052256e2ed Refactored, analogue tested 2023-02-01 07:18:04 +10:00
peteGSX
1073e142e6 Add new drivers 2023-01-31 19:32:12 +10:00
peteGSX
a18c06d021 Cleaned up PWM start 2023-01-31 19:29:39 +10:00
peteGSX
77b20e6a16 Merge branch 'ex-io-28-feature-request-enable-pwm-support' of https://github.com/DCC-EX/CommandStation-EX into ex-io-28-feature-request-enable-pwm-support 2023-01-30 05:00:40 +10:00
peteGSX
1d27eb67e4 Brief PWM start 2023-01-30 05:00:31 +10:00
peteGSX
7f19a92d2a Refactored, analogue tested 2023-01-30 05:00:31 +10:00
peteGSX
28caa9e8d3 Brief PWM start 2023-01-29 19:26:33 +10:00
Harald Barth
95945eab4c version bump 2023-01-29 08:50:19 +01:00
Harald Barth
638682f05c STM32F4xx fast ADC read implementation (merge branch 'stm32_adcee_pma' into devel) 2023-01-29 08:47:05 +01:00
peteGSX
ffb08523da Merge branch 'ex-io-28-feature-request-enable-pwm-support' of https://github.com/DCC-EX/CommandStation-EX into ex-io-28-feature-request-enable-pwm-support 2023-01-29 17:18:33 +10:00
peteGSX
d8a1bcaf34 Refactored, analogue tested 2023-01-29 17:18:23 +10:00
Harald Barth
212bf8d80e Broadcast power for <s> again 2023-01-29 08:13:52 +01:00
peteGSX
a17c02444d Refactored, analogue tested 2023-01-29 10:06:01 +10:00
Harald Barth
290d878063 version 2023-01-28 19:09:16 +01:00
Harald Barth
2a7588b1b5 jT answer should contain empty string 2023-01-28 19:07:59 +01:00
pmantoine
be33bafa66 Fixed logic of ADC ready 2023-01-28 14:39:00 +08:00
pmantoine
6cc66e26c1 Initial STM32F4xx fast ADC read implementation 2023-01-28 13:58:55 +08:00
Harald Barth
c91d66549c Remove warnings 2023-01-27 19:42:55 +01:00
Harald Barth
9e5d780c14 Bugfix for issue #299 TurnoutDescription NULL 2023-01-27 18:42:26 +01:00
Harald Barth
2c0886bc2f version and copyright info 2023-01-27 17:03:39 +01:00
17 changed files with 332 additions and 134 deletions

View File

@@ -99,6 +99,9 @@ void setup()
// Initialise HAL layer before reading EEprom or setting up MotorDrivers
IODevice::begin();
// As the setup of a motor shield may require a read of the current sense input from the ADC,
// let's make sure to initialise the ADCee class!
ADCee::begin();
// Responsibility 3: Start the DCC engine.
// Note: this provides DCC with two motor drivers, main and prog, which handle the motor shield(s)
// Standard supported devices have pre-configured macros but custome hardware installations require

View File

@@ -510,6 +510,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
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
Output::printAll(stream); //send all Output states
Sensor::printAll(stream); //send all Sensor states
@@ -626,14 +627,17 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
else { // <JT id>
Turnout * t=Turnout::get(id);
if (!t || t->isHidden()) StringFormatter::send(stream, F(" %d X"),id);
else StringFormatter::send(stream, F(" %d %c \"%S\""),
id,t->isThrown()?'T':'C',
else {
const FSH *tdesc = NULL;
#ifdef EXRAIL_ACTIVE
RMFT2::getTurnoutDescription(id)
#else
F("")
#endif
);
tdesc = RMFT2::getTurnoutDescription(id);
#endif
if (tdesc == NULL)
tdesc = F("");
StringFormatter::send(stream, F(" %d %c \"%S\""),
id,t->isThrown()?'T':'C',
tdesc);
}
}
StringFormatter::send(stream, F(">\n"));
return;

View File

@@ -1,5 +1,5 @@
/*
* © 2022 Paul M. Antoine
* © 2022-2023 Paul M. Antoine
* © 2021 Mike S
* © 2021-2022 Harald Barth
* © 2021 Fred Decker
@@ -102,9 +102,14 @@ private:
// that an offset can be initialized.
class ADCee {
public:
// init does add the pin to the list of scanned pins (if this
// begin is called for any setup that must be done before
// **init** can be called. On some architectures this involves ADC
// initialisation and clock routing, sampling times etc.
static void begin();
// init adds the pin to the list of scanned pins (if this
// platform's implementation scans pins) and returns the first
// read value. It is called before the regular scan is started.
// read value (which is why it required begin to have been called first!)
// It must be called before the regular scan is started.
static int init(uint8_t pin);
// read does read the pin value from the scanned cache or directly
// if this is a platform that does not scan. fromISR is a hint if
@@ -117,9 +122,6 @@ private:
// On platforms that scan, it is called from waveform ISR
// only on a regular basis.
static void scan();
// begin is called for any setup that must be done before
// scan can be called.
static void begin();
// bit array of used pins (max 16)
static uint16_t usedpins;
// cached analog values (malloc:ed to actual number of ADC channels)

View File

@@ -168,23 +168,6 @@ int ADCee::init(uint8_t pin) {
if (id > NUM_ADC_INPUTS)
return -1023;
// Dummy read using Arduino library
analogReadResolution(12);
value = analogRead(pin);
// Reconfigure ADC
ADC->CTRLA.bit.ENABLE = 0; // disable ADC
while( ADC->STATUS.bit.SYNCBUSY == 1 ); // wait for synchronization
ADC->CTRLB.reg &= 0b1111100011001111; // mask PRESCALER and RESSEL bits
ADC->CTRLB.reg |= ADC_CTRLB_PRESCALER_DIV64 | // divide Clock by 16
ADC_CTRLB_RESSEL_12BIT; // Result 12 bits, 10 bits possible
ADC->AVGCTRL.reg = ADC_AVGCTRL_SAMPLENUM_1 | // take 1 sample at a time
ADC_AVGCTRL_ADJRES(0x00ul); // adjusting result by 0
ADC->SAMPCTRL.reg = 0x00ul; // sampling Time Length = 0
ADC->CTRLA.bit.ENABLE = 1; // enable ADC
while( ADC->STATUS.bit.SYNCBUSY == 1 ); // wait for synchronization
// Permanently configure SAMD IO MUX for that pin
pinPeripheral(pin, PIO_ANALOG);
ADC->INPUTCTRL.bit.MUXPOS = g_APinDescription[pin].ulADCChannelNumber; // Selection for the positive ADC input
@@ -205,9 +188,11 @@ int ADCee::init(uint8_t pin) {
return value;
}
int16_t ADCee::ADCmax() {
return 4095;
}
/*
* Read function ADCee::read(pin) to get value instead of analogRead(pin)
*/

View File

@@ -96,7 +96,7 @@ void DCCTimer::clearPWM() {
void DCCTimer::getSimulatedMacAddress(byte mac[6]) {
volatile uint32_t *serno1 = (volatile uint32_t *)0x1FFF7A10;
volatile uint32_t *serno2 = (volatile uint32_t *)0x1FFF7A14;
volatile uint32_t *serno3 = (volatile uint32_t *)0x1FFF7A18;
// volatile uint32_t *serno3 = (volatile uint32_t *)0x1FFF7A18;
volatile uint32_t m1 = *serno1;
volatile uint32_t m2 = *serno2;
@@ -131,31 +131,148 @@ void DCCTimer::reset() {
while(true) {};
}
#define NUM_ADC_INPUTS NUM_ANALOG_INPUTS
// TODO: may need to use uint32_t on STMF4xx variants with > 16 analog inputs!
uint16_t ADCee::usedpins = 0;
int * ADCee::analogvals = NULL;
uint32_t * analogchans = NULL;
bool adc1configured = false;
int16_t ADCee::ADCmax() {
return 4095;
}
int ADCee::init(uint8_t pin) {
return analogRead(pin);
uint id = pin - A0;
int value = 0;
PinName stmpin = digitalPin[analogInputPin[id]];
uint32_t stmgpio = stmpin / 16; // 16-bits per GPIO 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;
// Port config - find which port we're on and power it up
switch(stmgpio) {
case 0x00:
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; //Power up PORTA
gpioBase = GPIOA;
break;
case 0x01:
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN; //Power up PORTB
gpioBase = GPIOB;
break;
case 0x02:
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN; //Power up PORTC
gpioBase = GPIOC;
break;
}
// Set pin mux mode to analog input
gpioBase->MODER |= (0b011 << (stmpin << 1)); // Set pin mux to analog mode
// Set the sampling rate for that analog input
if (adcchan < 10)
ADC1->SMPR2 |= (0b111 << (adcchan * 3)); // Channel sampling rate 480 cycles
else
ADC1->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
if (analogvals == NULL)
{
analogvals = (int *)calloc(NUM_ADC_INPUTS+1, sizeof(int));
analogchans = (uint32_t *)calloc(NUM_ADC_INPUTS+1, sizeof(uint32_t));
}
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
return value;
}
/*
* Read function ADCee::read(pin) to get value instead of analogRead(pin)
*/
int ADCee::read(uint8_t pin, bool fromISR) {
int current;
if (!fromISR) noInterrupts();
current = analogRead(pin);
if (!fromISR) interrupts();
return current;
uint8_t id = pin - A0;
// Was this pin initialised yet?
if ((usedpins & (1<<id) ) == 0)
return -1023;
// We do not need to check (analogvals == NULL)
// because usedpins would still be 0 in that case
return analogvals[id];
}
/*
* Scan function that is called from interrupt
*/
#pragma GCC push_options
#pragma GCC optimize ("-O3")
void ADCee::scan() {
static uint 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;
if (waiting) {
// look if we have a result
if (!(ADC1->SR & (1 << 1)))
return; // no result, continue to wait
// found value
analogvals[id] = ADC1->DR;
// advance at least one track
// for scope debug TrackManager::track[1]->setBrake(0);
waiting = false;
id++;
mask = mask << 1;
if (id == NUM_ADC_INPUTS+1) {
id = 0;
mask = 1;
}
}
if (!waiting) {
if (usedpins == 0) // otherwise we would loop forever
return;
// 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
// for scope debug TrackManager::track[1]->setBrake(1);
waiting = true;
return;
}
id++;
mask = mask << 1;
if (id == NUM_ADC_INPUTS+1) {
id = 0;
mask = 1;
}
}
}
}
#pragma GCC pop_options
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)
// 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
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
interrupts();
}
#endif

View File

@@ -62,7 +62,6 @@ const bool signalTransform[]={
/* WAVE_PENDING (should not happen) -> */ LOW};
void DCCWaveform::begin() {
ADCee::begin();
DCCTimer::begin(DCCWaveform::interruptHandler);
}

View File

@@ -1,6 +1,6 @@
/*
* © 2021 Neil McKechnie
* © 2021-2022 Harald Barth
* © 2021-2023 Harald Barth
* © 2020-2022 Chris Harlow
* All rights reserved.
*
@@ -748,7 +748,7 @@ void RMFT2::loop2() {
break;
case OPCODE_IFLOCO: // do if the loco is the active one
skipIf=loco!=operand;
skipIf=loco!=(uint16_t)operand; // bad luck if someone enters negative loco numbers into EXRAIL
break;
case OPCODE_IFNOT: // do next operand if sensor not set

View File

@@ -1,6 +1,7 @@
/*
* © 2021 Neil McKechnie
* © 2020-2022 Chris Harlow
* © 2023 Harald Barth
* All rights reserved.
*
* This file is part of CommandStation-EX

View File

@@ -1,6 +1,6 @@
/*
* © 2021-2022 Chris Harlow
* © 2020,2021 Chris Harlow. All rights reserved.
* © 2020-2022 Chris Harlow. All rights reserved.
* © 2023 Harald Barth
*
* This file is part of CommandStation-EX
*

View File

@@ -1,6 +1,7 @@
/*
* © 2021 Neil McKechnie
* © 2020-2022 Chris Harlow
* © 2023 Harald Barth
* All rights reserved.
*
* This file is part of CommandStation-EX

View File

@@ -1 +1 @@
#define GITHUB_SHA "devel-202212051450Z"
#define GITHUB_SHA "devel-202302121935Z"

View File

@@ -1,5 +1,5 @@
/*
* © 2021, Peter Cole. All rights reserved.
* © 2022, Peter Cole. All rights reserved.
*
* This file is part of EX-CommandStation
*
@@ -26,19 +26,14 @@
* (Note the device driver is included by default)
*
* void halSetup() {
* // EXIOExpander::create(vpin, num_vpins, i2c_address, digitalPinCount, analoguePinCount);
* EXIOExpander::create(800, 18, 0x65, 12, 8);
* // EXIOExpander::create(vpin, num_vpins, i2c_address);
* EXIOExpander::create(800, 18, 0x65);
* }
*
* Note when defining the number of digital and analogue pins, there is no way to sanity check
* this from the device driver, and it is up to the user to define the correct values here.
*
* All pins available on the EX-IOExpander device must be accounted for.
*
* Vpins are allocated to digital pins first, and then analogue pins, so digital pins will
* populate the first part of the specified vpin range, with the analogue pins populating the
* last part of the vpin range.
* Eg. for a default Nano, 800 - 811 are digital (D2 - D13), 812 to 817 are analogue (A0 - A3, A6/A7).
* All pins on an EX-IOExpander device are allocated according to the pin map for the specific
* device in use. There is no way for the device driver to sanity check pins are used for the
* correct purpose, however the EX-IOExpander device's pin map will prevent pins being used
* incorrectly (eg. A6/7 on Nano cannot be used for digital input/output).
*/
#ifndef IO_EX_IOEXPANDER_H
@@ -54,22 +49,27 @@
*/
class EXIOExpander : public IODevice {
public:
static void create(VPIN vpin, int nPins, uint8_t i2cAddress, int numDigitalPins, int numAnaloguePins) {
if (checkNoOverlap(vpin, nPins, i2cAddress)) new EXIOExpander(vpin, nPins, i2cAddress, numDigitalPins, numAnaloguePins);
enum ProfileType : uint8_t {
Instant = 0, // Moves immediately between positions (if duration not specified)
UseDuration = 0, // Use specified duration
Fast = 1, // Takes around 500ms end-to-end
Medium = 2, // 1 second end-to-end
Slow = 3, // 2 seconds end-to-end
Bounce = 4, // For semaphores/turnouts with a bit of bounce!!
NoPowerOff = 0x80, // Flag to be ORed in to suppress power off after move.
};
static void create(VPIN vpin, int nPins, uint8_t i2cAddress) {
if (checkNoOverlap(vpin, nPins, i2cAddress)) new EXIOExpander(vpin, nPins, i2cAddress);
}
private:
// Constructor
EXIOExpander(VPIN firstVpin, int nPins, uint8_t i2cAddress, int numDigitalPins, int numAnaloguePins) {
EXIOExpander(VPIN firstVpin, int nPins, uint8_t i2cAddress) {
_firstVpin = firstVpin;
_nPins = nPins;
_i2cAddress = i2cAddress;
_numDigitalPins = numDigitalPins;
_numAnaloguePins = numAnaloguePins;
_digitalPinBytes = (numDigitalPins+7)/8;
_analoguePinBytes = numAnaloguePins * 2;
_digitalInputStates=(byte*) calloc(_digitalPinBytes,1);
_analogueInputStates=(byte*) calloc(_analoguePinBytes,1);
addDevice(this);
}
@@ -77,20 +77,31 @@ private:
// Initialise EX-IOExander device
I2CManager.begin();
if (I2CManager.exists(_i2cAddress)) {
_digitalOutBuffer[0] = EXIOINIT;
_digitalOutBuffer[1] = _numDigitalPins;
_digitalOutBuffer[2] = _numAnaloguePins;
// Send config, if EXIORDY returned, we're good, otherwise go offline
I2CManager.read(_i2cAddress, _commandBuffer, 1, _digitalOutBuffer, 3);
if (_commandBuffer[0] != EXIORDY) {
_command4Buffer[0] = EXIOINIT;
_command4Buffer[1] = _nPins;
_command4Buffer[2] = _firstVpin & 0xFF;
_command4Buffer[3] = _firstVpin >> 8;
// Send config, if EXIOPINS returned, we're good, setup pin buffers, otherwise go offline
I2CManager.read(_i2cAddress, _receive3Buffer, 3, _command4Buffer, 4);
if (_receive3Buffer[0] == EXIOPINS) {
_numDigitalPins = _receive3Buffer[1];
_numAnaloguePins = _receive3Buffer[2];
_digitalPinBytes = (_numDigitalPins + 7)/8;
_digitalInputStates=(byte*) calloc(_digitalPinBytes,1);
_analoguePinBytes = _numAnaloguePins * 2;
_analogueInputStates = (byte*) calloc(_analoguePinBytes, 1);
_analoguePinMap = (uint8_t*) calloc(_numAnaloguePins, 1);
} else {
DIAG(F("ERROR configuring EX-IOExpander device, I2C:x%x"), _i2cAddress);
_deviceState = DEVSTATE_FAILED;
return;
}
// We now need to retrieve the analogue pin map
_command1Buffer[0] = EXIOINITA;
I2CManager.read(_i2cAddress, _analoguePinMap, _numAnaloguePins, _command1Buffer, 1);
// Attempt to get version, if we don't get it, we don't care, don't go offline
// Using digital in buffer in reverse to save RAM
_commandBuffer[0] = EXIOVER;
I2CManager.read(_i2cAddress, _versionBuffer, 3, _commandBuffer, 1);
_command1Buffer[0] = EXIOVER;
I2CManager.read(_i2cAddress, _versionBuffer, 3, _command1Buffer, 1);
_majorVer = _versionBuffer[0];
_minorVer = _versionBuffer[1];
_patchVer = _versionBuffer[2];
@@ -105,87 +116,117 @@ private:
}
}
// Digital input pin configuration, used to enable on EX-IOExpander device and set pullups if in use
bool _configure(VPIN vpin, ConfigTypeEnum configType, int paramCount, int params[]) override {
if (configType != CONFIGURE_INPUT) return false;
if (paramCount != 1) return false;
if (vpin >= _firstVpin + _numDigitalPins) {
DIAG(F("EX-IOExpander ERROR: Vpin %d is an analogue pin, cannot use as a digital pin"), vpin);
int pin = vpin - _firstVpin;
if (configType == CONFIGURE_INPUT) {
bool pullup = params[0];
_digitalOutBuffer[0] = EXIODPUP;
_digitalOutBuffer[1] = pin;
_digitalOutBuffer[2] = pullup;
I2CManager.read(_i2cAddress, _command1Buffer, 1, _digitalOutBuffer, 3);
if (_command1Buffer[0] == EXIORDY) {
return true;
} else {
DIAG(F("Vpin %d cannot be used as a digital input pin"), (int)vpin);
return false;
}
} else {
return false;
}
bool pullup = params[0];
int pin = vpin - _firstVpin;
_digitalOutBuffer[0] = EXIODPUP;
_digitalOutBuffer[1] = pin;
_digitalOutBuffer[2] = pullup;
I2CManager.write(_i2cAddress, _digitalOutBuffer, 3);
return true;
}
// We only use this to detect incorrect use of analogue pins
// Analogue input pin configuration, used to enable on EX-IOExpander device
int _configureAnalogIn(VPIN vpin) override {
if (vpin < _firstVpin + _numDigitalPins) {
DIAG(F("EX-IOExpander ERROR: Vpin %d is a digital pin, cannot use as an analogue pin"), vpin);
int pin = vpin - _firstVpin;
_command2Buffer[0] = EXIOENAN;
_command2Buffer[1] = pin;
I2CManager.read(_i2cAddress, _command1Buffer, 1, _command2Buffer, 2);
if (_command1Buffer[0] == EXIORDY) {
return true;
} else {
DIAG(F("Vpin %d cannot be used as an analogue input pin"), (int)vpin);
return false;
}
int pin = vpin - _firstVpin;
_analogueOutBuffer[0] = EXIOENAN;
_analogueOutBuffer[1] = pin;
I2CManager.write(_i2cAddress, _analogueOutBuffer, 2);
return true;
}
// Main loop, collect both digital and analogue pin states continuously (faster sensor/input reads)
void _loop(unsigned long currentMicros) override {
_commandBuffer[0] = EXIORDD;
I2CManager.read(_i2cAddress, _digitalInputStates, _digitalPinBytes, _commandBuffer, 1);
_commandBuffer[0] = EXIORDAN;
I2CManager.read(_i2cAddress, _analogueInputStates, _analoguePinBytes, _commandBuffer, 1);
(void)currentMicros; // remove warning
if (_deviceState == DEVSTATE_FAILED) return;
_command1Buffer[0] = EXIORDD;
I2CManager.read(_i2cAddress, _digitalInputStates, _digitalPinBytes, _command1Buffer, 1);
_command1Buffer[0] = EXIORDAN;
I2CManager.read(_i2cAddress, _analogueInputStates, _analoguePinBytes, _command1Buffer, 1);
}
// Obtain the correct analogue input value
int _readAnalogue(VPIN vpin) override {
if (vpin < _firstVpin + _numDigitalPins) return false;
int pin = vpin - _firstVpin - _numDigitalPins;
uint8_t _pinLSBByte = pin * 2;
if (_deviceState == DEVSTATE_FAILED) return 0;
int pin = vpin - _firstVpin;
uint8_t _pinLSBByte;
for (uint8_t aPin = 0; aPin < _numAnaloguePins; aPin++) {
if (_analoguePinMap[aPin] == pin) {
_pinLSBByte = aPin * 2;
}
}
uint8_t _pinMSBByte = _pinLSBByte + 1;
return (_analogueInputStates[_pinMSBByte] << 8) + _analogueInputStates[_pinLSBByte];
}
// Obtain the correct digital input value
int _read(VPIN vpin) override {
if (vpin >= _firstVpin + _numDigitalPins) return false;
if (_deviceState == DEVSTATE_FAILED) return 0;
int pin = vpin - _firstVpin;
uint8_t pinByte = pin / 8;
bool value = _digitalInputStates[pinByte] >> (pin - pinByte * 8);
bool value = bitRead(_digitalInputStates[pinByte], pin - pinByte * 8);
return value;
}
void _write(VPIN vpin, int value) override {
if (vpin >= _firstVpin + _numDigitalPins) return;
if (_deviceState == DEVSTATE_FAILED) return;
int pin = vpin - _firstVpin;
_digitalOutBuffer[0] = EXIOWRD;
_digitalOutBuffer[1] = pin;
_digitalOutBuffer[2] = value;
I2CManager.write(_i2cAddress, _digitalOutBuffer, 3);
I2CManager.read(_i2cAddress, _command1Buffer, 1, _digitalOutBuffer, 3);
if (_command1Buffer[0] != EXIORDY) {
DIAG(F("Vpin %d cannot be used as a digital output pin"), (int)vpin);
}
}
void _writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration) override {
if (_deviceState == DEVSTATE_FAILED) return;
int pin = vpin - _firstVpin;
#ifdef DIAG_IO
DIAG(F("Servo: WriteAnalogue Vpin:%d Value:%d Profile:%d Duration:%d %S"),
vpin, value, profile, duration, _deviceState == DEVSTATE_FAILED?F("DEVSTATE_FAILED"):F(""));
#endif
_servoBuffer[0] = EXIOWRAN;
_servoBuffer[1] = pin;
_servoBuffer[2] = value & 0xFF;
_servoBuffer[3] = value >> 8;
_servoBuffer[4] = profile;
_servoBuffer[5] = duration & 0xFF;
_servoBuffer[6] = duration >> 8;
I2CManager.read(_i2cAddress, _command1Buffer, 1, _servoBuffer, 7);
if (_command1Buffer[0] != EXIORDY) {
DIAG(F("Vpin %d cannot be used as a servo/PWM pin"), (int)vpin);
}
}
void _display() override {
int _firstAnalogue, _lastAnalogue;
if (_numAnaloguePins == 0) {
_firstAnalogue = 0;
_lastAnalogue = 0;
} else {
_firstAnalogue = _firstVpin + _numDigitalPins;
_lastAnalogue = _firstVpin + _nPins - 1;
}
DIAG(F("EX-IOExpander I2C:x%x v%d.%d.%d: %d Digital Vpins %d-%d, %d Analogue Vpins %d-%d %S"),
DIAG(F("EX-IOExpander I2C:x%x v%d.%d.%d Vpins %d-%d %S"),
_i2cAddress, _majorVer, _minorVer, _patchVer,
_numDigitalPins, _firstVpin, _firstVpin + _numDigitalPins - 1,
_numAnaloguePins, _firstAnalogue, _lastAnalogue,
(int)_firstVpin, (int)_firstVpin+_nPins-1,
_deviceState == DEVSTATE_FAILED ? F("OFFLINE") : F(""));
}
uint8_t _i2cAddress;
uint8_t _numDigitalPins;
uint8_t _numAnaloguePins;
byte _analogueOutBuffer[2];
uint8_t _numDigitalPins = 0;
uint8_t _numAnaloguePins = 0;
byte _digitalOutBuffer[3];
uint8_t _versionBuffer[3];
uint8_t _majorVer = 0;
@@ -195,8 +236,14 @@ private:
byte* _analogueInputStates;
uint8_t _digitalPinBytes = 0;
uint8_t _analoguePinBytes = 0;
byte _commandBuffer[1];
byte _command1Buffer[1];
byte _command2Buffer[2];
byte _command4Buffer[4];
byte _receive3Buffer[3];
byte _servoBuffer[7];
uint8_t* _analoguePinMap;
// EX-IOExpander protocol flags
enum {
EXIOINIT = 0xE0, // Flag to initialise setup procedure
EXIORDY = 0xE1, // Flag we have completed setup procedure, also for EX-IO to ACK setup
@@ -205,8 +252,12 @@ private:
EXIORDAN = 0xE4, // Flag to read an analogue input
EXIOWRD = 0xE5, // Flag for digital write
EXIORDD = 0xE6, // Flag to read digital input
EXIOENAN = 0xE7, // Flag eo enable an analogue pin
EXIOENAN = 0xE7, // Flag to enable an analogue pin
EXIOINITA = 0xE8, // Flag we're receiving analogue pin mappings
EXIOPINS = 0xE9, // Flag we're receiving pin counts for buffers
EXIOWRAN = 0xEA, // Flag we're sending an analogue write (PWM)
EXIOERR = 0xEF, // Flag we've received an error
};
};
#endif
#endif

View File

@@ -43,7 +43,7 @@ void LCN::loop() {
while (stream->available()) {
int ch = stream->read();
if (ch >= 0 && ch <= '9') { // accumulate id value
if (ch >= '0' && ch <= '9') { // accumulate id value
id = 10 * id + ch - '0';
}
else if (ch == 't' || ch == 'T') { // Turnout opcodes

View File

@@ -525,7 +525,7 @@ void WiThrottle::sendTurnouts(Print* stream) {
}
void WiThrottle::sendRoster(Print* stream) {
rosterSent=true;
#ifdef EXRAIL_ACTIVE
#ifdef EXRAIL_ACTIVE
StringFormatter::send(stream,F("RL%d"), RMFT2::rosterNameCount);
for (int16_t r=0;r<RMFT2::rosterNameCount;r++) {
int16_t cabid=GETHIGHFLASHW(RMFT2::rosterIdList,r*2);
@@ -533,11 +533,13 @@ void WiThrottle::sendRoster(Print* stream) {
RMFT2::getRosterName(cabid),cabid,cabid<128?'S':'L');
}
StringFormatter::send(stream,F("\n"));
#else
(void)stream; // remove warning
#endif
}
void WiThrottle::sendRoutes(Print* stream) {
routesSent=true;
#ifdef EXRAIL_ACTIVE
#ifdef EXRAIL_ACTIVE
StringFormatter::send(stream,F("PRT]\\[Routes}|{Route]\\[Set}|{2]\\[Handoff}|{4\nPRL"));
// first pass automations
for (int ix=0;;ix+=2) {
@@ -554,7 +556,8 @@ void WiThrottle::sendRoutes(Print* stream) {
StringFormatter::send(stream,F("]\\[R%d}|{%S}|{2"),id,desc);
}
StringFormatter::send(stream,F("\n"));
#else
(void)stream; // remove warning
#endif
}
@@ -609,4 +612,4 @@ void WiThrottle::sendFunctions(Print* stream, byte loco) {
int fstate=DCC::getFn(locoid,fKey);
if (fstate>=0) StringFormatter::send(stream,F("M%cA%c%d<;>F%d%d\n"),myLocos[loco].throttle,LorS(locoid),locoid,fstate,fKey);
}
}
}

View File

@@ -37,6 +37,10 @@ function need () {
need git
if test -d `basename "$DCCEXGITURL"` ; then
: assume we are almost there
cd `basename "$DCCEXGITURL"` || exit 255
fi
if test -d .git ; then
: assume we are right here
git pull
@@ -44,6 +48,21 @@ else
git clone "$DCCEXGITURL"
cd `basename "$DCCEXGITURL"` || exit 255
fi
# prepare versions
VERSIONS=/tmp/versions.$$
git tag --sort=v:refname | grep Prod | tail -1 > $VERSIONS
echo master >> $VERSIONS
git tag --sort=v:refname | grep Devel | tail -1 >> $VERSIONS
echo devel >> $VERSIONS
# ask user what version to use
echo "What version to use? (give line number) If in doubt, use 1"
cat -n $VERSIONS
echo -n "> "
LINE=`awk 'BEGIN {getline A < "/dev/tty"} ; A == NR {print}' $VERSIONS`
git checkout $LINE
if test -f config.h ; then
: all well
else
@@ -63,7 +82,14 @@ $ACLI core update-index || exit 255
# Board discovery
BOARDS=/tmp/boards.$$
$ACLI board list | grep serial > $BOARDS
$ACLI board list > /dev/null # download missing components
$ACLI board list | grep serial > $BOARDS # real run
if test -s $BOARDS ; then
: all well
else
echo "$ACLI: No boards found"
exit 255
fi
if test x`< $BOARDS wc -l` = 'x1' ; then
LINE=`cat $BOARDS`
else
@@ -96,6 +122,6 @@ echo FQBN is $FQBN
# Install phase
$ACLI core install `echo $FQBN | sed 's,:[^:]*$,,1'` # remove last component to get package
$ACLI board attach -p $PORT --fqbn $FQBN $PWD
$ACLI compile --fqbn $FQBN $PWD
$ACLI upload -v -t -p $PORT $PWD
$ACLI board attach -p $PORT --fqbn $FQBN "$PWD"
$ACLI compile --fqbn $FQBN "$PWD"
$ACLI upload -v -t -p $PORT "$PWD"

View File

@@ -179,19 +179,17 @@ void halSetup() {
//=======================================================================
// The following directive defines an EX-IOExpander instance.
//=======================================================================
// EXIOExpander::create(VPIN, Number of VPINs, I2C Address, Digital pin count, Analogue pin count)
// EXIOExpander::create(VPIN, Number of VPINs, I2C Address)
//
// The parameters are:
// VPIN=an available Vpin
// Number of VPINs=Digital pin count + Analogue pin count (must match device in use as per documentation)
// Number of VPINs=pin count (must match device in use as per documentation)
// I2C address=an available I2C address (default 0x65)
//
// Note that the I2C address is defined in the EX-IOExpander code, and 0x65 is the default.
// The first example is for an Arduino Nano with the default pin allocations.
// The second example is for an Arduino Uno using all pins as digital only.
// The example is for an Arduino Nano.
//EXIOExpander::create(800, 18, 0x65, 12, 6);
//EXIOExpander::create(820, 16, 0x66, 16, 0);
//EXIOExpander::create(800, 18, 0x65);
//=======================================================================

View File

@@ -4,7 +4,15 @@
#include "StringFormatter.h"
#define VERSION "4.2.10pre1"
#define VERSION "4.2.17"
// 4.2.17 LCN bugfix
// 4.2.16 Move EX-IOExpander servo support to the EX-IOExpander software
// 4.2.15 Add basic experimental PWM support to EX-IOExpander
// EX-IOExpander 0.0.14 minimum required
// 4.2.14 STM32F4xx fast ADC read implementation
// 4.2.13 Broadcast power for <s> again
// 4.2.12 Bugfix for issue #299 TurnoutDescription NULL
// 4.2.11 Exrail IFLOCO feature added
// 4.2.10 SIGNAL/SIGNALH bug fix as they were inverted
// IO_EXIOExpander.h input speed optimisation
// 4.2.9 duinoNodes support