mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2025-07-28 18:03:45 +02:00
Compare commits
4 Commits
v5.4.14-Pr
...
ESP32-moto
Author | SHA1 | Date | |
---|---|---|---|
|
164e1a52f8 | ||
|
0fbfb5945f | ||
|
0e5c89df23 | ||
|
6e05de275d |
23
DCC.cpp
23
DCC.cpp
@@ -28,7 +28,6 @@
|
||||
#include "IODevice.h"
|
||||
|
||||
#include "MotorDriver.h"
|
||||
extern MotorDriverContainer mDC;
|
||||
|
||||
// This module is responsible for converting API calls into
|
||||
// messages to be sent to the waveform generator.
|
||||
@@ -54,22 +53,28 @@ byte DCC::joinRelay=UNUSED_PIN;
|
||||
byte DCC::globalSpeedsteps=128;
|
||||
|
||||
void DCC::begin() {
|
||||
StringFormatter::send(Serial,F("<iDCC-EX V-%S / %S / %S G-%S>\n"), F(VERSION), F(ARDUINO_TYPE), mDC.getMotorShieldName(), F(GITHUB_SHA));
|
||||
StringFormatter::send(Serial,F("<iDCC-EX V-%S / %S / %S G-%S>\n"), F(VERSION), F(ARDUINO_TYPE),
|
||||
MotorDriverContainer::mDC.getMotorShieldName(), F(GITHUB_SHA));
|
||||
|
||||
// BE AWARE, USES I2C PINS, MAY LEAD TO PIN CONFLICTS
|
||||
// Initialise HAL layer before reading EEprom.
|
||||
IODevice::begin();
|
||||
|
||||
//example how to use add:
|
||||
//MotorDriverContainer::mDC.add(new MotorDriver(16, 21, UNUSED_PIN, UNUSED_PIN, UNUSED_PIN, 2.00, 2000, UNUSED_PIN, RMT_MAIN));
|
||||
// Load stuff from EEprom
|
||||
(void)EEPROM; // tell compiler not to warn this is unused
|
||||
EEStore::init();
|
||||
|
||||
DCCWaveform::begin(mDC.mainTrack(),mDC.progTrack());
|
||||
DCCTrack::mainTrack.addDriver(mDC.mainTrack());
|
||||
DCCTrack::progTrack.addDriver(mDC.progTrack());
|
||||
MotorDriverContainer::mDC.diag();
|
||||
DCCWaveform::begin(MotorDriverContainer::mDC.mainTrack(),MotorDriverContainer::mDC.progTrack());
|
||||
|
||||
MotorDriver *md;
|
||||
mDC.add(2, md = new MotorDriver(16, 21, UNUSED_PIN, UNUSED_PIN, UNUSED_PIN, 2.00, 2000, UNUSED_PIN, RMT_MAIN));
|
||||
DCCTrack::mainTrack.addDriver(md);
|
||||
// Add main and prog drivers to the main and prog packet sources (dcc-tracks).
|
||||
std::vector<MotorDriver*> v;
|
||||
v = MotorDriverContainer::mDC.getDriverType(RMT_MAIN|TIMER_MAIN);
|
||||
for (const auto& d: v) DCCTrack::mainTrack.addDriver(d);
|
||||
v = MotorDriverContainer::mDC.getDriverType(RMT_PROG|TIMER_PROG);
|
||||
for (const auto& d: v) DCCTrack::progTrack.addDriver(d);
|
||||
}
|
||||
|
||||
void DCC::setJoinRelayPin(byte joinRelayPin) {
|
||||
@@ -578,7 +583,7 @@ byte DCC::loopStatus=0;
|
||||
|
||||
void DCC::loop() {
|
||||
DCCWaveform::loop(ackManagerProg!=NULL); // power overload checks
|
||||
mDC.loop();
|
||||
MotorDriverContainer::mDC.loop();
|
||||
ackManagerLoop(); // maintain prog track ack manager
|
||||
issueReminders();
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@
|
||||
#include "DIAG.h"
|
||||
#include "DCCRMT.h"
|
||||
#include "DCCWaveform.h" // for MAX_PACKET_SIZE
|
||||
#include "soc/gpio_sig_map.h"
|
||||
|
||||
#define DATA_LEN(X) ((X)*9+1) // Each byte has one bit extra and we have one EOF marker
|
||||
|
||||
@@ -104,6 +105,14 @@ RMTChannel::RMTChannel(byte pin, byte ch, byte plen) {
|
||||
// 2 mem block of 64 RMT items should be enough
|
||||
|
||||
ESP_ERROR_CHECK(rmt_config(&config));
|
||||
/*
|
||||
// test: config another gpio pin
|
||||
gpio_num_t gpioNum = (gpio_num_t)(pin-1);
|
||||
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[gpioNum], PIN_FUNC_GPIO);
|
||||
gpio_set_direction(gpioNum, GPIO_MODE_OUTPUT);
|
||||
gpio_matrix_out(gpioNum, RMT_SIG_OUT0_IDX, 0, 0);
|
||||
*/
|
||||
|
||||
// NOTE: ESP_INTR_FLAG_IRAM is *NOT* included in this bitmask
|
||||
ESP_ERROR_CHECK(rmt_driver_install(config.channel, 0, ESP_INTR_FLAG_LOWMED|ESP_INTR_FLAG_SHARED));
|
||||
|
||||
|
@@ -29,7 +29,7 @@ void DCCTrack::schedulePacket(dccPacket packet) {
|
||||
//DIAG(F("DCCTrack::schedulePacket RMT l=%d d=%x"),packet.length, packet.data[0]);
|
||||
driver->schedulePacket(packet);
|
||||
}
|
||||
if (driver->type() == TIMERINTERRUPT && waveform && once) {
|
||||
if (driver->type() & (TIMER_MAIN | TIMER_PROG) && waveform && once) {
|
||||
//DIAG(F("DCCTrack::schedulePacket WAVE l=%d d=%x"),packet.length, packet.data[0]);
|
||||
waveform->schedulePacket(packet);
|
||||
once=false;
|
||||
|
@@ -3,13 +3,16 @@
|
||||
#include <Arduino.h>
|
||||
#include "DCCPacket.h"
|
||||
#include "DCCWaveform.h"
|
||||
#include "DIAG.h"
|
||||
|
||||
class DCCTrack {
|
||||
public:
|
||||
DCCTrack(DCCWaveform *w);
|
||||
void schedulePacket(const byte buffer[], byte byteCount, byte repeats);
|
||||
void schedulePacket(dccPacket packet);
|
||||
inline void addDriver(MotorDriver *m) { mD.push_back(m); };
|
||||
inline void addDriver(MotorDriver *m) {
|
||||
mD.push_back(m);
|
||||
};
|
||||
static DCCTrack mainTrack;
|
||||
static DCCTrack progTrack;
|
||||
private:
|
||||
|
@@ -27,9 +27,16 @@
|
||||
#include "DIAG.h"
|
||||
#include "freeMemory.h"
|
||||
|
||||
// The two Waveforms which defines what happens when the
|
||||
// interrupt driven DCC signal is generated. This is tied
|
||||
// to the timer interrupts of the hardware.
|
||||
DCCWaveform DCCWaveform::mainTrack(PREAMBLE_BITS_MAIN, true);
|
||||
DCCWaveform DCCWaveform::progTrack(PREAMBLE_BITS_PROG, false);
|
||||
|
||||
|
||||
// The two different DCC _kinds_ of signals we want to be able
|
||||
// to genrate at the same time. When timer interupts are used,
|
||||
// these need the respective waveform
|
||||
DCCTrack DCCTrack::mainTrack(&DCCWaveform::mainTrack);
|
||||
DCCTrack DCCTrack::progTrack(&DCCWaveform::progTrack);
|
||||
|
||||
@@ -42,19 +49,27 @@ uint8_t DCCWaveform::trailingEdgeCounter=0;
|
||||
|
||||
void DCCWaveform::begin(MotorDriver * mainDriver, MotorDriver * progDriver) {
|
||||
|
||||
mainTrack.motorDriver=mainDriver;
|
||||
progTrack.motorDriver=progDriver;
|
||||
progTripValue = progDriver->mA2raw(TRIP_CURRENT_PROG); // need only calculate once hence static
|
||||
mainTrack.setPowerMode(POWERMODE::OFF);
|
||||
progTrack.setPowerMode(POWERMODE::OFF);
|
||||
// Fault pin config for odd motor boards (example pololu)
|
||||
MotorDriver::commonFaultPin = ((mainDriver->getFaultPin() == progDriver->getFaultPin())
|
||||
&& (mainDriver->getFaultPin() != UNUSED_PIN));
|
||||
// Only use PWM if both pins are PWM capable. Otherwise JOIN does not work
|
||||
MotorDriver::usePWM= mainDriver->isPWMCapable() && progDriver->isPWMCapable();
|
||||
DIAG(F("Signal pin config: %S accuracy waveform"),
|
||||
if(mainDriver) {
|
||||
mainTrack.motorDriver=mainDriver;
|
||||
mainTrack.setPowerMode(POWERMODE::OFF);
|
||||
}
|
||||
if(progDriver) {
|
||||
progTrack.motorDriver=progDriver;
|
||||
progTripValue = progDriver->mA2raw(TRIP_CURRENT_PROG); // need only calculate once hence static
|
||||
progTrack.setPowerMode(POWERMODE::OFF);
|
||||
}
|
||||
if(mainDriver && progDriver) {
|
||||
// Fault pin config for odd motor boards (example pololu)
|
||||
MotorDriver::commonFaultPin = ((mainDriver->getFaultPin() == progDriver->getFaultPin())
|
||||
&& (mainDriver->getFaultPin() != UNUSED_PIN));
|
||||
// Only use PWM if both pins are PWM capable. Otherwise JOIN does not work
|
||||
MotorDriver::usePWM= mainDriver->isPWMCapable() && progDriver->isPWMCapable();
|
||||
}
|
||||
if(mainDriver || progDriver) {
|
||||
DIAG(F("Signal pin config: %S accuracy waveform"),
|
||||
MotorDriver::usePWM ? F("high") : F("normal") );
|
||||
DCCTimer::begin(DCCWaveform::interruptHandler);
|
||||
}
|
||||
DCCTimer::begin(DCCWaveform::interruptHandler);
|
||||
}
|
||||
|
||||
#ifdef SLOW_ANALOG_READ
|
||||
|
2
FSH.h
2
FSH.h
@@ -41,6 +41,7 @@
|
||||
typedef char FSH;
|
||||
#define GETFLASH(addr) (*(const unsigned char *)(addr))
|
||||
#define GETFLASHW(addr) (*(const unsigned short *)(addr))
|
||||
#define GETFLASHP(addr) (*(void * const *)(addr))
|
||||
#define FLASH
|
||||
#define strlen_P strlen
|
||||
#define strcpy_P strcpy
|
||||
@@ -48,6 +49,7 @@ typedef char FSH;
|
||||
typedef __FlashStringHelper FSH;
|
||||
#define GETFLASH(addr) pgm_read_byte_near(addr)
|
||||
#define GETFLASHW(addr) pgm_read_word_near(addr)
|
||||
#define GETFLASHP(addr) pgm_read_ptr_near(addr)
|
||||
#define FLASH PROGMEM
|
||||
#endif
|
||||
#endif
|
||||
|
@@ -1 +1 @@
|
||||
#define GITHUB_SHA "ESP32-motordriver-2021122-23:21"
|
||||
#define GITHUB_SHA "ESP32-motordriver-2021129-00:12"
|
||||
|
@@ -44,7 +44,7 @@ MotorDriver::MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, int8
|
||||
rmtChannel = new RMTChannel(signalPin, 0, PREAMBLE_BITS_MAIN);
|
||||
#endif
|
||||
dualSignal=false;
|
||||
} else if (dtype == TIMERINTERRUPT) {
|
||||
} else if (dtype & (TIMER_MAIN | TIMER_PROG)) {
|
||||
signalPin=signal_pin;
|
||||
getFastPin(F("SIG"),signalPin,fastSignalPin);
|
||||
pinMode(signalPin, OUTPUT);
|
||||
@@ -212,8 +212,9 @@ bool MotorDriver::schedulePacket(dccPacket packet) {
|
||||
if(!rmtChannel) return true; // fake success if functionality is not there
|
||||
|
||||
outQueue.push(packet);
|
||||
if (outQueue.size() > 10) {
|
||||
DIAG(F("Warning: outQueue > 10"));
|
||||
uint16_t size = outQueue.size();
|
||||
if (size > 10) {
|
||||
DIAG(F("Warning: outQueue %d > 10"),size);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -232,26 +233,43 @@ MotorDriverContainer::MotorDriverContainer(const FSH * motorShieldName,
|
||||
MotorDriver *m5,
|
||||
MotorDriver *m6,
|
||||
MotorDriver *m7) {
|
||||
mD[0]=m0;
|
||||
mD[1]=m1;
|
||||
mD[2]=m2;
|
||||
mD[3]=m3;
|
||||
mD[4]=m4;
|
||||
mD[5]=m5;
|
||||
mD[6]=m6;
|
||||
mD[7]=m7;
|
||||
// THIS AUTOMATIC DOES NOT WORK YET. TIMER_MAIN AND TIMER_PROG required in CONSTRUCTOR
|
||||
// AND CAN NOT BE ADDED LATER
|
||||
if (m0) {
|
||||
if (m0->type() == TYPE_UNKNOWN)
|
||||
m0->setType(TIMER_MAIN);
|
||||
mD.push_back(m0);
|
||||
}
|
||||
if (m1) {
|
||||
if (m1->type() == TYPE_UNKNOWN)
|
||||
m1->setType(TIMER_PROG);
|
||||
mD.push_back(m1);
|
||||
}
|
||||
if (m2) mD.push_back(m2);
|
||||
if (m3) mD.push_back(m3);
|
||||
if (m4) mD.push_back(m4);
|
||||
if (m5) mD.push_back(m5);
|
||||
if (m6) mD.push_back(m6);
|
||||
if (m7) mD.push_back(m7);
|
||||
shieldName = (FSH *)motorShieldName;
|
||||
}
|
||||
|
||||
void MotorDriverContainer::loop() {
|
||||
static byte i = 0;
|
||||
|
||||
// loops over MotorDrivers which have loop tasks
|
||||
if (mD[i])
|
||||
if (mD[i]->type() == RMT_MAIN || mD[i]->type() == RMT_PROG)
|
||||
mD[i]->loop();
|
||||
i++;
|
||||
if(i > 7) i=0;
|
||||
if (mD.empty())
|
||||
return;
|
||||
for(const auto& d: mD)
|
||||
if (d->type() & (RMT_MAIN | RMT_PROG))
|
||||
d->loop();
|
||||
}
|
||||
|
||||
MotorDriverContainer mDC(MOTOR_SHIELD_TYPE);
|
||||
std::vector<MotorDriver*> MotorDriverContainer::getDriverType(driverType t) {
|
||||
std::vector<MotorDriver*> v;
|
||||
for(const auto& d: mD){
|
||||
if (d->type() & t)
|
||||
v.push_back(d);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
MotorDriverContainer MotorDriverContainer::mDC(MOTOR_SHIELD_TYPE);
|
||||
|
@@ -19,8 +19,10 @@
|
||||
|
||||
#ifndef MotorDriver_h
|
||||
#define MotorDriver_h
|
||||
#include <vector>
|
||||
#include "defines.h"
|
||||
#include "FSH.h"
|
||||
#include "DIAG.h"
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
#include <queue>
|
||||
@@ -56,13 +58,20 @@ struct FASTPIN {
|
||||
#define isHIGH(fastpin) (*fastpin.inout & fastpin.maskHIGH)
|
||||
#define isLOW(fastpin) (!isHIGH(fastpin))
|
||||
|
||||
enum driverType { TIMERINTERRUPT, RMT_MAIN, RMT_PROG, DC_ENA, DC_BRAKE };
|
||||
typedef byte driverType;
|
||||
const driverType TYPE_UNKNOWN=0;
|
||||
const driverType TIMER_MAIN=1;
|
||||
const driverType TIMER_PROG=2;
|
||||
const driverType RMT_MAIN=4;
|
||||
const driverType RMT_PROG=16;
|
||||
const driverType DC_ENA=32;
|
||||
const driverType DC_BRAKE=64;
|
||||
|
||||
class MotorDriver {
|
||||
public:
|
||||
MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, int8_t brake_pin,
|
||||
byte current_pin, float senseFactor, unsigned int tripMilliamps, byte faultPin,
|
||||
driverType t=TIMERINTERRUPT);
|
||||
driverType t=TYPE_UNKNOWN);
|
||||
void setPower( bool on);
|
||||
void setSignal( bool high);
|
||||
void setBrake( bool on);
|
||||
@@ -82,6 +91,7 @@ class MotorDriver {
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
void loop();
|
||||
inline driverType type() { return dtype; };
|
||||
inline void setType(driverType t) { dtype = t; };
|
||||
bool schedulePacket(dccPacket packet);
|
||||
#endif
|
||||
|
||||
@@ -127,18 +137,35 @@ public:
|
||||
MotorDriver *m5=NULL,
|
||||
MotorDriver *m6=NULL,
|
||||
MotorDriver *m7=NULL);
|
||||
inline void add(byte n, MotorDriver *m) {
|
||||
if (n>8) return;
|
||||
mD[n] = m;
|
||||
static MotorDriverContainer mDC;
|
||||
inline void add(MotorDriver *m) {
|
||||
mD.push_back(m);
|
||||
};
|
||||
// void SetCapability(byte n, byte cap, char [] name);
|
||||
inline FSH *getMotorShieldName() { return shieldName; };
|
||||
inline MotorDriver *mainTrack() { return mD[0]; }; //start fixed
|
||||
inline MotorDriver *progTrack() { return mD[1]; };
|
||||
inline void diag() {
|
||||
if (mD.empty()) {
|
||||
DIAG(F("Container empty"));
|
||||
return;
|
||||
}
|
||||
for(const auto& d: mD)
|
||||
DIAG(F("Container: mDType=%d"),d->type());
|
||||
};
|
||||
inline MotorDriver *mainTrack() {
|
||||
std::vector<MotorDriver *> v = getDriverType(TIMER_MAIN);
|
||||
if(v.empty()) return NULL;
|
||||
return v.front();
|
||||
};
|
||||
inline MotorDriver *progTrack() {
|
||||
std::vector<MotorDriver *> v = getDriverType(TIMER_PROG);
|
||||
if(v.empty()) return NULL;
|
||||
return v.front();
|
||||
};
|
||||
void loop();
|
||||
std::vector<MotorDriver*> getDriverType(driverType t);
|
||||
|
||||
private:
|
||||
MotorDriver *mD[8];
|
||||
std::vector<MotorDriver *>mD;
|
||||
FSH *shieldName;
|
||||
};
|
||||
#endif
|
||||
|
@@ -137,7 +137,7 @@ void SSD1306AsciiWire::begin(const DevType* dev, uint8_t i2cAddr) {
|
||||
m_i2cAddr = i2cAddr;
|
||||
m_col = 0;
|
||||
m_row = 0;
|
||||
const uint8_t* table = (const uint8_t*)GETFLASHW(&dev->initcmds);
|
||||
const uint8_t* table = (const uint8_t*)GETFLASHP(&dev->initcmds);
|
||||
uint8_t size = GETFLASH(&dev->initSize);
|
||||
m_displayWidth = GETFLASH(&dev->lcdWidth);
|
||||
m_displayHeight = GETFLASH(&dev->lcdHeight);
|
||||
|
@@ -45,9 +45,12 @@ The configuration file for DCC-EX Command Station
|
||||
|
||||
// https://randomnerdtutorials.com/esp8266-pinout-reference-gpios/
|
||||
// 4 high at boot
|
||||
#define ESP8266_MOTOR_SHIELD F("ESP8266"), \
|
||||
new MotorDriver(D3, D5, UNUSED_PIN, UNUSED_PIN, UNUSED_PIN, 2.99, 2000, UNUSED_PIN),\
|
||||
new MotorDriver(D2, D6, UNUSED_PIN, UNUSED_PIN, A0 , 2.99, 2000, UNUSED_PIN)
|
||||
// BUG: WE STILL NEED AT LEAST ONE TIMER_* motor shield defined, otherwise the packet scheduling does not work
|
||||
// BUG: WE DO NOT HAVE ANY RMT_PROG CAPABILITY yet.
|
||||
#define ESP32_MOTOR_SHIELD F("ESP32"), \
|
||||
NULL /*new MotorDriver(16, 17, UNUSED_PIN, UNUSED_PIN, 36, 2.00, 2000, UNUSED_PIN, TIMER_MAIN)*/, \
|
||||
new MotorDriver(18, 19, UNUSED_PIN, UNUSED_PIN, 39, 2.00, 2000, UNUSED_PIN, TIMER_PROG), \
|
||||
new MotorDriver(16, 23, UNUSED_PIN, UNUSED_PIN, UNUSED_PIN, 2.00, 2000, UNUSED_PIN, RMT_MAIN)
|
||||
|
||||
// ESP32 ADC1 only supported GPIO pins 32 to 39, for example
|
||||
// ADC1 CH4 = GPIO32, ADC1 CH5 = GPIO33, ADC1 CH0 = GPIO36
|
||||
|
Reference in New Issue
Block a user