mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-11-30 03:26:13 +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"
|
#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);
|
||||||
DCC::begin();
|
DCC::begin();
|
||||||
|
|
||||||
DIAG(F("\n===== CVReader begin ==============================\n"));
|
DIAG(F("\n===== CVReader begin ==============================\n"));
|
||||||
|
|
||||||
for (byte x=0;x<sizeof(cvnums)/sizeof(cvnums[0]);x++) {
|
for (byte x = 0; x < sizeof(cvnums) / sizeof(cvnums[0]); x++) {
|
||||||
int value=DCC::readCV(cvnums[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("\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"));
|
DIAG(F("\nReady for JMRI commands\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
DCC::loop(); // required to keep locos running and check powwer
|
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);
|
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) {
|
bool DCC::verifyCV(int cv, byte value) {
|
||||||
byte message[] = { cv1(0x74, cv), cv2(cv), 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);
|
DCCWaveform::progTrack.schedulePacket(message, sizeof(message), 5);
|
||||||
return DCCWaveform::progTrack.getAck();
|
return DCCWaveform::progTrack.getAck();
|
||||||
}
|
}
|
||||||
|
@ -206,4 +206,4 @@ void DCC::updateLocoReminder(int loco, byte tSpeed, bool forward) {
|
||||||
speedTable[reg].forward = forward;
|
speedTable[reg].forward = forward;
|
||||||
}
|
}
|
||||||
DCC::LOCO DCC::speedTable[MAX_LOCOS];
|
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 <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);
|
mainTrack.beginTrack();
|
||||||
Timer3.disablePwm(PROG_SIGNAL_PIN);
|
progTrack.beginTrack();
|
||||||
Timer3.attachInterrupt(interruptHandler);
|
|
||||||
mainTrack.beginTrack();
|
|
||||||
progTrack.beginTrack();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DCCWaveform::loop() {
|
void DCCWaveform::loop() {
|
||||||
mainTrack.checkPowerOverload();
|
mainTrack.checkPowerOverload();
|
||||||
progTrack.checkPowerOverload();
|
progTrack.checkPowerOverload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// static //
|
// static //
|
||||||
void DCCWaveform::interruptHandler() {
|
void DCCWaveform::interruptHandler() {
|
||||||
// call the timer edge sensitive actions for progtrack and maintrack
|
// call the timer edge sensitive actions for progtrack and maintrack
|
||||||
bool mainCall2=mainTrack.interrupt1();
|
bool mainCall2 = mainTrack.interrupt1();
|
||||||
bool progCall2=progTrack.interrupt1();
|
bool progCall2 = progTrack.interrupt1();
|
||||||
|
|
||||||
// call (if necessary) the procs to get the current bits
|
// call (if necessary) the procs to get the current bits
|
||||||
// these must complete within 50microsecs of the interrupt
|
// these must complete within 50microsecs of the interrupt
|
||||||
// but they are only called ONCE PER BIT TRANSMITTED
|
// but they are only called ONCE PER BIT TRANSMITTED
|
||||||
// after the rising edge of the signal
|
// after the rising edge of the signal
|
||||||
if (mainCall2) mainTrack.interrupt2();
|
if (mainCall2) mainTrack.interrupt2();
|
||||||
if (progCall2) progTrack.interrupt2();
|
if (progCall2) progTrack.interrupt2();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// An instance of this class handles the DCC transmissions for one track. (main or prog)
|
// An instance of this class handles the DCC transmissions for one track. (main or prog)
|
||||||
// Interrupts are marshalled via the statics.
|
// Interrupts are marshalled via the statics.
|
||||||
// A track has a current transmit buffer, and a pending buffer.
|
// 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.
|
// 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) {
|
DCCWaveform::DCCWaveform( byte preambleBits, bool isMain) {
|
||||||
// establish appropriate pins
|
// establish appropriate pins
|
||||||
powerPin=Arduino_to_GPIO_pin(powerPinNo);
|
isMainTrack = isMain;
|
||||||
directionPin=Arduino_to_GPIO_pin(directionPinNo);
|
packetPending = false;
|
||||||
sensePin=sensePinNo;
|
memcpy(transmitPacket, idlePacket, sizeof(idlePacket));
|
||||||
isMainTrack=isMain;
|
state = 0;
|
||||||
packetPending=false;
|
requiredPreambles = preambleBits;
|
||||||
memcpy(transmitPacket,idlePacket,sizeof(idlePacket));
|
bytes_sent = 0;
|
||||||
state=0;
|
bits_sent = 0;
|
||||||
requiredPreambles=preambleBits;
|
nextSampleDue = 0;
|
||||||
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;
|
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() {
|
void DCCWaveform::checkPowerOverload() {
|
||||||
if (millis()<nextSampleDue) return;
|
if (millis() < nextSampleDue) return;
|
||||||
int current;
|
int current;
|
||||||
int delay;
|
int delay;
|
||||||
|
|
||||||
switch (powerMode) {
|
switch (powerMode) {
|
||||||
case POWERMODE::OFF:
|
case POWERMODE::OFF:
|
||||||
delay=POWER_SAMPLE_OFF_WAIT;
|
delay = POWER_SAMPLE_OFF_WAIT;
|
||||||
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;
|
||||||
case POWERMODE::OVERLOAD:
|
case POWERMODE::OVERLOAD:
|
||||||
// Try setting it back on after the OVERLOAD_WAIT
|
// Try setting it back on after the OVERLOAD_WAIT
|
||||||
setPowerMode(POWERMODE::ON);
|
setPowerMode(POWERMODE::ON);
|
||||||
delay=POWER_SAMPLE_ON_WAIT;
|
delay = POWER_SAMPLE_ON_WAIT;
|
||||||
break;
|
break;
|
||||||
default:
|
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
|
// process time-edge sensitive part of interrupt
|
||||||
// return true if second level required
|
// return true if second level required
|
||||||
bool DCCWaveform::interrupt1() {
|
bool DCCWaveform::interrupt1() {
|
||||||
// NOTE: this must consume transmission buffers even if the power is off
|
// NOTE: this must consume transmission buffers even if the power is off
|
||||||
// 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
|
||||||
state = 0;
|
state = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DCCWaveform::interrupt2() {
|
void DCCWaveform::interrupt2() {
|
||||||
// set currentBit to be the next bit to be sent.
|
// set currentBit to be the next bit to be sent.
|
||||||
|
|
||||||
if (remainingPreambles > 0 ) {
|
if (remainingPreambles > 0 ) {
|
||||||
currentBit=true;
|
currentBit = true;
|
||||||
remainingPreambles--;
|
remainingPreambles--;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// beware OF 9-BIT MASK generating a zero to start each byte
|
// beware OF 9-BIT MASK generating a zero to start each byte
|
||||||
currentBit=transmitPacket[bytes_sent] & bitMask[bits_sent];
|
currentBit = transmitPacket[bytes_sent] & bitMask[bits_sent];
|
||||||
bits_sent++;
|
bits_sent++;
|
||||||
|
|
||||||
// If this is the last bit of a byte, prepare for the next byte
|
// 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 (transmitRepeats > 0) {
|
if (bits_sent == 9) { // zero followed by 8 bits of a byte
|
||||||
transmitRepeats--;
|
//end of Byte
|
||||||
}
|
bits_sent = 0;
|
||||||
else if (packetPending) {
|
bytes_sent++;
|
||||||
// Copy pending packet to transmit packet
|
// if this is the last byte, prepere for next packet
|
||||||
for (int b=0;b<pendingLength;b++) transmitPacket[b]= pendingPacket[b];
|
if (bytes_sent >= transmitLength) {
|
||||||
transmitLength=pendingLength;
|
// end of transmission buffer... repeat or switch to next message
|
||||||
transmitRepeats=pendingRepeats;
|
bytes_sent = 0;
|
||||||
packetPending=false;
|
remainingPreambles = requiredPreambles;
|
||||||
}
|
|
||||||
else {
|
if (transmitRepeats > 0) {
|
||||||
// Fortunately reset and idle packets are the same length
|
transmitRepeats--;
|
||||||
memcpy( transmitPacket,isMainTrack?idlePacket:resetPacket, sizeof(idlePacket));
|
}
|
||||||
transmitLength=sizeof(idlePacket);
|
else if (packetPending) {
|
||||||
transmitRepeats=0;
|
// 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::checkRailcom() {
|
||||||
void DCCWaveform::schedulePacket(const byte buffer[], byte byteCount, byte repeats) {
|
if (isMainTrack && RAILCOM_CUTOUT) {
|
||||||
if (byteCount>=MAX_PACKET_SIZE) return; // allow for chksum
|
byte preamble = PREAMBLE_BITS_MAIN - remainingPreambles;
|
||||||
while(packetPending);
|
if (preamble == RAILCOM_PREAMBLES_BEFORE_CUTOUT) {
|
||||||
|
// TODO Railcom::startCutout();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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()
|
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
|
if (isMainTrack) return false; // cant do this on main track
|
||||||
while(result==false && timeout>millis()) {
|
|
||||||
|
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++;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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;
|
||||||
}
|
}
|
||||||
// The following DIAG is really useful as it can show how long and how far the
|
// The following DIAG is really useful as it can show how long and how far the
|
||||||
// current changes during an ACK from the decoder.
|
// 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;
|
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_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
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