mirror of
https://github.com/RfidResearchGroup/ChameleonMini.git
synced 2026-05-12 11:20:37 -07:00
reader codec upgrade
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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_ */
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
#include <avr/io.h>
|
||||
|
||||
#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
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user