From d47795e54a0a67a93d40809064641736247bd100 Mon Sep 17 00:00:00 2001 From: MicroBahner Date: Sun, 3 Jan 2016 14:17:02 +0100 Subject: [PATCH] No HW dependencies Compiles for AVR and Teensy (Arm) without modifications --- NmraDcc.cpp | 355 +++++++++++++++++++++++++++++++++++++++++----------- NmraDcc.h | 12 +- 2 files changed, 294 insertions(+), 73 deletions(-) diff --git a/NmraDcc.cpp b/NmraDcc.cpp index e8155c4..82d70b7 100644 --- a/NmraDcc.cpp +++ b/NmraDcc.cpp @@ -19,6 +19,7 @@ // 2015-11-06 Martin Pischky (martin@pischky.de): // Experimental Version to support 14 speed steps // and new signature of notifyDccSpeed and notifyDccFunc +// 2015-12-16 Version without use of Timer0 by Franz-Peter Müller // //------------------------------------------------------------------------ // @@ -50,21 +51,149 @@ // |----------->| // ^Timer-INT: reads one // +// new DCC Receive Routine without Timer0 ........................................................ +// +// Howto: uses only one interrupt at the rising or falling edge of the DCC signal +// The time between two edges is measured to determine the bit value +// Synchronising to the edge of the first part of a bit is done after recognizing the start bit +// During synchronizing each part of a bit is detected ( Interruptmode 'change' ) +// +// |<-----116us----->| +// DCC 1: _________XXXXXXXXX_________XXXXXXXXX_________ +// |<--------138us------>| +// ^-INTx ^-INTx +// less than 138us: its a one-Bit +// +// +// |<-----------------232us----------->| +// DCC 0: _________XXXXXXXXXXXXXXXXXX__________________XXXXXXXX__________ +// |<--------138us------->| +// ^-INTx ^-INTx +// greater than 138us: its a zero bit +// +// +// +// //------------------------------------------------------------------------ +#define MAX_ONEBITFULL 146 +#define MAX_PRAEAMBEL 146 //138 +#define MAX_ONEBITHALF 82 -// The Timer0 prescaler is hard-coded in wiring.c -#define TIMER_PRESCALER 64 - -// We will use a time period of 80us and not 87us as this gives us a bit more time to do other stuff -#define DCC_BIT_SAMPLE_PERIOD (F_CPU * 70L / TIMER_PRESCALER / 1000000L) - -#if (DCC_BIT_SAMPLE_PERIOD > 254) -#error DCC_BIT_SAMPLE_PERIOD too big, use either larger prescaler or slower processor +// Debug-Ports +//#define debug // Testpulse for logic analyser +#ifdef debug + #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) + #define MODE_TP1 DDRF |= (1<<2) //pinA2 + #define SET_TP1 PORTF |= (1<<2) + #define CLR_TP1 PORTF &= ~(1<<2) + #define MODE_TP2 DDRF |= (1<<3) //pinA3 + #define SET_TP2 PORTF |= (1<<3) + #define CLR_TP2 PORTF &= ~(1<<3) + #define MODE_TP3 DDRF |= (1<<4) //pinA4 + #define SET_TP3 PORTF |= (1<<4) + #define CLR_TP3 PORTF &= ~(1<<4) + #define MODE_TP4 DDRF |= (1<<5) //pinA5 + #define SET_TP4 PORTF |= (1<<5) + #define CLR_TP4 PORTF &= ~(1<<5) + #elif defined(__AVR_ATmega32U4__) + #define MODE_TP1 DDRF |= (1<<4) //A3 + #define SET_TP1 PORTF |= (1<<4) + #define CLR_TP1 PORTF &= ~(1<<4) + #define MODE_TP2 DDRF |= (1<<5) //A2 + #define SET_TP2 PORTF |= (1<<5) + #define CLR_TP2 PORTF &= ~(1<<5) + #define MODE_TP3 + #define SET_TP3 + #define CLR_TP3 + #define MODE_TP4 + #define SET_TP4 + #define CLR_TP4 + #elif defined(__AVR_ATmega328P__) + #define MODE_TP1 DDRC |= (1<<1) //A1 + #define SET_TP1 PORTC |= (1<<1) + #define CLR_TP1 PORTC &= ~(1<<1) + #define MODE_TP2 DDRC |= (1<<2) // A2 + #define SET_TP2 PORTC |= (1<<2) + #define CLR_TP2 PORTC &= ~(1<<2) + #define MODE_TP3 DDRC |= (1<<3) //A3 + #define SET_TP3 PORTC |= (1<<3) + #define CLR_TP3 PORTC &= ~(1<<3) + #define MODE_TP4 DDRC |= (1<<4) //A4 + #define SET_TP4 PORTC |= (1<<4) + #define CLR_TP4 PORTC &= ~(1<<4) + #elif defined(__arm__) && (defined(__MK20DX128__) || defined(__MK20DX256__)) + // Teensys 3.x + #define MODE_TP1 pinMode( A1,OUTPUT ) // A1= PortC, Bit0 + #define SET_TP1 GPIOC_PSOR = 0x01 + #define CLR_TP1 GPIOC_PCOR = 0x01 + #define MODE_TP2 pinMode( A2,OUTPUT ) // A2= PortB Bit0 + #define SET_TP2 GPIOB_PSOR = 0x01 + #define CLR_TP2 GPIOB_PCOR = 0x01 + #define MODE_TP3 pinMode( A3,OUTPUT ) // A3 = PortB Bit1 + #define SET_TP3 GPIOB_PSOR = 0x02 + #define CLR_TP3 GPIOB_PCOR = 0x02 + #define MODE_TP4 pinMode( A4,OUTPUT ) // A4 = PortB Bit3 + #define SET_TP4 GPIOB_PSOR = 0x08 + #define CLR_TP4 GPIOB_PCOR = 0x08 + #elif defined (__SAM3X8E__) + // Arduino Due + #define MODE_TP1 pinMode( A1,OUTPUT ) // A1= PA24 + #define SET_TP1 REG_PIOA_SODR = (1<<24) + #define CLR_TP1 REG_PIOA_CODR = (1<<24) + #define MODE_TP2 pinMode( A2,OUTPUT ) // A2= PA23 + #define SET_TP2 REG_PIOA_SODR = (1<<23) + #define CLR_TP2 REG_PIOA_CODR = (1<<23) + #define MODE_TP3 pinMode( A3,OUTPUT ) // A3 = PA22 + #define SET_TP3 REG_PIOA_SODR = (1<<22) + #define CLR_TP3 REG_PIOA_CODR = (1<<22) + #define MODE_TP4 pinMode( A4,OUTPUT ) // A4 = PA6 + #define SET_TP4 REG_PIOA_SODR = (1<<6) + #define CLR_TP4 REG_PIOA_CODR = (1<<6) + + //#elif defined(__AVR_ATmega128__) ||defined(__AVR_ATmega1281__)||defined(__AVR_ATmega2561__) + #else + #define MODE_TP1 + #define SET_TP1 + #define CLR_TP1 + #define MODE_TP2 + #define SET_TP2 + #define CLR_TP2 + #define MODE_TP3 + #define SET_TP3 + #define CLR_TP3 + #define MODE_TP4 + #define SET_TP4 + #define CLR_TP4 + + #endif +#else + #define MODE_TP1 + #define SET_TP1 + #define CLR_TP1 + #define MODE_TP2 + #define SET_TP2 + #define CLR_TP2 + //#define MODE_TP2 DDRC |= (1<<2) // A2 + //#define SET_TP2 PORTC |= (1<<2) + //#define CLR_TP2 PORTC &= ~(1<<2) + #define MODE_TP3 + #define SET_TP3 + #define CLR_TP3 + #define MODE_TP4 + #define SET_TP4 + #define CLR_TP4 + //#define MODE_TP4 DDRC |= (1<<4) //A4 + //#define SET_TP4 PORTC |= (1<<4) + //#define CLR_TP4 PORTC &= ~(1<<4) + #endif -#if (DCC_BIT_SAMPLE_PERIOD < 8) -#error DCC_BIT_SAMPLE_PERIOD too small, use either smaller prescaler or faster processor +#ifdef DCC_DBGVAR +struct countOf_t countOf; #endif +static byte ISREdge; // RISING or FALLING +static word bitMax; + typedef enum { WAIT_PREAMBLE = 0, @@ -107,71 +236,138 @@ DCC_PROCESSOR_STATE DccProcState ; void ExternalInterruptHandler(void) { - OCR0B = TCNT0 + DCC_BIT_SAMPLE_PERIOD ; - -#if defined(TIMSK0) - TIMSK0 |= (1< 10 ) + SET_TP1; + DccRx.BitCount++; + if( DccRx.BitCount > 10 ) { DccRx.State = WAIT_START_BIT ; + // While waiting for the start bit, detect halfbit lengths. We will detect the correct + // sync and detect whether we see a false (e.g. motorola) protocol + attachInterrupt( DccProcState.ExtIntNum, ExternalInterruptHandler, CHANGE); + halfBit = 0; + bitMax = MAX_ONEBITHALF; + CLR_TP1; + } + } else { + SET_TP1; + DccRx.BitCount = 0 ; + CLR_TP1; } - else - DccRx.BitCount = 0 ; - break; case WAIT_START_BIT: - if( !DccBitVal ) - { - DccRx.State = WAIT_DATA ; - DccRx.PacketBuf.Size = 0; - DccRx.PacketBuf.PreambleBits = 0; - for(uint8_t i = 0; i< MAX_DCC_MESSAGE_LEN; i++ ) - DccRx.PacketBuf.Data[i] = 0; + // we are looking for first half "0" bit after preamble + switch ( halfBit ) { + case 0: //SET_TP1; + // check first part + if ( DccBitVal ) { + // is still 1-bit (Preamble) + halfBit=1; + bit1=bitMicros; + } else { + // was "0" half bit, maybe the startbit + halfBit = 4; + } + break; + case 1: //SET_TP1; // previous halfbit was '1' + if ( DccBitVal ) { + CLR_TP1; + // its a '1' halfBit -> we are still in the preamble + halfBit = 0; + bit2=bitMicros; + DccRx.BitCount++; + if( abs(bit2-bit1) > 14 ) { + // the length of the 2 halbits differ too much -> wrong protokoll + CLR_TP2; + CLR_TP3; + DccRx.State = WAIT_PREAMBLE; + bitMax = MAX_PRAEAMBEL; + DccRx.BitCount = 0; + attachInterrupt( DccProcState.ExtIntNum, ExternalInterruptHandler, ISREdge ); + SET_TP3; + } + } else { + // first '0' half detected in second halfBit + // wrong sync or not a DCC protokoll + halfBit = 3; + } + break; + case 3: //SET_TP1; // previous halfbit was '0' in second halfbit + if ( DccBitVal ) { + // its a '1' halfbit -> we got only a half '0' bit -> cannot be DCC + DccRx.State = WAIT_PREAMBLE; + bitMax = MAX_PRAEAMBEL; + DccRx.BitCount = 0; + } else { + // we got two '0' halfbits -> it's the startbit + // but sync is NOT ok, change IRQ edge. + if ( ISREdge == RISING ) ISREdge = FALLING; else ISREdge = RISING; + DccRx.State = WAIT_DATA ; + bitMax = MAX_ONEBITFULL; + DccRx.PacketBuf.Size = 0; + DccRx.PacketBuf.PreambleBits = 0; + for(uint8_t i = 0; i< MAX_DCC_MESSAGE_LEN; i++ ) + DccRx.PacketBuf.Data[i] = 0; - // We now have 1 too many PreambleBits so decrement before copying - DccRx.PacketBuf.PreambleBits = DccRx.BitCount - 1 ; + DccRx.PacketBuf.PreambleBits = DccRx.BitCount; + DccRx.BitCount = 0 ; + DccRx.TempByte = 0 ; + } + attachInterrupt( DccProcState.ExtIntNum, ExternalInterruptHandler, ISREdge ); + CLR_TP1; + break; + case 4: SET_TP1; // previous (first) halfbit was 0 + // if this halfbit is 0 too, we got the startbit + if ( DccBitVal ) { + // second halfbit is 1 -> unknown protokoll + DccRx.State = WAIT_PREAMBLE; + bitMax = MAX_PRAEAMBEL; + DccRx.BitCount = 0; + } else { + // we got the startbit + DccRx.State = WAIT_DATA ; + bitMax = MAX_ONEBITFULL; + DccRx.PacketBuf.Size = 0; + DccRx.PacketBuf.PreambleBits = 0; + for(uint8_t i = 0; i< MAX_DCC_MESSAGE_LEN; i++ ) + DccRx.PacketBuf.Data[i] = 0; - DccRx.BitCount = 0 ; - DccRx.TempByte = 0 ; - } + DccRx.PacketBuf.PreambleBits = DccRx.BitCount; + DccRx.BitCount = 0 ; + DccRx.TempByte = 0 ; + } + attachInterrupt( DccProcState.ExtIntNum, ExternalInterruptHandler, ISREdge ); + CLR_TP1; + break; + + } break; case WAIT_DATA: + DccRx.BitCount++; DccRx.TempByte = ( DccRx.TempByte << 1 ) ; if( DccBitVal ) DccRx.TempByte |= 1 ; @@ -181,6 +377,7 @@ ISR(TIMER0_COMPB_vect) if( DccRx.PacketBuf.Size == MAX_DCC_MESSAGE_LEN ) // Packet is too long - abort { DccRx.State = WAIT_PREAMBLE ; + bitMax = MAX_PRAEAMBEL; DccRx.BitCount = 0 ; } else @@ -192,11 +389,15 @@ ISR(TIMER0_COMPB_vect) break; case WAIT_END_BIT: + DccRx.BitCount++; if( DccBitVal ) // End of packet? { + CLR_TP3; DccRx.State = WAIT_PREAMBLE ; + bitMax = MAX_PRAEAMBEL; DccRx.PacketCopy = DccRx.PacketBuf ; DccRx.DataReady = 1 ; + SET_TP3; } else // Get next Byte DccRx.State = WAIT_DATA ; @@ -204,6 +405,8 @@ ISR(TIMER0_COMPB_vect) DccRx.BitCount = 0 ; DccRx.TempByte = 0 ; } + CLR_TP1; + CLR_TP3; } void ackCV(void) @@ -231,7 +434,7 @@ uint8_t validCV( uint16_t CV, uint8_t Writable ) return Valid ; } -uint8_t readCV( uint16_t CV ) +uint8_t readCV( unsigned int CV ) { uint8_t Value ; @@ -243,7 +446,7 @@ uint8_t readCV( uint16_t CV ) return Value ; } -uint8_t writeCV( uint16_t CV, uint8_t Value) +uint8_t writeCV( unsigned int CV, uint8_t Value) { if( notifyCVWrite ) return notifyCVWrite( CV, Value ) ; @@ -255,7 +458,6 @@ uint8_t writeCV( uint16_t CV, uint8_t Value) if( notifyCVChange ) notifyCVChange( CV, Value) ; } - return eeprom_read_byte( (uint8_t*) CV ) ; } @@ -733,12 +935,12 @@ void NmraDcc::init( uint8_t ManufacturerId, uint8_t VersionId, uint8_t Flags, ui // Clear all the static member variables memset( &DccRx, 0, sizeof( DccRx) ); -#ifdef TCCR0A - // Change Timer0 Waveform Generation Mode from Fast PWM back to Normal Mode - TCCR0A &= ~((1<= 100 #include "Arduino.h" @@ -166,6 +166,16 @@ typedef enum #define FN_BIT_27 0x40 #define FN_BIT_28 0x80 +#define DCC_DBGVAR +#ifdef DCC_DBGVAR +typedef struct countOf_t { + unsigned long Tel; + unsigned long Err; +}countOf_t ; + +extern struct countOf_t countOf; +#endif + class NmraDcc { private: