1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2025-02-18 15:06:03 +01:00

Abstracted Hardware Interface

The hardware interface for all pins and timers is now in Hardware.cpp
This commit is contained in:
Asbelos 2020-05-26 12:44:02 +01:00
parent 28be07610a
commit 22406b44ed
7 changed files with 239 additions and 210 deletions

View File

@ -3,22 +3,22 @@
#include "JMRIParser.h" #include "JMRIParser.h"
/* this code is here to test the waveforwe generator and reveal the issues involved in programming track operations. /* this code is here to test the waveforwe generator and reveal the issues involved in programming track operations.
*
* It tests the Waveform genartor and demonstrates how a DCC API function can be simply written It tests the Waveform genartor and demonstrates how a DCC API function can be simply written
* to transmit and receive DCC data on the programming track. to transmit and receive DCC data on the programming track.
*
* Important... DCCWaveform.h contains hardware specific confioguration settings Once soem CVs have been listed, it then drops into JMRI input moce so you can play.
* that you will need to check.
* Important... Config.h contains hardware specific confioguration settings
* Notes: the waveform generator sends reset packets on progTrack when it has nothing better to do, this means that that you will need to check.
* the decoder does not step out of service mode. (The main track sends idles).
* It also means that the API functions dont have to mess with reset packets.
*
*/ */
const int cvnums[]={1,2,3,4,5,17,18,19,21,22,29}; const int cvnums[] = {1, 2, 3, 4, 5, 8, 17, 18, 19, 21, 22, 29};
void setup() { void setup() {
Serial.begin(115200); Serial.begin(115200);
@ -40,4 +40,3 @@ void loop() {
// This line passes input on Serial to the JMRIparser // This line passes input on Serial to the JMRIparser
StringParser::loop(Serial, JMRIParser::parse); StringParser::loop(Serial, JMRIParser::parse);
} }

13
Config.h Normal file
View File

@ -0,0 +1,13 @@
// This hardware configuration would normally be setup using a bunch of #ifdefs.
const byte MAIN_POWER_PIN = 3;
const byte MAIN_SIGNAL_PIN = 12;
const byte MAIN_SIGNAL_PIN_ALT = 0; // for hardware that flipflops signal pins
const byte MAIN_SENSE_PIN = A0;
const byte MAIN_SENSE_FACTOR=1; // analgRead(MAIN_SENSE_PIN) * MAIN_SENSE_FACTOR = milliamps
const byte PROG_POWER_PIN = 11;
const byte PROG_SIGNAL_PIN = 13;
const byte PROG_SIGNAL_PIN_ALT = 0; // for hardware that flipflops signal pins
const byte PROG_SENSE_PIN = A1;
const float PROG_SENSE_FACTOR=1; // analgRead(PROG_SENSE_PIN) * PROG_SENSE_FACTOR = milliamps

View File

@ -1,16 +1,13 @@
#include <Arduino.h> #include <Arduino.h>
#include <TimerThree.h> #include "Hardware.h"
#include "DCCWaveform.h" #include "DCCWaveform.h"
#include "DIAG.h" #include "DIAG.h"
DCCWaveform DCCWaveform::mainTrack(MAIN_POWER_PIN,MAIN_SIGNAL_PIN,MAIN_SENSE_PIN,PREAMBLE_BITS_MAIN,true); DCCWaveform DCCWaveform::mainTrack(PREAMBLE_BITS_MAIN, true);
DCCWaveform DCCWaveform::progTrack(PROG_POWER_PIN,PROG_SIGNAL_PIN,PROG_SENSE_PIN,PREAMBLE_BITS_PROG,false); DCCWaveform DCCWaveform::progTrack(PREAMBLE_BITS_PROG, false);
void DCCWaveform::begin() { void DCCWaveform::begin() {
Hardware::init();
Timer3.initialize(58); Hardware::setCallback(58, interruptHandler);
Timer3.disablePwm(MAIN_SIGNAL_PIN);
Timer3.disablePwm(PROG_SIGNAL_PIN);
Timer3.attachInterrupt(interruptHandler);
mainTrack.beginTrack(); mainTrack.beginTrack();
progTrack.beginTrack(); progTrack.beginTrack();
} }
@ -46,11 +43,8 @@ void DCCWaveform::interruptHandler() {
const byte bitMask[] = {0x00, 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}; const byte bitMask[] = {0x00, 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
DCCWaveform::DCCWaveform(byte powerPinNo, byte directionPinNo, byte sensePinNo, byte preambleBits, bool isMain) { DCCWaveform::DCCWaveform( byte preambleBits, bool isMain) {
// establish appropriate pins // establish appropriate pins
powerPin=Arduino_to_GPIO_pin(powerPinNo);
directionPin=Arduino_to_GPIO_pin(directionPinNo);
sensePin=sensePinNo;
isMainTrack = isMain; isMainTrack = isMain;
packetPending = false; packetPending = false;
memcpy(transmitPacket, idlePacket, sizeof(idlePacket)); memcpy(transmitPacket, idlePacket, sizeof(idlePacket));
@ -62,18 +56,7 @@ DCCWaveform::DCCWaveform(byte powerPinNo, byte directionPinNo, byte sensePinNo,
} }
void DCCWaveform::beginTrack() { void DCCWaveform::beginTrack() {
pinMode2f(powerPin,OUTPUT);
pinMode2f(directionPin,OUTPUT);
pinMode(sensePin,INPUT);
if (isMainTrack && RAILCOM_CUTOUT) {
railcomBrakePin=Arduino_to_GPIO_pin(RAILCOM_BRAKE_PIN);
pinMode2f(railcomBrakePin, OUTPUT);
digitalWrite2f(railcomBrakePin, LOW);
}
setPowerMode(POWERMODE::ON); setPowerMode(POWERMODE::ON);
DIAG(F("\nTrack started sensePin=%d\n"),sensePin);
} }
POWERMODE DCCWaveform::getPowerMode() { POWERMODE DCCWaveform::getPowerMode() {
@ -82,7 +65,7 @@ DCCWaveform::DCCWaveform(byte powerPinNo, byte directionPinNo, byte sensePinNo,
void DCCWaveform::setPowerMode(POWERMODE mode) { void DCCWaveform::setPowerMode(POWERMODE mode) {
powerMode = mode; powerMode = mode;
digitalWrite2f(powerPin, mode==POWERMODE::ON ? HIGH:LOW); Hardware::setPower(isMainTrack, mode == POWERMODE::ON);
if (mode == POWERMODE::ON) delay(200); if (mode == POWERMODE::ON) delay(200);
} }
@ -98,11 +81,11 @@ void DCCWaveform::checkPowerOverload() {
break; break;
case POWERMODE::ON: case POWERMODE::ON:
// Check current // Check current
current=analogRead(sensePin); current = Hardware::getCurrentMilliamps(isMainTrack);
if (current < POWER_SAMPLE_MAX) delay = POWER_SAMPLE_ON_WAIT; if (current < POWER_SAMPLE_MAX) delay = POWER_SAMPLE_ON_WAIT;
else { else {
setPowerMode(POWERMODE::OVERLOAD); setPowerMode(POWERMODE::OVERLOAD);
DIAG(F("\n*** %s TRACK POWER OVERLOAD pin=%d current=%d max=%d ***\n"),isMainTrack?"MAIN":"PROG",sensePin,current,POWER_SAMPLE_MAX); DIAG(F("\n*** %s TRACK POWER OVERLOAD current=%d max=%d ***\n"), isMainTrack ? "MAIN" : "PROG", current, POWER_SAMPLE_MAX);
delay = POWER_SAMPLE_OVERLOAD_WAIT; delay = POWER_SAMPLE_OVERLOAD_WAIT;
} }
break; break;
@ -128,20 +111,20 @@ bool DCCWaveform::interrupt1() {
// otherwise can cause hangs in main loop waiting for the pendingBuffer. // otherwise can cause hangs in main loop waiting for the pendingBuffer.
switch (state) { switch (state) {
case 0: // start of bit transmission case 0: // start of bit transmission
digitalWrite2f(directionPin, HIGH); Hardware::setSignal(isMainTrack, HIGH);
checkRailcom(); checkRailcom();
state = 1; state = 1;
return true; // must call interrupt2 to set currentBit return true; // must call interrupt2 to set currentBit
case 1: // 58us after case 0 case 1: // 58us after case 0
if (currentBit) { if (currentBit) {
digitalWrite2f(directionPin, LOW); Hardware::setSignal(isMainTrack, LOW);
state = 0; state = 0;
} }
else state = 2; else state = 2;
break; break;
case 2: // 116us after case 0 case 2: // 116us after case 0
digitalWrite2f(directionPin, LOW); Hardware::setSignal(isMainTrack, LOW);
state = 3; state = 3;
break; break;
case 3: // finished sending zero bit case 3: // finished sending zero bit
@ -202,10 +185,7 @@ void DCCWaveform::checkRailcom() {
if (isMainTrack && RAILCOM_CUTOUT) { if (isMainTrack && RAILCOM_CUTOUT) {
byte preamble = PREAMBLE_BITS_MAIN - remainingPreambles; byte preamble = PREAMBLE_BITS_MAIN - remainingPreambles;
if (preamble == RAILCOM_PREAMBLES_BEFORE_CUTOUT) { if (preamble == RAILCOM_PREAMBLES_BEFORE_CUTOUT) {
digitalWrite2f(railcomBrakePin,HIGH); // TODO Railcom::startCutout();
}
else if (preamble== RAILCOM_PREAMBLES_BEFORE_CUTOUT+RAILCOM_PREAMBLES_SKIPPED_IN_CUTOUT) {
digitalWrite2f(railcomBrakePin,LOW);
} }
} }
} }
@ -243,7 +223,7 @@ bool DCCWaveform::getAck()
// Monitor looking for a reading high enough to be an ack // Monitor looking for a reading high enough to be an ack
while (result == false && timeout > millis()) { while (result == false && timeout > millis()) {
upsamples++; upsamples++;
int current=analogRead(sensePin); int current = Hardware::getCurrentMilliamps(isMainTrack);
maxCurrent = max(maxCurrent, current); maxCurrent = max(maxCurrent, current);
result = current > ACK_MIN_PULSE; result = current > ACK_MIN_PULSE;
} }
@ -251,7 +231,7 @@ bool DCCWaveform::getAck()
// Monitor current until ack signal dies back // Monitor current until ack signal dies back
if (result) while ( true) { if (result) while ( true) {
downsamples++; downsamples++;
int current=analogRead(sensePin); int current = Hardware::getCurrentMilliamps(isMainTrack);
maxCurrent = max(maxCurrent, current); maxCurrent = max(maxCurrent, current);
if (current <= ACK_MAX_NOT_PULSE) break; if (current <= ACK_MAX_NOT_PULSE) break;
} }

View File

@ -1,12 +1,5 @@
#include <DIO2.h>
// This hardware configuration would normally be setup in a .h file elsewhere
const byte MAIN_POWER_PIN = 3;
const byte MAIN_SIGNAL_PIN = 12;
const byte MAIN_SENSE_PIN = A0;
const byte PROG_POWER_PIN = 11;
const byte PROG_SIGNAL_PIN = 13;
const byte PROG_SENSE_PIN = A1;
const int POWER_SAMPLE_MAX = 300; const int POWER_SAMPLE_MAX = 300;
const int POWER_SAMPLE_ON_WAIT = 100; const int POWER_SAMPLE_ON_WAIT = 100;
@ -36,14 +29,13 @@ const byte MAX_PACKET_SIZE = 12;
enum class POWERMODE { OFF, ON, OVERLOAD }; enum class POWERMODE { OFF, ON, OVERLOAD };
const byte idleMessage[] = {0xFF, 0x00};
const byte resetMessage[] = {0x00, 0x00};
const byte idlePacket[] = {0xFF, 0x00, 0xFF}; const byte idlePacket[] = {0xFF, 0x00, 0xFF};
const byte resetPacket[] = {0x00, 0x00, 0x00}; const byte resetPacket[] = {0x00, 0x00, 0x00};
class DCCWaveform { class DCCWaveform {
public: public:
DCCWaveform(byte powerPinNo, byte directionPinNo, byte sensePinNo, byte preambleBits, bool isMain); DCCWaveform( byte preambleBits, bool isMain);
static void begin(); static void begin();
static void loop(); static void loop();
static DCCWaveform mainTrack; static DCCWaveform mainTrack;
@ -83,15 +75,8 @@ class DCCWaveform {
byte pendingLength; byte pendingLength;
byte pendingRepeats; byte pendingRepeats;
// Hardware fast pins
GPIO_pin_t directionPin;
GPIO_pin_t powerPin;
GPIO_pin_t railcomBrakePin;
// current sampling // current sampling
POWERMODE powerMode; POWERMODE powerMode;
byte sensePin;
unsigned long nextSampleDue; unsigned long nextSampleDue;
}; };

42
Hardware.cpp Normal file
View File

@ -0,0 +1,42 @@
#include <Arduino.h>
#include <TimerThree.h>
#include "Hardware.h"
#include "Config.h"
void Hardware::init() {
pinMode(MAIN_POWER_PIN, OUTPUT);
pinMode(MAIN_SIGNAL_PIN, OUTPUT);
if (MAIN_SIGNAL_PIN_ALT) pinMode(MAIN_SIGNAL_PIN_ALT, OUTPUT);
pinMode(MAIN_SENSE_PIN, INPUT);
pinMode(PROG_POWER_PIN, OUTPUT);
pinMode(PROG_SIGNAL_PIN, OUTPUT);
if (PROG_SIGNAL_PIN_ALT) pinMode(PROG_SIGNAL_PIN_ALT, OUTPUT);
pinMode(PROG_SENSE_PIN, INPUT);
}
void Hardware::setPower(bool isMainTrack, bool on) {
digitalWrite(isMainTrack ? MAIN_POWER_PIN : PROG_POWER_PIN, on ? HIGH : LOW);
}
void Hardware::setSignal(bool isMainTrack, bool high) {
byte pin = isMainTrack ? MAIN_SIGNAL_PIN : PROG_SIGNAL_PIN;
byte pin2 = isMainTrack ? MAIN_SIGNAL_PIN_ALT : PROG_SIGNAL_PIN_ALT;
digitalWrite(pin, high ? HIGH : LOW);
if (pin2) digitalWrite(pin2, high ? LOW : HIGH);
}
int Hardware::getCurrentMilliamps(bool isMainTrack) {
int pin = isMainTrack ? MAIN_SENSE_PIN : PROG_SENSE_PIN;
float factor = isMainTrack ? MAIN_SENSE_FACTOR : PROG_SENSE_FACTOR;
int rawCurrent = analogRead(pin);
return (int)(rawCurrent * factor);
}
void Hardware::setCallback(int duration, void (*isr)()) {
Timer3.initialize(duration);
Timer3.disablePwm(TIMER3_A_PIN);
Timer3.disablePwm(TIMER3_B_PIN);
Timer3.attachInterrupt(isr);
}

10
Hardware.h Normal file
View File

@ -0,0 +1,10 @@
// Virtualised hardware Interface
class Hardware {
public:
static void init();
static void setPower(bool isMainTrack, bool on);
static void setSignal(bool isMainTrack, bool high);
static int getCurrentMilliamps(bool isMainTrack);
static void setCallback(int duration, void (*isr)());
};