mirror of
https://github.com/RfidResearchGroup/ChameleonMini.git
synced 2026-05-12 11:20:37 -07:00
Requires enabling in Makefile and disabling other options to fit in program space below 64k
720 lines
29 KiB
C
720 lines
29 KiB
C
/*
|
||
* ISO15693.c
|
||
*
|
||
* Created on: 25.01.2017
|
||
* Author: Phillip Nash
|
||
* Modified by: ceres-c
|
||
*
|
||
* Interrupts information
|
||
* - Compare/Capture Channels are used as compare. They're used to sample the field in different moments of time
|
||
* - Reader field demodulation events are routed on Event Channel 0, card data modulation on Event Channel 6
|
||
* - Reader field demodulation uses Compare Channel C, card data modulation Compare Channel D (PORTB in both cases, of course)
|
||
*
|
||
* Loadmod code flow:
|
||
* Loadmodulation is performed via a state machine inside a ISR, triggered by a counter's (TCD0) overflow interrupt. The interrupt
|
||
* is configured in StartISO15693Demod to have a PER (period) equal to ISO15693 t1 nominal time: 4352 carrier pulses.
|
||
* (see ISO15693-3:2009, section 9.1).
|
||
* Once a EOF is reached when demodulating data from the reader, TCD0's PERBUF register is configured with the period of
|
||
* bit transmission (32 carrier pulses). This means that, once the first period overflow is reached (t1 expiration)
|
||
* the ISR will output data at the frequency dictated by the standard. This is due to the behaviour of buffered registers, see
|
||
* 8045A-AVR-02/08 section 3.7.
|
||
*
|
||
* Once t1 is reached (the counter overflowed), transmission of data should theoretically begin, but this is not guaranteed,
|
||
* due to possible slowdowns of data generation in the currently running Application. Until the application is done,
|
||
* the state machine will be stuck in LOADMOD_WAIT state, not outputting any data.
|
||
* The ISR will now be invoked every 32 carrier pulses (see ISO15693-2:2006, section 8.2), even when waiting for data.
|
||
*/
|
||
|
||
#include "ISO15693.h"
|
||
#include "../System.h"
|
||
#include "../Application/Application.h"
|
||
#include "LEDHook.h"
|
||
#include "AntennaLevel.h"
|
||
#include "Terminal/Terminal.h"
|
||
#include <avr/io.h>
|
||
|
||
|
||
#define SOC_1_OF_4_CODE 0x7B
|
||
#define SOC_1_OF_256_CODE 0x7E
|
||
#define EOC_CODE 0xDF
|
||
#define REQ_SUBCARRIER_SINGLE 0x00
|
||
#define REQ_SUBCARRIER_DUAL 0x01
|
||
#define REQ_DATARATE_LOW 0x00
|
||
#define REQ_DATARATE_HIGH 0x02
|
||
#define ISO15693_SAMPLE_CLK TC_CLKSEL_DIV2_gc // 13.56MHz
|
||
#define ISO15693_SAMPLE_PERIOD 128 // 9.4us
|
||
#define ISO15693_T1_TIME 4352 - 14 /* ISO t1 time - ISR prologue compensation */ // 4192 + 128 + 128 - 1
|
||
|
||
#define SUBCARRIER_1 32
|
||
#define SUBCARRIER_2 28
|
||
#define SUBCARRIER_OFF 0
|
||
#define SOF_PATTERN 0x1D // 0001 1101
|
||
#define EOF_PATTERN 0xB8 // 1011 1000
|
||
|
||
// These registers provide quick access but are limited
|
||
// so global vars will be necessary
|
||
#define DataRegister Codec8Reg0
|
||
#define StateRegister Codec8Reg1
|
||
#define ModulationPauseCount Codec8Reg2
|
||
#define SampleRegister Codec8Reg3
|
||
#define BitSent CodecCount16Register1
|
||
#define BitSampleCount CodecCount16Register2
|
||
#define CodecBufferPtr CodecPtrRegister1
|
||
|
||
static volatile struct {
|
||
volatile bool DemodFinished;
|
||
volatile bool LoadmodFinished;
|
||
} Flags = { 0 };
|
||
|
||
typedef enum {
|
||
DEMOD_SOC_STATE,
|
||
DEMOD_1_OUT_OF_4_STATE,
|
||
DEMOD_1_OUT_OF_256_STATE
|
||
} DemodStateType;
|
||
|
||
static volatile enum {
|
||
LOADMOD_WAIT,
|
||
|
||
/* Single subcarrier */
|
||
LOADMOD_START_SINGLE,
|
||
LOADMOD_SOF_SINGLE,
|
||
LOADMOD_BIT0_SINGLE,
|
||
LOADMOD_BIT1_SINGLE,
|
||
LOADMOD_EOF_SINGLE,
|
||
|
||
/* Dual subcarrier */
|
||
LOADMOD_START_DUAL,
|
||
LOADMOD_SOF_DUAL,
|
||
LOADMOD_BIT0_DUAL,
|
||
LOADMOD_BIT1_DUAL,
|
||
LOADMOD_EOF_DUAL,
|
||
|
||
LOADMOD_FINISHED
|
||
} LoadModState;
|
||
|
||
static volatile DemodStateType DemodState;
|
||
static volatile uint8_t ShiftRegister;
|
||
static volatile uint8_t ByteCount;
|
||
static volatile uint16_t BitRate1;
|
||
static volatile uint16_t BitRate2;
|
||
static volatile uint16_t SampleDataCount;
|
||
|
||
/* This function implements CODEC_DEMOD_IN_INT0_VECT interrupt vector.
|
||
* It is called when a pulse is detected in CODEC_DEMOD_IN_PORT (PORTB).
|
||
* The relevant interrupt vector was registered to CODEC_DEMOD_IN_MASK0 (PIN1) via:
|
||
* CODEC_DEMOD_IN_PORT.INT0MASK = CODEC_DEMOD_IN_MASK0;
|
||
* and unregistered writing the INT0MASK to 0
|
||
*/
|
||
ISR_SHARED isr_ISO15693_CODEC_DEMOD_IN_INT0_VECT(void) {
|
||
/* Clear Compare Channel C (CCC) interrupt Flags - From 14.12.10 [8331F–AVR–04/2013] */
|
||
CODEC_TIMER_SAMPLING.INTFLAGS = TC0_CCCIF_bm;
|
||
/* Enable compare/capture for high level interrupts on Capture Channel C for TCD0 - From 14.12.7 [8331F–AVR–04/2013] */
|
||
CODEC_TIMER_SAMPLING.INTCTRLB = TC_CCCINTLVL_HI_gc;
|
||
|
||
/* Disable this interrupt. From now on we will sample the field using our internal clock - From 13.13.11 [8331F–AVR–04/2013] */
|
||
CODEC_DEMOD_IN_PORT.INT0MASK = 0;
|
||
|
||
}
|
||
|
||
/* This function is called from isr_ISO15693_CODEC_TIMER_SAMPLING_CCC_VECT
|
||
* when we have 8 bits in SampleRegister and they represent an end of frame.
|
||
*/
|
||
INLINE void ISO15693_EOC(void) {
|
||
/* Set bitrate required by the reader on SOF for our following response */
|
||
if (CodecBuffer[0] & REQ_DATARATE_HIGH || ActiveConfiguration.TagFamily == TAG_FAMILY_ISO15693_ICLASS) {
|
||
BitRate1 = 256;
|
||
BitRate2 = 252; /* If single subcarrier mode is requested, BitRate2 is useless, but setting it nevertheless was still faster than checking */
|
||
} else {
|
||
BitRate1 = 256 * 4; // Possible value: 256 * 4 - 1
|
||
BitRate2 = 252 * 4; // Possible value: 252 * 4 - 3
|
||
}
|
||
|
||
Flags.DemodFinished = 1;
|
||
/* Disable demodulation interrupt */
|
||
/* Sets timer off for TCD0, disabling clock source. We're done receiving data from reader and don't need to probe the antenna anymore - From 14.12.1 [8331F–AVR–04/2013] */
|
||
CODEC_TIMER_SAMPLING.CTRLA = TC_CLKSEL_OFF_gc;
|
||
/* Disable event action for CODEC_TIMER_SAMPLING (TCD0) - From 14.12.4 [8331F–AVR–04/2013] */
|
||
CODEC_TIMER_SAMPLING.CTRLD = TC_EVACT_OFF_gc;
|
||
/* Disable compare/capture interrupts on Channel C - From 14.12.7 [8331F–AVR–04/2013] */
|
||
CODEC_TIMER_SAMPLING.INTCTRLB = TC_CCCINTLVL_OFF_gc;
|
||
/* Clear Compare Channel C (CCC) interrupt Flags - From 14.12.10 [8331F–AVR–04/2013] */
|
||
CODEC_TIMER_SAMPLING.INTFLAGS = TC0_CCCIF_bm;
|
||
|
||
/* Enable loadmodulation interrupt */
|
||
/* Disable the event action for TCE0 - From 14.12.4 [8331F–AVR–04/2013] */
|
||
CODEC_TIMER_LOADMOD.CTRLD = TC_EVACT_OFF_gc;
|
||
/* Enable compare/capture for high level interrupts on channel B for TCE0 - From 14.12.7 [8331F–AVR–04/2013] */
|
||
CODEC_TIMER_LOADMOD.INTCTRLB = TC_CCBINTLVL_HI_gc;
|
||
/* Clear TCE0 interrupt flags for Capture Channel B - From 14.12.10 [8331F–AVR–04/2013] */
|
||
CODEC_TIMER_LOADMOD.INTFLAGS = TC0_CCBIF_bm;
|
||
/* Set the BUFFERED PERIOD to Bitrate - 1 (because PERBUF is 0-based).
|
||
* The current PERIOD was set in StartISO15693Demod to ISO15693_T1_TIME, once ISO15693_T1_TIME is reached, this will be the new PERIOD.
|
||
* From 3.7 [8045A-AVR-02/08] */
|
||
CODEC_TIMER_LOADMOD.PERBUF = BitRate1 - 1;
|
||
}
|
||
|
||
/* Disable data demodulation interrupt and inform the codec to restart demodulation from scratch */
|
||
INLINE void ISO15693_GARBAGE(void) {
|
||
Flags.DemodFinished = 1;
|
||
|
||
/* Sets timer off for TCD0, disabling clock source. We have demodulated rubbish and don't need to probe the antenna anymore - From 14.12.1 [8331F–AVR–04/2013] */
|
||
CODEC_TIMER_SAMPLING.CTRLA = TC_CLKSEL_OFF_gc;
|
||
/* Disable event action for CODEC_TIMER_SAMPLING (TCD0) - From 14.12.4 [8331F–AVR–04/2013] */
|
||
CODEC_TIMER_SAMPLING.CTRLD = TC_EVACT_OFF_gc;
|
||
/* Disable compare/capture interrupts on Channel C - From 14.12.7 [8331F–AVR–04/2013] */
|
||
CODEC_TIMER_SAMPLING.INTCTRLB = TC_CCCINTLVL_OFF_gc;
|
||
/* Clear Compare Channel C (CCC) interrupt Flags - From 14.12.10 [8331F–AVR–04/2013] */
|
||
CODEC_TIMER_SAMPLING.INTFLAGS = TC0_CCCIF_bm;
|
||
}
|
||
|
||
/* This function is registered to CODEC_TIMER_SAMPLING (TCD0)'s Counter Channel C (CCC).
|
||
* When the timer is enabled, this is called on counter's overflow
|
||
*
|
||
* It demodulates bits received from the reader and saves them in CodecBuffer.
|
||
*
|
||
* It disables its own interrupt when an EOF is found (calling ISO15693_EOC) or when it receives garbage
|
||
*/
|
||
ISR_SHARED isr_ISO15693_CODEC_TIMER_SAMPLING_CCC_VECT(void) {
|
||
/* Shift demod data */
|
||
SampleRegister = (SampleRegister << 1) | (!(CODEC_DEMOD_IN_PORT.IN & CODEC_DEMOD_IN_MASK) ? 0x01 : 0x00);
|
||
|
||
if (++BitSampleCount == 8) {
|
||
BitSampleCount = 0;
|
||
switch (DemodState) {
|
||
case DEMOD_SOC_STATE:
|
||
if (SampleRegister == SOC_1_OF_4_CODE) {
|
||
DemodState = DEMOD_1_OUT_OF_4_STATE;
|
||
SampleDataCount = 0;
|
||
ModulationPauseCount = 0;
|
||
} else if (SampleRegister == SOC_1_OF_256_CODE) {
|
||
DemodState = DEMOD_1_OUT_OF_256_STATE;
|
||
SampleDataCount = 0;
|
||
} else { // No SOC. Restart and try again, we probably received garbage.
|
||
ISO15693_GARBAGE();
|
||
}
|
||
break;
|
||
|
||
case DEMOD_1_OUT_OF_4_STATE:
|
||
if (SampleRegister == EOC_CODE) {
|
||
ISO15693_EOC();
|
||
} else {
|
||
uint8_t SampleData = ~SampleRegister;
|
||
if (SampleData == (0x01 << 6)) {
|
||
/* ^_^^^^^^ -> 00 */
|
||
ModulationPauseCount++;
|
||
DataRegister >>= 2;
|
||
|
||
} else if (SampleData == (0x01 << 4)) {
|
||
/* ^^^_^^^^ -> 01 */
|
||
ModulationPauseCount++;
|
||
DataRegister >>= 2;
|
||
DataRegister |= 0b01 << 6;
|
||
|
||
} else if (SampleData == (0x01 << 2)) {
|
||
/* ^^^^^_^^ -> 10 */
|
||
ModulationPauseCount++;
|
||
DataRegister >>= 2;
|
||
DataRegister |= 0b10 << 6;
|
||
|
||
} else if (SampleData == (0x01 << 0)) {
|
||
/* ^^^^^^^_ -> 11 */
|
||
ModulationPauseCount++;
|
||
DataRegister >>= 2;
|
||
DataRegister |= 0b11 << 6;
|
||
}
|
||
|
||
if (ModulationPauseCount == 4) {
|
||
ModulationPauseCount = 0;
|
||
*CodecBufferPtr = DataRegister;
|
||
++CodecBufferPtr;
|
||
++ByteCount;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case DEMOD_1_OUT_OF_256_STATE:
|
||
if (SampleRegister == EOC_CODE) {
|
||
ISO15693_EOC();
|
||
} else {
|
||
uint8_t Position = ((SampleDataCount / 2) % 256) - 1;
|
||
uint8_t SampleData = ~SampleRegister;
|
||
|
||
if (SampleData == (0x01 << 6)) {
|
||
/* ^_^^^^^^ -> N-3 */
|
||
DataRegister = Position - 3;
|
||
ModulationPauseCount++;
|
||
|
||
} else if (SampleData == (0x01 << 4)) {
|
||
/* ^^^_^^^^ -> N-2 */
|
||
DataRegister = Position - 2;
|
||
ModulationPauseCount++;
|
||
|
||
} else if (SampleData == (0x01 << 2)) {
|
||
/* ^^^^^_^^ -> N-1 */
|
||
DataRegister = Position - 1;
|
||
ModulationPauseCount++;
|
||
|
||
} else if (SampleData == (0x01 << 0)) {
|
||
/* ^^^^^^^_ -> N-0 */
|
||
DataRegister = Position - 0;
|
||
ModulationPauseCount++;
|
||
}
|
||
|
||
if (ModulationPauseCount == 1) {
|
||
ModulationPauseCount = 0;
|
||
*CodecBufferPtr = DataRegister;
|
||
++CodecBufferPtr;
|
||
++ByteCount;
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
SampleDataCount++;
|
||
}
|
||
|
||
/* This function is registered to CODEC_TIMER_LOADMOD (TCE0)'s Counter Channel B (CCB).
|
||
* When the timer is enabled, this is called on counter's overflow
|
||
*
|
||
* It modulates the carrier with consuming bytes in CodecBuffer until ByteCount is 0.
|
||
*
|
||
* It disables its own interrupt when all data has been sent
|
||
*/
|
||
ISR_SHARED isr_ISO15693_CODEC_TIMER_LOADMOD_CCB_VECT(void) {
|
||
static void *JumpTable[] = {
|
||
[LOADMOD_WAIT] = && LOADMOD_WAIT_LABEL,
|
||
[LOADMOD_START_SINGLE] = && LOADMOD_START_SINGLE_LABEL,
|
||
[LOADMOD_SOF_SINGLE] = && LOADMOD_SOF_SINGLE_LABEL,
|
||
[LOADMOD_BIT0_SINGLE] = && LOADMOD_BIT0_SINGLE_LABEL,
|
||
[LOADMOD_BIT1_SINGLE] = && LOADMOD_BIT1_SINGLE_LABEL,
|
||
[LOADMOD_EOF_SINGLE] = && LOADMOD_EOF_SINGLE_LABEL,
|
||
[LOADMOD_START_DUAL] = && LOADMOD_START_DUAL_LABEL,
|
||
[LOADMOD_SOF_DUAL] = && LOADMOD_SOF_DUAL_LABEL,
|
||
[LOADMOD_BIT0_DUAL] = && LOADMOD_BIT0_DUAL_LABEL,
|
||
[LOADMOD_BIT1_DUAL] = && LOADMOD_BIT1_DUAL_LABEL,
|
||
[LOADMOD_EOF_DUAL] = && LOADMOD_EOF_DUAL_LABEL,
|
||
[LOADMOD_FINISHED] = && LOADMOD_FINISHED_LABEL
|
||
};
|
||
|
||
goto *JumpTable[StateRegister]; /* It takes 28 clock cycles to get here from function entry point */
|
||
|
||
LOADMOD_WAIT_LABEL:
|
||
/* ISO t1 is over, but application is not done generating data yet */
|
||
return;
|
||
|
||
LOADMOD_START_SINGLE_LABEL:
|
||
/* Application produced data. With this interrupt we are aligned to the bit-grid. */
|
||
ShiftRegister = SOF_PATTERN;
|
||
BitSent = 0;
|
||
/* Fallthrough */
|
||
LOADMOD_SOF_SINGLE_LABEL:
|
||
if (ShiftRegister & 0x80) {
|
||
CodecSetLoadmodState(true);
|
||
} else {
|
||
CodecSetLoadmodState(false);
|
||
}
|
||
|
||
CodecStartSubcarrier();
|
||
|
||
ShiftRegister <<= 1;
|
||
BitSent++;
|
||
|
||
if ((BitSent % 8) == 0) {
|
||
/* Last SOF bit has been put out. Start sending out data */
|
||
if (ByteCount == 0) {
|
||
StateRegister = LOADMOD_FINISHED;
|
||
} else {
|
||
StateRegister = LOADMOD_BIT0_SINGLE;
|
||
ShiftRegister = (*CodecBufferPtr++);
|
||
}
|
||
} else {
|
||
StateRegister = LOADMOD_SOF_SINGLE;
|
||
}
|
||
return;
|
||
|
||
LOADMOD_BIT0_SINGLE_LABEL: //Manchester encoding
|
||
if (ShiftRegister & 0x01) {
|
||
/* Deactivate carrier */
|
||
CodecSetLoadmodState(false);
|
||
} else {
|
||
/* Activate carrier */
|
||
CodecSetLoadmodState(true);
|
||
}
|
||
|
||
StateRegister = LOADMOD_BIT1_SINGLE;
|
||
return;
|
||
|
||
LOADMOD_BIT1_SINGLE_LABEL: //Manchester encoding
|
||
if (ShiftRegister & 0x01) {
|
||
CodecSetLoadmodState(true);
|
||
} else {
|
||
CodecSetLoadmodState(false);
|
||
}
|
||
|
||
ShiftRegister >>= 1;
|
||
BitSent++;
|
||
|
||
StateRegister = LOADMOD_BIT0_SINGLE;
|
||
|
||
if ((BitSent % 8) == 0) {
|
||
/* Byte boundary */
|
||
if (--ByteCount == 0) {
|
||
/* No more data left */
|
||
ShiftRegister = EOF_PATTERN;
|
||
StateRegister = LOADMOD_EOF_SINGLE;
|
||
} else {
|
||
ShiftRegister = (*CodecBufferPtr++);
|
||
}
|
||
}
|
||
return;
|
||
|
||
LOADMOD_EOF_SINGLE_LABEL: //End of Manchester encoding
|
||
/* Output EOF */
|
||
if (ShiftRegister & 0x80) {
|
||
CodecSetLoadmodState(true);
|
||
} else {
|
||
CodecSetLoadmodState(false);
|
||
}
|
||
|
||
ShiftRegister <<= 1;
|
||
BitSent++;
|
||
|
||
if ((BitSent % 8) == 0) {
|
||
/* Last bit has been put out */
|
||
StateRegister = LOADMOD_FINISHED;
|
||
} /* else: stay in this state. No changes needed */
|
||
return;
|
||
|
||
// -------------------------------------------------------------
|
||
|
||
LOADMOD_START_DUAL_LABEL:
|
||
ShiftRegister = SOF_PATTERN;
|
||
BitSent = 0;
|
||
CodecSetLoadmodState(true);
|
||
CodecStartSubcarrier();
|
||
// fallthrough
|
||
LOADMOD_SOF_DUAL_LABEL:
|
||
if (ShiftRegister & 0x80) {
|
||
CodecChangeDivider(SUBCARRIER_1);
|
||
CODEC_TIMER_LOADMOD.PER = BitRate1 - 1;
|
||
} else {
|
||
CodecChangeDivider(SUBCARRIER_2);
|
||
CODEC_TIMER_LOADMOD.PER = BitRate2 - 1;
|
||
}
|
||
|
||
ShiftRegister <<= 1;
|
||
BitSent++;
|
||
|
||
if ((BitSent % 8) == 0) {
|
||
/* Last SOF bit has been put out. Start sending out data */
|
||
if (ByteCount == 0) {
|
||
StateRegister = LOADMOD_FINISHED;
|
||
} else {
|
||
StateRegister = LOADMOD_BIT0_DUAL;
|
||
ShiftRegister = (*CodecBufferPtr++);
|
||
}
|
||
} else {
|
||
StateRegister = LOADMOD_SOF_DUAL;
|
||
}
|
||
return;
|
||
|
||
LOADMOD_BIT0_DUAL_LABEL: //Manchester encoding
|
||
if (ShiftRegister & 0x01) {
|
||
CodecChangeDivider(SUBCARRIER_2);
|
||
CODEC_TIMER_LOADMOD.PER = BitRate2 - 1;
|
||
} else {
|
||
CodecChangeDivider(SUBCARRIER_1);
|
||
CODEC_TIMER_LOADMOD.PER = BitRate1 - 1;
|
||
}
|
||
|
||
StateRegister = LOADMOD_BIT1_DUAL;
|
||
return;
|
||
|
||
LOADMOD_BIT1_DUAL_LABEL: //Manchester encoding
|
||
if (ShiftRegister & 0x01) {
|
||
/* fc / 32 */
|
||
CodecChangeDivider(SUBCARRIER_1);
|
||
CODEC_TIMER_LOADMOD.PER = BitRate1 - 1;
|
||
} else {
|
||
/* fc / 28 */
|
||
CodecChangeDivider(SUBCARRIER_2);
|
||
CODEC_TIMER_LOADMOD.PER = BitRate2 - 1;
|
||
}
|
||
|
||
ShiftRegister >>= 1;
|
||
BitSent++;
|
||
|
||
StateRegister = LOADMOD_BIT0_DUAL;
|
||
|
||
if ((BitSent % 8) == 0) {
|
||
/* Byte boundary */
|
||
if (--ByteCount == 0) {
|
||
/* No more data left */
|
||
ShiftRegister = EOF_PATTERN;
|
||
StateRegister = LOADMOD_EOF_DUAL;
|
||
} else {
|
||
ShiftRegister = (*CodecBufferPtr++);
|
||
}
|
||
}
|
||
return;
|
||
|
||
LOADMOD_EOF_DUAL_LABEL: //End of Manchester encoding
|
||
/* Output EOF */
|
||
if (ShiftRegister & 0x80) {
|
||
CodecChangeDivider(SUBCARRIER_1);
|
||
CODEC_TIMER_LOADMOD.PER = BitRate1 - 1;
|
||
} else {
|
||
CodecChangeDivider(SUBCARRIER_2);
|
||
CODEC_TIMER_LOADMOD.PER = BitRate2 - 1;
|
||
}
|
||
|
||
ShiftRegister <<= 1;
|
||
BitSent++;
|
||
|
||
if ((BitSent % 8) == 0) {
|
||
/* Last bit has been put out */
|
||
StateRegister = LOADMOD_FINISHED;
|
||
} /* else: stay in this state. No changes needed */
|
||
return;
|
||
|
||
LOADMOD_FINISHED_LABEL:
|
||
/* Sets timer off for CODEC_TIMER_LOADMOD (TCE0) disabling clock source as we're done modulating */
|
||
CODEC_TIMER_LOADMOD.CTRLA = TC_CLKSEL_OFF_gc;
|
||
/* Disable compare/capture for all level interrupts on channel B for TCE0 - From 14.12.7 [8331F–AVR–04/2013] */
|
||
CODEC_TIMER_LOADMOD.INTCTRLB = TC_CCBINTLVL_OFF_gc;
|
||
CodecSetSubcarrier(CODEC_SUBCARRIERMOD_OFF, 0);
|
||
Flags.LoadmodFinished = 1;
|
||
return;
|
||
}
|
||
|
||
/* Reset global variables/interrupts to demodulate incoming reader data via ISRs */
|
||
void StartISO15693Demod(void) {
|
||
/* Reset global variables to default values */
|
||
CodecBufferPtr = CodecBuffer;
|
||
Flags.DemodFinished = 0;
|
||
Flags.LoadmodFinished = 0;
|
||
DemodState = DEMOD_SOC_STATE;
|
||
StateRegister = LOADMOD_WAIT;
|
||
DataRegister = 0;
|
||
SampleRegister = 0;
|
||
BitSampleCount = 0;
|
||
SampleDataCount = 0;
|
||
ModulationPauseCount = 0;
|
||
ByteCount = 0;
|
||
ShiftRegister = 0;
|
||
|
||
/* Set clock source for TCD0 to ISO15693_SAMPLE_CLK = TC_CLKSEL_DIV2_gc = System Clock / 2.
|
||
* Since the Chameleon is clocked at 13.56*2 MHz (see Makefile), this counter will hit at the same frequency of reader field.
|
||
* From 14.12.1 [8331F–AVR–04/2013]
|
||
*/
|
||
CODEC_TIMER_SAMPLING.CTRLA = ISO15693_SAMPLE_CLK;
|
||
/* Set up TCD0 action/source:
|
||
* - Event Action: Restart waveform period (reset PER register)
|
||
* - Event Source Select: trigger on CODEC_TIMER_MODSTART_EVSEL = TC_EVSEL_CH0_gc = Event Channel 0
|
||
* From 14.12.4 [8331F–AVR–04/2013]
|
||
*/
|
||
CODEC_TIMER_SAMPLING.CTRLD = TC_EVACT_RESTART_gc | CODEC_TIMER_MODSTART_EVSEL;
|
||
/* Resets the sampling counter (TCD0) to 0 - From 14.12.12 [8331F–AVR–04/2013] */
|
||
CODEC_TIMER_SAMPLING.CNT = 0;
|
||
|
||
/* Set Clock Source for TCE0 to CODEC_TIMER_CARRIER_CLKSEL = TC_CLKSEL_EVCH6_gc = Event Channel 6 */
|
||
CODEC_TIMER_LOADMOD.CTRLA = CODEC_TIMER_CARRIER_CLKSEL;
|
||
/* Set up TCE0 action/source:
|
||
* - Event Action: Restart waveform period (reset PER register)
|
||
* - Event Source Select: trigger on CODEC_TIMER_MODSTART_EVSEL = TC_EVSEL_CH0_gc = Event Channel 0
|
||
* From 14.12.4 [8331F–AVR–04/2013]
|
||
*/
|
||
CODEC_TIMER_LOADMOD.CTRLD = TC_EVACT_RESTART_gc | CODEC_TIMER_MODSTART_EVSEL;
|
||
/* Temporarily disable compare/capture for all level interrupts on channel B for TCE0. They'll be enabled later when load modulation will be needed.
|
||
* From 14.12.7 [8331F–AVR–04/2013] */
|
||
CODEC_TIMER_LOADMOD.INTCTRLB = TC_CCBINTLVL_OFF_gc;
|
||
/* Set the period for CODEC_TIMER_LOADMOD (TCE0) ISO standard t1 time.
|
||
* Once this timer is enabled, it will wait for a time equal to t1 and then its ISR will be called.
|
||
* From 14.12.18 [8331F–AVR–04/2013]
|
||
*/
|
||
CODEC_TIMER_LOADMOD.PER = ISO15693_T1_TIME;
|
||
|
||
/* Start looking out for modulation pause via interrupt. */
|
||
/* Clear PORTB interrupt flags - From 13.13.13 [8331F–AVR–04/2013] */
|
||
CODEC_DEMOD_IN_PORT.INTFLAGS = PORT_INT0IF_bm;
|
||
/* Use PIN1 as a source for modulation pauses. Will trigger PORTB Interrput 0 - From 13.13.11 [8331F–AVR–04/2013] */
|
||
CODEC_DEMOD_IN_PORT.INT0MASK = CODEC_DEMOD_IN_MASK0;
|
||
}
|
||
|
||
/* Configure unvarying interrupts settings.
|
||
* All configured interrupts will still be disabled once this function is done, they'll be enabled one by one as the communication flow requires them.
|
||
*/
|
||
void ISO15693CodecInit(void) {
|
||
CodecInitCommon();
|
||
|
||
/**************************************************
|
||
* Register function handlers to shared ISR *
|
||
**************************************************/
|
||
/* Register isr_ISO15693_CODEC_TIMER_SAMPLING_CCC_VECT function to CODEC_TIMER_SAMPLING (TCD0)'s Counter Channel C (CCC) */
|
||
isr_func_TCD0_CCC_vect = &isr_ISO15693_CODEC_TIMER_SAMPLING_CCC_VECT;
|
||
/* Register isr_ISO15693_CODEC_DEMOD_IN_INT0_VECT function to CODEC_DEMOD_IN_PORT (PORTB) interrupt 0 */
|
||
isr_func_CODEC_DEMOD_IN_INT0_VECT = &isr_ISO15693_CODEC_DEMOD_IN_INT0_VECT;
|
||
/* Register isr_ISO15693_CODEC_TIMER_LOADMOD_CCB_VECT function to CODEC_TIMER_LOADMOD (TCE0)'s Counter Channel B (CCB) */
|
||
isr_func_CODEC_TIMER_LOADMOD_CCB_VECT = &isr_ISO15693_CODEC_TIMER_LOADMOD_CCB_VECT;
|
||
|
||
/**************************************************
|
||
* Configure demod interrupt *
|
||
**************************************************/
|
||
/* Configure sampling-timer free running and sync to first modulation-pause */
|
||
/* Set the period for TCD0 to ISO15693_SAMPLE_PERIOD - 1 because PER is 0-based - From 14.12.14 [8331F–AVR–04/2013] */
|
||
CODEC_TIMER_SAMPLING.PER = ISO15693_SAMPLE_PERIOD - 1;
|
||
/* Set Compare Channel C (CCC) period to half bit period - 1. (- 14 to compensate ISR timing overhead) - From 14.12.16 [8331F–AVR–04/2013] */
|
||
CODEC_TIMER_SAMPLING.CCC = ISO15693_SAMPLE_PERIOD / 2 - 14 - 1;
|
||
/* Temporarily disable Compare Channel C (CCC) interrupts.
|
||
* They'll be enabled later in isr_ISO15693_CODEC_DEMOD_IN_INT0_VECT once we sensed the reader is sending data and we're in sync with the pulses.
|
||
* From 14.12.7 [8331F–AVR–04/2013]
|
||
*/
|
||
CODEC_TIMER_SAMPLING.INTCTRLB = TC_CCCINTLVL_OFF_gc;
|
||
/* Clear Compare Channel C (CCC) interrupt Flags - From 14.12.10 [8331F–AVR–04/2013] */
|
||
CODEC_TIMER_SAMPLING.INTFLAGS = TC0_CCCIF_bm;
|
||
|
||
/**************************************************
|
||
* Configure loadmod interrupt *
|
||
**************************************************/
|
||
/* Disable timer error and overflow interrupts - From 14.12.6 [8331F–AVR–04/2013] */
|
||
CODEC_TIMER_LOADMOD.INTCTRLA = 0;
|
||
|
||
/* Activate Power for demodulator */
|
||
CodecSetDemodPower(true);
|
||
|
||
StartISO15693Demod();
|
||
}
|
||
|
||
void ISO15693CodecDeInit(void) {
|
||
/* Gracefully shutdown codec */
|
||
CODEC_DEMOD_IN_PORT.INT0MASK = 0;
|
||
|
||
/* Reset global variables to default values */
|
||
CodecBufferPtr = CodecBuffer;
|
||
Flags.DemodFinished = 0;
|
||
Flags.LoadmodFinished = 0;
|
||
DemodState = DEMOD_SOC_STATE;
|
||
StateRegister = LOADMOD_WAIT;
|
||
DataRegister = 0;
|
||
SampleRegister = 0;
|
||
BitSampleCount = 0;
|
||
SampleDataCount = 0;
|
||
ModulationPauseCount = 0;
|
||
ByteCount = 0;
|
||
ShiftRegister = 0;
|
||
|
||
/* Disable sample timer */
|
||
/* Sets timer off for TCD0, disabling clock source - From 14.12.1 [8331F–AVR–04/2013] */
|
||
CODEC_TIMER_SAMPLING.CTRLA = TC_CLKSEL_OFF_gc;
|
||
/* Disable event action for CODEC_TIMER_SAMPLING (TCD0) - From 14.12.4 [8331F–AVR–04/2013] */
|
||
CODEC_TIMER_SAMPLING.CTRLD = TC_EVACT_OFF_gc;
|
||
/* Disable Compare Channel C (CCC) interrupts - From 14.12.7 [8331F–AVR–04/2013] */
|
||
CODEC_TIMER_SAMPLING.INTCTRLB = TC_CCCINTLVL_OFF_gc;
|
||
/* Clear Compare Channel C (CCC) interrupt Flags - From 14.12.10 [8331F–AVR–04/2013] */
|
||
CODEC_TIMER_SAMPLING.INTFLAGS = TC0_CCCIF_bm;
|
||
|
||
/* Disable load modulation */
|
||
/* Disable the event action for TCE0 - From 14.12.4 [8331F–AVR–04/2013] */
|
||
CODEC_TIMER_LOADMOD.CTRLD = TC_EVACT_OFF_gc;
|
||
/* Disable compare/capture for all level interrupts on channel B for TCE0 - From 14.12.7 [8331F–AVR–04/2013] */
|
||
CODEC_TIMER_LOADMOD.INTCTRLB = TC_CCBINTLVL_OFF_gc;
|
||
/* Clear TCE0 interrupt flags for Capture Channel B - From 14.12.10 [8331F–AVR–04/2013] */
|
||
CODEC_TIMER_LOADMOD.INTFLAGS = TC0_CCBIF_bm;
|
||
|
||
CodecSetSubcarrier(CODEC_SUBCARRIERMOD_OFF, 0);
|
||
CodecSetDemodPower(false);
|
||
CodecSetLoadmodState(false);
|
||
}
|
||
|
||
void ISO15693StartEarlySend(bool bDualSubcarrier, uint16_t byteCount) {
|
||
// Buy a little more time to ready the responce (400 cycles == 14.7uS) before sending the SOF
|
||
CODEC_TIMER_LOADMOD.PER = ISO15693_T1_TIME + 200;
|
||
|
||
ByteCount = byteCount;
|
||
CodecBufferPtr = CodecBuffer;
|
||
|
||
/* Start loadmodulating */
|
||
if (bDualSubcarrier) {
|
||
CodecSetSubcarrier(CODEC_SUBCARRIERMOD_OOK, SUBCARRIER_2);
|
||
StateRegister = LOADMOD_START_DUAL;
|
||
} else {
|
||
CodecSetSubcarrier(CODEC_SUBCARRIERMOD_OOK, SUBCARRIER_1);
|
||
StateRegister = LOADMOD_START_SINGLE;
|
||
}
|
||
}
|
||
|
||
void ISO15693CodecTask(void) {
|
||
if (Flags.DemodFinished) {
|
||
Flags.DemodFinished = 0;
|
||
|
||
uint16_t DemodByteCount = ByteCount;
|
||
uint16_t AppReceivedByteCount = 0;
|
||
bool bDualSubcarrier = false;
|
||
|
||
if (DemodByteCount > 0) {
|
||
LogEntry(LOG_INFO_CODEC_RX_DATA, CodecBuffer, DemodByteCount);
|
||
LEDHook(LED_CODEC_RX, LED_PULSE);
|
||
|
||
if (CodecBuffer[0] & REQ_SUBCARRIER_DUAL && ActiveConfiguration.TagFamily != TAG_FAMILY_ISO15693_ICLASS) {
|
||
bDualSubcarrier = true;
|
||
}
|
||
LogEntry(LOG_INFO_GENERIC, "Test1", 5);
|
||
AppReceivedByteCount = ApplicationProcess(CodecBuffer, DemodByteCount);
|
||
}
|
||
|
||
/* This is only reached when we've received a valid frame */
|
||
if (AppReceivedByteCount != ISO15693_APP_NO_RESPONSE && AppReceivedByteCount != ISO15693_APP_EARLY_SEND) {
|
||
if (AppReceivedByteCount == ISO15693_APP_SOF_ONLY) {
|
||
AppReceivedByteCount = 0;
|
||
}
|
||
|
||
if (AppReceivedByteCount > CODEC_BUFFER_SIZE - ISO15693_CRC16_SIZE) { /* CRC would be written outside codec buffer */
|
||
CodecBuffer[ISO15693_ADDR_FLAGS] = ISO15693_RES_FLAG_ERROR;
|
||
CodecBuffer[ISO15693_RES_ADDR_PARAM] = ISO15693_RES_ERR_NOT_SUPP;
|
||
AppReceivedByteCount = 2;
|
||
LogEntry(LOG_INFO_GENERIC, "Too much data requested - See PR #274", 38);
|
||
}
|
||
|
||
LEDHook(LED_CODEC_TX, LED_PULSE);
|
||
|
||
ByteCount = AppReceivedByteCount;
|
||
CodecBufferPtr = CodecBuffer;
|
||
|
||
/* Start loadmodulating */
|
||
if (bDualSubcarrier) {
|
||
CodecSetSubcarrier(CODEC_SUBCARRIERMOD_OOK, SUBCARRIER_2);
|
||
StateRegister = LOADMOD_START_DUAL;
|
||
} else {
|
||
CodecSetSubcarrier(CODEC_SUBCARRIERMOD_OOK, SUBCARRIER_1);
|
||
StateRegister = LOADMOD_START_SINGLE;
|
||
}
|
||
|
||
/* Calculate the CRC while modulation is already ongoing */
|
||
if (ActiveConfiguration.TagFamily != TAG_FAMILY_ISO15693_ICLASS && AppReceivedByteCount != 0) {
|
||
ISO15693AppendCRC(CodecBuffer, AppReceivedByteCount);
|
||
ByteCount += ISO15693_CRC16_SIZE; /* Increase this variable as it will be read by the codec during loadmodulation */
|
||
LogEntry(LOG_INFO_CODEC_TX_DATA, CodecBuffer, AppReceivedByteCount + ISO15693_CRC16_SIZE);
|
||
} else {
|
||
LogEntry(LOG_INFO_CODEC_TX_DATA, CodecBuffer, AppReceivedByteCount);
|
||
}
|
||
} else if (AppReceivedByteCount != ISO15693_APP_EARLY_SEND) {
|
||
/* Overwrite the PERBUF register, which was configured in ISO15693_EOC, with the new appropriate value.
|
||
* This is expecially needed because, since load modulation has not been performed, we're jumping here straight after
|
||
* ISO15693_EOC. This implies that the data stored in the PERBUF register by ISO15693_EOC still has to be copied
|
||
* in the PER register and that will happen on the next UPDATE event.
|
||
* The next UPDATE event will be timer/counter enablement in ISO15693_EOC, which is executed after StartISO15693Demod,
|
||
* effectively overwriting PER with an incorrect value.
|
||
* See 8045A-AVR-02/08 section 3.7 for information about buffered registers.
|
||
*/
|
||
CODEC_TIMER_LOADMOD.PERBUF = ISO15693_T1_TIME;
|
||
StartISO15693Demod();
|
||
}
|
||
}
|
||
|
||
if (Flags.LoadmodFinished) {
|
||
Flags.LoadmodFinished = 0;
|
||
/* Load modulation has been finished. Stop it and start to listen for incoming data again. */
|
||
StartISO15693Demod();
|
||
}
|
||
}
|