diff --git a/MotorDriver.cpp b/MotorDriver.cpp index 701816a..a77c2d2 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -41,16 +41,20 @@ MotorDriver::MotorDriver(VPIN power_pin, byte signal_pin, byte signal_pin2, int8 getFastPin(F("SIG"),signalPin,fastSignalPin); pinMode(signalPin, OUTPUT); + fastSignalPin.shadowinout = NULL; if (HAVE_PORTA(fastSignalPin.inout == &PORTA)) { DIAG(F("Found PORTA pin %d"),signalPin); + fastSignalPin.shadowinout = fastSignalPin.inout; fastSignalPin.inout = &fakePORTA; } if (HAVE_PORTB(fastSignalPin.inout == &PORTB)) { DIAG(F("Found PORTB pin %d"),signalPin); + fastSignalPin.shadowinout = fastSignalPin.inout; fastSignalPin.inout = &fakePORTB; } if (HAVE_PORTC(fastSignalPin.inout == &PORTC)) { DIAG(F("Found PORTC pin %d"),signalPin); + fastSignalPin.shadowinout = fastSignalPin.inout; fastSignalPin.inout = &fakePORTC; } @@ -162,15 +166,39 @@ int MotorDriver::getCurrentRaw() { void MotorDriver::setDCSignal(byte speedcode) { if (brakePin == UNUSED_PIN) return; - // spedcoode is a dcc speed /direction - byte tSpeed=speedcode & 0x7F; - // DCC Speed with 0,1 stop and speed steps 2 to 127 + // spedcoode is a dcc speed & direction + byte tSpeed=speedcode & 0x7F; // DCC Speed with 0,1 stop and speed steps 2 to 127 + byte tDir=speedcode & 0x80; byte brake; if (tSpeed <= 1) brake = 255; else if (tSpeed >= 127) brake = 0; else brake = 2 * (128-tSpeed); analogWrite(brakePin,brake); - setSignal(speedcode & 0x80); + // as the port registers can be shadowed to get syncronized DCC signals + // we need to take care of that and we have to turn off interrupts during + // that time as otherwise setDCCSignal() which is called from interrupt + // contect can undo whatever we do here. + if (fastSignalPin.shadowinout != NULL) { + if (HAVE_PORTA(fastSignalPin.shadowinout == &PORTA)) { + noInterrupts(); + fakePORTA=PORTA; + setSignal(tDir); + PORTA=fakePORTA; + interrupts(); + } else if (HAVE_PORTB(fastSignalPin.shadowinout == &PORTB)) { + noInterrupts(); + fakePORTB=PORTB; + setSignal(tDir); + PORTB=fakePORTB; + interrupts(); + } else if (HAVE_PORTC(fastSignalPin.shadowinout == &PORTC)) { + noInterrupts(); + fakePORTC=PORTC; + setSignal(tDir); + PORTC=fakePORTC; + interrupts(); + } + } } int MotorDriver::getCurrentRawInInterrupt() { diff --git a/MotorDriver.h b/MotorDriver.h index 4c7d6ff..7f0b169 100644 --- a/MotorDriver.h +++ b/MotorDriver.h @@ -65,13 +65,15 @@ struct FASTPIN { volatile uint32_t *inout; uint32_t maskHIGH; - uint32_t maskLOW; + uint32_t maskLOW; + volatile uint32_t *shadowinout; }; #else struct FASTPIN { volatile uint8_t *inout; uint8_t maskHIGH; - uint8_t maskLOW; + uint8_t maskLOW; + volatile uint8_t *shadowinout; }; #endif diff --git a/TrackManager.cpp b/TrackManager.cpp index 87ea735..4727dc8 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -87,11 +87,15 @@ void TrackManager::addTrack(byte t, MotorDriver* driver) { } } +// The port registers that are shadowing +// the real port registers. These are // defined in Motordriver.cpp extern byte fakePORTA; extern byte fakePORTB; extern byte fakePORTC; +// setDCCSignal(), called from interrupt context +// does assume ports are shadowed if they can be void TrackManager::setDCCSignal( bool on) { HAVE_PORTA(fakePORTA=PORTA); HAVE_PORTB(fakePORTB=PORTB); @@ -108,6 +112,8 @@ void TrackManager::setCutout( bool on) { // TODO APPLY_BY_MODE(TRACK_MODE_MAIN,setCutout(on)); } +// setPROGSignal(), called from interrupt context +// does assume ports are shadowed if they can be void TrackManager::setPROGSignal( bool on) { HAVE_PORTA(fakePORTA=PORTA); HAVE_PORTB(fakePORTB=PORTB); @@ -118,18 +124,15 @@ void TrackManager::setPROGSignal( bool on) { HAVE_PORTC(PORTC=fakePORTC); } +// setDCSignal(), called from normal context +// MotorDriver::setDCSignal handles shadowed IO port changes. +// with interrupts turned off around the critical section void TrackManager::setDCSignal(int16_t cab, byte speedbyte) { - HAVE_PORTA(fakePORTA=PORTA); - HAVE_PORTB(fakePORTB=PORTB); - HAVE_PORTC(fakePORTC=PORTC); FOR_EACH_TRACK(t) { if (trackDCAddr[t]!=cab) continue; if (trackMode[t]==TRACK_MODE_DC) track[t]->setDCSignal(speedbyte); else if (trackMode[t]==TRACK_MODE_DCX) track[t]->setDCSignal(speedbyte ^ 128); } - HAVE_PORTA(PORTA=fakePORTA); - HAVE_PORTB(PORTB=fakePORTB); - HAVE_PORTC(PORTC=fakePORTC); } @@ -193,10 +196,11 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr } else if (trackMode[t]==TRACK_MODE_MAIN || trackMode[t]==TRACK_MODE_PROG) canDo &= track[t]->isPWMCapable(); } - //DIAG(F("HAMode=%d"),canDo); if (!canDo) { DCCTimer::clearPWM(); } + //if (MotorDriver::usePWM != canDo) + // DIAG(F("HA mode changed from %d to %d"), MotorDriver::usePWM, canDo); MotorDriver::usePWM=canDo; @@ -211,7 +215,7 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr void TrackManager::applyDCSpeed(byte t) { uint8_t speedByte=DCC::getThrottleSpeedByte(trackDCAddr[t]); if (trackMode[t]==TRACK_MODE_DCX) - speedByte = (speedByte & 0xF7) | ~(speedByte & 0x80); // Reverse highest bit + speedByte = speedByte ^ 128; // reverse direction bit track[t]->setDCSignal(speedByte); }