From 9a0ee807aac9a271b1510d225794d8e672b448c7 Mon Sep 17 00:00:00 2001 From: Federico Cerutti Date: Wed, 26 Dec 2018 18:06:10 +0100 Subject: [PATCH] Added comments and fixed a bug in codec's source ISO15693 codec was undocumented and without comments in relevant parts. I decided to spend 3 days diving into XMega interrupts and Chameleon's lowest level code. Now, I hope, it should be more readable for everyone, even newbies like my old self was before I started this deep dive. Also, I fixed the bug referenced here https://github.com/geo-rg/ChameleonMini/issues/4 which locked the chameleon when no response was to be sent to the reader --- Firmware/Chameleon-Mini/Codec/ISO15693.c | 154 ++++++++++++++++++----- 1 file changed, 121 insertions(+), 33 deletions(-) diff --git a/Firmware/Chameleon-Mini/Codec/ISO15693.c b/Firmware/Chameleon-Mini/Codec/ISO15693.c index 6d1f706..e1aa507 100644 --- a/Firmware/Chameleon-Mini/Codec/ISO15693.c +++ b/Firmware/Chameleon-Mini/Codec/ISO15693.c @@ -3,6 +3,7 @@ * * Created on: 25.01.2017 * Author: Phillip Nash + * Modified by: ceres-c */ #include "ISO15693.h" @@ -81,19 +82,33 @@ static volatile uint16_t ReadCommandFromReader = 0; #ifdef CONFIG_VICINITY_SUPPORT -// Started when a single pulse has been detected -// ISR(CODEC_DEMOD_IN_INT0_VECT) +/* 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 relevatn interrupt vector is 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(CODEC_DEMOD_IN_INT0_VECT) void isr_ISO15693_CODEC_DEMOD_IN_INT0_VECT(void) { - // Start sample timer + /* Start sample timer CODEC_TIMER_SAMPLING (TCD0). + * Set Counter Channel C (CCC) with relevant bitmask (TC0_CCCIF_bm), + * the period for clock sampling is specified in StartISO15693Demod. + */ CODEC_TIMER_SAMPLING.INTFLAGS = TC0_CCCIF_bm; + /* Sets register INTCTRLB to TC_CCCINTLVL_HI_gc = (0x03<<4) to enable compare/capture for high level interrupts on Channel C (CCC) */ CODEC_TIMER_SAMPLING.INTCTRLB = TC_CCCINTLVL_HI_gc; - /* Disable this interrupt */ + + /* Disable this interrupt as we've already sensed the relevant pulse and will use our internal clock from now on */ 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 */ BitRate1 = 256 * 4; // 256 * 4 - 1 if (CodecBuffer[0] & ISO15693_REQ_DATARATE_HIGH) BitRate1 = 256; @@ -107,18 +122,35 @@ INLINE void ISO15693_EOC(void) BitRate2 = BitRate1; } - CODEC_TIMER_LOADMOD.CTRLD = 0; - CODEC_TIMER_LOADMOD.INTFLAGS = TC0_CCBIF_bm; + /* Disable event action for CODEC_TIMER_LOADMOD (TCE0) as we're done receiving data */ + CODEC_TIMER_LOADMOD.CTRLD = TC_EVACT_OFF_gc; + /* Set Counter Channel B (CCB) with relevant bitmask (TC0_CCBIF_bm), the period for clock sampling is specified below */ + CODEC_TIMER_LOADMOD.INTFLAGS = TC0_CCBIF_bm; // TODO This might not be needed since commenting it does not break anything + /* Sets register INTCTRLB to TC_CCBINTLVL_HI_gc = (0x03<<2) to enable compare/capture for high level interrupts on channel B */ CODEC_TIMER_LOADMOD.INTCTRLB = TC_CCBINTLVL_HI_gc; + /* Set the period for CODEC_TIMER_LOADMOD (TCE0) to Bitrate - 1 because PERBUF is 0-based + * + * TODO Why are we using PERBUF instead of PER? + * With PERBUF the period register will occur on the next overflow. + */ CODEC_TIMER_LOADMOD.PERBUF = BitRate1 - 1; Flags.DemodFinished = 1; + /* Sets timer off for CODEC_TIMER_SAMPLING (TCD0) disabling clock source */ CODEC_TIMER_SAMPLING.CTRLA = TC_CLKSEL_OFF_gc; + /* Sets register INTCTRLB to 0 to disable all compare/capture interrupts */ CODEC_TIMER_SAMPLING.INTCTRLB = 0; } +/* 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 receives an EOF (calling ISO15693_EOC) or when it receives garbage + */ // ISR(CODEC_TIMER_SAMPLING_CCC_VECT) // Reading data sent from the reader -void isr_ISO15693_CODEC_TIMER_SAMPLING_CCC_VECT(void) +void 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); @@ -137,9 +169,11 @@ void isr_ISO15693_CODEC_TIMER_SAMPLING_CCC_VECT(void) } else if (SampleRegister == SOC_1_OF_256_CODE) { DemodState = DEMOD_1_OUT_OF_256_STATE; SampleDataCount = 0; - } else { // No SOC. Restart and try again + } else { // No SOC. Restart and try again, we probably received garbage. Flags.DemodFinished = 1; + /* Sets timer off for CODEC_TIMER_SAMPLING (TCD0) disabling clock source */ CODEC_TIMER_SAMPLING.CTRLA = TC_CLKSEL_OFF_gc; + /* Sets register INTCTRLB to 0 to disable all compare/capture interrupts */ CODEC_TIMER_SAMPLING.INTCTRLB = 0; } break; @@ -230,21 +264,28 @@ void isr_ISO15693_CODEC_TIMER_SAMPLING_CCC_VECT(void) } +/* 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(CODEC_TIMER_LOADMOD_CCB_VECT) void isr_ISO15693_CODEC_TIMER_LOADMOD_CCB_VECT(void) { static void* JumpTable[] = { - [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 + [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 }; if ( (StateRegister >= LOADMOD_START_SINGLE) && (StateRegister <= LOADMOD_FINISHED) ) { @@ -255,7 +296,7 @@ void isr_ISO15693_CODEC_TIMER_LOADMOD_CCB_VECT(void) LOADMOD_START_SINGLE_LABEL: /* Application produced data. With this interrupt we are aligned to the bit-grid. */ - ShiftRegister = SOF_PATTERN; + ShiftRegister = SOF_PATTERN; BitSent = 0; /* Fallthrough */ LOADMOD_SOF_SINGLE_LABEL: @@ -425,22 +466,25 @@ void isr_ISO15693_CODEC_TIMER_LOADMOD_CCB_VECT(void) 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; + /* Sets register INTCTRLB to 0 to disable all compare/capture interrupts */ CODEC_TIMER_LOADMOD.INTCTRLB = 0; - CodecSetSubcarrier(CODEC_SUBCARRIERMOD_OFF, SUBCARRIER_1); + CodecSetSubcarrier(CODEC_SUBCARRIERMOD_OFF, 0); Flags.LoadmodFinished = 1; return; } #endif /* CONFIG_VICINITY_SUPPORT */ +/* This functions resets all global variables used in the codec and enables interrupts to wait for reader data */ void StartISO15693Demod(void) { - + /* Reset global variables to default values */ CodecBufferPtr = CodecBuffer; Flags.DemodFinished = 0; Flags.LoadmodFinished = 0; DemodState = DEMOD_SOC_STATE; - LoadModState = LOADMOD_WAIT; + StateRegister = LOADMOD_WAIT; DataRegister = 0; SampleRegister = 0; BitSampleCount = 0; @@ -453,31 +497,66 @@ void StartISO15693Demod(void) { CodecSetDemodPower(true); /* Configure sampling-timer free running and sync to first modulation-pause. */ + /* Resets the counter to 0 */ CODEC_TIMER_SAMPLING.CNT = 0; - CODEC_TIMER_SAMPLING.PER = ISO15693_SAMPLE_PERIOD - 1; - CODEC_TIMER_SAMPLING.CCC = ISO15693_SAMPLE_PERIOD / 2 - 14 - 1; /* Half bit. ISR compensate*/ + /* Set the period for CODEC_TIMER_SAMPLING (TCD0) to ISO15693_SAMPLE_PERIOD - 1 because PER is 0-based */ + CODEC_TIMER_SAMPLING.PER = ISO15693_SAMPLE_PERIOD - 1; + /* Set Counter Channel C (CCC) register with half bit period - 1. (- 14 to compensate ISR timing overhead) */ + CODEC_TIMER_SAMPLING.CCC = ISO15693_SAMPLE_PERIOD / 2 - 14 - 1; + /* Set timer for CODEC_TIMER_SAMPLING (TCD0) to ISO15693_SAMPLE_CLK = TC_CLKSEL_DIV2_gc = System Clock / 2 + * + * TODO Why system clock / 2 and not iso period? + */ CODEC_TIMER_SAMPLING.CTRLA = ISO15693_SAMPLE_CLK; + /* Set event action for CODEC_TIMER_SAMPLING (TCD0) to restart and trigger CODEC_TIMER_MODSTART_EVSEL = TC_EVSEL_CH0_gc = Event Channel 0 */ CODEC_TIMER_SAMPLING.CTRLD = TC_EVACT_RESTART_gc | CODEC_TIMER_MODSTART_EVSEL; + /* Set Counter Channel C (CCC) with relevant bitmask (TC0_CCCIF_bm), the period for clock sampling is specified above */ CODEC_TIMER_SAMPLING.INTFLAGS = TC0_CCCIF_bm; + /* Sets register INTCTRLB to TC_CCCINTLVL_OFF_gc = (0x00<<4) to disable compare/capture C interrupts + * + * TODO Why turn it off? + */ CODEC_TIMER_SAMPLING.INTCTRLB = TC_CCCINTLVL_OFF_gc; + /* Set event action for CODEC_TIMER_LOADMOD (TCE0) to restart and trigger CODEC_TIMER_MODSTART_EVSEL = TC_EVSEL_CH0_gc = Event Channel 0 */ CODEC_TIMER_LOADMOD.CTRLD = TC_EVACT_RESTART_gc | CODEC_TIMER_MODSTART_EVSEL; - CODEC_TIMER_LOADMOD.PER = 4192 + 128 + 128 - 1; + /* Set the period for CODEC_TIMER_LOADMOD (TCE0) to... some magic numbers? + * Using PER instead of PERBUF breaks it when receiving ISO15693_APP_NO_RESPONSE from Application. + * + * TODO What are these numbers? + */ + CODEC_TIMER_LOADMOD.PERBUF = 4192 + 128 + 128 - 1; + /* Sets register INTCTRLA to 0 to disable timer error or overflow interrupts */ CODEC_TIMER_LOADMOD.INTCTRLA = 0; + /* Sets register INTCTRLB to 0 to disable all compare/capture interrupts */ CODEC_TIMER_LOADMOD.INTCTRLB = 0; + /* Set timer for CODEC_TIMER_SAMPLING (TCD0) to TC_CLKSEL_EVCH6_gc = Event Channel 6 */ CODEC_TIMER_LOADMOD.CTRLA = TC_CLKSEL_EVCH6_gc; /* Start looking out for modulation pause via interrupt. */ - CODEC_DEMOD_IN_PORT.INTFLAGS = 0x03; + /* Sets register INTFLAGS to PORT_INT0LVL_HI_gc = (0x03<<0) to enable compare/capture for high level interrupts on CODEC_DEMOD_IN_PORT (PORTB) */ + CODEC_DEMOD_IN_PORT.INTFLAGS = PORT_INT0LVL_HI_gc; + /* Sets INT0MASK to CODEC_DEMOD_IN_MASK0 = PIN1_bm to use it as source for port interrupt 0 */ CODEC_DEMOD_IN_PORT.INT0MASK = CODEC_DEMOD_IN_MASK0; } void ISO15693CodecInit(void) { CodecInitCommon(); + + /* 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; + StartISO15693Demod(); } @@ -486,11 +565,12 @@ 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; - LoadModState = LOADMOD_WAIT; + StateRegister = LOADMOD_WAIT; DataRegister = 0; SampleRegister = 0; BitSampleCount = 0; @@ -499,15 +579,22 @@ void ISO15693CodecDeInit(void) ByteCount = 0; ShiftRegister = 0; - //Disable sample timer + /* Disable sample timer */ + /* Sets timer off for CODEC_TIMER_SAMPLING (TCD0) disabling clock source */ CODEC_TIMER_SAMPLING.CTRLA = TC_CLKSEL_OFF_gc; + /* Disable event action for CODEC_TIMER_SAMPLING (TCD0) */ CODEC_TIMER_SAMPLING.CTRLD = TC_EVACT_OFF_gc; + /* Sets register INTCTRLB to TC_CCCINTLVL_OFF_gc = (0x00<<4) to disable compare/capture C interrupts */ CODEC_TIMER_SAMPLING.INTCTRLB = TC_CCCINTLVL_OFF_gc; + /* Restore Counter Channel C (CCC) interrupt mask (TC0_CCCIF_bm) */ CODEC_TIMER_SAMPLING.INTFLAGS = TC0_CCCIF_bm; - //Disable load modulation + /* Disable load modulation */ + /* Disable event action for CODEC_TIMER_LOADMOD (TCE0) */ CODEC_TIMER_LOADMOD.CTRLD = TC_EVACT_OFF_gc; + /* Sets register INTCTRLB to TC_CCBINTLVL_OFF_gc = (0x00<<2) to disable compare/capture B interrupts */ CODEC_TIMER_LOADMOD.INTCTRLB = TC_CCBINTLVL_OFF_gc; + /* Restore Counter Channel B (CCB) interrupt mask (TC0_CCBIF_bm) */ CODEC_TIMER_LOADMOD.INTFLAGS = TC0_CCBIF_bm; CodecSetSubcarrier(CODEC_SUBCARRIERMOD_OFF, 0); @@ -554,8 +641,10 @@ void ISO15693CodecTask(void) } } else { - /* No data to be processed. Disable T1 waiting and start listening again */ + /* No data to process. Disable CODEC_TIMER_LOADMOD (TCE0) counter and start listening again */ + /* Sets timer off for CODEC_TIMER_LOADMOD (TCE0) disabling clock source as we're done modulating */ CODEC_TIMER_LOADMOD.CTRLA = TC_CLKSEL_OFF_gc; + /* Sets register INTCTRLB to 0 to disable all compare/capture interrupts */ CODEC_TIMER_LOADMOD.INTCTRLB = 0; StartISO15693Demod(); @@ -564,8 +653,7 @@ void ISO15693CodecTask(void) if (Flags.LoadmodFinished) { Flags.LoadmodFinished = 0; - /* Load modulation has been finished. Stop it and start to listen - * for incoming data again. */ + /* Load modulation has been finished. Stop it and start to listen for incoming data again. */ StartISO15693Demod(); } }