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:
parent
28be07610a
commit
22406b44ed
43
CVReader.ino
43
CVReader.ino
|
@ -3,41 +3,40 @@
|
|||
#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);
|
||||
DCC::begin();
|
||||
|
||||
|
||||
DIAG(F("\n===== CVReader begin ==============================\n"));
|
||||
|
||||
for (byte x=0;x<sizeof(cvnums)/sizeof(cvnums[0]);x++) {
|
||||
int value=DCC::readCV(cvnums[x]);
|
||||
DIAG(F("\nCV %d = %d 0x%x %s\n"),cvnums[x],value,value, value>=0?" VERIFIED OK":"FAILED VERIFICATION");
|
||||
|
||||
for (byte x = 0; x < sizeof(cvnums) / sizeof(cvnums[0]); x++) {
|
||||
int value = DCC::readCV(cvnums[x]);
|
||||
DIAG(F("\nCV %d = %d 0x%x %s\n"), cvnums[x], value, value, value >= 0 ? " VERIFIED OK" : "FAILED VERIFICATION");
|
||||
}
|
||||
DIAG(F("\n===== CVReader done ==============================\n"));
|
||||
DIAG(F("\n===== CVReader done ==============================\n"));
|
||||
DIAG(F("\nReady for JMRI commands\n"));
|
||||
}
|
||||
|
||||
void loop() {
|
||||
DCC::loop(); // required to keep locos running and check powwer
|
||||
|
||||
// This line passes input on Serial to the JMRIparser
|
||||
|
||||
// This line passes input on Serial to the JMRIparser
|
||||
StringParser::loop(Serial, JMRIParser::parse);
|
||||
}
|
||||
|
||||
|
|
13
Config.h
Normal file
13
Config.h
Normal 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
|
4
DCC.cpp
4
DCC.cpp
|
@ -182,7 +182,7 @@ byte DCC::cv2(int cv) {
|
|||
|
||||
bool DCC::verifyCV(int cv, byte value) {
|
||||
byte message[] = { cv1(0x74, cv), cv2(cv), value};
|
||||
DIAG(F("\n\nVerifying cv %d = %d"),cv, value);
|
||||
DIAG(F("\n\nVerifying cv %d = %d"), cv, value);
|
||||
DCCWaveform::progTrack.schedulePacket(message, sizeof(message), 5);
|
||||
return DCCWaveform::progTrack.getAck();
|
||||
}
|
||||
|
@ -206,4 +206,4 @@ void DCC::updateLocoReminder(int loco, byte tSpeed, bool forward) {
|
|||
speedTable[reg].forward = forward;
|
||||
}
|
||||
DCC::LOCO DCC::speedTable[MAX_LOCOS];
|
||||
int DCC::nextLoco=0;
|
||||
int DCC::nextLoco = 0;
|
||||
|
|
314
DCCWaveform.cpp
314
DCCWaveform.cpp
|
@ -1,120 +1,103 @@
|
|||
#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);
|
||||
mainTrack.beginTrack();
|
||||
progTrack.beginTrack();
|
||||
Hardware::init();
|
||||
Hardware::setCallback(58, interruptHandler);
|
||||
mainTrack.beginTrack();
|
||||
progTrack.beginTrack();
|
||||
}
|
||||
|
||||
void DCCWaveform::loop() {
|
||||
mainTrack.checkPowerOverload();
|
||||
progTrack.checkPowerOverload();
|
||||
}
|
||||
|
||||
|
||||
void DCCWaveform::loop() {
|
||||
mainTrack.checkPowerOverload();
|
||||
progTrack.checkPowerOverload();
|
||||
}
|
||||
|
||||
|
||||
// static //
|
||||
void DCCWaveform::interruptHandler() {
|
||||
// call the timer edge sensitive actions for progtrack and maintrack
|
||||
bool mainCall2=mainTrack.interrupt1();
|
||||
bool progCall2=progTrack.interrupt1();
|
||||
|
||||
// call (if necessary) the procs to get the current bits
|
||||
// these must complete within 50microsecs of the interrupt
|
||||
// but they are only called ONCE PER BIT TRANSMITTED
|
||||
// after the rising edge of the signal
|
||||
if (mainCall2) mainTrack.interrupt2();
|
||||
if (progCall2) progTrack.interrupt2();
|
||||
// call the timer edge sensitive actions for progtrack and maintrack
|
||||
bool mainCall2 = mainTrack.interrupt1();
|
||||
bool progCall2 = progTrack.interrupt1();
|
||||
|
||||
// call (if necessary) the procs to get the current bits
|
||||
// these must complete within 50microsecs of the interrupt
|
||||
// but they are only called ONCE PER BIT TRANSMITTED
|
||||
// after the rising edge of the signal
|
||||
if (mainCall2) mainTrack.interrupt2();
|
||||
if (progCall2) progTrack.interrupt2();
|
||||
}
|
||||
|
||||
|
||||
// An instance of this class handles the DCC transmissions for one track. (main or prog)
|
||||
// Interrupts are marshalled via the statics.
|
||||
// A track has a current transmit buffer, and a pending buffer.
|
||||
// When the current buffer is exhausted, either the pending buffer (if there is one waiting) or an idle buffer.
|
||||
// When the current buffer is exhausted, either the pending buffer (if there is one waiting) or an idle buffer.
|
||||
|
||||
|
||||
// This bitmask has 9 entries as each byte is trasmitted as a zero + 8 bits.
|
||||
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) {
|
||||
// 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));
|
||||
state=0;
|
||||
requiredPreambles=preambleBits;
|
||||
bytes_sent=0;
|
||||
bits_sent=0;
|
||||
nextSampleDue=0;
|
||||
|
||||
DCCWaveform::DCCWaveform( byte preambleBits, bool isMain) {
|
||||
// establish appropriate pins
|
||||
isMainTrack = isMain;
|
||||
packetPending = false;
|
||||
memcpy(transmitPacket, idlePacket, sizeof(idlePacket));
|
||||
state = 0;
|
||||
requiredPreambles = preambleBits;
|
||||
bytes_sent = 0;
|
||||
bits_sent = 0;
|
||||
nextSampleDue = 0;
|
||||
|
||||
}
|
||||
void DCCWaveform::beginTrack() {
|
||||
setPowerMode(POWERMODE::ON);
|
||||
}
|
||||
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() {
|
||||
POWERMODE DCCWaveform::getPowerMode() {
|
||||
return powerMode;
|
||||
}
|
||||
}
|
||||
|
||||
void DCCWaveform::setPowerMode(POWERMODE mode) {
|
||||
powerMode = mode;
|
||||
Hardware::setPower(isMainTrack, mode == POWERMODE::ON);
|
||||
if (mode == POWERMODE::ON) delay(200);
|
||||
}
|
||||
|
||||
|
||||
void DCCWaveform::setPowerMode(POWERMODE mode) {
|
||||
powerMode=mode;
|
||||
digitalWrite2f(powerPin, mode==POWERMODE::ON ? HIGH:LOW);
|
||||
if (mode==POWERMODE::ON) delay(200);
|
||||
}
|
||||
|
||||
|
||||
void DCCWaveform::checkPowerOverload() {
|
||||
if (millis()<nextSampleDue) return;
|
||||
if (millis() < nextSampleDue) return;
|
||||
int current;
|
||||
int delay;
|
||||
|
||||
|
||||
switch (powerMode) {
|
||||
case POWERMODE::OFF:
|
||||
delay=POWER_SAMPLE_OFF_WAIT;
|
||||
case POWERMODE::OFF:
|
||||
delay = POWER_SAMPLE_OFF_WAIT;
|
||||
break;
|
||||
case POWERMODE::ON:
|
||||
// Check current
|
||||
current=analogRead(sensePin);
|
||||
if (current < POWER_SAMPLE_MAX) delay=POWER_SAMPLE_ON_WAIT;
|
||||
case POWERMODE::ON:
|
||||
// Check current
|
||||
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);
|
||||
delay=POWER_SAMPLE_OVERLOAD_WAIT;
|
||||
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;
|
||||
case POWERMODE::OVERLOAD:
|
||||
case POWERMODE::OVERLOAD:
|
||||
// Try setting it back on after the OVERLOAD_WAIT
|
||||
setPowerMode(POWERMODE::ON);
|
||||
delay=POWER_SAMPLE_ON_WAIT;
|
||||
delay = POWER_SAMPLE_ON_WAIT;
|
||||
break;
|
||||
default:
|
||||
delay=999; // cant get here..meaningless statement to avoid compiler warning.
|
||||
delay = 999; // cant get here..meaningless statement to avoid compiler warning.
|
||||
}
|
||||
nextSampleDue=millis()+delay;
|
||||
nextSampleDue = millis() + delay;
|
||||
}
|
||||
|
||||
|
||||
|
@ -122,141 +105,138 @@ void DCCWaveform::checkPowerOverload() {
|
|||
|
||||
|
||||
// process time-edge sensitive part of interrupt
|
||||
// return true if second level required
|
||||
// return true if second level required
|
||||
bool DCCWaveform::interrupt1() {
|
||||
// NOTE: this must consume transmission buffers even if the power is off
|
||||
// otherwise can cause hangs in main loop waiting for the pendingBuffer.
|
||||
// NOTE: this must consume transmission buffers even if the power is off
|
||||
// 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
|
||||
case 3: // finished sending zero bit
|
||||
state = 0;
|
||||
break;
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
void DCCWaveform::interrupt2() {
|
||||
// set currentBit to be the next bit to be sent.
|
||||
|
||||
|
||||
if (remainingPreambles > 0 ) {
|
||||
currentBit=true;
|
||||
currentBit = true;
|
||||
remainingPreambles--;
|
||||
return;
|
||||
}
|
||||
|
||||
// beware OF 9-BIT MASK generating a zero to start each byte
|
||||
currentBit=transmitPacket[bytes_sent] & bitMask[bits_sent];
|
||||
|
||||
// beware OF 9-BIT MASK generating a zero to start each byte
|
||||
currentBit = transmitPacket[bytes_sent] & bitMask[bits_sent];
|
||||
bits_sent++;
|
||||
|
||||
// If this is the last bit of a byte, prepare for the next byte
|
||||
|
||||
if (bits_sent==9) { // zero followed by 8 bits of a byte
|
||||
//end of Byte
|
||||
bits_sent=0;
|
||||
bytes_sent++;
|
||||
// if this is the last byte, prepere for next packet
|
||||
if (bytes_sent >= transmitLength) {
|
||||
// end of transmission buffer... repeat or switch to next message
|
||||
bytes_sent = 0;
|
||||
remainingPreambles=requiredPreambles;
|
||||
// If this is the last bit of a byte, prepare for the next byte
|
||||
|
||||
if (transmitRepeats > 0) {
|
||||
transmitRepeats--;
|
||||
}
|
||||
else if (packetPending) {
|
||||
// Copy pending packet to transmit packet
|
||||
for (int b=0;b<pendingLength;b++) transmitPacket[b]= pendingPacket[b];
|
||||
transmitLength=pendingLength;
|
||||
transmitRepeats=pendingRepeats;
|
||||
packetPending=false;
|
||||
}
|
||||
else {
|
||||
// Fortunately reset and idle packets are the same length
|
||||
memcpy( transmitPacket,isMainTrack?idlePacket:resetPacket, sizeof(idlePacket));
|
||||
transmitLength=sizeof(idlePacket);
|
||||
transmitRepeats=0;
|
||||
}
|
||||
if (bits_sent == 9) { // zero followed by 8 bits of a byte
|
||||
//end of Byte
|
||||
bits_sent = 0;
|
||||
bytes_sent++;
|
||||
// if this is the last byte, prepere for next packet
|
||||
if (bytes_sent >= transmitLength) {
|
||||
// end of transmission buffer... repeat or switch to next message
|
||||
bytes_sent = 0;
|
||||
remainingPreambles = requiredPreambles;
|
||||
|
||||
if (transmitRepeats > 0) {
|
||||
transmitRepeats--;
|
||||
}
|
||||
else if (packetPending) {
|
||||
// Copy pending packet to transmit packet
|
||||
for (int b = 0; b < pendingLength; b++) transmitPacket[b] = pendingPacket[b];
|
||||
transmitLength = pendingLength;
|
||||
transmitRepeats = pendingRepeats;
|
||||
packetPending = false;
|
||||
}
|
||||
else {
|
||||
// Fortunately reset and idle packets are the same length
|
||||
memcpy( transmitPacket, isMainTrack ? idlePacket : resetPacket, sizeof(idlePacket));
|
||||
transmitLength = sizeof(idlePacket);
|
||||
transmitRepeats = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wait until there is no packet pending, then make this pending
|
||||
void DCCWaveform::schedulePacket(const byte buffer[], byte byteCount, byte repeats) {
|
||||
if (byteCount>=MAX_PACKET_SIZE) return; // allow for chksum
|
||||
while(packetPending);
|
||||
|
||||
byte checksum=0;
|
||||
for (int b=0;b<byteCount; b++) {
|
||||
checksum^=buffer[b];
|
||||
pendingPacket[b]=buffer[b];
|
||||
void DCCWaveform::checkRailcom() {
|
||||
if (isMainTrack && RAILCOM_CUTOUT) {
|
||||
byte preamble = PREAMBLE_BITS_MAIN - remainingPreambles;
|
||||
if (preamble == RAILCOM_PREAMBLES_BEFORE_CUTOUT) {
|
||||
// TODO Railcom::startCutout();
|
||||
}
|
||||
pendingPacket[byteCount]=checksum;
|
||||
pendingLength=byteCount+1;
|
||||
pendingRepeats=repeats;
|
||||
packetPending=true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Wait until there is no packet pending, then make this pending
|
||||
void DCCWaveform::schedulePacket(const byte buffer[], byte byteCount, byte repeats) {
|
||||
if (byteCount >= MAX_PACKET_SIZE) return; // allow for chksum
|
||||
while (packetPending);
|
||||
|
||||
byte checksum = 0;
|
||||
for (int b = 0; b < byteCount; b++) {
|
||||
checksum ^= buffer[b];
|
||||
pendingPacket[b] = buffer[b];
|
||||
}
|
||||
pendingPacket[byteCount] = checksum;
|
||||
pendingLength = byteCount + 1;
|
||||
pendingRepeats = repeats;
|
||||
packetPending = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool DCCWaveform::getAck()
|
||||
{
|
||||
|
||||
if (isMainTrack) return false; // cant do this on main track
|
||||
|
||||
while(packetPending); // wait until transmitter has started transmitting the message
|
||||
unsigned long timeout=millis()+ACK_TIMEOUT;
|
||||
int maxCurrent=0;
|
||||
bool result=false;
|
||||
int upsamples=0;
|
||||
int downsamples=0;
|
||||
|
||||
// Monitor looking for a reading high enough to be an ack
|
||||
while(result==false && timeout>millis()) {
|
||||
if (isMainTrack) return false; // cant do this on main track
|
||||
|
||||
while (packetPending); // wait until transmitter has started transmitting the message
|
||||
unsigned long timeout = millis() + ACK_TIMEOUT;
|
||||
int maxCurrent = 0;
|
||||
bool result = false;
|
||||
int upsamples = 0;
|
||||
int downsamples = 0;
|
||||
|
||||
// Monitor looking for a reading high enough to be an ack
|
||||
while (result == false && timeout > millis()) {
|
||||
upsamples++;
|
||||
int current=analogRead(sensePin);
|
||||
maxCurrent=max(maxCurrent,current);
|
||||
result=current > ACK_MIN_PULSE;
|
||||
int current = Hardware::getCurrentMilliamps(isMainTrack);
|
||||
maxCurrent = max(maxCurrent, current);
|
||||
result = current > ACK_MIN_PULSE;
|
||||
}
|
||||
|
||||
// Monitor current until ack signal dies back
|
||||
if (result) while( true) {
|
||||
downsamples++;
|
||||
int current=analogRead(sensePin);
|
||||
maxCurrent=max(maxCurrent,current);
|
||||
if (current<= ACK_MAX_NOT_PULSE) break;
|
||||
}
|
||||
// The following DIAG is really useful as it can show how long and how far the
|
||||
if (result) while ( true) {
|
||||
downsamples++;
|
||||
int current = Hardware::getCurrentMilliamps(isMainTrack);
|
||||
maxCurrent = max(maxCurrent, current);
|
||||
if (current <= ACK_MAX_NOT_PULSE) break;
|
||||
}
|
||||
// The following DIAG is really useful as it can show how long and how far the
|
||||
// current changes during an ACK from the decoder.
|
||||
DIAG(F("\nack=%d max=%d, up=%d, down=%d "),result,maxCurrent, upsamples,downsamples);
|
||||
DIAG(F("\nack=%d max=%d, up=%d, down=%d "), result, maxCurrent, upsamples, downsamples);
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -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
42
Hardware.cpp
Normal 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
10
Hardware.h
Normal 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)());
|
||||
};
|
Loading…
Reference in New Issue
Block a user