diff --git a/Firmware/Chameleon-Mini/Codec/Codec.h b/Firmware/Chameleon-Mini/Codec/Codec.h index 0a74d25..1e530bc 100644 --- a/Firmware/Chameleon-Mini/Codec/Codec.h +++ b/Firmware/Chameleon-Mini/Codec/Codec.h @@ -141,7 +141,8 @@ INLINE void CodecInitCommon(void) CODEC_DEMOD_IN_PORT.CODEC_DEMOD_IN_PINCTRL0 = PORT_ISC_RISING_gc; CODEC_DEMOD_IN_PORT.CODEC_DEMOD_IN_PINCTRL1 = PORT_ISC_FALLING_gc; CODEC_DEMOD_IN_PORT.INT0MASK = 0; - CODEC_DEMOD_IN_PORT.INTCTRL = PORT_INT0LVL_HI_gc; + CODEC_DEMOD_IN_PORT.INT1MASK = 0; + CODEC_DEMOD_IN_PORT.INTCTRL = PORT_INT0LVL_HI_gc | PORT_INT1LVL_HI_gc; EVSYS.CH0MUX = CODEC_DEMOD_IN_EVMUX0; EVSYS.CH1MUX = CODEC_DEMOD_IN_EVMUX1; diff --git a/Firmware/Chameleon-Mini/Codec/ISO14443-2A.c b/Firmware/Chameleon-Mini/Codec/ISO14443-2A.c index b0db926..1f9d965 100644 --- a/Firmware/Chameleon-Mini/Codec/ISO14443-2A.c +++ b/Firmware/Chameleon-Mini/Codec/ISO14443-2A.c @@ -75,7 +75,7 @@ static void StartDemod(void) { CODEC_TIMER_SAMPLING.INTCTRLB = TC_CCAINTLVL_HI_gc; /* Start looking out for modulation pause via interrupt. */ - CODEC_DEMOD_IN_PORT.INTFLAGS = 0x03; + CODEC_DEMOD_IN_PORT.INTFLAGS = PORT_INT0IF_bm; CODEC_DEMOD_IN_PORT.INT0MASK = CODEC_DEMOD_IN_MASK0; } @@ -408,9 +408,7 @@ void ISO14443ACodecDeInit(void) CODEC_TIMER_LOADMOD.INTFLAGS = TC0_OVFIF_bm; CodecSetSubcarrier(CODEC_SUBCARRIERMOD_OFF, 0); - if (!SniffEnable) { - CodecSetDemodPower(false); - } + CodecSetDemodPower(false); CodecSetLoadmodState(false); } @@ -427,18 +425,6 @@ void ISO14443ACodecTask(void) { LogEntry(LOG_INFO_CODEC_RX_DATA, CodecBuffer, (DemodBitCount+7)/8); LEDHook(LED_CODEC_RX, LED_PULSE); - - if(SniffEnable == true){ - // TODO: Start interrupt for finding Card -> Reader direction traffic - - ISO14443ACodecDeInit(); - - TestSniff14443ACodecInit(); - TrafficSource = TRAFFIC_CARD; - return; - - } - /* Call application if we received data */ AnswerBitCount = ApplicationProcess(CodecBuffer, DemodBitCount); @@ -451,7 +437,6 @@ void ISO14443ACodecTask(void) { /* We have to generate the parity bits ourself */ ParityBufferPtr = 0; } - } if (AnswerBitCount != ISO14443A_APP_NO_RESPONSE) { diff --git a/Firmware/Chameleon-Mini/Codec/Reader14443-2A.c b/Firmware/Chameleon-Mini/Codec/Reader14443-2A.c index 0e2b794..cb52073 100644 --- a/Firmware/Chameleon-Mini/Codec/Reader14443-2A.c +++ b/Firmware/Chameleon-Mini/Codec/Reader14443-2A.c @@ -412,7 +412,8 @@ void Reader14443ACodecTask(void) LED_PORT.OUTCLR = LED_RED; Reader14443ACodecDeInit(); - ISO14443ACodecInit(); + Sniff14443ACodecInit(); +// ISO14443ACodecInit(); TrafficSource = TRAFFIC_READER; return; diff --git a/Firmware/Chameleon-Mini/Codec/SniffISO14443-2A.c b/Firmware/Chameleon-Mini/Codec/SniffISO14443-2A.c index 0813e6a..0ab899c 100644 --- a/Firmware/Chameleon-Mini/Codec/SniffISO14443-2A.c +++ b/Firmware/Chameleon-Mini/Codec/SniffISO14443-2A.c @@ -1,5 +1,7 @@ // // Created by Zitai Chen on 05/07/2018. +// Some interrupt handling vect +// modified form ISO14443-2A.c and Reader14443-2A.c // #include "SniffISO14443-2A.h" @@ -12,14 +14,262 @@ #include "Terminal/Terminal.h" #include +/* Sampling is done using internal clock, synchronized to the field modulation. + * For that we need to convert the bit rate for the internal clock. */ +#define SAMPLE_RATE_SYSTEM_CYCLES ((uint16_t) (((uint64_t) F_CPU * ISO14443A_BIT_RATE_CYCLES) / CODEC_CARRIER_FREQ) ) + +#define ISO14443A_MIN_BITS_PER_FRAME 7 + + +/* Define pseudo variables to use fast register access. This is useful for global vars */ +#define DataRegister Codec8Reg0 +#define StateRegister Codec8Reg1 +#define ParityRegister Codec8Reg2 +#define SampleIdxRegister Codec8Reg2 +#define SampleRegister Codec8Reg3 +#define BitSent CodecCount16Register1 +#define BitCount CodecCount16Register2 +#define CodecBufferPtr CodecPtrRegister1 +#define ParityBufferPtr CodecPtrRegister2 bool SniffEnable; enum RCTraffic TrafficSource; +static volatile struct { + volatile bool DemodFinished; +// volatile bool LoadmodFinished; +} Flags = { 0 }; + +typedef enum { + /* Demod */ + DEMOD_DATA_BIT, + DEMOD_PARITY_BIT, + LOADMOD_FDT, + +} StateType; + +///////////////////////////////////////////////// +// Reader->Card Direction Traffic +///////////////////////////////////////////////// + +void ReaderSniffInit(void) +{ + /* Initialize some global vars and start looking out for reader commands */ + Flags.DemodFinished = 0; + +// ReaderStartDemod(); + + CodecBufferPtr = CodecBuffer; + ParityBufferPtr = &CodecBuffer[ISO14443A_BUFFER_PARITY_OFFSET]; + DataRegister = 0; + SampleRegister = 0; + SampleIdxRegister = 0; + BitCount = 0; + StateRegister = DEMOD_DATA_BIT; + + + /* Configure sampling-timer free running and sync to first modulation-pause. */ + CODEC_TIMER_SAMPLING.CNT = 0; // Reset the timer count + CODEC_TIMER_SAMPLING.PER = SAMPLE_RATE_SYSTEM_CYCLES - 1; // Set Period regisiter + CODEC_TIMER_SAMPLING.CCD = 0xFFFF; /* CCD Interrupt is not active! */ + CODEC_TIMER_SAMPLING.CTRLA = TC_CLKSEL_DIV1_gc; + CODEC_TIMER_SAMPLING.CTRLD = TC_EVACT_RESTART_gc | CODEC_TIMER_MODSTART_EVSEL; + CODEC_TIMER_SAMPLING.INTFLAGS = TC0_CCDIF_bm; + CODEC_TIMER_SAMPLING.INTCTRLB = TC_CCDINTLVL_HI_gc; + + /* Start looking out for modulation pause via interrupt. */ + CODEC_DEMOD_IN_PORT.INTFLAGS = PORT_INT1IF_bm; + CODEC_DEMOD_IN_PORT.INT1MASK = CODEC_DEMOD_IN_MASK0; + +// ISO14443ACodecInit(); +} + + +// Find first pause and start sampling +ISR(PORTB_INT1_vect) { +// LED_PORT.OUTSET = LED_RED; + /* This is the first edge of the first modulation-pause after StartDemod. + * Now we have time to start + * demodulating beginning from one bit-width after this edge. */ + + /* Sampling timer has been preset to sample-rate and has automatically synced + * to THIS first modulation pause. Thus after exactly one bit-width from here, + * an OVF is generated. We want to start sampling with the next bit and use the + * XYZBUF mechanism of the xmega to automatically double the sampling rate on the + * next overflow. For this we have to temporarily deactivate the automatical alignment + * in order to catch next overflow event for updating the BUF registers. + * We want to sample the demodulated data stream in the first quarter of the half-bit + * where the pulsed miller encoded is located. */ + CODEC_TIMER_SAMPLING.CTRLD = TC_EVACT_OFF_gc; + CODEC_TIMER_SAMPLING.PERBUF = SAMPLE_RATE_SYSTEM_CYCLES/2 - 1; /* Half bit width */ + CODEC_TIMER_SAMPLING.CCDBUF = SAMPLE_RATE_SYSTEM_CYCLES/8 - 14 - 1; /* Compensate for DIGFILT and ISR prolog */ + + /* Setup Frame Delay Timer and wire to EVSYS. Frame delay time is + * measured from last change in RF field, therefore we use + * the event channel 1 (end of modulation pause) as the restart event. + * The preliminary frame delay time chosen here is irrelevant, because + * the correct FDT gets set automatically after demodulation. */ + +// CODEC_TIMER_LOADMOD.CNT = 0; +// CODEC_TIMER_LOADMOD.PER = 0xFFFF; +//// CODEC_TIMER_LOADMOD.CTRLD = TC_EVACT_RESTART_gc | CODEC_TIMER_MODEND_EVSEL; +//// CODEC_TIMER_LOADMOD.INTCTRLA = TC_OVFINTLVL_OFF_gc; +//// CODEC_TIMER_LOADMOD.INTFLAGS = TC0_OVFIF_bm; +//// CODEC_TIMER_LOADMOD.CTRLA = CODEC_TIMER_CARRIER_CLKSEL; +// +// CODEC_TIMER_LOADMOD.CTRLA = TC_CLKSEL_OFF_gc; +// CODEC_TIMER_LOADMOD.INTCTRLA = 0; + + /* Disable this interrupt */ + CODEC_DEMOD_IN_PORT.INT1MASK = 0; +} + +// Sampling with timer and demod +ISR(TCD0_CCD_vect) { + /* This interrupt gets called twice for every bit to sample it. */ + uint8_t SamplePin = CODEC_DEMOD_IN_PORT.IN & CODEC_DEMOD_IN_MASK; + + /* Shift sampled bit into sampling register */ + SampleRegister = (SampleRegister << 1) | (!SamplePin ? 0x01 : 0x00); + + if (SampleIdxRegister) { + SampleIdxRegister = 0; + /* Analyze the sampling register after 2 samples. */ + if ((SampleRegister & 0x07) == 0x07) { + /* No carrier modulation for 3 sample points. EOC! */ + CODEC_TIMER_SAMPLING.CTRLA = TC_CLKSEL_OFF_gc; + CODEC_TIMER_SAMPLING.INTFLAGS = TC0_CCDIF_bm; + + /* By this time, the FDT timer is aligned to the last modulation + * edge of the reader. So we disable the auto-synchronization and + * let it count the frame delay time in the background, and generate + * an interrupt once it has reached the FDT. */ + // Do not activate loadmod +// CODEC_TIMER_LOADMOD.CTRLD = TC_EVACT_OFF_gc; +// CODEC_TIMER_LOADMOD.CTRLA = TC_CLKSEL_OFF_gc; +// CODEC_TIMER_LOADMOD.INTCTRLA = 0; + +// if (SampleRegister & 0x08) { +// CODEC_TIMER_LOADMOD.PER = ISO14443A_FRAME_DELAY_PREV1 - 40; /* compensate for ISR prolog */ +// } else { +// CODEC_TIMER_LOADMOD.PER = ISO14443A_FRAME_DELAY_PREV0 - 40; /* compensate for ISR prolog */ +// } + + StateRegister = LOADMOD_FDT; + +// CODEC_TIMER_LOADMOD.INTFLAGS = TC0_OVFIF_bm; +// CODEC_TIMER_LOADMOD.INTCTRLA = TC_OVFINTLVL_HI_gc; + + /* Determine if we did not receive a multiple of 8 bits. + * If this is the case, right-align the remaining data and + * store it into the buffer. */ + uint8_t RemainingBits = BitCount % 8; + if (RemainingBits != 0) { + uint8_t NewDataRegister = DataRegister; + + while (RemainingBits++ < 8) { + /* Pad with zeroes to right-align. */ + NewDataRegister >>= 1; + } + + /* TODO: Prevent buffer overflow */ + *CodecBufferPtr = NewDataRegister; + } + + /* Signal, that we have finished sampling */ + Flags.DemodFinished = 1; + } else { + /* Otherwise, we check the two sample bits from the bit before. */ + uint8_t BitSample = SampleRegister & 0xC; + uint8_t Bit = 0; + + if (BitSample != (0x0 << 2)) { + /* We have a valid bit. decode and process it. */ + if (BitSample & (0x1 << 2)) { + /* 01 sequence or 11 sequence -> This is a zero bit */ + Bit = 0; + } else { + /* 10 sequence -> This is a one bit */ + Bit = 1; + } + + if (StateRegister == DEMOD_DATA_BIT) { + /* This is a data bit, so shift it into the data register and + * hold a local copy of it. */ + uint8_t NewDataRegister = DataRegister >> 1; + NewDataRegister |= (Bit ? 0x80 : 0x00); + DataRegister = NewDataRegister; + + /* Update bitcount */ + uint16_t NewBitCount = ++BitCount; + if ((NewBitCount & 0x07) == 0) { + /* We have reached a byte boundary! Store the data register. */ + /* TODO: Prevent buffer overflow */ + *CodecBufferPtr++ = NewDataRegister; + + /* Store bit for determining FDT at EOC and enable parity + * handling on next bit. */ + StateRegister = DEMOD_PARITY_BIT; + } + + } else if (StateRegister == DEMOD_PARITY_BIT) { + /* This is a parity bit. Store it */ + *ParityBufferPtr++ = Bit; + StateRegister = DEMOD_DATA_BIT; + } else { + /* Should never Happen (TM) */ + } + } else { + /* 00 sequence. -> No valid data yet. This also occurs if we just started + * sampling and have sampled less than 2 bits yet. Thus ignore. */ + } + } + } else { + /* On odd sample position just sample. */ + SampleIdxRegister = ~SampleIdxRegister; + } + + /* Make sure the sampling timer gets automatically aligned to the + * modulation pauses by using the RESTART event. + * This can be understood as a "poor mans PLL" and makes sure that we are + * never too far out the bit-grid while sampling. */ + CODEC_TIMER_SAMPLING.CTRLD = TC_EVACT_RESTART_gc | CODEC_TIMER_MODSTART_EVSEL; +} + + + + +INLINE void ReaderSniffDeInit(void) +{ + /* Gracefully shutdown codec */ + CODEC_DEMOD_IN_PORT.INT1MASK = 0; + + Flags.DemodFinished = 0; + + CODEC_TIMER_SAMPLING.CTRLA = TC_CLKSEL_OFF_gc; + CODEC_TIMER_SAMPLING.CTRLD = TC_EVACT_OFF_gc; + CODEC_TIMER_SAMPLING.INTCTRLB = TC_CCDINTLVL_OFF_gc; + CODEC_TIMER_SAMPLING.INTFLAGS = TC0_CCDIF_bm; + + +// CODEC_TIMER_LOADMOD.CTRLA = TC_CLKSEL_OFF_gc; +// CODEC_TIMER_LOADMOD.CTRLD = TC_EVACT_OFF_gc; +// CODEC_TIMER_LOADMOD.INTCTRLA = TC_OVFINTLVL_OFF_gc; +// CODEC_TIMER_LOADMOD.INTFLAGS = TC0_OVFIF_bm; + +} + + + + + +///////////////////////////////////////////////// // Card->Reader Direction Traffic +///////////////////////////////////////////////// + INLINE void CardSniffInit(void) { - + TestSniff14443ACodecInit(); } INLINE void CardSniffDeinit(void) @@ -27,22 +277,20 @@ INLINE void CardSniffDeinit(void) Reader14443ACodecDeInit(); } -// Reader->Card Direction Traffic -INLINE void ReaderSniffInit(void) -{ - ISO14443ACodecInit(); -} - -INLINE void ReaderSniffDeInit(void) -{ - ISO14443ACodecDeInit(); -} void Sniff14443ACodecInit(void) { + Flags.DemodFinished = 0; + SniffEnable = true; TrafficSource = TRAFFIC_READER; + + // Common Codec Register settings + CodecInitCommon(); + // Enable demodulator power + CodecSetDemodPower(true); + // Start with sniffing Reader->Card direction traffic // Card -> Reader traffic sniffing interrupt will be enbaled // when Reader-> Card direction sniffing finished @@ -58,12 +306,47 @@ void Sniff14443ACodecDeInit(void) SniffEnable = false; CardSniffDeinit(); ReaderSniffDeInit(); - } + + void Sniff14443ACodecTask(void) { + if (TrafficSource == TRAFFIC_READER){ - ISO14443ACodecTask(); + if (Flags.DemodFinished) { + Flags.DemodFinished = 0; + /* Reception finished. Process the received bytes */ + uint16_t DemodBitCount = BitCount; + + if (DemodBitCount >= ISO14443A_MIN_BITS_PER_FRAME) { + // For logging data + LogEntry(LOG_INFO_CODEC_RX_DATA, CodecBuffer, (DemodBitCount+7)/8); + LEDHook(LED_CODEC_RX, LED_PULSE); + + if(SniffEnable == true){ + // TODO: Start interrupt for finding Card -> Reader direction traffic + + ReaderSniffDeInit(); + CardSniffInit(); + +// ISO14443ACodecDeInit(); + +// TestSniff14443ACodecInit(); + TrafficSource = TRAFFIC_CARD; + return; + } + } +// +// /* No data to be processed. Disable loadmodding and start listening again */ +// CODEC_TIMER_LOADMOD.CTRLA = TC_CLKSEL_OFF_gc; +// CODEC_TIMER_LOADMOD.INTCTRLA = 0; + + ReaderSniffInit(); + + } + + + } else{ Reader14443ACodecTask(); } @@ -72,3 +355,4 @@ void Sniff14443ACodecTask(void) } +