From a5d0a8d216ce55df921470ba9acdf5a61c6b914e Mon Sep 17 00:00:00 2001 From: Georg Date: Mon, 22 Jan 2018 08:47:59 +0100 Subject: [PATCH] reader codec upgrade --- .../Chameleon-Mini/Application/Reader14443A.c | 197 ++++++++---- .../Chameleon-Mini/Application/Reader14443A.h | 1 - Firmware/Chameleon-Mini/Codec/Codec.h | 5 +- .../Chameleon-Mini/Codec/Reader14443-2A.c | 296 +++++++++--------- .../Chameleon-Mini/Codec/Reader14443-2A.h | 2 + .../Chameleon-Mini/Codec/Reader14443-ISR.S | 110 +++++++ Firmware/Chameleon-Mini/Makefile | 2 +- 7 files changed, 390 insertions(+), 223 deletions(-) create mode 100644 Firmware/Chameleon-Mini/Codec/Reader14443-ISR.S diff --git a/Firmware/Chameleon-Mini/Application/Reader14443A.c b/Firmware/Chameleon-Mini/Application/Reader14443A.c index 7c0aa9e..7cea928 100644 --- a/Firmware/Chameleon-Mini/Application/Reader14443A.c +++ b/Firmware/Chameleon-Mini/Application/Reader14443A.c @@ -10,6 +10,13 @@ #define CHECK_BCC(B) ((B[0] ^ B[1] ^ B[2] ^ B[3]) == B[4]) #define IS_CASCADE_BIT_SET(buf) (buf[0] & 0x04) #define IS_ISO14443A_4_COMPLIANT(buf) (buf[0] & 0x20) +#define MF_CLASSIC_READER_NONCE 0x00000000 + +#define TRYCOUNT_MAX 16 + +#define FLAGS_MASK 0x03 +#define FLAGS_PARITY_OK 0x01 +#define FLAGS_NO_DATA 0x02 // TODO replace remaining magic numbers @@ -197,15 +204,13 @@ void Reader14443AAppTick(void) INLINE uint16_t Reader14443A_Deselect(uint8_t* Buffer) // deselects the card because of an error, so we will continue to select the card afterwards { Buffer[0] = 0xC2; - uint16_t crc = ISO14443_CRCA(Buffer, 1); - Buffer[1] = crc; - Buffer[2] = crc >> 8; + ISO14443AAppendCRCA(Buffer, 1); ReaderState = STATE_DESELECT; Selected = false; return addParityBits(Buffer, 24); } -INLINE uint16_t Reader14443A_Select(uint8_t * Buffer, uint16_t BitCount) +static uint16_t Reader14443A_Select(uint8_t * Buffer, uint16_t BitCount) { if (Selected) { @@ -343,21 +348,17 @@ INLINE uint16_t Reader14443A_Halt(uint8_t* Buffer) { Buffer[0] = ISO14443A_CMD_HLTA; Buffer[1] = 0x00; - uint16_t crc = ISO14443_CRCA(Buffer, 2); - Buffer[2] = crc; - Buffer[3] = crc >> 8; + ISO14443AAppendCRCA(Buffer, 2); ReaderState = STATE_HALT; Selected = false; - return addParityBits(Buffer, 32); + return addParityBits(Buffer, 4 * BITS_PER_BYTE); } INLINE uint16_t Reader14443A_RATS(uint8_t* Buffer) { Buffer[0] = 0xE0; // RATS command Buffer[1] = 0x80; - uint16_t crc = ISO14443_CRCA(Buffer, 2); - Buffer[2] = crc; - Buffer[3] = crc >> 8; + ISO14443AAppendCRCA(Buffer, 2); ReaderState = STATE_ATS; return addParityBits(Buffer, 32); } @@ -457,77 +458,143 @@ uint16_t Reader14443AAppProcess(uint8_t* Buffer, uint16_t BitCount) RT_STATE_IDLE, RT_STATE_SEARCHING } RTState = RT_STATE_IDLE; - static uint8_t ErrorCount = 0; - static uint8_t Thresholds[(CODEC_THRESHOLD_CALIBRATE_MAX - CODEC_THRESHOLD_CALIBRATE_MIN) / CODEC_THRESHOLD_CALIBRATE_STEPS] = {0}; // todo this could be improved by using the bits and not the whole bytes + static uint8_t TryCount = 0; + static uint8_t Thresholds[(CODEC_THRESHOLD_CALIBRATE_MAX - CODEC_THRESHOLD_CALIBRATE_MIN) / CODEC_THRESHOLD_CALIBRATE_STEPS] = {0}; if (RTState == RT_STATE_IDLE) { CodecThresholdSet(CODEC_THRESHOLD_CALIBRATE_MIN); RTState = RT_STATE_SEARCHING; - ErrorCount = 0; - } else if (RTState == RT_STATE_SEARCHING && ReaderState <= STATE_HALT && ErrorCount++ == 8) { - if (CodecThresholdIncrement() >= CODEC_THRESHOLD_CALIBRATE_MAX) + TryCount = 0; + } else if (RTState == RT_STATE_SEARCHING && ReaderState <= STATE_HALT) { + if (++TryCount == TRYCOUNT_MAX) { - RTState = RT_STATE_IDLE; - - bool block = false; - uint16_t min = 0; - uint16_t max = 0; - uint16_t maxdiff = 0; - uint16_t maxdiffoffset = 0; - - CommandLinePendingTaskFinished(COMMAND_INFO_OK_WITH_TEXT_ID, NULL); - uint16_t i; - for (i = 0; i < (CODEC_THRESHOLD_CALIBRATE_MAX - CODEC_THRESHOLD_CALIBRATE_MIN) / CODEC_THRESHOLD_CALIBRATE_STEPS; i++) + uint16_t tmp_th = CodecThresholdIncrement(); + if ((tmp_th >= CODEC_THRESHOLD_CALIBRATE_MID && (tmp_th - CODEC_THRESHOLD_CALIBRATE_STEPS) < CODEC_THRESHOLD_CALIBRATE_MID) + || + tmp_th >= CODEC_THRESHOLD_CALIBRATE_MAX) { - char tmpBuf[10]; - snprintf(tmpBuf, 10, "%4" PRIu16 ": ", i*CODEC_THRESHOLD_CALIBRATE_STEPS + CODEC_THRESHOLD_CALIBRATE_MIN); - TerminalSendString(tmpBuf); - if (Thresholds[i]) + bool block = false, finished = false; + uint16_t min = 0; + uint16_t max = 0; + uint16_t maxdiff = 0; + uint16_t maxdiffoffset = 0; + uint16_t numworked; + uint16_t i; + + // first, search inside the usual search space + if (tmp_th >= CODEC_THRESHOLD_CALIBRATE_MID && (tmp_th - CODEC_THRESHOLD_CALIBRATE_STEPS) < CODEC_THRESHOLD_CALIBRATE_MID) { - TerminalSendStringP(PSTR("+\r\n")); - if (!block) + for (i = 0; i < (CODEC_THRESHOLD_CALIBRATE_MID - CODEC_THRESHOLD_CALIBRATE_MIN) / CODEC_THRESHOLD_CALIBRATE_STEPS; i++) { - block = true; - min = i; - } - } else { - TerminalSendStringP(PSTR("-\r\n")); - if (block) - { - block = false; - max = i; - if ((max - min) > maxdiff) + if (Thresholds[i] == TRYCOUNT_MAX) { - maxdiff = max - min; - maxdiffoffset = min; + if (!block) + { + block = true; + min = i; + } + } else { + if (block) + { + block = false; + max = i; + if ((max - min) >= maxdiff) + { + maxdiff = max - min; + maxdiffoffset = min; + } + } } } + if (maxdiff >= 4) // if we have found something with at least 5 consecutive working thresholds (only if these thresholds have worked for evers attempt), we are done + { + finished = true; + } + } else { // we have searched the whole space + for (numworked = TRYCOUNT_MAX; numworked > 0; numworked--) + { + for (i = 0; i < (CODEC_THRESHOLD_CALIBRATE_MAX - CODEC_THRESHOLD_CALIBRATE_MIN) / CODEC_THRESHOLD_CALIBRATE_STEPS; i++) + { + if (Thresholds[i] >= numworked) + { + if (!block) + { + block = true; + min = i; + } + } else { + if (block) + { + block = false; + max = i; + if ((max - min) >= maxdiff) + { + maxdiff = max - min; + maxdiffoffset = min; + } + } + } + } + if (maxdiff > 0) + { + break; + } + } + + finished = true; + } + + if (finished) + { + + RTState = RT_STATE_IDLE; + + if (maxdiff != 0) + CodecThresholdSet((maxdiffoffset + maxdiff / 2) * CODEC_THRESHOLD_CALIBRATE_STEPS + CODEC_THRESHOLD_CALIBRATE_MIN); + else + CodecThresholdReset(); + + CommandLinePendingTaskFinished(COMMAND_INFO_OK_WITH_TEXT_ID, NULL); + uint16_t i_max = (CODEC_THRESHOLD_CALIBRATE_MID - CODEC_THRESHOLD_CALIBRATE_MIN) / CODEC_THRESHOLD_CALIBRATE_STEPS; + if (tmp_th >= CODEC_THRESHOLD_CALIBRATE_MAX) + i_max = (CODEC_THRESHOLD_CALIBRATE_MAX - CODEC_THRESHOLD_CALIBRATE_MIN) / CODEC_THRESHOLD_CALIBRATE_STEPS; + for (i = 0; i < i_max; i++) + { + char tmpBuf[10]; + snprintf(tmpBuf, 10, "%4" PRIu16 ": ", i*CODEC_THRESHOLD_CALIBRATE_STEPS + CODEC_THRESHOLD_CALIBRATE_MIN); + TerminalSendString(tmpBuf); + if (Thresholds[i]) + { + snprintf(tmpBuf, 10, "%3" PRIu16, Thresholds[i]); + TerminalSendString(tmpBuf); + } else { + TerminalSendChar('-'); + } + TerminalSendStringP(PSTR("\r\n")); + Thresholds[i] = 0; // reset the threshold so the next run won't show old results + } + + + Selected = false; + Reader14443CurrentCommand = Reader14443_Do_Nothing; + Reader14443ACodecReset(); + return 0; } - Thresholds[i] = 0; // reset the threshold so the next run won't show old results } - - if (maxdiff != 0) - CodecThresholdSet((maxdiffoffset + maxdiff / 2) * CODEC_THRESHOLD_CALIBRATE_STEPS + CODEC_THRESHOLD_CALIBRATE_MIN); - else - CodecThresholdReset(); - - Selected = false; - Reader14443CurrentCommand = Reader14443_Do_Nothing; - CodecReaderFieldStop(); - return 0; + TryCount = 0; } - - Thresholds[(ReaderThreshold - CODEC_THRESHOLD_CALIBRATE_MIN) / CODEC_THRESHOLD_CALIBRATE_STEPS] = 0; - CodecThresholdIncrement(); - ErrorCount = 0; } uint16_t rVal = Reader14443A_Select(Buffer, BitCount); if (Selected) // we are done finding the threshold { - Thresholds[(ReaderThreshold - CODEC_THRESHOLD_CALIBRATE_MIN) / CODEC_THRESHOLD_CALIBRATE_STEPS] = 1; - CodecThresholdIncrement(); + Thresholds[(ReaderThreshold - CODEC_THRESHOLD_CALIBRATE_MIN) / CODEC_THRESHOLD_CALIBRATE_STEPS] += 1; + if (TryCount == TRYCOUNT_MAX) + { + CodecThresholdIncrement(); + TryCount = 0; + } ReaderState = STATE_IDLE; Reader14443ACodecStart(); return Reader14443A_Halt(Buffer); @@ -592,13 +659,11 @@ uint16_t Reader14443AAppProcess(uint8_t* Buffer, uint16_t BitCount) } Buffer[0] = 0x30; // MiFare Ultralight read command Buffer[1] = MFURead_CurrentAdress; - uint16_t crc = ISO14443_CRCA(Buffer, 2); - Buffer[2] = crc; - Buffer[3] = crc >> 8; + ISO14443AAppendCRCA(Buffer, 2); MFURead_CurrentAdress += 4; - return addParityBits(Buffer, 32); + return addParityBits(Buffer, 4 * BITS_PER_BYTE); } return rVal; } diff --git a/Firmware/Chameleon-Mini/Application/Reader14443A.h b/Firmware/Chameleon-Mini/Application/Reader14443A.h index e9519ed..edc83b2 100644 --- a/Firmware/Chameleon-Mini/Application/Reader14443A.h +++ b/Firmware/Chameleon-Mini/Application/Reader14443A.h @@ -19,7 +19,6 @@ uint16_t Reader14443AAppProcess(uint8_t* Buffer, uint16_t BitCount); uint16_t addParityBits(uint8_t * Buffer, uint16_t bits); uint16_t removeParityBits(uint8_t * Buffer, uint16_t BitCount); -uint16_t removeSOC(uint8_t * Buffer, uint16_t BitCount); bool checkParityBits(uint8_t * Buffer, uint16_t BitCount); uint16_t ISO14443_CRCA(uint8_t * Buffer, uint8_t ByteCount); diff --git a/Firmware/Chameleon-Mini/Codec/Codec.h b/Firmware/Chameleon-Mini/Codec/Codec.h index 38927dc..54f9e9d 100644 --- a/Firmware/Chameleon-Mini/Codec/Codec.h +++ b/Firmware/Chameleon-Mini/Codec/Codec.h @@ -74,8 +74,9 @@ #define CODEC_AC_DEMOD_SETTINGS AC_HSMODE_bm | AC_HYSMODE_NO_gc #define CODEC_MAXIMUM_THRESHOLD 0xFFF // the maximum voltage can be calculated with ch0data * Vref / 0xFFF #define CODEC_THRESHOLD_CALIBRATE_MIN 128 -#define CODEC_THRESHOLD_CALIBRATE_MAX 1024 -#define CODEC_THRESHOLD_CALIBRATE_STEPS 8 +#define CODEC_THRESHOLD_CALIBRATE_MID 768 +#define CODEC_THRESHOLD_CALIBRATE_MAX 2048 +#define CODEC_THRESHOLD_CALIBRATE_STEPS 16 #define CODEC_TIMER_TIMESTAMPS TCD1 #define CODEC_TIMER_TIMESTAMPS_CCA_VECT TCD1_CCA_vect diff --git a/Firmware/Chameleon-Mini/Codec/Reader14443-2A.c b/Firmware/Chameleon-Mini/Codec/Reader14443-2A.c index 26afb64..6a70968 100644 --- a/Firmware/Chameleon-Mini/Codec/Reader14443-2A.c +++ b/Firmware/Chameleon-Mini/Codec/Reader14443-2A.c @@ -28,12 +28,8 @@ static volatile uint16_t RxPendingSince; static volatile enum { STATE_IDLE, - STATE_MILLER_SOF0, - STATE_MILLER_SOF1, - STATE_MILLER_TX0, - STATE_MILLER_TX1, - STATE_MILLER_EOF0, - STATE_MILLER_EOF1, + STATE_MILLER_SEND, + STATE_MILLER_EOF, STATE_FDT } State; @@ -54,11 +50,13 @@ void Reader14443ACodecInit(void) { CodecInitCommon(); CodecSetDemodPower(true); - CODEC_TIMER_SAMPLING.PER = SAMPLE_RATE_SYSTEM_CYCLES/2 - 1; - CODEC_TIMER_SAMPLING.CCB = 1; + CODEC_TIMER_SAMPLING.PER = SAMPLE_RATE_SYSTEM_CYCLES - 1; + CODEC_TIMER_SAMPLING.CCB = 0; + CODEC_TIMER_SAMPLING.CCC = 0; CODEC_TIMER_SAMPLING.CTRLA = TC_CLKSEL_OFF_gc; CODEC_TIMER_SAMPLING.INTCTRLA = 0; - CODEC_TIMER_SAMPLING.INTCTRLB = TC_CCBINTLVL_HI_gc; + CODEC_TIMER_SAMPLING.INTCTRLB = TC_CCBINTLVL_OFF_gc; + CODEC_TIMER_SAMPLING.CTRLA = TC_CLKSEL_DIV1_gc; CODEC_TIMER_LOADMOD.CTRLA = 0; State = STATE_IDLE; @@ -80,6 +78,22 @@ void Reader14443ACodecDeInit(void) { Flags.Start = false; } +INLINE void Insert0(void) +{ + SampleRegister >>= 1; + if (++BitCount % 8) + return; + *CodecBufferPtr++ = SampleRegister; +} + +INLINE void Insert1(void) +{ + SampleRegister = (SampleRegister >> 1) | 0x80; + if (++BitCount % 8) + return; + *CodecBufferPtr++ = SampleRegister; +} + INLINE void Reader14443A_EOC(void) { CODEC_TIMER_LOADMOD.INTCTRLB = 0; @@ -88,6 +102,14 @@ INLINE void Reader14443A_EOC(void) CODEC_TIMER_TIMESTAMPS.CTRLA = TC_CLKSEL_OFF_gc; ACA.AC1CTRL &= ~AC_ENABLE_bm; + if (BitCount & 1) + { + if (SampleRegister & 0x80) + Insert0(); + else + Insert1(); + } + if (BitCount % 8) // copy the last byte, if there is an incomplete byte CodecBuffer[BitCount / 8] = SampleRegister >> (8 - (BitCount % 8)); Flags.RxDone = true; @@ -104,140 +126,96 @@ INLINE void Reader14443A_EOC(void) State = STATE_FDT; } -ISR(CODEC_TIMER_SAMPLING_CCB_VECT) +INLINE void BufferToSequence(void) { - static void * JumpTable[] = { - [STATE_IDLE] = &&STATE_IDLE_LABEL, - [STATE_MILLER_SOF0] = &&STATE_MILLER_SOF0_LABEL, - [STATE_MILLER_SOF1] = &&STATE_MILLER_SOF1_LABEL, - [STATE_MILLER_TX0] = &&STATE_MILLER_TX0_LABEL, - [STATE_MILLER_TX1] = &&STATE_MILLER_TX1_LABEL, - [STATE_MILLER_EOF0] = &&STATE_MILLER_EOF0_LABEL, - [STATE_MILLER_EOF1] = &&STATE_MILLER_EOF1_LABEL, - [STATE_FDT] = &&STATE_FDT_LABEL - }; - - goto *JumpTable[State]; - - STATE_MILLER_SOF0_LABEL: - /* Output a SOF */ - CodecSetReaderField(false); - _delay_us(2.5); - CodecSetReaderField(true); - - CodecBufferIdx = 0; - SampleRegister = CodecBuffer[0]; - BitCountUp = 0; - - State = STATE_MILLER_SOF1; + uint16_t count = BitCount; + if (count > BITS_PER_BYTE * CODEC_BUFFER_SIZE / 2) // todo is this correct? return; - STATE_MILLER_SOF1_LABEL: - State = STATE_MILLER_TX0; - return; + BitCount = 0; - STATE_MILLER_TX0_LABEL: - /* First half of bit-slot */ - if (SampleRegister & 0x01) { - /* Do Nothing */ + memcpy(CodecBuffer + CODEC_BUFFER_SIZE / 2, CodecBuffer, (count + 7) / 8); + uint8_t * Buffer = CodecBuffer + CODEC_BUFFER_SIZE / 2; + CodecBufferPtr = CodecBuffer; + + Insert1(); // SOC + Insert0(); + + uint16_t i; + uint8_t last = 0; + for (i = 1; i <= count; i++) + { + if ((*Buffer) & 1) + { + Insert0(); + Insert1(); + last = 1; } else { - if (LastBit == 0) { - CodecSetReaderField(false); - _delay_us(2.5); - CodecSetReaderField(true); + if (last) + { + Insert0(); + Insert0(); } else { - /* Do Nothing */ + Insert1(); + Insert0(); } + last = 0; } - - State = STATE_MILLER_TX1; - return; - - STATE_MILLER_TX1_LABEL: - /* Second half of bit-slot */ - if (SampleRegister & 0x01) { - CodecSetReaderField(false); - _delay_us(2.5); - CodecSetReaderField(true); - LastBit = 1; - } else { - /* No modulation pauses */ - LastBit = 0; - } - - SampleRegister >>= 1; - if (--BitCount == 0) { - State = STATE_MILLER_EOF0; - } else { - State = STATE_MILLER_TX0; - } - - if ((++BitCountUp % 8) == 0) - SampleRegister = CodecBuffer[++CodecBufferIdx]; - return; - - STATE_MILLER_EOF0_LABEL: - if (LastBit == 0) { - CodecSetReaderField(false); - _delay_us(2.5); - CodecSetReaderField(true); - } else { - /* Do Nothing */ - } - - State = STATE_MILLER_EOF1; - return; - - STATE_MILLER_EOF1_LABEL: - CODEC_TIMER_SAMPLING.CTRLA = TC_CLKSEL_OFF_gc; - _delay_us(50); // wait for DEMOD signal to stabilize - - /* Enable the AC interrupt, which either finds the SOC and then starts the pause-finding timer, - * or it is triggered before the SOC, which mostly isn't bad at all, since the first pause - * needs to be found. */ - ACA.STATUS = AC_AC1IF_bm; - ACA.AC1CTRL = AC_HSMODE_bm | AC_HYSMODE_NO_gc | AC_INTMODE_BOTHEDGES_gc | AC_INTLVL_HI_gc | AC_ENABLE_bm; - - CodecBufferPtr = CodecBuffer; // use GPIOR for faster access - BitCount = 1; // the first modulation of the SOC is "found" implicitly - SampleRegister = 0x80; - - RxPendingSince = SystemGetSysTick(); - Flags.RxPending = true; - - // reset for future use - CodecBufferIdx = 0; - BitCountUp = 0; - - State = STATE_IDLE; - return; - - STATE_IDLE_LABEL: - STATE_FDT_LABEL: - return; + *Buffer >>= 1; + if ((i % 8) == 0) + Buffer++; } + if (last == 0) // EOC + { + Insert1(); + Insert0(); + } + + if (BitCount % 8) + CodecBuffer[BitCount / 8] = SampleRegister >> (8 - (BitCount % 8)); +} + +ISR (TCD0_CCC_vect) +{ + CODEC_TIMER_SAMPLING.INTFLAGS = TC0_CCCIF_bm; + CODEC_TIMER_SAMPLING.INTCTRLB = TC_CCCINTLVL_OFF_gc; + + /* Enable the AC interrupt, which either finds the SOC and then starts the pause-finding timer, + * or it is triggered before the SOC, which mostly isn't bad at all, since the first pause + * needs to be found. */ + ACA.STATUS = AC_AC1IF_bm; + ACA.AC1CTRL = AC_HSMODE_bm | AC_HYSMODE_NO_gc | AC_INTMODE_FALLING_gc | AC_INTLVL_HI_gc | AC_ENABLE_bm; + + CodecBufferPtr = CodecBuffer; // use GPIOR for faster access + BitCount = 1; // FALSCH todo the first modulation of the SOC is "found" implicitly + SampleRegister = 0x00; + + RxPendingSince = SystemGetSysTick(); + Flags.RxPending = true; + + // reset for future use + CodecBufferIdx = 0; + BitCountUp = 0; + + State = STATE_IDLE; + PORTE.OUTTGL = PIN3_bm; +} + +void Reader14443AMillerEOC(void) +{ + CODEC_TIMER_SAMPLING.PER = 5*SAMPLE_RATE_SYSTEM_CYCLES - 1; + CODEC_TIMER_SAMPLING.INTFLAGS = TC0_CCBIF_bm | TC0_CCCIF_bm; + CODEC_TIMER_SAMPLING.INTCTRLB = TC_CCBINTLVL_OFF_gc | TC_CCCINTLVL_HI_gc; + CODEC_TIMER_SAMPLING.PERBUF = SAMPLE_RATE_SYSTEM_CYCLES - 1; + PORTE.OUTTGL = PIN3_bm; +} + ISR(CODEC_TIMER_TIMESTAMPS_CCA_VECT) // EOC found { Reader14443A_EOC(); } -INLINE void Insert0(void) -{ - SampleRegister >>= 1; - if (++BitCount % 8) - return; - *CodecBufferPtr++ = SampleRegister; -} - -INLINE void Insert1(void) -{ - SampleRegister = (SampleRegister >> 1) | 0x80; - if (++BitCount % 8) - return; - *CodecBufferPtr++ = SampleRegister; -} - ISR(ACA_AC1_vect) // this interrupt either finds the SOC or gets triggered before { ACA.AC1CTRL &= ~AC_INTLVL_HI_gc; // disable this interrupt @@ -253,27 +231,36 @@ ISR(CODEC_TIMER_LOADMOD_CCA_VECT) // pause found /* This needs to be done only on the first call, * but doing this only on a condition means wasting time, so we do it every time. */ - CODEC_TIMER_TIMESTAMPS.CTRLA = TC_CLKSEL_DIV2_gc; + CODEC_TIMER_TIMESTAMPS.CTRLA = TC_CLKSEL_DIV4_gc; switch (tmp) // decide how many half bit periods have been modulations { - case 0 ... 96: // 64 ticks is one half of a bit period - Insert0(); - // If one full bit period is over AND the last half bit period also was a pause, - // we have found the EOC. - if ((BitCount & 1) == 0 && (SampleRegister & 0x40) == 0) - Reader14443A_EOC(); + case 0 ... 48: // 32 ticks is one half of a bit period return; - case 97 ... 160: // 128 ticks is a full bit period + case 49 ... 80: // 64 ticks are a full bit period Insert1(); Insert0(); return; - default: // every value over 128 + 32 (tolerance) is considered to be 3 half bit periods + case 81 ... 112: // 96 ticks are 3 half bit periods + if (BitCount & 1) + { + Insert1(); + Insert1(); + Insert0(); + } else { + Insert1(); + Insert0(); + Insert0(); + } + return; + + default: // every value over 96 + 16 (tolerance) is considered to be 4 half bit periods Insert1(); Insert1(); Insert0(); + Insert0(); return; } return; @@ -283,6 +270,7 @@ void Reader14443ACodecTask(void) { if (Flags.RxPending && SYSTICK_DIFF(RxPendingSince) > Reader_FWT + 1) { + Reader14443A_EOC(); BitCount = 0; Flags.RxDone = true; Flags.RxPending = false; @@ -307,7 +295,7 @@ void Reader14443ACodecTask(void) BitCount = 0; bool breakflag = false; - TmpCodecBuffer[0] >>= 2; // with this (and BitCountTmp = 2), the SOC is ignored and thus it's not neccessary to remove it later + TmpCodecBuffer[0] >>= 2; // with this (and BitCountTmp = 2), the SOC is ignored while (!breakflag && BitCountTmp < TotalBitCount) { uint8_t Bit = TmpCodecBuffer[BitCountTmp / 8] & 0x03; @@ -315,18 +303,14 @@ void Reader14443ACodecTask(void) switch (Bit) { case 0b10: - Insert0(); - break; - - case 0b01: Insert1(); break; + case 0b01: + Insert0(); + break; + case 0b00: // EOC - if (BitCount % 8) // copy the last byte, if there is an incomplete byte - CodecBuffer[BitCount / 8] = SampleRegister >> (8 - (BitCount % 8)); - LEDHook(LED_CODEC_RX, LED_PULSE); - LogEntry(LOG_INFO_CODEC_RX_DATA_W_PARITY, CodecBuffer, (BitCount + 7) / 8); breakflag = true; break; @@ -336,6 +320,10 @@ void Reader14443ACodecTask(void) } BitCountTmp += 2; } + if (BitCount % 8) // copy the last byte, if there is an incomplete byte + CodecBuffer[BitCount / 8] = SampleRegister >> (8 - (BitCount % 8)); + LEDHook(LED_CODEC_RX, LED_PULSE); + LogEntry(LOG_INFO_CODEC_RX_DATA_W_PARITY, CodecBuffer, (BitCount + 7) / 8); } } Flags.Start = false; @@ -357,12 +345,12 @@ void Reader14443ACodecTask(void) */ /* Configure and enable the analog comparator for finding pauses in the DEMOD signal. */ - ACA.AC1CTRL = AC_HSMODE_bm | AC_HYSMODE_NO_gc | AC_INTMODE_BOTHEDGES_gc | AC_ENABLE_bm; + ACA.AC1CTRL = AC_HSMODE_bm | AC_HYSMODE_NO_gc | AC_INTMODE_FALLING_gc | AC_ENABLE_bm; /* This timer will be used to detect the pauses between the modulation sequences. */ CODEC_TIMER_LOADMOD.CTRLA = 0; CODEC_TIMER_LOADMOD.CNT = 0; - CODEC_TIMER_LOADMOD.PER = 127; // with 27.12 MHz this is exactly one half bit width + CODEC_TIMER_LOADMOD.PER = 0xFFFF; // with 27.12 MHz this is exactly one half bit width CODEC_TIMER_LOADMOD.CCA = 95; // with 27.12 MHz this is 3/4 of a half bit width CODEC_TIMER_LOADMOD.INTCTRLA = 0; CODEC_TIMER_LOADMOD.INTFLAGS = TC1_CCAIF_bm; @@ -371,7 +359,7 @@ void Reader14443ACodecTask(void) /* This timer will be used to find out how many bit halfs since the last pause have been passed. */ CODEC_TIMER_TIMESTAMPS.CNT = 0; CODEC_TIMER_TIMESTAMPS.PER = 0xFFFF; - CODEC_TIMER_TIMESTAMPS.CCA = 256; + CODEC_TIMER_TIMESTAMPS.CCA = 160; CODEC_TIMER_TIMESTAMPS.INTCTRLA = 0; CODEC_TIMER_TIMESTAMPS.INTFLAGS = TC1_CCAIF_bm; CODEC_TIMER_TIMESTAMPS.INTCTRLB = TC_CCAINTLVL_LO_gc; @@ -387,10 +375,12 @@ void Reader14443ACodecTask(void) LogEntry(LOG_INFO_CODEC_TX_DATA_W_PARITY, CodecBuffer, (BitCount + 7) / 8); /* Set state and start timer for Miller encoding. */ - State = STATE_MILLER_SOF0; - LastBit = 0; - CODEC_TIMER_SAMPLING.CNT = 0; - CODEC_TIMER_SAMPLING.CTRLA = TC_CLKSEL_DIV1_gc; + BufferToSequence(); + State = STATE_MILLER_SEND; + CodecBufferPtr = CodecBuffer; + CODEC_TIMER_SAMPLING.INTFLAGS = TC0_CCBIF_bm; + CODEC_TIMER_SAMPLING.INTCTRLB = TC_CCBINTLVL_HI_gc; + _delay_loop_1(85); } } } diff --git a/Firmware/Chameleon-Mini/Codec/Reader14443-2A.h b/Firmware/Chameleon-Mini/Codec/Reader14443-2A.h index 5abca80..ed6fec8 100644 --- a/Firmware/Chameleon-Mini/Codec/Reader14443-2A.h +++ b/Firmware/Chameleon-Mini/Codec/Reader14443-2A.h @@ -19,5 +19,7 @@ void Reader14443ACodecTask(void); /* Application Interface */ void Reader14443ACodecStart(void); void Reader14443ACodecReset(void); +bool Reader14443ACodecSendingOrReceiving(void); +void Reader14443AMillerEOC(void); #endif /* READER14443_2A_H_ */ diff --git a/Firmware/Chameleon-Mini/Codec/Reader14443-ISR.S b/Firmware/Chameleon-Mini/Codec/Reader14443-ISR.S new file mode 100644 index 0000000..f41b045 --- /dev/null +++ b/Firmware/Chameleon-Mini/Codec/Reader14443-ISR.S @@ -0,0 +1,110 @@ +#include + +#define Zero R0 +#define Tmp R16 +#define BitCountL R18 +#define BitCountH R19 +#define NewLoad R20 +#define SampleRegister R21 + +#define GPIORBitCountL 4 +#define GPIORBitCountH 5 +#define CodecBufferPtrL 10 +#define CodecBufferPtrH 11 + +#define AWEXC__OUTOVEN 0x088C +#define CODEC_READER_TIMER__CTRLA 0x0800 + +.global TCD0_CCB_vect, Reader14443AMillerEOC +TCD0_CCB_vect: +push Zero ; 1 +eor Zero, Zero ; 1 +push Tmp ; 1 +push BitCountL ; 1 +push BitCountH ; 1 +push NewLoad ; 1 +push SampleRegister ; 1 +in Tmp, 0x3f ; SREG ; 1 +push Tmp ; 1 +push ZL ; 1 +push ZH ; 1 + ; SUM: 8 +in ZL, CodecBufferPtrL +in ZH, CodecBufferPtrH +ld SampleRegister, Z+ +clr NewLoad + +in BitCountH, GPIORBitCountH +in BitCountL, GPIORBitCountL + + + +LOOP: +; POINT ZERO +lsr SampleRegister ; 1 +brcc NO_TURNOFF_COMPENSATION ; 1 / 2 + + sts AWEXC__OUTOVEN, Zero ; turn off field ; 2 + sts CODEC_READER_TIMER__CTRLA, Zero ; 2 + + ldi Tmp, 0x16 ; 1 + TURNOFF_LOOP: + dec Tmp ; 1 + brne TURNOFF_LOOP ; 1 / 2 ; sums up to 21 * 3 + 2 + rjmp .+0 ; double nop ; 2 + + ldi Tmp, 0x01 ; 1 + sts CODEC_READER_TIMER__CTRLA, Tmp ; turn on field ; 2 + ldi Tmp, 0x03 ; 1 + sts AWEXC__OUTOVEN, Tmp ; 2 + + rjmp NO_TURNOFF ; 2 + ; SUM: 82 until now + + +NO_TURNOFF_COMPENSATION: +ldi Tmp, 26 ; 1 +NO_TURNOFF_COMPENSATION_LOOP: + dec Tmp ; 1 + brne NO_TURNOFF_COMPENSATION_LOOP ; 1 / 2 sums up to 25 * 3 + 2 +nop ; 1 +NO_TURNOFF: ; 82 at this point +subi BitCountL, 1 ; decrement BitCount ; 1 +sbci BitCountH, 0 ; 1 +brne NO_EOC ; 1 / 2 + + ; EOC: + call Reader14443AMillerEOC + rjmp RETURN + +NO_EOC: ; 86 at this point +subi NewLoad, 0xFF ; 1 +andi NewLoad, 0x07 ; 1 +brne LOAD_COMPENSATION ; 1 / 2 + ; SUM: 4 + ld SampleRegister, Z+ ; 3 + rjmp NOP_LOOP_INIT ; 2 + +LOAD_COMPENSATION: ; 90 at this point +rjmp .+0 ; double nop ; 2 +rjmp .+0 ; double nop ; 2 +NOP_LOOP_INIT: +ldi Tmp, 10 ; 1 +NOP_LOOP: + dec Tmp ; 1 + brne NOP_LOOP ; 1 / 2 sums up to 9 * 3 + 2 +rjmp .+0 ; 2 +rjmp LOOP ; 2 + +RETURN: +pop ZH +pop ZL +pop Tmp +out 0x3f, Tmp ; SREG +pop SampleRegister +pop NewLoad +pop BitCountH +pop BitCountL +pop Tmp +pop Zero +reti ; 2 diff --git a/Firmware/Chameleon-Mini/Makefile b/Firmware/Chameleon-Mini/Makefile index 6ce0adc..9ac56db 100644 --- a/Firmware/Chameleon-Mini/Makefile +++ b/Firmware/Chameleon-Mini/Makefile @@ -90,7 +90,7 @@ TARGET = Chameleon-Mini OPTIMIZATION = s SRC += $(TARGET).c LUFADescriptors.c System.c Configuration.c Random.c Common.c Memory.c MemoryAsm.S Button.c Log.c Settings.c LED.c Map.c AntennaLevel.c SRC += Terminal/Terminal.c Terminal/Commands.c Terminal/XModem.c Terminal/CommandLine.c -SRC += Codec/Codec.c Codec/ISO14443-2A.c Codec/Reader14443-2A.c +SRC += Codec/Codec.c Codec/ISO14443-2A.c Codec/Reader14443-2A.c Codec/Reader14443-ISR.S SRC += Application/MifareUltralight.c Application/MifareClassic.c Application/ISO14443-3A.c Application/Crypto1.c Application/Reader14443A.c SRC += $(LUFA_SRC_USB) $(LUFA_SRC_USBCLASS) LUFA_PATH = ../LUFA