mjs513
Senior Member+
Was working on my DCC++ for Model Train Control with Teensy 3.x and Teensy 4.x as a result of problem that some one was running into with running on a T4.1. Going to get complicated so here it goes.
In DCC you have 2 tracks available - the main track where you run you DCC enabled trains and a second track (PROG) that allows you program DCC engines and run diagnostics on the engine. While running the train on the main track works without a problem on both the T3.5 and the T4.1 running diagnostic commands on the program track seems to hang the T4.1 somewhere - in this case I mean seems like its stuck in a infinite loop somewhere - and only way to get out of it is to restart. Just in case there was a hard fault somewhere I added in @Frank B's HardFault code but when the sketch failed no hard faults were indicated. Now for the details.
Using a Arduino Motor Shield (yes its compatible with 3.3v processors) even the current sense pins on my breakout board (well tested and been using for awhile) so I know its not that I went ahead and issued 2 commands:
First one is to turn on acknowledgement on the program track with a
this seems to be working - I did have to change a bunch on int's to int16's since there was some conflict between using 16-bit (uno and mega) and a 32-bit teensy.
Second thing you have to do is to issue a
which sends a message to the DCC enabled loco to send by Manufactures ID.
Note: This is all interrupt driven since the message is encoded in the signal.
On a T3.5 I can send that command all day with an issue. On a T4.1 I may be able to send it once or twice and then it stops working and I can't figure out why - it looks like it should work. Here is what the output looks like when it works:
and when it fails (the I did add a bunch of serial prints since it seemed to hanging around there);
Basically what I was looking at is a flow something like this:
In DCC you have 2 tracks available - the main track where you run you DCC enabled trains and a second track (PROG) that allows you program DCC engines and run diagnostics on the engine. While running the train on the main track works without a problem on both the T3.5 and the T4.1 running diagnostic commands on the program track seems to hang the T4.1 somewhere - in this case I mean seems like its stuck in a infinite loop somewhere - and only way to get out of it is to restart. Just in case there was a hard fault somewhere I added in @Frank B's HardFault code but when the sketch failed no hard faults were indicated. Now for the details.
Using a Arduino Motor Shield (yes its compatible with 3.3v processors) even the current sense pins on my breakout board (well tested and been using for awhile) so I know its not that I went ahead and issued 2 commands:
First one is to turn on acknowledgement on the program track with a
Code:
<D ACK ON>
Second thing you have to do is to issue a
Code:
<R 8 1 1>
Note: This is all interrupt driven since the message is encoded in the signal.
On a T3.5 I can send that command all day with an issue. On a T4.1 I may be able to send it once or twice and then it stops working and I can't figure out why - it looks like it should work. Here is what the output looks like when it works:
Code:
Auto Prog power on
ACK baseline=29/86mA Threshold=49/146mA Duration: 2000us <= pulse <= 8500us
V0 cv=8 bit=7DCCWaveform
DCCWaveform ack pending
Ack timeout
ackPending: 0
ACK PENDING FALSE: getACKackDetected: 0
NO-ACK after 145mS max=31/92mA pulse=0uS
V1 cv=8 bit=7DCCWaveform
DCCWaveform ack pending
6380
Got Ack
ACK PENDING FALSE: getACKackDetected: 1
ACK after 32mS max=163/487mA pulse=6380uS
V0 cv=8 bit=6DCCWaveform
DCCWaveform ack pending
6322
Got Ack
ACK PENDING FALSE: getACKackDetected: 1
ACK after 32mS max=155/463mA pulse=6322uS
V0 cv=8 bit=5DCCWaveform
DCCWaveform ack pending
6322
Got Ack
ACK PENDING FALSE: getACKackDetected: 1
ACK after 32mS max=147/439mA pulse=6322uS
V0 cv=8 bit=4DCCWaveform
DCCWaveform ack pending
6380
Got Ack
ACK PENDING FALSE: getACKackDetected: 1
ACK after 32mS max=163/487mA pulse=6380uS
V0 cv=8 bit=3DCCWaveform
DCCWaveform ack pending
6322
Got Ack
ACK PENDING FALSE: getACKackDetected: 1
ACK after 32mS max=159/475mA pulse=6322uS
V0 cv=8 bit=2DCCWaveform
DCCWaveform ack pending
6264
Got Ack
ACK PENDING FALSE: getACKackDetected: 1
ACK after 32mS max=144/430mA pulse=6264uS
V0 cv=8 bit=1DCCWaveform
DCCWaveform ack pending
6264
Got Ack
ACK PENDING FALSE: getACKackDetected: 1
ACK after 32mS max=164/490mA pulse=6264uS
V0 cv=8 bit=0DCCWaveform
DCCWaveform ack pending
Ack timeout
ackPending: 0
ACK PENDING FALSE: getACKackDetected: 0
NO-ACK after 145mS max=31/92mA pulse=0uS
VB cv=8 value=1296322
Got Ack
ACK PENDING FALSE: getACKackDetected: 1
ACK after 32mS max=165/493mA pulse=6322uS
Auto Prog power off
Callback(129)
<r1|1|8 129>
and when it fails (the I did add a bunch of serial prints since it seemed to hanging around there);
Code:
Auto Prog power on
ACK baseline=29/86mA Threshold=49/146mA Duration: 2000us <= pulse <= 8500us
V0 cv=8 bit=7DCCWaveform
DCCWaveform ack pending
Ack timeout
ackPending: 0
Basically what I was looking at is a flow something like this:
Code:
void DCCEXParser::parse(Print *stream, byte *com, bool blocking)
|
|-- case 'R': // READ CV ON PROG //I am using <R 8 1 1>
if (params == 3)
{ // <R CV CALLBACKNUM CALLBACKSUB>
if (!stashCallback(stream, p))
break;
DCC::readCV(p[0], callback_R, blocking);
return;
}
|
|---void DCC::readCV(int16_t cv, ACK_CALLBACK callback, bool blocking) {
ackManagerSetup(cv, 0,READ_CV_PROG, callback, blocking);
}
|
|---void DCC::ackManagerSetup(int16_t wordval, ackOp const program[], ACK_CALLBACK callback, bool blocking) {
ackManagerWord=wordval;
ackManagerProg = program;
ackManagerCallback = callback;
if (blocking) ackManagerLoop(blocking);
}
|
|---void DCC::ackManagerLoop(bool blocking) {
while (ackManagerProg) {
byte opcode=pgm_read_byte_near(ackManagerProg);
.....
case V0:
case V1: // Issue validate bit=0 or bit=1 packet
{
if (checkResets(blocking, RESET_MIN)) return;
if (Diag::ACK) DIAG(F("\nV%d cv=%d bit=%d"),opcode==V1, ackManagerCv,ackManagerBitNum);
byte instruction = VERIFY_BIT | (opcode==V0?BIT_OFF:BIT_ON) | ackManagerBitNum;
byte message[] = {cv1(BIT_MANIPULATE, ackManagerCv), cv2(ackManagerCv), instruction };
Serial.println("DCCWaveform");
DCCWaveform::progTrack.schedulePacket(message, sizeof(message), PROG_REPEATS);
Serial.println("DCCWaveform ack pending");
DCCWaveform::progTrack.setAckPending();
}
break;
|
|--// Wait until there is no packet pending, then make this pending
void DCCWaveform::schedulePacket(const byte buffer[], byte byteCount, byte repeats) {
void DCCWaveform::setAckPending() { //sets ackPending = true
//NOTE DCCWaveform is all part of the interrupt handler using Timer.h -- uses IntervalTimer
|
|--// process time-edge sensitive part of interrupt
// return true if second level required
bool DCCWaveform::interrupt1() {
|
|-- // ACK check is prog track only and will only be checked if
// this is not case(0) which needs relatively expensive packet change code to be called.
if (ackPending) checkAck();
|
| -- void DCCWaveform::checkAck() {
// This function operates in interrupt() time so must be fast and can't DIAG
if (sentResetsSincePacket > 6) { //ACK timeout
Serial.println("Ack timeout");
ackCheckDuration=millis()-ackCheckStart;
ackPending = false;
Serial.printf("ackPending: %d\n", ackPending);
return;
}
lastCurrent=motorDriver->getCurrentRaw();
if (lastCurrent > ackMaxCurrent) ackMaxCurrent=lastCurrent;
// An ACK is a pulse lasting between minAckPulseDuration and maxAckPulseDuration uSecs (refer @haba)
if (lastCurrent>ackThreshold) {
if (ackPulseStart==0) ackPulseStart=micros(); // leading edge of pulse detected
return;
}
// not in pulse
if (ackPulseStart==0) return; // keep waiting for leading edge
// detected trailing edge of pulse
ackPulseDuration=micros()-ackPulseStart;
Serial.println(ackPulseDuration);
if (ackPulseDuration>=minAckPulseDuration && ackPulseDuration<=maxAckPulseDuration) {
ackCheckDuration=millis()-ackCheckStart;
ackDetected=true;
ackPending=false;
transmitRepeats=0; // shortcut remaining repeat packets
Serial.println("Got Ack");
return; // we have a genuine ACK result
}
ackPulseStart=0; // We have detected a too-short or too-long pulse so ignore and wait for next leading edge
}
Last edited: