From 6fb74b976fbec4d156d0b50e5b3f506a813004dc Mon Sep 17 00:00:00 2001 From: Yann GASCUEL <34003959+lnv42@users.noreply.github.com> Date: Wed, 16 Mar 2022 15:04:07 +0100 Subject: [PATCH 1/5] iso15: fpga: add support for 2SC in READER_MODE_RECEIVE_AMPLITUDE FPGA_HF_READER_MODE_RECEIVE_AMPLITUDE | FPGA_HF_READER_2SUBCARRIERS_424_484_KHZ flags will return 2 fsk bits (same ones as in sniffing mode: '10' => 424kHz, '01' => 484kHz, '11' => unknown signal and '00' for no signal received) in addition to the signal amplitude (on 14 bits) on 424kHz. --- fpga-xc2s30/fpga_hf_15.bit | Bin 42178 -> 42178 bytes fpga-xc2s30/hi_reader_15.v | 19 ++++++++++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/fpga-xc2s30/fpga_hf_15.bit b/fpga-xc2s30/fpga_hf_15.bit index 678e746c48393129982605bf251edeb50880c306..c1ea7d0706540b02e4d9bf05a1fb4284742d650a 100644 GIT binary patch literal 42178 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 literal 42178 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�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 diff --git a/fpga-xc2s30/hi_reader_15.v b/fpga-xc2s30/hi_reader_15.v index 28b8a7cae..357dc9e08 100644 --- a/fpga-xc2s30/hi_reader_15.v +++ b/fpga-xc2s30/hi_reader_15.v @@ -245,11 +245,11 @@ begin if (minor_mode == `FPGA_HF_READER_MODE_SNIFF_AMPLITUDE) begin if (subcarrier_frequency == `FPGA_HF_READER_2SUBCARRIERS_424_484_KHZ) - begin + begin // send amplitude + 2 bits fsk (2sc) signal + 2 bits reader signal corr_i_out <= corr_amplitude[13:6]; corr_q_out <= {corr_amplitude[5:2], fskout, after_hysteresis_prev_prev, after_hysteresis_prev}; - end + end else begin // send amplitude plus 2 bits reader signal @@ -279,9 +279,18 @@ begin end else if (minor_mode == `FPGA_HF_READER_MODE_RECEIVE_AMPLITUDE) begin - // send amplitude - corr_i_out <= {2'b00, corr_amplitude[13:8]}; - corr_q_out <= corr_amplitude[7:0]; + if (subcarrier_frequency == `FPGA_HF_READER_2SUBCARRIERS_424_484_KHZ) + begin + // send 2 bits fsk (2sc) signal + amplitude + corr_i_out <= {fskout, corr_amplitude[13:8]}; + corr_q_out <= corr_amplitude[7:0]; + end + else + begin + // send amplitude + corr_i_out <= {2'b00, corr_amplitude[13:8]}; + corr_q_out <= corr_amplitude[7:0]; + end end else if (minor_mode == `FPGA_HF_READER_MODE_RECEIVE_IQ) begin From a387f6774e9f2956a369bc08ccc5a329e8608af5 Mon Sep 17 00:00:00 2001 From: Yann GASCUEL <34003959+lnv42@users.noreply.github.com> Date: Wed, 16 Mar 2022 13:58:04 +0100 Subject: [PATCH 2/5] iso15: move FSK code up (before all functions that may use it) --- armsrc/iso15693.c | 634 +++++++++++++++++++++++----------------------- 1 file changed, 317 insertions(+), 317 deletions(-) diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index ac5c0fdaa..d36f7f790 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -647,6 +647,323 @@ static void DecodeTagInit(DecodeTag_t *tag, uint8_t *data, uint16_t max_len) { DecodeTagReset(tag); } +//============================================================================= +// An ISO 15693 decoder for tag responses in FSK (two subcarriers) mode. +// Subcarriers frequencies are 424kHz and 484kHz (fc/32 and fc/28), +// LED handling: +// LED C -> ON once we have received the SOF and are expecting the rest. +// LED C -> OFF once we have received EOF or are unsynced +// +// Returns: true if we received a EOF +// false if we are still waiting for some more +//============================================================================= +//#define DEBUG 1 +#define FREQ_IS_484(f) ((f & 1) == 1) //(f >= 26 && f <= 30) +#define FREQ_IS_424(f) ((f & 2) == 2) //(f >= 30 && f <= 34) +#define FREQ_IS_0(f) ((f & 3) == 0) // (f <= 24 || f >= 36) +#define SEOF_COUNT(c, s) ((s) ? (c >= 11 && c <= 13) : (c >= 44 && c <= 52)) +#define LOGIC_COUNT(c, s) ((s) ? (c >= 3 && c <= 6) : (c >= 13 && c <= 21)) +#define MAX_COUNT(c, s) ((s) ? (c >= 13) : (c >= 52)) +#define MIN_COUNT(c, s) ((s) ? (c <= 2) : (c <= 4)) + +typedef struct DecodeTagFSK { + enum { + STATE_FSK_ERROR, + STATE_FSK_BEFORE_SOF, + STATE_FSK_SOF_484, + STATE_FSK_SOF_424, + STATE_FSK_SOF_END_484, + STATE_FSK_SOF_END_424, + STATE_FSK_RECEIVING_DATA_484, + STATE_FSK_RECEIVING_DATA_424, + STATE_FSK_EOF + } state; + enum { + LOGIC0_PART1, + LOGIC1_PART1, + LOGIC0_PART2, + LOGIC1_PART2, + SOF + } lastBit; + uint8_t count; + uint8_t bitCount; + uint8_t shiftReg; + uint16_t len; + uint16_t max_len; + uint8_t *output; +} DecodeTagFSK_t; + +static void DecodeTagFSKReset(DecodeTagFSK_t *DecodeTag) { + DecodeTag->state = STATE_FSK_BEFORE_SOF; + DecodeTag->bitCount = 0; + DecodeTag->len = 0; + DecodeTag->shiftReg = 0; +} + +static void DecodeTagFSKInit(DecodeTagFSK_t *DecodeTag, uint8_t *data, uint16_t max_len) { + DecodeTag->output = data; + DecodeTag->max_len = max_len; + DecodeTagFSKReset(DecodeTag); +} + +// Performances of this function are crutial for stability +// as it is called in real time for every samples +static int RAMFUNC Handle15693FSKSamplesFromTag(uint8_t freq, DecodeTagFSK_t *DecodeTag, bool recv_speed) +{ + switch(DecodeTag->state) { + case STATE_FSK_BEFORE_SOF: + if (FREQ_IS_484(freq)) + { // possible SOF starting + DecodeTag->state = STATE_FSK_SOF_484; + DecodeTag->lastBit = LOGIC0_PART1; + DecodeTag->count = 1; + } + break; + + case STATE_FSK_SOF_484: + //DbpString("STATE_FSK_SOF_484"); + if (FREQ_IS_424(freq) && SEOF_COUNT(DecodeTag->count, recv_speed)) + { // SOF part1 continue at 424 + DecodeTag->state = STATE_FSK_SOF_424; + DecodeTag->count = 1; + } + else if (FREQ_IS_484(freq) && !MAX_COUNT(DecodeTag->count, recv_speed)) // still in SOF at 484 + { + DecodeTag->count++; + } + else // SOF failed, roll back + { + DecodeTag->state = STATE_FSK_BEFORE_SOF; + } + break; + + case STATE_FSK_SOF_424: + //DbpString("STATE_FSK_SOF_424"); + if (FREQ_IS_484(freq) && SEOF_COUNT(DecodeTag->count, recv_speed)) + { // SOF part 1 finished + DecodeTag->state = STATE_FSK_SOF_END_484; + DecodeTag->count = 1; + } + else if (FREQ_IS_424(freq) && !MAX_COUNT(DecodeTag->count, recv_speed)) // still in SOF at 424 + DecodeTag->count++; + else // SOF failed, roll back + { +#ifdef DEBUG + if (DEBUG) + Dbprintf("SOF_424 failed: freq=%d, count=%d, recv_speed=%d", freq, DecodeTag->count, recv_speed); +#endif + DecodeTag->state = STATE_FSK_BEFORE_SOF; + } + break; + + case STATE_FSK_SOF_END_484: + if (FREQ_IS_424(freq) && LOGIC_COUNT(DecodeTag->count, recv_speed)) + { + DecodeTag->state = STATE_FSK_SOF_END_424; + DecodeTag->count = 1; + } + else if (FREQ_IS_484(freq) && !MAX_COUNT(DecodeTag->count, recv_speed)) // still in SOF_END_484 + DecodeTag->count++; + else // SOF failed, roll back + { +#ifdef DEBUG + if (DEBUG) + Dbprintf("SOF_END_484 failed: freq=%d, count=%d, recv_speed=%d", freq, DecodeTag->count, recv_speed); +#endif + DecodeTag->state = STATE_FSK_BEFORE_SOF; + } + break; + case STATE_FSK_SOF_END_424: + if (FREQ_IS_484(freq) && LOGIC_COUNT(DecodeTag->count, recv_speed)) + { // SOF finished at 484 + DecodeTag->count = 1; + DecodeTag->lastBit = SOF; + DecodeTag->state = STATE_FSK_RECEIVING_DATA_484; + LED_C_ON(); + } + else if (FREQ_IS_424(freq) && LOGIC_COUNT(DecodeTag->count-2, recv_speed)) + { // SOF finished at 424 (wait count+2 to be sure that next freq is 424) + DecodeTag->count = 2; + DecodeTag->lastBit = SOF; + DecodeTag->state = STATE_FSK_RECEIVING_DATA_424; + LED_C_ON(); + } + else if (FREQ_IS_424(freq) && !MAX_COUNT(DecodeTag->count, recv_speed)) // still in SOF_END_424 + DecodeTag->count++; + else // SOF failed, roll back + { +#ifdef DEBUG + if (DEBUG) + Dbprintf("SOF_END_424 failed: freq=%d, count=%d, recv_speed=%d", freq, DecodeTag->count, recv_speed); +#endif + DecodeTag->state = STATE_FSK_BEFORE_SOF; + } + break; + + + case STATE_FSK_RECEIVING_DATA_424: + if (FREQ_IS_484(freq) && LOGIC_COUNT(DecodeTag->count, recv_speed)) + { + if (DecodeTag->lastBit == LOGIC1_PART1) + { // logic 1 finished, goto 484 + DecodeTag->lastBit = LOGIC1_PART2; + + DecodeTag->shiftReg >>= 1; + DecodeTag->shiftReg |= 0x80; + DecodeTag->bitCount++; + if (DecodeTag->bitCount == 8) { + DecodeTag->output[DecodeTag->len++] = DecodeTag->shiftReg; + if (DecodeTag->len > DecodeTag->max_len) { + // buffer overflow, give up + LED_C_OFF(); + return true; + } + DecodeTag->bitCount = 0; + DecodeTag->shiftReg = 0; + } + } + else + { // end of LOGIC0_PART1 + DecodeTag->lastBit = LOGIC0_PART1; + } + DecodeTag->count = 1; + DecodeTag->state = STATE_FSK_RECEIVING_DATA_484; + } + else if (FREQ_IS_424(freq) && LOGIC_COUNT(DecodeTag->count-2, recv_speed) && + DecodeTag->lastBit == LOGIC1_PART1) + { // logic 1 finished, stay in 484 + DecodeTag->lastBit = LOGIC1_PART2; + + DecodeTag->shiftReg >>= 1; + DecodeTag->shiftReg |= 0x80; + DecodeTag->bitCount++; + if (DecodeTag->bitCount == 8) { + DecodeTag->output[DecodeTag->len++] = DecodeTag->shiftReg; + if (DecodeTag->len > DecodeTag->max_len) { + // buffer overflow, give up + LED_C_OFF(); + return true; + } + DecodeTag->bitCount = 0; + DecodeTag->shiftReg = 0; + } + DecodeTag->count = 2; + } + else if (FREQ_IS_424(freq) && !MAX_COUNT(DecodeTag->count, recv_speed)) // still at 424 + DecodeTag->count++; + + else if (FREQ_IS_484(freq) && DecodeTag->lastBit == LOGIC0_PART2 && + SEOF_COUNT(DecodeTag->count, recv_speed)) + { // EOF has started +#ifdef DEBUG + if (DEBUG) + Dbprintf("RECEIVING_DATA_424->EOF: freq=%d, count=%d, recv_speed=%d, lastbit=%d, state=%d", freq, DecodeTag->count, recv_speed, DecodeTag->lastBit, DecodeTag->state); +#endif + DecodeTag->count = 1; + DecodeTag->state = STATE_FSK_EOF; + LED_C_OFF(); + } + else // error + { +#ifdef DEBUG + if (DEBUG) + Dbprintf("RECEIVING_DATA_424 error: freq=%d, count=%d, recv_speed=%d, lastbit=%d, state=%d", freq, DecodeTag->count, recv_speed, DecodeTag->lastBit, DecodeTag->state); +#endif + DecodeTag->state = STATE_FSK_ERROR; + LED_C_OFF(); + return true; + } + break; + + case STATE_FSK_RECEIVING_DATA_484: + if (FREQ_IS_424(freq) && LOGIC_COUNT(DecodeTag->count, recv_speed)) + { + if (DecodeTag->lastBit == LOGIC0_PART1) + { // logic 0 finished, goto 424 + DecodeTag->lastBit = LOGIC0_PART2; + + DecodeTag->shiftReg >>= 1; + DecodeTag->bitCount++; + if (DecodeTag->bitCount == 8) { + DecodeTag->output[DecodeTag->len++] = DecodeTag->shiftReg; + if (DecodeTag->len > DecodeTag->max_len) { + // buffer overflow, give up + LED_C_OFF(); + return true; + } + DecodeTag->bitCount = 0; + DecodeTag->shiftReg = 0; + } + } + else + { // end of LOGIC1_PART1 + DecodeTag->lastBit = LOGIC1_PART1; + } + DecodeTag->count = 1; + DecodeTag->state = STATE_FSK_RECEIVING_DATA_424; + } + else if (FREQ_IS_484(freq) && LOGIC_COUNT(DecodeTag->count-2, recv_speed) && + DecodeTag->lastBit == LOGIC0_PART1) + { // logic 0 finished, stay in 424 + DecodeTag->lastBit = LOGIC0_PART2; + + DecodeTag->shiftReg >>= 1; + DecodeTag->bitCount++; + if (DecodeTag->bitCount == 8) { + DecodeTag->output[DecodeTag->len++] = DecodeTag->shiftReg; + if (DecodeTag->len > DecodeTag->max_len) { + // buffer overflow, give up + LED_C_OFF(); + return true; + } + DecodeTag->bitCount = 0; + DecodeTag->shiftReg = 0; + } + DecodeTag->count = 2; + } + else if (FREQ_IS_484(freq) && !MAX_COUNT(DecodeTag->count, recv_speed)) // still at 484 + DecodeTag->count++; + else // error + { +#ifdef DEBUG + if (DEBUG) + Dbprintf("RECEIVING_DATA_484 error: freq=%d, count=%d, recv_speed=%d, lastbit=%d, state=%d", freq, DecodeTag->count, recv_speed, DecodeTag->lastBit, DecodeTag->state); +#endif + LED_C_OFF(); + DecodeTag->state = STATE_FSK_ERROR; + return true; + } + break; + + case STATE_FSK_EOF: + if (FREQ_IS_484(freq) && !MAX_COUNT(DecodeTag->count, recv_speed)) // still at 484 + { + DecodeTag->count++; + if (SEOF_COUNT(DecodeTag->count, recv_speed)) + return true; // end of the transmission + } + else // error + { +#ifdef DEBUG + if (DEBUG) + Dbprintf("EOF error: freq=%d, count=%d, recv_speed=%d", freq, DecodeTag->count, recv_speed); +#endif + DecodeTag->state = STATE_FSK_ERROR; + return true; + } + break; + case STATE_FSK_ERROR: + LED_C_OFF(); +#ifdef DEBUG + if (DEBUG) + Dbprintf("FSK error: freq=%d, count=%d, recv_speed=%d", freq, DecodeTag->count, recv_speed); +#endif + return true; // error + break; + } + return false; +} + /* * Receive and decode the tag response, also log to tracebuffer */ @@ -1259,323 +1576,6 @@ void AcquireRawAdcSamplesIso15693(void) { LEDsoff(); } -//============================================================================= -// An ISO 15693 decoder for tag responses in FSK (two subcarriers) mode. -// Subcarriers frequencies are 424kHz and 484kHz (fc/32 and fc/28), -// LED handling: -// LED C -> ON once we have received the SOF and are expecting the rest. -// LED C -> OFF once we have received EOF or are unsynced -// -// Returns: true if we received a EOF -// false if we are still waiting for some more -//============================================================================= -//#define DEBUG 1 -#define FREQ_IS_484(f) ((f & 1) == 1) //(f >= 26 && f <= 30) -#define FREQ_IS_424(f) ((f & 2) == 2) //(f >= 30 && f <= 34) -#define FREQ_IS_0(f) ((f & 3) == 0) // (f <= 24 || f >= 36) -#define SEOF_COUNT(c, s) ((s) ? (c >= 11 && c <= 13) : (c >= 44 && c <= 52)) -#define LOGIC_COUNT(c, s) ((s) ? (c >= 3 && c <= 6) : (c >= 13 && c <= 21)) -#define MAX_COUNT(c, s) ((s) ? (c >= 13) : (c >= 52)) -#define MIN_COUNT(c, s) ((s) ? (c <= 2) : (c <= 4)) - -typedef struct DecodeTagFSK { - enum { - STATE_FSK_ERROR, - STATE_FSK_BEFORE_SOF, - STATE_FSK_SOF_484, - STATE_FSK_SOF_424, - STATE_FSK_SOF_END_484, - STATE_FSK_SOF_END_424, - STATE_FSK_RECEIVING_DATA_484, - STATE_FSK_RECEIVING_DATA_424, - STATE_FSK_EOF - } state; - enum { - LOGIC0_PART1, - LOGIC1_PART1, - LOGIC0_PART2, - LOGIC1_PART2, - SOF - } lastBit; - uint8_t count; - uint8_t bitCount; - uint8_t shiftReg; - uint16_t len; - uint16_t max_len; - uint8_t *output; -} DecodeTagFSK_t; - -static void DecodeTagFSKReset(DecodeTagFSK_t *DecodeTag) { - DecodeTag->state = STATE_FSK_BEFORE_SOF; - DecodeTag->bitCount = 0; - DecodeTag->len = 0; - DecodeTag->shiftReg = 0; -} - -static void DecodeTagFSKInit(DecodeTagFSK_t *DecodeTag, uint8_t *data, uint16_t max_len) { - DecodeTag->output = data; - DecodeTag->max_len = max_len; - DecodeTagFSKReset(DecodeTag); -} - -// Performances of this function are crutial for stability -// as it is called in real time for every samples -static int RAMFUNC Handle15693FSKSamplesFromTag(uint8_t freq, DecodeTagFSK_t *DecodeTag, bool recv_speed) -{ - switch(DecodeTag->state) { - case STATE_FSK_BEFORE_SOF: - if (FREQ_IS_484(freq)) - { // possible SOF starting - DecodeTag->state = STATE_FSK_SOF_484; - DecodeTag->lastBit = LOGIC0_PART1; - DecodeTag->count = 1; - } - break; - - case STATE_FSK_SOF_484: - //DbpString("STATE_FSK_SOF_484"); - if (FREQ_IS_424(freq) && SEOF_COUNT(DecodeTag->count, recv_speed)) - { // SOF part1 continue at 424 - DecodeTag->state = STATE_FSK_SOF_424; - DecodeTag->count = 1; - } - else if (FREQ_IS_484(freq) && !MAX_COUNT(DecodeTag->count, recv_speed)) // still in SOF at 484 - { - DecodeTag->count++; - } - else // SOF failed, roll back - { - DecodeTag->state = STATE_FSK_BEFORE_SOF; - } - break; - - case STATE_FSK_SOF_424: - //DbpString("STATE_FSK_SOF_424"); - if (FREQ_IS_484(freq) && SEOF_COUNT(DecodeTag->count, recv_speed)) - { // SOF part 1 finished - DecodeTag->state = STATE_FSK_SOF_END_484; - DecodeTag->count = 1; - } - else if (FREQ_IS_424(freq) && !MAX_COUNT(DecodeTag->count, recv_speed)) // still in SOF at 424 - DecodeTag->count++; - else // SOF failed, roll back - { -#ifdef DEBUG - if (DEBUG) - Dbprintf("SOF_424 failed: freq=%d, count=%d, recv_speed=%d", freq, DecodeTag->count, recv_speed); -#endif - DecodeTag->state = STATE_FSK_BEFORE_SOF; - } - break; - - case STATE_FSK_SOF_END_484: - if (FREQ_IS_424(freq) && LOGIC_COUNT(DecodeTag->count, recv_speed)) - { - DecodeTag->state = STATE_FSK_SOF_END_424; - DecodeTag->count = 1; - } - else if (FREQ_IS_484(freq) && !MAX_COUNT(DecodeTag->count, recv_speed)) // still in SOF_END_484 - DecodeTag->count++; - else // SOF failed, roll back - { -#ifdef DEBUG - if (DEBUG) - Dbprintf("SOF_END_484 failed: freq=%d, count=%d, recv_speed=%d", freq, DecodeTag->count, recv_speed); -#endif - DecodeTag->state = STATE_FSK_BEFORE_SOF; - } - break; - case STATE_FSK_SOF_END_424: - if (FREQ_IS_484(freq) && LOGIC_COUNT(DecodeTag->count, recv_speed)) - { // SOF finished at 484 - DecodeTag->count = 1; - DecodeTag->lastBit = SOF; - DecodeTag->state = STATE_FSK_RECEIVING_DATA_484; - LED_C_ON(); - } - else if (FREQ_IS_424(freq) && LOGIC_COUNT(DecodeTag->count-2, recv_speed)) - { // SOF finished at 424 (wait count+2 to be sure that next freq is 424) - DecodeTag->count = 2; - DecodeTag->lastBit = SOF; - DecodeTag->state = STATE_FSK_RECEIVING_DATA_424; - LED_C_ON(); - } - else if (FREQ_IS_424(freq) && !MAX_COUNT(DecodeTag->count, recv_speed)) // still in SOF_END_424 - DecodeTag->count++; - else // SOF failed, roll back - { -#ifdef DEBUG - if (DEBUG) - Dbprintf("SOF_END_424 failed: freq=%d, count=%d, recv_speed=%d", freq, DecodeTag->count, recv_speed); -#endif - DecodeTag->state = STATE_FSK_BEFORE_SOF; - } - break; - - - case STATE_FSK_RECEIVING_DATA_424: - if (FREQ_IS_484(freq) && LOGIC_COUNT(DecodeTag->count, recv_speed)) - { - if (DecodeTag->lastBit == LOGIC1_PART1) - { // logic 1 finished, goto 484 - DecodeTag->lastBit = LOGIC1_PART2; - - DecodeTag->shiftReg >>= 1; - DecodeTag->shiftReg |= 0x80; - DecodeTag->bitCount++; - if (DecodeTag->bitCount == 8) { - DecodeTag->output[DecodeTag->len++] = DecodeTag->shiftReg; - if (DecodeTag->len > DecodeTag->max_len) { - // buffer overflow, give up - LED_C_OFF(); - return true; - } - DecodeTag->bitCount = 0; - DecodeTag->shiftReg = 0; - } - } - else - { // end of LOGIC0_PART1 - DecodeTag->lastBit = LOGIC0_PART1; - } - DecodeTag->count = 1; - DecodeTag->state = STATE_FSK_RECEIVING_DATA_484; - } - else if (FREQ_IS_424(freq) && LOGIC_COUNT(DecodeTag->count-2, recv_speed) && - DecodeTag->lastBit == LOGIC1_PART1) - { // logic 1 finished, stay in 484 - DecodeTag->lastBit = LOGIC1_PART2; - - DecodeTag->shiftReg >>= 1; - DecodeTag->shiftReg |= 0x80; - DecodeTag->bitCount++; - if (DecodeTag->bitCount == 8) { - DecodeTag->output[DecodeTag->len++] = DecodeTag->shiftReg; - if (DecodeTag->len > DecodeTag->max_len) { - // buffer overflow, give up - LED_C_OFF(); - return true; - } - DecodeTag->bitCount = 0; - DecodeTag->shiftReg = 0; - } - DecodeTag->count = 2; - } - else if (FREQ_IS_424(freq) && !MAX_COUNT(DecodeTag->count, recv_speed)) // still at 424 - DecodeTag->count++; - - else if (FREQ_IS_484(freq) && DecodeTag->lastBit == LOGIC0_PART2 && - SEOF_COUNT(DecodeTag->count, recv_speed)) - { // EOF has started -#ifdef DEBUG - if (DEBUG) - Dbprintf("RECEIVING_DATA_424->EOF: freq=%d, count=%d, recv_speed=%d, lastbit=%d, state=%d", freq, DecodeTag->count, recv_speed, DecodeTag->lastBit, DecodeTag->state); -#endif - DecodeTag->count = 1; - DecodeTag->state = STATE_FSK_EOF; - LED_C_OFF(); - } - else // error - { -#ifdef DEBUG - if (DEBUG) - Dbprintf("RECEIVING_DATA_424 error: freq=%d, count=%d, recv_speed=%d, lastbit=%d, state=%d", freq, DecodeTag->count, recv_speed, DecodeTag->lastBit, DecodeTag->state); -#endif - DecodeTag->state = STATE_FSK_ERROR; - LED_C_OFF(); - return true; - } - break; - - case STATE_FSK_RECEIVING_DATA_484: - if (FREQ_IS_424(freq) && LOGIC_COUNT(DecodeTag->count, recv_speed)) - { - if (DecodeTag->lastBit == LOGIC0_PART1) - { // logic 0 finished, goto 424 - DecodeTag->lastBit = LOGIC0_PART2; - - DecodeTag->shiftReg >>= 1; - DecodeTag->bitCount++; - if (DecodeTag->bitCount == 8) { - DecodeTag->output[DecodeTag->len++] = DecodeTag->shiftReg; - if (DecodeTag->len > DecodeTag->max_len) { - // buffer overflow, give up - LED_C_OFF(); - return true; - } - DecodeTag->bitCount = 0; - DecodeTag->shiftReg = 0; - } - } - else - { // end of LOGIC1_PART1 - DecodeTag->lastBit = LOGIC1_PART1; - } - DecodeTag->count = 1; - DecodeTag->state = STATE_FSK_RECEIVING_DATA_424; - } - else if (FREQ_IS_484(freq) && LOGIC_COUNT(DecodeTag->count-2, recv_speed) && - DecodeTag->lastBit == LOGIC0_PART1) - { // logic 0 finished, stay in 424 - DecodeTag->lastBit = LOGIC0_PART2; - - DecodeTag->shiftReg >>= 1; - DecodeTag->bitCount++; - if (DecodeTag->bitCount == 8) { - DecodeTag->output[DecodeTag->len++] = DecodeTag->shiftReg; - if (DecodeTag->len > DecodeTag->max_len) { - // buffer overflow, give up - LED_C_OFF(); - return true; - } - DecodeTag->bitCount = 0; - DecodeTag->shiftReg = 0; - } - DecodeTag->count = 2; - } - else if (FREQ_IS_484(freq) && !MAX_COUNT(DecodeTag->count, recv_speed)) // still at 484 - DecodeTag->count++; - else // error - { -#ifdef DEBUG - if (DEBUG) - Dbprintf("RECEIVING_DATA_484 error: freq=%d, count=%d, recv_speed=%d, lastbit=%d, state=%d", freq, DecodeTag->count, recv_speed, DecodeTag->lastBit, DecodeTag->state); -#endif - LED_C_OFF(); - DecodeTag->state = STATE_FSK_ERROR; - return true; - } - break; - - case STATE_FSK_EOF: - if (FREQ_IS_484(freq) && !MAX_COUNT(DecodeTag->count, recv_speed)) // still at 484 - { - DecodeTag->count++; - if (SEOF_COUNT(DecodeTag->count, recv_speed)) - return true; // end of the transmission - } - else // error - { -#ifdef DEBUG - if (DEBUG) - Dbprintf("EOF error: freq=%d, count=%d, recv_speed=%d", freq, DecodeTag->count, recv_speed); -#endif - DecodeTag->state = STATE_FSK_ERROR; - return true; - } - break; - case STATE_FSK_ERROR: - LED_C_OFF(); -#ifdef DEBUG - if (DEBUG) - Dbprintf("FSK error: freq=%d, count=%d, recv_speed=%d", freq, DecodeTag->count, recv_speed); -#endif - return true; // error - break; - } - return false; -} - void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string, bool iclass) { LEDsoff(); From f65ca5f14c8b5480a57f1250abf87e003f44b1fa Mon Sep 17 00:00:00 2001 From: Yann GASCUEL <34003959+lnv42@users.noreply.github.com> Date: Wed, 16 Mar 2022 15:21:28 +0100 Subject: [PATCH 3/5] iso15 sniff: add support for slow answers with single subcarrier Before this commit, slow answers was only supported for dual subcarriers answers. --- armsrc/iso15693.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index d36f7f790..a23e0805c 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -410,7 +410,7 @@ typedef struct { //----------------------------------------------------------------------------- // DEMODULATE tag answer //----------------------------------------------------------------------------- -static RAMFUNC int Handle15693SamplesFromTag(uint16_t amplitude, DecodeTag_t *tag) { +static RAMFUNC int Handle15693SamplesFromTag(uint16_t amplitude, DecodeTag_t *tag, bool recv_speed) { switch (tag->state) { @@ -454,7 +454,7 @@ static RAMFUNC int Handle15693SamplesFromTag(uint16_t amplitude, DecodeTag_t *ta if (tag->posCount > 2) { tag->threshold_half += amplitude; // keep track of average high value } - if (tag->posCount == 10) { + if (tag->posCount == (recv_speed?10:40)) { tag->threshold_half >>= 2; // (4 times 1/2 average) tag->state = STATE_TAG_SOF_HIGH_END; } @@ -468,7 +468,7 @@ static RAMFUNC int Handle15693SamplesFromTag(uint16_t amplitude, DecodeTag_t *ta case STATE_TAG_SOF_HIGH_END: { // check for falling edge - if (tag->posCount == 13 && amplitude < tag->threshold_sof) { + if (tag->posCount == (recv_speed?13:52) && amplitude < tag->threshold_sof) { tag->lastBit = SOF_PART1; // detected 1st part of SOF (12 samples low and 12 samples high) tag->shiftReg = 0; tag->bitCount = 0; @@ -480,7 +480,7 @@ static RAMFUNC int Handle15693SamplesFromTag(uint16_t amplitude, DecodeTag_t *ta LED_C_ON(); } else { tag->posCount++; - if (tag->posCount > 13) { // high phase too long + if (tag->posCount > (recv_speed?13:52)) { // high phase too long tag->posCount = 0; tag->previous_amplitude = amplitude; tag->state = STATE_TAG_SOF_LOW; @@ -496,13 +496,13 @@ static RAMFUNC int Handle15693SamplesFromTag(uint16_t amplitude, DecodeTag_t *ta tag->sum2 = 0; } - if (tag->posCount <= 4) { + if (tag->posCount <= (recv_speed?4:16)) { tag->sum1 += amplitude; } else { tag->sum2 += amplitude; } - if (tag->posCount == 8) { + if (tag->posCount == (recv_speed?8:32)) { if (tag->sum1 > tag->threshold_half && tag->sum2 > tag->threshold_half) { // modulation in both halves if (tag->lastBit == LOGIC0) { // this was already part of EOF tag->state = STATE_TAG_EOF; @@ -583,13 +583,13 @@ static RAMFUNC int Handle15693SamplesFromTag(uint16_t amplitude, DecodeTag_t *ta tag->sum2 = 0; } - if (tag->posCount <= 4) { + if (tag->posCount <= (recv_speed?4:16)) { tag->sum1 += amplitude; } else { tag->sum2 += amplitude; } - if (tag->posCount == 8) { + if (tag->posCount == (recv_speed?8:32)) { if (tag->sum1 > tag->threshold_half && tag->sum2 < tag->threshold_half) { // modulation in first half tag->posCount = 0; tag->state = STATE_TAG_EOF_TAIL; @@ -610,13 +610,13 @@ static RAMFUNC int Handle15693SamplesFromTag(uint16_t amplitude, DecodeTag_t *ta tag->sum2 = 0; } - if (tag->posCount <= 4) { + if (tag->posCount <= (recv_speed?4:16)) { tag->sum1 += amplitude; } else { tag->sum2 += amplitude; } - if (tag->posCount == 8) { + if (tag->posCount == (recv_speed?8:32)) { if (tag->sum1 < tag->threshold_half && tag->sum2 < tag->threshold_half) { // no modulation in both halves LED_C_OFF(); return true; @@ -1036,7 +1036,7 @@ int GetIso15693AnswerFromTag(uint8_t *response, uint16_t max_len, uint16_t timeo } } - if (Handle15693SamplesFromTag(tagdata, dt)) { + if (Handle15693SamplesFromTag(tagdata, dt, true)) { *eof_time = dma_start_time + (samples * 16) - DELAY_TAG_TO_ARM; // end of EOF @@ -1730,7 +1730,7 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string, bool icla if (!expect_fsk_answer) { - if (Handle15693SamplesFromTag((sniffdata >> 4) << 2, &dtag)) { + if (Handle15693SamplesFromTag((sniffdata >> 4) << 2, &dtag, expect_fast_answer)) { uint32_t eof_time = dma_start_time + (samples * 16) - DELAY_TAG_TO_ARM_SNIFF; // end of EOF if (dtag.lastBit == SOF_PART2) { @@ -1786,7 +1786,6 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string, bool icla } } } - } FpgaDisableTracing(); From abb840558f2cc3b89536cf9e4d6663681c1fa112 Mon Sep 17 00:00:00 2001 From: Yann GASCUEL <34003959+lnv42@users.noreply.github.com> Date: Wed, 16 Mar 2022 15:45:18 +0100 Subject: [PATCH 4/5] iso15: add support for slow and 2SC to GetIso15693AnswerFromTag() Allow running raw iso15 cmd expecting slow and/or dual subcarriers answers. E.g. sending slow 2SC inventory ("hf 15 raw -c -d 250100") is now working. --- armsrc/iclass.c | 18 +++---- armsrc/iso15693.c | 133 +++++++++++++++++++++++++++++++++------------- armsrc/iso15693.h | 4 +- 3 files changed, 107 insertions(+), 48 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index e261efc59..cabec62ca 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -1264,7 +1264,7 @@ static bool iclass_send_cmd_with_retries(uint8_t *cmd, size_t cmdsize, uint8_t * return true; } - if (expected_size == GetIso15693AnswerFromTag(resp, max_resp_size, timeout, eof_time)) { + if (expected_size == GetIso15693AnswerFromTag(resp, max_resp_size, timeout, eof_time, false, true)) { return true; } } @@ -1296,7 +1296,7 @@ static bool select_iclass_tag_ex(picopass_hdr_t *hdr, bool use_credit_key, uint3 // wakeup uint32_t start_time = GetCountSspClk(); iclass_send_as_reader(act_all, 1, &start_time, eof_time); - int len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_ACTALL, eof_time); + int len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_ACTALL, eof_time, false, true); if (len < 0) return false; @@ -1305,7 +1305,7 @@ static bool select_iclass_tag_ex(picopass_hdr_t *hdr, bool use_credit_key, uint3 iclass_send_as_reader(identify, 1, &start_time, eof_time); // expect a 10-byte response here, 8 byte anticollision-CSN and 2 byte CRC - len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time); + len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time, false, true); if (len != 10) return false; @@ -1317,7 +1317,7 @@ static bool select_iclass_tag_ex(picopass_hdr_t *hdr, bool use_credit_key, uint3 iclass_send_as_reader(select, sizeof(select), &start_time, eof_time); // expect a 10-byte response here, 8 byte CSN and 2 byte CRC - len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time); + len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time, false, true); if (len != 10) return false; @@ -1329,7 +1329,7 @@ static bool select_iclass_tag_ex(picopass_hdr_t *hdr, bool use_credit_key, uint3 iclass_send_as_reader(read_conf, sizeof(read_conf), &start_time, eof_time); // expect a 8-byte response here - len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time); + len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time, false, true); if (len != 10) return false; @@ -1347,7 +1347,7 @@ static bool select_iclass_tag_ex(picopass_hdr_t *hdr, bool use_credit_key, uint3 iclass_send_as_reader(read_aia, sizeof(read_aia), &start_time, eof_time); // expect a 10-byte response here - len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time); + len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time, false, true); if (len != 10) return false; @@ -1361,7 +1361,7 @@ static bool select_iclass_tag_ex(picopass_hdr_t *hdr, bool use_credit_key, uint3 iclass_send_as_reader(read_check_cc, sizeof(read_check_cc), &start_time, eof_time); // expect a 8-byte response here - len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time); + len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time, false, true); if (len != 8) return false; @@ -1383,7 +1383,7 @@ static bool select_iclass_tag_ex(picopass_hdr_t *hdr, bool use_credit_key, uint3 iclass_send_as_reader(read_aia, sizeof(read_aia), &start_time, eof_time); // expect a 10-byte response here - len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time); + len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time, false, true); if (len != 10) return false; @@ -1870,7 +1870,7 @@ void iClass_WriteBlock(uint8_t *msg) { return; } else { - if (GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_UPDATE, &eof_time) == 10) { + if (GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_UPDATE, &eof_time, false, true) == 10) { res = true; break; } diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index a23e0805c..fd22ce953 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -967,20 +967,27 @@ static int RAMFUNC Handle15693FSKSamplesFromTag(uint8_t freq, DecodeTagFSK_t *De /* * Receive and decode the tag response, also log to tracebuffer */ -int GetIso15693AnswerFromTag(uint8_t *response, uint16_t max_len, uint16_t timeout, uint32_t *eof_time) { +int GetIso15693AnswerFromTag(uint8_t *response, uint16_t max_len, uint16_t timeout, uint32_t *eof_time, bool fsk, bool recv_speed) { int samples = 0, ret = 0; // the Decoder data structure DecodeTag_t dtm = { 0 }; DecodeTag_t *dt = &dtm; - DecodeTagInit(dt, response, max_len); + + DecodeTagFSK_t dtfm = { 0 }; + DecodeTagFSK_t *dtf = &dtfm; + + if (!fsk) + DecodeTagInit(dt, response, max_len); + else + DecodeTagFSKInit(dtf, response, max_len); // wait for last transfer to complete while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)); // And put the FPGA in the appropriate mode - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_424_KHZ | FPGA_HF_READER_MODE_RECEIVE_AMPLITUDE); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_2SUBCARRIERS_424_484_KHZ | FPGA_HF_READER_MODE_RECEIVE_AMPLITUDE); // Setup and start DMA. FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); @@ -1036,56 +1043,102 @@ int GetIso15693AnswerFromTag(uint8_t *response, uint16_t max_len, uint16_t timeo } } - if (Handle15693SamplesFromTag(tagdata, dt, true)) { + if (!fsk) { + if (Handle15693SamplesFromTag(tagdata & 0x3FFF, dt, recv_speed)) { - *eof_time = dma_start_time + (samples * 16) - DELAY_TAG_TO_ARM; // end of EOF + *eof_time = dma_start_time + (samples * 16) - DELAY_TAG_TO_ARM; // end of EOF - if (dt->lastBit == SOF_PART2) { - *eof_time -= (8 * 16); // needed 8 additional samples to confirm single SOF (iCLASS) + if (dt->lastBit == SOF_PART2) { + *eof_time -= (8 * 16); // needed 8 additional samples to confirm single SOF (iCLASS) + } + if (dt->len > dt->max_len) { + ret = -2; // buffer overflow + Dbprintf("overflow (%d > %d", dt->len, dt->max_len); + } + break; } - if (dt->len > dt->max_len) { - ret = -2; // buffer overflow - Dbprintf("overflow (%d > %d", dt->len, dt->max_len); + + // timeout + if (samples > timeout && dt->state < STATE_TAG_RECEIVING_DATA) { + ret = -3; + break; } - break; } + else { + if (Handle15693FSKSamplesFromTag(tagdata >> 14, dtf, recv_speed)) { - // timeout - if (samples > timeout && dt->state < STATE_TAG_RECEIVING_DATA) { - ret = -3; - break; + *eof_time = dma_start_time + (samples * 16) - DELAY_TAG_TO_ARM; // end of EOF + + if (dtf->lastBit == SOF) { + *eof_time -= (8 * 16); // needed 8 additional samples to confirm single SOF (iCLASS) + } + if (dtf->len > dtf->max_len) { + ret = -2; // buffer overflow + Dbprintf("overflow (%d > %d", dtf->len, dtf->max_len); + } + break; + } + + // timeout + if (samples > timeout && dtf->state < STATE_FSK_RECEIVING_DATA_484) { + ret = -3; + break; + } } - } FpgaDisableSscDma(); FpgaDisableTracing(); - uint32_t sof_time = *eof_time - - (dt->len * 8 * 8 * 16) // time for byte transfers - - (32 * 16) // time for SOF transfer - - (dt->lastBit != SOF_PART2 ? (32 * 16) : 0); // time for EOF transfer + uint32_t sof_time = *eof_time - (32 * 16); // time for SOF transfer - if (g_dbglevel >= DBG_EXTENDED) { - Dbprintf("samples = %d, ret = %d, Decoder: state = %d, lastBit = %d, len = %d, bitCount = %d, posCount = %d, maxlen = %u", - samples, - ret, - dt->state, - dt->lastBit, - dt->len, - dt->bitCount, - dt->posCount, - dt->max_len + if (!fsk) { + sof_time -= (dt->len * 8 * 8 * 16) // time for byte transfers + + (dt->lastBit != SOF_PART2 ? (32 * 16) : 0); // time for EOF transfer + + if (g_dbglevel >= DBG_EXTENDED) { + Dbprintf("samples = %d, ret = %d, Decoder: state = %d, lastBit = %d, len = %d, bitCount = %d, posCount = %d, maxlen = %u", + samples, + ret, + dt->state, + dt->lastBit, + dt->len, + dt->bitCount, + dt->posCount, + dt->max_len ); - Dbprintf("timing: sof_time = %d, eof_time = %d", (sof_time * 4), (*eof_time * 4)); + Dbprintf("timing: sof_time = %d, eof_time = %d", (sof_time * 4), (*eof_time * 4)); + } + } + else { + sof_time -= (dtf->len * 8 * 8 * 16) // time for byte transfers + + (dtf->lastBit != SOF ? (32 * 16) : 0); // time for EOF transfer + + if (g_dbglevel >= DBG_EXTENDED) { + Dbprintf("samples = %d, ret = %d, FSK Decoder: state = %d, lastBit = %d, len = %d, bitCount = %d, count = %d, maxlen = %u", + samples, + ret, + dtf->state, + dtf->lastBit, + dtf->len, + dtf->bitCount, + dtf->count, + dtf->max_len + ); + Dbprintf("timing: sof_time = %d, eof_time = %d", (sof_time * 4), (*eof_time * 4)); + } } if (ret < 0) { return ret; } - LogTrace_ISO15693(dt->output, dt->len, (sof_time * 4), (*eof_time * 4), NULL, false); - return dt->len; + if (!fsk) { + LogTrace_ISO15693(dt->output, dt->len, (sof_time * 4), (*eof_time * 4), NULL, false); + return dt->len; + } + LogTrace_ISO15693(dtf->output, dtf->len, (sof_time * 4), (*eof_time * 4), NULL, false); + return dtf->len; } @@ -1869,6 +1922,9 @@ int SendDataTag(uint8_t *send, int sendlen, bool init, bool speed_fast, uint8_t start_time = GetCountSspClk(); } + bool fsk = send[0] & ISO15_REQ_SUBCARRIER_TWO; + bool recv_speed = send[0] & ISO15_REQ_DATARATE_HIGH; + if (speed_fast) { // high speed (1 out of 4) CodeIso15693AsReader(send, sendlen); @@ -1889,13 +1945,13 @@ int SendDataTag(uint8_t *send, int sendlen, bool init, bool speed_fast, uint8_t *eof_time = start_time + 32 * ((8 * ts->max) - 4); // subtract the 4 padding bits after EOF LogTrace_ISO15693(send, sendlen, (start_time * 4), (*eof_time * 4), NULL, true); if (recv != NULL) { - res = GetIso15693AnswerFromTag(recv, max_recv_len, timeout, eof_time); + res = GetIso15693AnswerFromTag(recv, max_recv_len, timeout, eof_time, fsk, recv_speed); } } return res; } -int SendDataTagEOF(uint8_t *recv, uint16_t max_recv_len, uint32_t start_time, uint16_t timeout, uint32_t *eof_time) { +int SendDataTagEOF(uint8_t *recv, uint16_t max_recv_len, uint32_t start_time, uint16_t timeout, uint32_t *eof_time, bool fsk, bool recv_speed) { CodeIso15693AsReaderEOF(); tosend_t *ts = get_tosend(); @@ -1905,7 +1961,7 @@ int SendDataTagEOF(uint8_t *recv, uint16_t max_recv_len, uint32_t start_time, ui int res = 0; if (recv != NULL) { - res = GetIso15693AnswerFromTag(recv, max_recv_len, timeout, eof_time); + res = GetIso15693AnswerFromTag(recv, max_recv_len, timeout, eof_time, fsk, recv_speed); } return res; } @@ -2335,10 +2391,13 @@ void DirectTag15693Command(uint32_t datalen, uint32_t speed, uint32_t recv, uint reply_mix(CMD_ACK, recvlen, 0, 0, NULL, 0); } else { + bool fsk = data[0] & ISO15_REQ_SUBCARRIER_TWO; + bool recv_speed = data[0] & ISO15_REQ_DATARATE_HIGH; + // send a single EOF to get the tag response if (request_answer) { start_time = eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; - recvlen = SendDataTagEOF((recv ? recvbuf : NULL), sizeof(recvbuf), start_time, ISO15693_READER_TIMEOUT, &eof_time); + recvlen = SendDataTagEOF((recv ? recvbuf : NULL), sizeof(recvbuf), start_time, ISO15693_READER_TIMEOUT, &eof_time, fsk, recv_speed); } if (recv) { diff --git a/armsrc/iso15693.h b/armsrc/iso15693.h index 31ca6e6c1..dbb3ff28b 100644 --- a/armsrc/iso15693.h +++ b/armsrc/iso15693.h @@ -41,7 +41,7 @@ void CodeIso15693AsTag(const uint8_t *cmd, size_t len); void TransmitTo15693Reader(const uint8_t *cmd, size_t len, uint32_t *start_time, uint32_t slot_time, bool slow); int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eof_time); void TransmitTo15693Tag(const uint8_t *cmd, int len, uint32_t *start_time); -int GetIso15693AnswerFromTag(uint8_t *response, uint16_t max_len, uint16_t timeout, uint32_t *eof_time); +int GetIso15693AnswerFromTag(uint8_t *response, uint16_t max_len, uint16_t timeout, uint32_t *eof_time, bool fsk, bool recv_speed); //void RecordRawAdcSamplesIso15693(void); void AcquireRawAdcSamplesIso15693(void); @@ -55,7 +55,7 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string, bool icla int SendDataTag(uint8_t *send, int sendlen, bool init, bool speed_fast, uint8_t *recv, uint16_t max_recv_len, uint32_t start_time, uint16_t timeout, uint32_t *eof_time); -int SendDataTagEOF(uint8_t *recv, uint16_t max_recv_len, uint32_t start_time, uint16_t timeout, uint32_t *eof_time); +int SendDataTagEOF(uint8_t *recv, uint16_t max_recv_len, uint32_t start_time, uint16_t timeout, uint32_t *eof_time, bool fsk, bool recv_speed); void SetTag15693Uid(const uint8_t *uid); From d1e186dbe9249cf9c2b0d05f16e0b63eaa758e12 Mon Sep 17 00:00:00 2001 From: Yann GASCUEL <34003959+lnv42@users.noreply.github.com> Date: Wed, 16 Mar 2022 17:36:37 +0100 Subject: [PATCH 5/5] iso15: fix 2SC (FSK) slow decoding Previous decoding "count" constants have too large scale that may bug with some answer. With those new constants, this bug can't happend anymore. --- armsrc/iso15693.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index fd22ce953..adba61be6 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -661,10 +661,9 @@ static void DecodeTagInit(DecodeTag_t *tag, uint8_t *data, uint16_t max_len) { #define FREQ_IS_484(f) ((f & 1) == 1) //(f >= 26 && f <= 30) #define FREQ_IS_424(f) ((f & 2) == 2) //(f >= 30 && f <= 34) #define FREQ_IS_0(f) ((f & 3) == 0) // (f <= 24 || f >= 36) -#define SEOF_COUNT(c, s) ((s) ? (c >= 11 && c <= 13) : (c >= 44 && c <= 52)) -#define LOGIC_COUNT(c, s) ((s) ? (c >= 3 && c <= 6) : (c >= 13 && c <= 21)) +#define SEOF_COUNT(c, s) ((s) ? (c >= 11 && c <= 13) : (c >= 45 && c <= 51)) +#define LOGIC_COUNT(c, s) ((s) ? (c >= 3 && c <= 6) : (c >= 14 && c <= 20)) #define MAX_COUNT(c, s) ((s) ? (c >= 13) : (c >= 52)) -#define MIN_COUNT(c, s) ((s) ? (c <= 2) : (c <= 4)) typedef struct DecodeTagFSK { enum {