2017-08-31 14:00:59 +02:00
/*
* ISO15693.c
*
* Created on: 25.01.2017
* Author: Phillip Nash
2018-12-26 18:06:10 +01:00
* Modified by: ceres-c
2020-09-04 01:41:22 +02:00
*
* 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.
2017-08-31 14:00:59 +02:00
*/
#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
2020-09-04 01:41:22 +02:00
#define REQ_SUBCARRIER_SINGLE 0x00
#define REQ_SUBCARRIER_DUAL 0x01
#define REQ_DATARATE_LOW 0x00
#define REQ_DATARATE_HIGH 0x02
2017-08-31 14:00:59 +02:00
#define ISO15693_SAMPLE_CLK TC_CLKSEL_DIV2_gc // 13.56MHz
#define ISO15693_SAMPLE_PERIOD 128 // 9.4us
2020-09-04 01:41:22 +02:00
#define ISO15693_T1_TIME 4352 - 14 /* ISO t1 time - ISR prologue compensation */ // 4192 + 128 + 128 - 1
2017-08-31 14:00:59 +02:00
#define SUBCARRIER_1 32
#define SUBCARRIER_2 28
#define SUBCARRIER_OFF 0
2019-11-15 10:21:40 +01:00
#define SOF_PATTERN 0x1D // 0001 1101
2018-10-16 23:45:43 +02:00
#define EOF_PATTERN 0xB8 // 1011 1000
2017-08-31 14:00:59 +02:00
// 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 ;
2018-12-26 18:06:10 +01:00
/* This function implements CODEC_DEMOD_IN_INT0_VECT interrupt vector.
* It is called when a pulse is detected in CODEC_DEMOD_IN_PORT (PORTB).
2020-09-04 01:41:22 +02:00
* The relevant interrupt vector was registered to CODEC_DEMOD_IN_MASK0 (PIN1) via:
2018-12-26 18:06:10 +01:00
* CODEC_DEMOD_IN_PORT.INT0MASK = CODEC_DEMOD_IN_MASK0;
* and unregistered writing the INT0MASK to 0
*/
2019-12-08 13:18:57 +01:00
ISR_SHARED isr_ISO15693_CODEC_DEMOD_IN_INT0_VECT ( void ) {
2020-09-04 01:41:22 +02:00
/* Clear Compare Channel C (CCC) interrupt Flags - From 14.12.10 [8331F– AVR– 04/2013] */
2017-08-31 14:00:59 +02:00
CODEC_TIMER_SAMPLING . INTFLAGS = TC0_CCCIF_bm ;
2020-09-04 01:41:22 +02:00
/* Enable compare/capture for high level interrupts on Capture Channel C for TCD0 - From 14.12.7 [8331F– AVR– 04/2013] */
2017-08-31 14:00:59 +02:00
CODEC_TIMER_SAMPLING . INTCTRLB = TC_CCCINTLVL_HI_gc ;
2018-12-26 18:06:10 +01:00
2020-09-04 01:41:22 +02:00
/* Disable this interrupt. From now on we will sample the field using our internal clock - From 13.13.11 [8331F– AVR– 04/2013] */
2017-08-31 14:00:59 +02:00
CODEC_DEMOD_IN_PORT . INT0MASK = 0 ;
2020-09-04 01:41:22 +02:00
2017-08-31 14:00:59 +02:00
}
2018-12-26 18:06:10 +01:00
/* 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.
*/
2019-11-15 10:21:40 +01:00
INLINE void ISO15693_EOC ( void ) {
2018-12-26 18:06:10 +01:00
/* Set bitrate required by the reader on SOF for our following response */
2020-09-04 01:41:22 +02:00
if ( CodecBuffer [ 0 ] & REQ_DATARATE_HIGH ) {
2017-08-31 14:00:59 +02:00
BitRate1 = 256 ;
2020-09-04 01:41:22 +02:00
BitRate2 = 252 ; /* If single subcarrier mode is requested, BitRate2 is useless, but setting it nevertheless was still faster than checking */
2017-08-31 14:00:59 +02:00
} else {
2020-09-04 01:41:22 +02:00
BitRate1 = 256 * 4 ; // Possible value: 256 * 4 - 1
BitRate2 = 252 * 4 ; // Possible value: 252 * 4 - 3
2017-08-31 14:00:59 +02:00
}
Flags . DemodFinished = 1 ;
2020-09-04 01:41:22 +02:00
/* 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] */
2017-08-31 14:00:59 +02:00
CODEC_TIMER_SAMPLING . CTRLA = TC_CLKSEL_OFF_gc ;
2020-09-04 01:41:22 +02:00
/* 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 ;
2017-08-31 14:00:59 +02:00
}
2018-12-26 18:06:10 +01:00
/* 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
2019-11-15 10:21:40 +01:00
*
2018-12-26 18:06:10 +01:00
* It demodulates bits received from the reader and saves them in CodecBuffer.
2019-11-15 10:21:40 +01:00
*
2020-09-04 01:41:22 +02:00
* It disables its own interrupt when an EOF is found (calling ISO15693_EOC) or when it receives garbage
2018-12-26 18:06:10 +01:00
*/
2019-12-08 13:18:57 +01:00
ISR_SHARED isr_ISO15693_CODEC_TIMER_SAMPLING_CCC_VECT ( void ) {
2017-08-31 14:00:59 +02:00
/* Shift demod data */
SampleRegister = ( SampleRegister << 1 ) | ( ! ( CODEC_DEMOD_IN_PORT . IN & CODEC_DEMOD_IN_MASK ) ? 0x01 : 0x00 );
2019-11-15 10:21:40 +01:00
if ( ++ BitSampleCount == 8 ) {
2017-08-31 14:00:59 +02:00
BitSampleCount = 0 ;
2019-11-15 10:21:40 +01:00
switch ( DemodState ) {
2017-08-31 14:00:59 +02:00
case DEMOD_SOC_STATE :
2019-11-15 10:21:40 +01:00
if ( SampleRegister == SOC_1_OF_4_CODE ) {
2017-08-31 14:00:59 +02:00
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 ;
2018-12-26 18:06:10 +01:00
} else { // No SOC. Restart and try again, we probably received garbage.
2020-09-04 01:41:22 +02:00
ISO15693_GARBAGE ();
2017-08-31 14:00:59 +02:00
}
break ;
2018-12-19 17:23:19 +01:00
2017-08-31 14:00:59 +02:00
case DEMOD_1_OUT_OF_4_STATE :
2019-11-15 10:21:40 +01:00
if ( SampleRegister == EOC_CODE ) {
2017-08-31 14:00:59 +02:00
ISO15693_EOC ();
} else {
uint8_t SampleData = ~ SampleRegister ;
2019-11-15 10:21:40 +01:00
if ( SampleData == ( 0x01 << 6 )) {
2017-08-31 14:00:59 +02:00
/* ^_^^^^^^ -> 00 */
ModulationPauseCount ++ ;
DataRegister >>= 2 ;
} else if ( SampleData == ( 0x01 << 4 )) {
/* ^^^_^^^^ -> 01 */
ModulationPauseCount ++ ;
DataRegister >>= 2 ;
DataRegister |= 0 b01 << 6 ;
} else if ( SampleData == ( 0x01 << 2 )) {
/* ^^^^^_^^ -> 10 */
ModulationPauseCount ++ ;
DataRegister >>= 2 ;
DataRegister |= 0 b10 << 6 ;
} else if ( SampleData == ( 0x01 << 0 )) {
/* ^^^^^^^_ -> 11 */
ModulationPauseCount ++ ;
DataRegister >>= 2 ;
DataRegister |= 0 b11 << 6 ;
}
2019-11-15 10:21:40 +01:00
if ( ModulationPauseCount == 4 ) {
2017-08-31 14:00:59 +02:00
ModulationPauseCount = 0 ;
* CodecBufferPtr = DataRegister ;
++ CodecBufferPtr ;
++ ByteCount ;
}
}
break ;
2018-12-19 17:23:19 +01:00
2017-08-31 14:00:59 +02:00
case DEMOD_1_OUT_OF_256_STATE :
2019-11-15 10:21:40 +01:00
if ( SampleRegister == EOC_CODE ) {
2017-08-31 14:00:59 +02:00
ISO15693_EOC ();
} else {
uint8_t Position = (( SampleDataCount / 2 ) % 256 ) - 1 ;
uint8_t SampleData = ~ SampleRegister ;
2019-11-15 10:21:40 +01:00
if ( SampleData == ( 0x01 << 6 )) {
2017-08-31 14:00:59 +02:00
/* ^_^^^^^^ -> 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 ++ ;
2019-11-15 10:21:40 +01:00
}
2017-08-31 14:00:59 +02:00
2019-11-15 10:21:40 +01:00
if ( ModulationPauseCount == 1 ) {
2017-08-31 14:00:59 +02:00
ModulationPauseCount = 0 ;
* CodecBufferPtr = DataRegister ;
++ CodecBufferPtr ;
++ ByteCount ;
2019-11-15 10:21:40 +01:00
}
2017-08-31 14:00:59 +02:00
}
break ;
}
}
SampleDataCount ++ ;
}
2018-12-26 18:06:10 +01:00
/* 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
2019-11-15 10:21:40 +01:00
*
2018-12-26 18:06:10 +01:00
* It modulates the carrier with consuming bytes in CodecBuffer until ByteCount is 0.
2019-11-15 10:21:40 +01:00
*
2018-12-26 18:06:10 +01:00
* It disables its own interrupt when all data has been sent
*/
2020-08-26 20:19:33 +02:00
ISR_SHARED isr_ISO15693_CODEC_TIMER_LOADMOD_CCB_VECT ( void ) {
2019-11-15 10:21:40 +01:00
static void * JumpTable [] = {
2020-09-04 01:41:22 +02:00
[ LOADMOD_WAIT ] = && LOADMOD_WAIT_LABEL ,
2019-11-15 10:21:40 +01:00
[ 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
2017-08-31 14:00:59 +02:00
};
2020-09-04 01:41:22 +02:00
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 ;
2018-12-19 17:23:19 +01:00
2019-11-15 10:21:40 +01:00
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 ) {
2017-08-31 14:00:59 +02:00
CodecSetLoadmodState ( true );
2019-11-15 10:21:40 +01:00
} else {
CodecSetLoadmodState ( false );
}
CodecStartSubcarrier ();
ShiftRegister <<= 1 ;
BitSent ++ ;
if (( BitSent % 8 ) == 0 ) {
/* Last SOF bit has been put out. Start sending out data */
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 ;
2017-08-31 14:00:59 +02:00
} else {
ShiftRegister = ( * CodecBufferPtr ++ );
}
2019-11-15 10:21:40 +01:00
}
2017-08-31 14:00:59 +02:00
return ;
2019-11-15 10:21:40 +01:00
LOADMOD_EOF_SINGLE_LABEL : //End of Manchester encoding
/* Output EOF */
if ( ShiftRegister & 0x80 ) {
CodecSetLoadmodState ( true );
} else {
CodecSetLoadmodState ( false );
}
2017-08-31 14:00:59 +02:00
2019-11-15 10:21:40 +01:00
ShiftRegister <<= 1 ;
BitSent ++ ;
if (( BitSent % 8 ) == 0 ) {
/* Last bit has been put out */
StateRegister = LOADMOD_FINISHED ;
2020-09-04 01:41:22 +02:00
} /* else: stay in this state. No changes needed */
2018-10-16 23:45:43 +02:00
return ;
2019-11-15 10:21:40 +01:00
// -------------------------------------------------------------
2017-08-31 14:00:59 +02:00
2019-11-15 10:21:40 +01:00
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 ;
}
2017-08-31 14:00:59 +02:00
2019-11-15 10:21:40 +01:00
ShiftRegister <<= 1 ;
BitSent ++ ;
if (( BitSent % 8 ) == 0 ) {
/* Last SOF bit has been put out. Start sending out data */
2017-08-31 14:00:59 +02:00
StateRegister = LOADMOD_BIT0_DUAL ;
2019-11-15 10:21:40 +01:00
ShiftRegister = ( * CodecBufferPtr ++ );
} else {
StateRegister = LOADMOD_SOF_DUAL ;
}
2017-08-31 14:00:59 +02:00
return ;
2019-11-15 10:21:40 +01:00
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 ;
}
2017-08-31 14:00:59 +02:00
2019-11-15 10:21:40 +01:00
StateRegister = LOADMOD_BIT1_DUAL ;
return ;
2017-08-31 14:00:59 +02:00
2019-11-15 10:21:40 +01:00
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 ;
2017-08-31 14:00:59 +02:00
StateRegister = LOADMOD_EOF_DUAL ;
2019-11-15 10:21:40 +01:00
} else {
ShiftRegister = ( * CodecBufferPtr ++ );
2017-08-31 14:00:59 +02:00
}
2019-11-15 10:21:40 +01:00
}
2017-08-31 14:00:59 +02:00
return ;
2019-11-15 10:21:40 +01:00
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 ;
2020-09-04 01:41:22 +02:00
} /* else: stay in this state. No changes needed */
2019-11-15 10:21:40 +01:00
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 ;
2020-09-04 01:41:22 +02:00
/* 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 ;
2019-11-15 10:21:40 +01:00
CodecSetSubcarrier ( CODEC_SUBCARRIERMOD_OFF , 0 );
Flags . LoadmodFinished = 1 ;
2017-08-31 14:00:59 +02:00
return ;
}
2020-09-04 01:41:22 +02:00
/* Reset global variables/interrupts to demodulate incoming reader data via ISRs */
2017-08-31 14:00:59 +02:00
void StartISO15693Demod ( void ) {
2018-12-26 18:06:10 +01:00
/* Reset global variables to default values */
2017-08-31 14:00:59 +02:00
CodecBufferPtr = CodecBuffer ;
Flags . DemodFinished = 0 ;
Flags . LoadmodFinished = 0 ;
DemodState = DEMOD_SOC_STATE ;
2018-12-26 18:06:10 +01:00
StateRegister = LOADMOD_WAIT ;
2017-08-31 14:00:59 +02:00
DataRegister = 0 ;
SampleRegister = 0 ;
BitSampleCount = 0 ;
SampleDataCount = 0 ;
ModulationPauseCount = 0 ;
ByteCount = 0 ;
ShiftRegister = 0 ;
2018-12-25 13:46:30 +01:00
2020-09-04 01:41:22 +02:00
/* 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]
2018-12-26 18:06:10 +01:00
*/
2017-08-31 14:00:59 +02:00
CODEC_TIMER_SAMPLING . CTRLA = ISO15693_SAMPLE_CLK ;
2020-09-04 01:41:22 +02:00
/* 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]
*/
2017-08-31 14:00:59 +02:00
CODEC_TIMER_SAMPLING . CTRLD = TC_EVACT_RESTART_gc | CODEC_TIMER_MODSTART_EVSEL ;
2020-09-04 01:41:22 +02:00
/* Resets the sampling counter (TCD0) to 0 - From 14.12.12 [8331F– AVR– 04/2013] */
CODEC_TIMER_SAMPLING . CNT = 0 ;
2017-08-31 14:00:59 +02:00
2020-09-04 01:41:22 +02:00
/* 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]
2018-12-26 18:06:10 +01:00
*/
2020-09-04 01:41:22 +02:00
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 ;
2017-08-31 14:00:59 +02:00
/* Start looking out for modulation pause via interrupt. */
2020-09-04 01:41:22 +02:00
/* 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] */
2017-08-31 14:00:59 +02:00
CODEC_DEMOD_IN_PORT . INT0MASK = CODEC_DEMOD_IN_MASK0 ;
}
2020-09-04 01:41:22 +02:00
/* 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.
*/
2019-11-15 10:21:40 +01:00
void ISO15693CodecInit ( void ) {
2017-08-31 14:00:59 +02:00
CodecInitCommon ();
2018-12-26 18:06:10 +01:00
2020-09-04 01:41:22 +02:00
/**************************************************
* 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) */
2018-08-18 12:35:35 +02:00
isr_func_TCD0_CCC_vect = & isr_ISO15693_CODEC_TIMER_SAMPLING_CCC_VECT ;
2020-09-04 01:41:22 +02:00
/* Register isr_ISO15693_CODEC_DEMOD_IN_INT0_VECT function to CODEC_DEMOD_IN_PORT (PORTB) interrupt 0 */
2018-08-18 12:35:35 +02:00
isr_func_CODEC_DEMOD_IN_INT0_VECT = & isr_ISO15693_CODEC_DEMOD_IN_INT0_VECT ;
2020-09-04 01:41:22 +02:00
/* Register isr_ISO15693_CODEC_TIMER_LOADMOD_CCB_VECT function to CODEC_TIMER_LOADMOD (TCE0)'s Counter Channel B (CCB) */
2018-10-17 07:46:57 +02:00
isr_func_CODEC_TIMER_LOADMOD_CCB_VECT = & isr_ISO15693_CODEC_TIMER_LOADMOD_CCB_VECT ;
2018-12-26 18:06:10 +01:00
2020-09-04 01:41:22 +02:00
/**************************************************
* 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 );
2017-08-31 14:00:59 +02:00
StartISO15693Demod ();
}
2019-11-15 10:21:40 +01:00
void ISO15693CodecDeInit ( void ) {
2017-08-31 14:00:59 +02:00
/* Gracefully shutdown codec */
CODEC_DEMOD_IN_PORT . INT0MASK = 0 ;
2018-12-26 18:06:10 +01:00
/* Reset global variables to default values */
2017-08-31 14:00:59 +02:00
CodecBufferPtr = CodecBuffer ;
Flags . DemodFinished = 0 ;
Flags . LoadmodFinished = 0 ;
DemodState = DEMOD_SOC_STATE ;
2018-12-26 18:06:10 +01:00
StateRegister = LOADMOD_WAIT ;
2017-08-31 14:00:59 +02:00
DataRegister = 0 ;
SampleRegister = 0 ;
BitSampleCount = 0 ;
SampleDataCount = 0 ;
ModulationPauseCount = 0 ;
ByteCount = 0 ;
ShiftRegister = 0 ;
2018-12-26 18:06:10 +01:00
/* Disable sample timer */
2020-09-04 01:41:22 +02:00
/* Sets timer off for TCD0, disabling clock source - From 14.12.1 [8331F– AVR– 04/2013] */
2017-08-31 14:00:59 +02:00
CODEC_TIMER_SAMPLING . CTRLA = TC_CLKSEL_OFF_gc ;
2020-09-04 01:41:22 +02:00
/* Disable event action for CODEC_TIMER_SAMPLING (TCD0) - From 14.12.4 [8331F– AVR– 04/2013] */
2017-08-31 14:00:59 +02:00
CODEC_TIMER_SAMPLING . CTRLD = TC_EVACT_OFF_gc ;
2020-09-04 01:41:22 +02:00
/* Disable Compare Channel C (CCC) interrupts - From 14.12.7 [8331F– AVR– 04/2013] */
2017-08-31 14:00:59 +02:00
CODEC_TIMER_SAMPLING . INTCTRLB = TC_CCCINTLVL_OFF_gc ;
2020-09-04 01:41:22 +02:00
/* Clear Compare Channel C (CCC) interrupt Flags - From 14.12.10 [8331F– AVR– 04/2013] */
2017-08-31 14:00:59 +02:00
CODEC_TIMER_SAMPLING . INTFLAGS = TC0_CCCIF_bm ;
2018-12-26 18:06:10 +01:00
/* Disable load modulation */
2020-09-04 01:41:22 +02:00
/* Disable the event action for TCE0 - From 14.12.4 [8331F– AVR– 04/2013] */
2017-08-31 14:00:59 +02:00
CODEC_TIMER_LOADMOD . CTRLD = TC_EVACT_OFF_gc ;
2020-09-04 01:41:22 +02:00
/* Disable compare/capture for all level interrupts on channel B for TCE0 - From 14.12.7 [8331F– AVR– 04/2013] */
2017-08-31 14:00:59 +02:00
CODEC_TIMER_LOADMOD . INTCTRLB = TC_CCBINTLVL_OFF_gc ;
2020-09-04 01:41:22 +02:00
/* Clear TCE0 interrupt flags for Capture Channel B - From 14.12.10 [8331F– AVR– 04/2013] */
2017-08-31 14:00:59 +02:00
CODEC_TIMER_LOADMOD . INTFLAGS = TC0_CCBIF_bm ;
CodecSetSubcarrier ( CODEC_SUBCARRIERMOD_OFF , 0 );
CodecSetDemodPower ( false );
CodecSetLoadmodState ( false );
}
2019-11-15 10:21:40 +01:00
void ISO15693CodecTask ( void ) {
2017-08-31 14:00:59 +02:00
if ( Flags . DemodFinished ) {
Flags . DemodFinished = 0 ;
2018-12-19 17:23:19 +01:00
2017-08-31 14:00:59 +02:00
uint16_t DemodByteCount = ByteCount ;
uint16_t AppReceivedByteCount = 0 ;
bool bDualSubcarrier = false ;
2018-12-19 17:23:19 +01:00
2019-11-15 10:21:40 +01:00
if ( DemodByteCount > 0 ) {
2018-12-25 13:46:30 +01:00
LogEntry ( LOG_INFO_CODEC_RX_DATA , CodecBuffer , DemodByteCount );
LEDHook ( LED_CODEC_RX , LED_PULSE );
2020-09-04 01:41:22 +02:00
if ( CodecBuffer [ 0 ] & REQ_SUBCARRIER_DUAL ) {
2017-08-31 14:00:59 +02:00
bDualSubcarrier = true ;
}
AppReceivedByteCount = ApplicationProcess ( CodecBuffer , DemodByteCount );
}
2018-12-19 17:23:19 +01:00
2018-12-25 13:46:30 +01:00
/* This is only reached when we've received a valid frame */
if ( AppReceivedByteCount != ISO15693_APP_NO_RESPONSE ) {
2020-09-10 16:05:35 +02:00
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 );
}
2018-12-25 13:46:30 +01:00
LEDHook ( LED_CODEC_TX , LED_PULSE );
2018-12-19 17:23:19 +01:00
2018-12-25 13:46:30 +01:00
ByteCount = AppReceivedByteCount ;
CodecBufferPtr = CodecBuffer ;
2017-08-31 14:00:59 +02:00
/* Start loadmodulating */
if ( bDualSubcarrier ) {
CodecSetSubcarrier ( CODEC_SUBCARRIERMOD_OOK , SUBCARRIER_2 );
2018-12-25 13:46:30 +01:00
StateRegister = LOADMOD_START_DUAL ;
2017-08-31 14:00:59 +02:00
} else {
CodecSetSubcarrier ( CODEC_SUBCARRIERMOD_OOK , SUBCARRIER_1 );
2018-12-25 13:46:30 +01:00
StateRegister = LOADMOD_START_SINGLE ;
2017-08-31 14:00:59 +02:00
}
2018-12-19 17:23:19 +01:00
2020-09-10 16:05:35 +02:00
/* Calculate the CRC while modulation is already ongoing */
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 );
2017-08-31 14:00:59 +02:00
} else {
2020-09-04 01:41:22 +02:00
/* 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 ;
2017-08-31 14:00:59 +02:00
StartISO15693Demod ();
}
}
2018-12-19 17:23:19 +01:00
2017-08-31 14:00:59 +02:00
if ( Flags . LoadmodFinished ) {
Flags . LoadmodFinished = 0 ;
2018-12-26 18:06:10 +01:00
/* Load modulation has been finished. Stop it and start to listen for incoming data again. */
2017-08-31 14:00:59 +02:00
StartISO15693Demod ();
}
}