1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2024-11-26 17:46:14 +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"
/* 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
* to transmit and receive DCC data on the programming track.
*
* Important... DCCWaveform.h contains hardware specific confioguration settings
* that you will need to check.
*
* Notes: the waveform generator sends reset packets on progTrack when it has nothing better to do, this means that
* 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.
*
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.
Once soem CVs have been listed, it then drops into JMRI input moce so you can play.
Important... Config.h contains hardware specific confioguration settings
that you will need to check.
*/
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() {
Serial.begin(115200);
@ -40,4 +40,3 @@ void loop() {
// This line passes input on Serial to the JMRIparser
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 <TimerThree.h>
#include "Hardware.h"
#include "DCCWaveform.h"
#include "DIAG.h"
DCCWaveform DCCWaveform::mainTrack(MAIN_POWER_PIN,MAIN_SIGNAL_PIN,MAIN_SENSE_PIN,PREAMBLE_BITS_MAIN,true);
DCCWaveform DCCWaveform::progTrack(PROG_POWER_PIN,PROG_SIGNAL_PIN,PROG_SENSE_PIN,PREAMBLE_BITS_PROG,false);
DCCWaveform DCCWaveform::mainTrack(PREAMBLE_BITS_MAIN, true);
DCCWaveform DCCWaveform::progTrack(PREAMBLE_BITS_PROG, false);
void DCCWaveform::begin() {
Timer3.initialize(58);
Timer3.disablePwm(MAIN_SIGNAL_PIN);
Timer3.disablePwm(PROG_SIGNAL_PIN);
Timer3.attachInterrupt(interruptHandler);
Hardware::init();
Hardware::setCallback(58, interruptHandler);
mainTrack.beginTrack();
progTrack.beginTrack();
}
@ -46,11 +43,8 @@ void DCCWaveform::interruptHandler() {
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
powerPin=Arduino_to_GPIO_pin(powerPinNo);
directionPin=Arduino_to_GPIO_pin(directionPinNo);
sensePin=sensePinNo;
isMainTrack = isMain;
packetPending = false;
memcpy(transmitPacket, idlePacket, sizeof(idlePacket));
@ -62,18 +56,7 @@ DCCWaveform::DCCWaveform(byte powerPinNo, byte directionPinNo, byte sensePinNo,
}
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);
DIAG(F("\nTrack started sensePin=%d\n"),sensePin);
}
POWERMODE DCCWaveform::getPowerMode() {
@ -82,7 +65,7 @@ DCCWaveform::DCCWaveform(byte powerPinNo, byte directionPinNo, byte sensePinNo,
void DCCWaveform::setPowerMode(POWERMODE mode) {
powerMode = mode;
digitalWrite2f(powerPin, mode==POWERMODE::ON ? HIGH:LOW);
Hardware::setPower(isMainTrack, mode == POWERMODE::ON);
if (mode == POWERMODE::ON) delay(200);
}
@ -98,11 +81,11 @@ void DCCWaveform::checkPowerOverload() {
break;
case POWERMODE::ON:
// Check current
current=analogRead(sensePin);
current = Hardware::getCurrentMilliamps(isMainTrack);
if (current < POWER_SAMPLE_MAX) delay = POWER_SAMPLE_ON_WAIT;
else {
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;
}
break;
@ -128,20 +111,20 @@ bool DCCWaveform::interrupt1() {
// otherwise can cause hangs in main loop waiting for the pendingBuffer.
switch (state) {
case 0: // start of bit transmission
digitalWrite2f(directionPin, HIGH);
Hardware::setSignal(isMainTrack, HIGH);
checkRailcom();
state = 1;
return true; // must call interrupt2 to set currentBit
case 1: // 58us after case 0
if (currentBit) {
digitalWrite2f(directionPin, LOW);
Hardware::setSignal(isMainTrack, LOW);
state = 0;
}
else state = 2;
break;
case 2: // 116us after case 0
digitalWrite2f(directionPin, LOW);
Hardware::setSignal(isMainTrack, LOW);
state = 3;
break;
case 3: // finished sending zero bit
@ -202,10 +185,7 @@ void DCCWaveform::checkRailcom() {
if (isMainTrack && RAILCOM_CUTOUT) {
byte preamble = PREAMBLE_BITS_MAIN - remainingPreambles;
if (preamble == RAILCOM_PREAMBLES_BEFORE_CUTOUT) {
digitalWrite2f(railcomBrakePin,HIGH);
}
else if (preamble== RAILCOM_PREAMBLES_BEFORE_CUTOUT+RAILCOM_PREAMBLES_SKIPPED_IN_CUTOUT) {
digitalWrite2f(railcomBrakePin,LOW);
// TODO Railcom::startCutout();
}
}
}
@ -243,7 +223,7 @@ bool DCCWaveform::getAck()
// Monitor looking for a reading high enough to be an ack
while (result == false && timeout > millis()) {
upsamples++;
int current=analogRead(sensePin);
int current = Hardware::getCurrentMilliamps(isMainTrack);
maxCurrent = max(maxCurrent, current);
result = current > ACK_MIN_PULSE;
}
@ -251,7 +231,7 @@ bool DCCWaveform::getAck()
// Monitor current until ack signal dies back
if (result) while ( true) {
downsamples++;
int current=analogRead(sensePin);
int current = Hardware::getCurrentMilliamps(isMainTrack);
maxCurrent = max(maxCurrent, current);
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_ON_WAIT = 100;
@ -36,14 +29,13 @@ const byte MAX_PACKET_SIZE = 12;
enum class POWERMODE { OFF, ON, OVERLOAD };
const byte idleMessage[] = {0xFF, 0x00};
const byte resetMessage[] = {0x00, 0x00};
const byte idlePacket[] = {0xFF, 0x00, 0xFF};
const byte resetPacket[] = {0x00, 0x00, 0x00};
class DCCWaveform {
public:
DCCWaveform(byte powerPinNo, byte directionPinNo, byte sensePinNo, byte preambleBits, bool isMain);
DCCWaveform( byte preambleBits, bool isMain);
static void begin();
static void loop();
static DCCWaveform mainTrack;
@ -83,15 +75,8 @@ class DCCWaveform {
byte pendingLength;
byte pendingRepeats;
// Hardware fast pins
GPIO_pin_t directionPin;
GPIO_pin_t powerPin;
GPIO_pin_t railcomBrakePin;
// current sampling
POWERMODE powerMode;
byte sensePin;
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)());
};