mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-06-04 03:14:49 -07:00
540 lines
19 KiB
C
540 lines
19 KiB
C
//-----------------------------------------------------------------------------
|
|
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// See LICENSE.txt for the text of the license.
|
|
//-----------------------------------------------------------------------------
|
|
// Hitag shared functionality
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "hitag_common.h"
|
|
|
|
#include "proxmark3_arm.h"
|
|
#include "cmd.h"
|
|
#include "BigBuf.h"
|
|
#include "fpgaloader.h"
|
|
#include "ticks.h"
|
|
#include "dbprint.h"
|
|
#include "util.h"
|
|
#include "string.h"
|
|
#include "commonutil.h"
|
|
#include "hitag2/hitag2_crypto.h"
|
|
#include "lfadc.h"
|
|
#include "crc.h"
|
|
#include "protocols.h"
|
|
#include "appmain.h" // tearoff_hook()
|
|
|
|
uint16_t timestamp_high = 0; // Timer Counter 2 overflow count, combined with TC2 counter for ~47min timing
|
|
|
|
static void hitag_stop_clock(void) {
|
|
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS;
|
|
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS;
|
|
AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKDIS;
|
|
}
|
|
|
|
static void hitag_init_clock(void) {
|
|
// Enable Peripheral Clock for
|
|
// Timer Counter 0, used to measure exact timing before answering
|
|
// Timer Counter 1, used to capture edges of the tag frames
|
|
// Timer Counter 2, used to log trace time
|
|
AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_TC0) | (1 << AT91C_ID_TC1) | (1 << AT91C_ID_TC2);
|
|
|
|
AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME;
|
|
|
|
// Disable timer during configuration
|
|
hitag_stop_clock();
|
|
|
|
// TC0: Capture mode, default timer source = MCK/32 (TIMER_CLOCK3), no triggers
|
|
AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK;
|
|
|
|
// TC1: Capture mode, default timer source = MCK/32 (TIMER_CLOCK3), TIOA is external trigger,
|
|
AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK // use MCK/32 (TIMER_CLOCK3)
|
|
| AT91C_TC_ABETRG // TIOA is used as an external trigger
|
|
| AT91C_TC_ETRGEDG_FALLING // external trigger on falling edge
|
|
| AT91C_TC_LDRA_RISING // load RA on on rising edge of TIOA
|
|
| AT91C_TC_LDRB_FALLING; // load RB on on falling edge of TIOA
|
|
|
|
// TC2: Capture mode, default timer source = MCK/32 (TIMER_CLOCK3), no triggers
|
|
AT91C_BASE_TC2->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK;
|
|
|
|
// Enable and reset counters
|
|
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
|
|
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
|
|
AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
|
|
|
|
// Assert a sync signal. This sets all timers to 0 on next active clock edge
|
|
AT91C_BASE_TCB->TCB_BCR = 1;
|
|
|
|
// synchronized startup procedure
|
|
// In theory, with MCK/32, we shouldn't be waiting longer than 32 instruction statements, right?
|
|
while (AT91C_BASE_TC0->TC_CV != 0) {
|
|
}; // wait until TC0 returned to zero
|
|
|
|
// reset timestamp
|
|
timestamp_high = 0;
|
|
}
|
|
|
|
// Initialize FPGA and timer for Hitag operations
|
|
void hitag_setup_fpga(uint16_t conf, uint8_t threshold, bool ledcontrol) {
|
|
StopTicks();
|
|
|
|
FpgaDownloadAndGo(FPGA_BITSTREAM_LF);
|
|
|
|
// Clean up trace and prepare it for storing frames
|
|
set_tracing(true);
|
|
clear_trace();
|
|
|
|
if (ledcontrol) LED_D_ON();
|
|
|
|
hitag_init_clock();
|
|
|
|
// Set fpga in edge detect with/without reader field, we can modulate as reader/tag now
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT | conf);
|
|
FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_125); //125kHz
|
|
if (threshold != 127) FpgaSendCommand(FPGA_CMD_SET_EDGE_DETECT_THRESHOLD, threshold);
|
|
SetAdcMuxFor(GPIO_MUXSEL_LOPKD);
|
|
|
|
// Configure output and enable pin that is connected to the FPGA (for modulating)
|
|
AT91C_BASE_PIOA->PIO_OER |= GPIO_SSC_DOUT;
|
|
AT91C_BASE_PIOA->PIO_PER |= GPIO_SSC_DOUT;
|
|
|
|
// Disable modulation at default, which means enable the field
|
|
LOW(GPIO_SSC_DOUT);
|
|
}
|
|
|
|
// Clean up and finalize Hitag operations
|
|
void hitag_cleanup(bool ledcontrol) {
|
|
hitag_stop_clock();
|
|
set_tracing(false);
|
|
lf_finalize(ledcontrol);
|
|
}
|
|
|
|
// Reader functions
|
|
static void hitag_reader_send_bit(int bit, bool ledcontrol) {
|
|
// Reset clock for the next bit
|
|
AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG;
|
|
while (AT91C_BASE_TC0->TC_CV != 0) {};
|
|
|
|
if (ledcontrol) LED_A_ON();
|
|
|
|
// Binary puls length modulation (BPLM) is used to encode the data stream
|
|
// This means that a transmission of a one takes longer than that of a zero
|
|
HIGH(GPIO_SSC_DOUT);
|
|
|
|
// Wait for 4-10 times the carrier period
|
|
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_LOW) {};
|
|
|
|
LOW(GPIO_SSC_DOUT);
|
|
|
|
if (bit == 0) {
|
|
// Zero bit: |_-|
|
|
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_0) {};
|
|
} else {
|
|
// One bit: |_--|
|
|
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_1) {};
|
|
}
|
|
|
|
if (ledcontrol) LED_A_OFF();
|
|
}
|
|
|
|
void hitag_reader_send_frame(const uint8_t *frame, size_t frame_len, bool ledcontrol, bool send_sof) {
|
|
// Send SOF (Start of Frame) for Hitag µ if requested
|
|
if (send_sof) {
|
|
hitag_reader_send_bit(0, ledcontrol);
|
|
|
|
// Reset clock for the code violation
|
|
AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG;
|
|
while (AT91C_BASE_TC0->TC_CV != 0) {};
|
|
|
|
if (ledcontrol) LED_A_ON();
|
|
|
|
// SOF is HIGH for HITAG_T_LOW
|
|
HIGH(GPIO_SSC_DOUT);
|
|
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_LOW) {};
|
|
|
|
// Then LOW for HITAG_T_CODE_VIOLATION
|
|
LOW(GPIO_SSC_DOUT);
|
|
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_CODE_VIOLATION) {};
|
|
|
|
if (ledcontrol) LED_A_OFF();
|
|
}
|
|
|
|
// Send the content of the frame
|
|
for (size_t i = 0; i < frame_len; i++) {
|
|
hitag_reader_send_bit(TEST_BIT_MSB(frame, i), ledcontrol);
|
|
}
|
|
|
|
// Send EOF
|
|
AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG;
|
|
while (AT91C_BASE_TC0->TC_CV != 0) {};
|
|
|
|
HIGH(GPIO_SSC_DOUT);
|
|
|
|
// Wait for 4-10 times the carrier period
|
|
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_LOW) {};
|
|
|
|
LOW(GPIO_SSC_DOUT);
|
|
}
|
|
|
|
void hitag_reader_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uint32_t *resptime, bool ledcontrol,
|
|
MOD modulation, int sof_bits) {
|
|
// Reset values for receiving frames
|
|
memset(rx, 0x00, sizeofrx);
|
|
*rxlen = 0;
|
|
|
|
int lastbit = 1;
|
|
bool bSkip = true;
|
|
uint32_t errorCount = 0;
|
|
bool bStarted = false;
|
|
uint16_t next_edge_event = AT91C_TC_LDRBS;
|
|
int double_speed = (modulation == AC4K || modulation == MC8K) ? 2 : 1;
|
|
|
|
uint32_t rb_i = 0;
|
|
uint8_t edges[160] = {0};
|
|
|
|
// Skip SOF bits
|
|
bool sof_received = false;
|
|
|
|
// Receive tag frame, watch for at most T0*HITAG_T_PROG_MAX periods
|
|
while (AT91C_BASE_TC0->TC_CV < (T0 * HITAG_T_PROG_MAX)) {
|
|
// Check if edge in tag modulation is detected
|
|
if (AT91C_BASE_TC1->TC_SR & next_edge_event) {
|
|
next_edge_event = next_edge_event ^ (AT91C_TC_LDRAS | AT91C_TC_LDRBS);
|
|
|
|
// only use AT91C_TC_LDRBS falling edge for now
|
|
if (next_edge_event == AT91C_TC_LDRBS) continue;
|
|
|
|
// Retrieve the new timing values
|
|
uint32_t rb = AT91C_BASE_TC1->TC_RB / T0;
|
|
edges[rb_i++] = rb;
|
|
|
|
// Reset timer every frame, we have to capture the last edge for timing
|
|
AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG;
|
|
|
|
if (ledcontrol) LED_B_INV();
|
|
|
|
// Capture tag frame (manchester decoding using only falling edges)
|
|
if (bStarted == false) {
|
|
if (rb >= HITAG_T_WAIT_RESP) {
|
|
bStarted = true;
|
|
|
|
// Capture tag response timestamp
|
|
*resptime = TIMESTAMP;
|
|
|
|
// We always receive a 'one' first, which has the falling edge after a half period |-_|
|
|
rx[0] = 0x80;
|
|
*rxlen = 1;
|
|
} else {
|
|
errorCount++;
|
|
}
|
|
} else {
|
|
// Handle different modulation types
|
|
if (modulation == AC2K || modulation == AC4K) {
|
|
// Anticollision Coding
|
|
if (rb >= HITAG_T_TAG_CAPTURE_FOUR_HALF / double_speed) {
|
|
// Anticollision Coding example |--__|--__| (00)
|
|
lastbit = 0;
|
|
// CLEAR_BIT_MSB(rx, *rxlen);
|
|
(*rxlen)++;
|
|
} else if (rb >= HITAG_T_TAG_CAPTURE_THREE_HALF / double_speed) {
|
|
// Anticollision Coding example |-_-_|--__| (10) or |--__|-_-_| (01)
|
|
lastbit = !lastbit;
|
|
if (lastbit) SET_BIT_MSB(rx, *rxlen);
|
|
(*rxlen)++;
|
|
|
|
bSkip = !!lastbit;
|
|
} else if (rb >= HITAG_T_TAG_CAPTURE_TWO_HALF / double_speed) {
|
|
// Anticollision Coding example |-_-_| (1)
|
|
if (bSkip == false) {
|
|
lastbit = 1;
|
|
SET_BIT_MSB(rx, *rxlen);
|
|
(*rxlen)++;
|
|
}
|
|
|
|
bSkip = !bSkip;
|
|
} else {
|
|
// Ignore weird value, is to small to mean anything
|
|
errorCount++;
|
|
}
|
|
} else {
|
|
// Manchester coding (MC4K, MC8K)
|
|
if (rb >= HITAG_T_TAG_CAPTURE_FOUR_HALF / double_speed) {
|
|
// Manchester coding example |-_|_-|-_| (101)
|
|
// CLEAR_BIT_MSB(rx, *rxlen);
|
|
(*rxlen)++;
|
|
|
|
SET_BIT_MSB(rx, *rxlen);
|
|
(*rxlen)++;
|
|
} else if (rb >= HITAG_T_TAG_CAPTURE_THREE_HALF / double_speed) {
|
|
// Manchester coding example |_-|...|_-|-_| (0...01)
|
|
// CLEAR_BIT_MSB(rx, *rxlen);
|
|
(*rxlen)++;
|
|
|
|
// We have to skip this half period at start and add the 'one' the second time
|
|
if (bSkip == false) {
|
|
SET_BIT_MSB(rx, *rxlen);
|
|
(*rxlen)++;
|
|
}
|
|
|
|
lastbit = !lastbit;
|
|
bSkip = !bSkip;
|
|
} else if (rb >= HITAG_T_TAG_CAPTURE_TWO_HALF / double_speed) {
|
|
// Manchester coding example |_-|_-| (00) or |-_|-_| (11)
|
|
// bit is same as last bit
|
|
if (lastbit) SET_BIT_MSB(rx, *rxlen);
|
|
(*rxlen)++;
|
|
} else {
|
|
// Ignore weird value, is to small to mean anything
|
|
errorCount++;
|
|
}
|
|
}
|
|
|
|
// Handle SOF bits
|
|
if (sof_received == false && *rxlen >= sof_bits) {
|
|
// Check if SOF is valid (all bits should be 1)
|
|
if ((rx[0] >> (8 - sof_bits)) != ((1 << sof_bits) - 1)) {
|
|
if (sof_bits == 4) {
|
|
sof_bits = 3;
|
|
// Hitag µ is LSB first 0b110
|
|
if ((rx[0] & 0xE0) != 0xC0) {
|
|
DBG Dbprintf("Warning, SOF is invalid rx[0]: 0x%02X", rx[0]);
|
|
}
|
|
} else {
|
|
DBG DbpString("Warning, not all bits of SOF are 1");
|
|
}
|
|
}
|
|
|
|
*rxlen -= sof_bits;
|
|
uint8_t tmp = rx[0];
|
|
rx[0] = 0x00;
|
|
for (size_t i = 0; i < *rxlen; i++) {
|
|
if (TEST_BIT_MSB(&tmp, sof_bits + i)) SET_BIT_MSB(rx, i);
|
|
}
|
|
// DBG Dbprintf("after sof_bits rxlen: %d rx[0]: 0x%02X", *rxlen, rx[0]);
|
|
sof_received = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// if we saw over 100 weird values break it probably isn't hitag...
|
|
if (errorCount > 100 || (*rxlen) / 8 >= sizeofrx) {
|
|
break;
|
|
}
|
|
|
|
// We can break this loop if we received the last bit from a frame
|
|
// max periods between 2 falling edge
|
|
// RTF AC64 |--__|--__| (00) 64 * T0
|
|
// RTF MC32 |_-|-_|_-| (010) 48 * T0
|
|
if (AT91C_BASE_TC1->TC_CV > (T0 * 80)) {
|
|
if (bStarted) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
DBG {
|
|
Dbprintf("RX %i:%02X.. resptime:%i edges:", *rxlen, rx[0], *resptime);
|
|
Dbhexdump(rb_i, edges, false);
|
|
}
|
|
}
|
|
|
|
// Tag functions - depends on modulation type
|
|
static void hitag_tag_send_bit(int bit, MOD modulation, bool ledcontrol) {
|
|
// Reset clock for the next bit
|
|
AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG;
|
|
|
|
if (ledcontrol) LED_A_ON();
|
|
|
|
switch (modulation) {
|
|
case AC2K: {
|
|
if (bit == 0) {
|
|
// AC Coding --__
|
|
HIGH(GPIO_SSC_DOUT);
|
|
while (AT91C_BASE_TC0->TC_CV < T0 * 32) {};
|
|
|
|
LOW(GPIO_SSC_DOUT);
|
|
while (AT91C_BASE_TC0->TC_CV < T0 * 64) {};
|
|
} else {
|
|
// AC coding -_-_
|
|
HIGH(GPIO_SSC_DOUT);
|
|
while (AT91C_BASE_TC0->TC_CV < T0 * 16) {};
|
|
|
|
LOW(GPIO_SSC_DOUT);
|
|
while (AT91C_BASE_TC0->TC_CV < T0 * 32) {};
|
|
|
|
HIGH(GPIO_SSC_DOUT);
|
|
while (AT91C_BASE_TC0->TC_CV < T0 * 48) {};
|
|
|
|
LOW(GPIO_SSC_DOUT);
|
|
while (AT91C_BASE_TC0->TC_CV < T0 * 64) {};
|
|
}
|
|
break;
|
|
}
|
|
case AC4K: {
|
|
if (bit == 0) {
|
|
// AC Coding --__
|
|
HIGH(GPIO_SSC_DOUT);
|
|
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_TAG_HALF_PERIOD) {};
|
|
|
|
LOW(GPIO_SSC_DOUT);
|
|
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_TAG_FULL_PERIOD) {};
|
|
} else {
|
|
// AC coding -_-_
|
|
HIGH(GPIO_SSC_DOUT);
|
|
while (AT91C_BASE_TC0->TC_CV < T0 * 8) {};
|
|
|
|
LOW(GPIO_SSC_DOUT);
|
|
while (AT91C_BASE_TC0->TC_CV < T0 * 16) {};
|
|
|
|
HIGH(GPIO_SSC_DOUT);
|
|
while (AT91C_BASE_TC0->TC_CV < T0 * 24) {};
|
|
|
|
LOW(GPIO_SSC_DOUT);
|
|
while (AT91C_BASE_TC0->TC_CV < T0 * 32) {};
|
|
}
|
|
break;
|
|
}
|
|
case MC4K: {
|
|
if (bit == 0) {
|
|
// Manchester: Unloaded, then loaded |__--|
|
|
LOW(GPIO_SSC_DOUT);
|
|
while (AT91C_BASE_TC0->TC_CV < T0 * 16) {};
|
|
|
|
HIGH(GPIO_SSC_DOUT);
|
|
while (AT91C_BASE_TC0->TC_CV < T0 * 32) {};
|
|
} else {
|
|
// Manchester: Loaded, then unloaded |--__|
|
|
HIGH(GPIO_SSC_DOUT);
|
|
while (AT91C_BASE_TC0->TC_CV < T0 * 16) {};
|
|
|
|
LOW(GPIO_SSC_DOUT);
|
|
while (AT91C_BASE_TC0->TC_CV < T0 * 32) {};
|
|
}
|
|
break;
|
|
}
|
|
case MC8K: {
|
|
if (bit == 0) {
|
|
// Manchester: Unloaded, then loaded |__--|
|
|
LOW(GPIO_SSC_DOUT);
|
|
while (AT91C_BASE_TC0->TC_CV < T0 * 8) {};
|
|
|
|
HIGH(GPIO_SSC_DOUT);
|
|
while (AT91C_BASE_TC0->TC_CV < T0 * 16) {};
|
|
} else {
|
|
// Manchester: Loaded, then unloaded |--__|
|
|
HIGH(GPIO_SSC_DOUT);
|
|
while (AT91C_BASE_TC0->TC_CV < T0 * 8) {};
|
|
|
|
LOW(GPIO_SSC_DOUT);
|
|
while (AT91C_BASE_TC0->TC_CV < T0 * 16) {};
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ledcontrol) LED_A_OFF();
|
|
}
|
|
|
|
void hitag_tag_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uint32_t *start_time, bool ledcontrol, int *overflow) {
|
|
uint16_t next_edge_event = AT91C_TC_LDRBS;
|
|
uint8_t edges[160] = {0};
|
|
uint32_t rb_i = 0;
|
|
|
|
// Receive frame, watch for at most T0*EOF periods
|
|
while (AT91C_BASE_TC1->TC_CV < T0 * HITAG_T_EOF) {
|
|
|
|
// Check if edge in modulation is detected
|
|
if (AT91C_BASE_TC1->TC_SR & next_edge_event) {
|
|
next_edge_event = next_edge_event ^ (AT91C_TC_LDRAS | AT91C_TC_LDRBS);
|
|
|
|
// only use AT91C_TC_LDRBS falling edge for now
|
|
if (next_edge_event == AT91C_TC_LDRBS) continue;
|
|
|
|
// Retrieve the new timing values
|
|
uint32_t rb = AT91C_BASE_TC1->TC_RB / T0 + *overflow;
|
|
*overflow = 0;
|
|
|
|
edges[rb_i++] = rb;
|
|
|
|
if (ledcontrol) LED_B_INV();
|
|
|
|
// Capture reader cmd start timestamp
|
|
if (*start_time == 0) {
|
|
*start_time = TIMESTAMP - HITAG_T_LOW;
|
|
}
|
|
|
|
// Capture reader frame
|
|
if (rb >= HITAG_T_STOP) {
|
|
// Hitag µ SOF
|
|
if (*rxlen != 0 && *rxlen != 1) {
|
|
// DBG DbpString("weird0?");
|
|
break;
|
|
}
|
|
*rxlen = 0;
|
|
} else if (rb >= HITAG_T_1_MIN) {
|
|
// '1' bit
|
|
SET_BIT_MSB(rx, *rxlen);
|
|
(*rxlen)++;
|
|
} else if (rb >= HITAG_T_0_MIN) {
|
|
// '0' bit
|
|
// CLEAR_BIT_MSB(rx, *rxlen);
|
|
(*rxlen)++;
|
|
} else {
|
|
// Ignore weird value, is too small to mean anything
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ledcontrol) LED_B_OFF();
|
|
|
|
DBG if (rb_i) {
|
|
Dbprintf("RX %i bits.. start_time:%i edges:", *rxlen, *start_time);
|
|
Dbhexdump(rb_i, edges, false);
|
|
}
|
|
}
|
|
|
|
void hitag_tag_send_frame(const uint8_t *frame, size_t frame_len, int sof_bits, MOD modulation, bool ledcontrol) {
|
|
// The beginning of the frame is hidden in some high level; pause until our bits will have an effect
|
|
AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG;
|
|
HIGH(GPIO_SSC_DOUT);
|
|
|
|
switch (modulation) {
|
|
case AC4K:
|
|
case MC8K: {
|
|
while (AT91C_BASE_TC0->TC_CV < T0 * 40) {}; // FADV
|
|
break;
|
|
}
|
|
case AC2K:
|
|
case MC4K: {
|
|
while (AT91C_BASE_TC0->TC_CV < T0 * 20) {}; // STD + ADV
|
|
break;
|
|
}
|
|
}
|
|
|
|
// SOF - send start of frame
|
|
for (size_t i = 0; i < sof_bits; i++) {
|
|
if (sof_bits == 4 && i == 3) {
|
|
// Hitag µ SOF is 110
|
|
hitag_tag_send_bit(0, modulation, ledcontrol);
|
|
break;
|
|
} else
|
|
hitag_tag_send_bit(1, modulation, ledcontrol);
|
|
}
|
|
|
|
// Send the content of the frame
|
|
for (size_t i = 0; i < frame_len; i++) {
|
|
hitag_tag_send_bit(TEST_BIT_MSB(frame, i), modulation, ledcontrol);
|
|
}
|
|
|
|
LOW(GPIO_SSC_DOUT);
|
|
}
|