Skip to content

Commit

Permalink
[FL-3626] NFC timer refactoring (#3155)
Browse files Browse the repository at this point in the history
* Improve fwt_max calculation

* Improve comparison

* Dynamically calculate nfc timer prescaler

* Remove useless check

* Disable update interrupt before setting the prescaler

* Fix unexpected timer behaviour

* Clean up furi_hal_nfc_timer

* Clean up furi_hal_nfc_timer more

* Adjust ISO15693 listener delay time

* Use the card-supplied FWT for ISO14443-4A based cards

* Use the card-supplied FWT for ISO14443-3,4B based cards

* Remove useless TODOs

* Update TODOs with ticket numbers

* Implement mf_desfire_is_equal()

* nfc: add waiting block tx timer in iso15693 sof tx

---------

Co-authored-by: gornekich <[email protected]>
  • Loading branch information
gsurkov and gornekich authored Oct 18, 2023
1 parent 597fd21 commit f97ff5c
Show file tree
Hide file tree
Showing 20 changed files with 162 additions and 175 deletions.
2 changes: 1 addition & 1 deletion firmware/targets/f7/furi_hal/furi_hal_nfc_iso15693.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

// Derived experimentally
#define FURI_HAL_NFC_ISO15693_POLLER_FWT_COMP_FC (-1300)
#define FURI_HAL_NFC_ISO15693_LISTENER_FDT_COMP_FC (2735)
#define FURI_HAL_NFC_ISO15693_LISTENER_FDT_COMP_FC (2850)

#define BITS_IN_BYTE (8U)

Expand Down
173 changes: 93 additions & 80 deletions firmware/targets/f7/furi_hal/furi_hal_nfc_timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
#include <furi_hal_resources.h>
#include <furi_hal_bus.h>

#define FURI_HAL_NFC_FREQ_KHZ (13560U)
#define TAG "FuriHalNfcTimer"

#define FURI_HAL_NFC_TIMER_US_IN_S (1000000UL)

/**
* To enable timer debug output on GPIO, define the FURI_HAL_NFC_TIMER_DEBUG macro
Expand All @@ -28,92 +30,85 @@ typedef struct {
FuriHalNfcEventInternalType event;
FuriHalInterruptId irq_id;
IRQn_Type irq_type;
bool is_configured;
#ifdef FURI_HAL_NFC_TIMER_DEBUG
const GpioPin* pin;
#endif
} FuriHalNfcTimerConfig;

static FuriHalNfcTimerConfig furi_hal_nfc_timers[FuriHalNfcTimerCount] = {
static const FuriHalNfcTimerConfig furi_hal_nfc_timers[FuriHalNfcTimerCount] = {
[FuriHalNfcTimerFwt] =
{
#ifdef FURI_HAL_NFC_TIMER_DEBUG
.pin = &gpio_ext_pa7,
#endif
.timer = TIM1,
.bus = FuriHalBusTIM1,
.prescaler = 15,
.freq_khz = 4000U,
.event = FuriHalNfcEventInternalTypeTimerFwtExpired,
.irq_id = FuriHalInterruptIdTim1UpTim16,
.irq_type = TIM1_UP_TIM16_IRQn,
.is_configured = false,
#ifdef FURI_HAL_NFC_TIMER_DEBUG
.pin = &gpio_ext_pa7,
#endif
},
[FuriHalNfcTimerBlockTx] =
{
#ifdef FURI_HAL_NFC_TIMER_DEBUG
.pin = &gpio_ext_pa6,
#endif
.timer = TIM17,
.bus = FuriHalBusTIM17,
.prescaler = 31,
.freq_khz = 2000U,
.event = FuriHalNfcEventInternalTypeTimerBlockTxExpired,
.irq_id = FuriHalInterruptIdTim1TrgComTim17,
.irq_type = TIM1_TRG_COM_TIM17_IRQn,
.is_configured = false,
#ifdef FURI_HAL_NFC_TIMER_DEBUG
.pin = &gpio_ext_pa6,
#endif
},
};

static void furi_hal_nfc_timer_irq_callback(void* context) {
FuriHalNfcTimerConfig* timer_config = context;
if(LL_TIM_IsActiveFlag_UPDATE(timer_config->timer)) {
LL_TIM_ClearFlag_UPDATE(timer_config->timer);
furi_hal_nfc_event_set(timer_config->event);
// Returning removed const-ness
const FuriHalNfcTimerConfig* config = context;
if(LL_TIM_IsActiveFlag_UPDATE(config->timer)) {
LL_TIM_ClearFlag_UPDATE(config->timer);
furi_hal_nfc_event_set(config->event);
#ifdef FURI_HAL_NFC_TIMER_DEBUG
furi_hal_gpio_write(timer_config->pin, false);
#endif
}
}

static void furi_hal_nfc_timer_init(FuriHalNfcTimer timer) {
furi_hal_bus_enable(furi_hal_nfc_timers[timer].bus);
const FuriHalNfcTimerConfig* config = &furi_hal_nfc_timers[timer];

LL_TIM_SetPrescaler(furi_hal_nfc_timers[timer].timer, furi_hal_nfc_timers[timer].prescaler);
LL_TIM_SetOnePulseMode(furi_hal_nfc_timers[timer].timer, LL_TIM_ONEPULSEMODE_SINGLE);
LL_TIM_EnableUpdateEvent(furi_hal_nfc_timers[timer].timer);
LL_TIM_SetCounterMode(furi_hal_nfc_timers[timer].timer, LL_TIM_COUNTERMODE_UP);
LL_TIM_SetClockSource(furi_hal_nfc_timers[timer].timer, LL_TIM_CLOCKSOURCE_INTERNAL);
furi_hal_bus_enable(config->bus);

LL_TIM_GenerateEvent_UPDATE(furi_hal_nfc_timers[timer].timer);
LL_TIM_ClearFlag_UPDATE(furi_hal_nfc_timers[timer].timer);
LL_TIM_SetOnePulseMode(config->timer, LL_TIM_ONEPULSEMODE_SINGLE);
LL_TIM_EnableUpdateEvent(config->timer);
LL_TIM_SetCounterMode(config->timer, LL_TIM_COUNTERMODE_UP);
LL_TIM_SetClockSource(config->timer, LL_TIM_CLOCKSOURCE_INTERNAL);

LL_TIM_EnableIT_UPDATE(furi_hal_nfc_timers[timer].timer);
furi_hal_interrupt_set_isr(
furi_hal_nfc_timers[timer].irq_id,
config->irq_id,
furi_hal_nfc_timer_irq_callback,
&furi_hal_nfc_timers[timer]);
NVIC_SetPriority(
furi_hal_nfc_timers[timer].irq_type,
NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0));
NVIC_EnableIRQ(furi_hal_nfc_timers[timer].irq_type);
furi_hal_nfc_timers[timer].is_configured = true;
// Warning: casting const-ness away
(FuriHalNfcTimerConfig*)config);
NVIC_SetPriority(config->irq_type, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0));
NVIC_EnableIRQ(config->irq_type);
#ifdef FURI_HAL_NFC_TIMER_DEBUG
furi_hal_gpio_init(
furi_hal_nfc_timers[timer].pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
furi_hal_gpio_write(furi_hal_nfc_timers[timer].pin, false);
furi_hal_gpio_init(config->pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
furi_hal_gpio_write(config->pin, false);
#endif
}

static void furi_hal_nfc_timer_deinit(FuriHalNfcTimer timer) {
LL_TIM_ClearFlag_UPDATE(furi_hal_nfc_timers[timer].timer);
furi_hal_interrupt_set_isr(furi_hal_nfc_timers[timer].irq_id, NULL, NULL);
NVIC_DisableIRQ(furi_hal_nfc_timers[timer].irq_type);
furi_hal_nfc_timers[timer].is_configured = false;
const FuriHalNfcTimerConfig* config = &furi_hal_nfc_timers[timer];

if(furi_hal_bus_is_enabled(furi_hal_nfc_timers[timer].bus)) {
furi_hal_bus_disable(furi_hal_nfc_timers[timer].bus);
LL_TIM_ClearFlag_UPDATE(config->timer);
furi_hal_interrupt_set_isr(config->irq_id, NULL, NULL);
NVIC_DisableIRQ(config->irq_type);

if(furi_hal_bus_is_enabled(config->bus)) {
furi_hal_bus_disable(config->bus);
}
#ifdef FURI_HAL_NFC_TIMER_DEBUG
furi_hal_gpio_init_simple(config->pin, GpioModeAnalog);
furi_hal_gpio_write(config->pin, false);
#endif
}

static int32_t furi_hal_nfc_timer_get_compensation(FuriHalNfcTimer timer) {
Expand All @@ -134,32 +129,66 @@ static int32_t furi_hal_nfc_timer_get_compensation(FuriHalNfcTimer timer) {
return 0;
}

static void furi_hal_nfc_timer_start(FuriHalNfcTimer timer, uint32_t time_fc) {
const int32_t comp_fc = furi_hal_nfc_timer_get_compensation(timer);
static inline bool furi_hal_nfc_timer_is_running(FuriHalNfcTimer timer) {
return LL_TIM_IsEnabledCounter(furi_hal_nfc_timers[timer].timer) != 0;
}

// Not starting the timer if the compensation value is greater than the requested delay
if(comp_fc >= (int32_t)time_fc) return;
static void furi_hal_nfc_timer_start_core_ticks(FuriHalNfcTimer timer, uint64_t core_ticks) {
furi_check(!furi_hal_nfc_timer_is_running(timer));

const uint32_t arr_reg =
furi_hal_nfc_timers[timer].freq_khz * (time_fc - comp_fc) / FURI_HAL_NFC_FREQ_KHZ;
furi_check(arr_reg < UINT16_MAX);
const FuriHalNfcTimerConfig* config = &furi_hal_nfc_timers[timer];
furi_check(furi_hal_bus_is_enabled(config->bus));

LL_TIM_SetAutoReload(furi_hal_nfc_timers[timer].timer, arr_reg);
LL_TIM_EnableCounter(furi_hal_nfc_timers[timer].timer);
const uint32_t prescaler = (core_ticks - 1) / UINT16_MAX;
furi_check(prescaler <= UINT16_MAX);

const uint32_t arr_reg = core_ticks / (prescaler + 1);
furi_check(arr_reg <= UINT16_MAX);

LL_TIM_DisableIT_UPDATE(config->timer);

LL_TIM_SetPrescaler(config->timer, prescaler);
LL_TIM_SetAutoReload(config->timer, arr_reg);

LL_TIM_GenerateEvent_UPDATE(config->timer);
while(!LL_TIM_IsActiveFlag_UPDATE(config->timer))
;
LL_TIM_ClearFlag_UPDATE(config->timer);

LL_TIM_EnableIT_UPDATE(config->timer);
LL_TIM_EnableCounter(config->timer);
#ifdef FURI_HAL_NFC_TIMER_DEBUG
furi_hal_gpio_write(furi_hal_nfc_timers[timer].pin, true);
furi_hal_gpio_write(config->pin, true);
#endif
}

static void furi_hal_nfc_timer_start_us(FuriHalNfcTimer timer, uint32_t time_us) {
furi_hal_nfc_timer_start_core_ticks(
timer, SystemCoreClock / FURI_HAL_NFC_TIMER_US_IN_S * time_us);
}

static void furi_hal_nfc_timer_start_fc(FuriHalNfcTimer timer, uint32_t time_fc) {
const int32_t comp_fc = furi_hal_nfc_timer_get_compensation(timer);
// Not starting the timer if the compensation value is greater than the requested delay
if(comp_fc >= (int32_t)time_fc) return;

furi_hal_nfc_timer_start_core_ticks(
timer, ((uint64_t)SystemCoreClock * (time_fc - comp_fc)) / FURI_HAL_NFC_CARRIER_HZ);
}

static void furi_hal_nfc_timer_stop(FuriHalNfcTimer timer) {
LL_TIM_DisableCounter(furi_hal_nfc_timers[timer].timer);
LL_TIM_SetCounter(furi_hal_nfc_timers[timer].timer, 0);
LL_TIM_SetAutoReload(furi_hal_nfc_timers[timer].timer, 0);
if(LL_TIM_IsActiveFlag_UPDATE(furi_hal_nfc_timers[timer].timer)) {
LL_TIM_ClearFlag_UPDATE(furi_hal_nfc_timers[timer].timer);
const FuriHalNfcTimerConfig* config = &furi_hal_nfc_timers[timer];

LL_TIM_DisableIT_UPDATE(config->timer);
LL_TIM_DisableCounter(config->timer);
LL_TIM_SetCounter(config->timer, 0);
LL_TIM_SetAutoReload(config->timer, 0);

if(LL_TIM_IsActiveFlag_UPDATE(config->timer)) {
LL_TIM_ClearFlag_UPDATE(config->timer);
}
#ifdef FURI_HAL_NFC_TIMER_DEBUG
furi_hal_gpio_write(furi_hal_nfc_timers[timer].pin, false);
furi_hal_gpio_write(config->pin, false);
#endif
}

Expand All @@ -176,41 +205,25 @@ void furi_hal_nfc_timers_deinit() {
}

void furi_hal_nfc_timer_fwt_start(uint32_t time_fc) {
furi_check(furi_hal_nfc_timers[FuriHalNfcTimerFwt].is_configured);
furi_hal_nfc_timer_start(FuriHalNfcTimerFwt, time_fc);
furi_hal_nfc_timer_start_fc(FuriHalNfcTimerFwt, time_fc);
}

void furi_hal_nfc_timer_fwt_stop() {
furi_check(furi_hal_nfc_timers[FuriHalNfcTimerFwt].is_configured);
furi_hal_nfc_timer_stop(FuriHalNfcTimerFwt);
}

void furi_hal_nfc_timer_block_tx_start(uint32_t time_fc) {
furi_check(furi_hal_nfc_timers[FuriHalNfcTimerBlockTx].is_configured);
furi_check(!furi_hal_nfc_timer_block_tx_is_running());

furi_hal_nfc_timer_start(FuriHalNfcTimerBlockTx, time_fc);
furi_hal_nfc_timer_start_fc(FuriHalNfcTimerBlockTx, time_fc);
}

void furi_hal_nfc_timer_block_tx_start_us(uint32_t time_us) {
furi_check(furi_hal_nfc_timers[FuriHalNfcTimerBlockTx].is_configured);
furi_check(!furi_hal_nfc_timer_block_tx_is_running());

uint32_t arr_reg = furi_hal_nfc_timers[FuriHalNfcTimerBlockTx].freq_khz / 1000 * time_us;
furi_check(arr_reg < UINT16_MAX);

LL_TIM_SetAutoReload(furi_hal_nfc_timers[FuriHalNfcTimerBlockTx].timer, arr_reg);
LL_TIM_EnableCounter(furi_hal_nfc_timers[FuriHalNfcTimerBlockTx].timer);
#ifdef FURI_HAL_NFC_TIMER_DEBUG
furi_hal_gpio_write(furi_hal_nfc_timers[FuriHalNfcTimerBlockTx].pin, true);
#endif
furi_hal_nfc_timer_start_us(FuriHalNfcTimerBlockTx, time_us);
}

void furi_hal_nfc_timer_block_tx_stop() {
furi_check(furi_hal_nfc_timers[FuriHalNfcTimerBlockTx].is_configured);
furi_hal_nfc_timer_stop(FuriHalNfcTimerBlockTx);
}

bool furi_hal_nfc_timer_block_tx_is_running() {
return LL_TIM_IsEnabledCounter(furi_hal_nfc_timers[FuriHalNfcTimerBlockTx].timer) == 1;
return furi_hal_nfc_timer_is_running(FuriHalNfcTimerBlockTx);
}
5 changes: 5 additions & 0 deletions firmware/targets/furi_hal_include/furi_hal_nfc.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@
extern "C" {
#endif

/**
* @brief NFC carrier frequency, in Hz.
*/
#define FURI_HAL_NFC_CARRIER_HZ (13560000UL)

/**
* @brief Special value indicating that waiting for an event shall never time out.
*/
Expand Down
3 changes: 3 additions & 0 deletions lib/nfc/nfc.c
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,9 @@ NfcError nfc_iso14443a_listener_tx_custom_parity(Nfc* instance, const BitBuffer*
NfcError nfc_iso15693_listener_tx_sof(Nfc* instance) {
furi_assert(instance);

while(furi_hal_nfc_timer_block_tx_is_running()) {
}

FuriHalNfcError error = furi_hal_nfc_iso15693_listener_tx_sof();
NfcError ret = nfc_process_hal_error(error);

Expand Down
4 changes: 3 additions & 1 deletion lib/nfc/protocols/iso14443_3b/iso14443_3b.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#define ISO14443_3B_APP_DATA_KEY "Application data"
#define ISO14443_3B_PROTOCOL_INFO_KEY "Protocol info"

#define ISO14443_3B_FDT_POLL_DEFAULT_FC (ISO14443_3B_FDT_POLL_FC)

const NfcDeviceBase nfc_device_iso14443_3b = {
.protocol_name = ISO14443_3B_PROTOCOL_NAME,
.alloc = (NfcDeviceAlloc)iso14443_3b_alloc,
Expand Down Expand Up @@ -217,5 +219,5 @@ uint32_t iso14443_3b_get_fwt_fc_max(const Iso14443_3bData* data) {
furi_assert(data);

const uint8_t fwi = data->protocol_info.fwi;
return fwi < 15 ? 4096UL << fwi : 0;
return fwi < 0x0F ? 4096UL << fwi : ISO14443_3B_FDT_POLL_DEFAULT_FC;
}
1 change: 0 additions & 1 deletion lib/nfc/protocols/iso14443_3b/iso14443_3b_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

#define ISO14443_3B_GUARD_TIME_US (5000U)
#define ISO14443_3B_FDT_POLL_FC (9000U)
#define ISO14443_3B_FDT_ATTRIB_FC (42000U)
#define ISO14443_3B_POLL_POLL_MIN_US (1280U)

#define ISO14443_3B_BIT_RATE_BOTH_106KBIT (0U << 0)
Expand Down
8 changes: 4 additions & 4 deletions lib/nfc/protocols/iso14443_3b/iso14443_3b_poller_i.c
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ Iso14443_3bError
bit_buffer_append_byte(instance->tx_buffer, 0x00);

ret = iso14443_3b_poller_frame_exchange(
instance, instance->tx_buffer, instance->rx_buffer, ISO14443_3B_FDT_ATTRIB_FC);
instance, instance->tx_buffer, instance->rx_buffer, iso14443_3b_get_fwt_fc_max(data));
if(ret != Iso14443_3bErrorNone) {
instance->state = Iso14443_3bPollerStateActivationFailed;
break;
Expand Down Expand Up @@ -179,15 +179,15 @@ Iso14443_3bError iso14443_3b_poller_halt(Iso14443_3bPoller* instance) {
Iso14443_3bError iso14443_3b_poller_send_frame(
Iso14443_3bPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt) {
BitBuffer* rx_buffer) {
Iso14443_3bError ret;

do {
ret = iso14443_3b_poller_prepare_trx(instance);
if(ret != Iso14443_3bErrorNone) break;

ret = iso14443_3b_poller_frame_exchange(instance, tx_buffer, rx_buffer, fwt);
ret = iso14443_3b_poller_frame_exchange(
instance, tx_buffer, rx_buffer, iso14443_3b_get_fwt_fc_max(instance->data));
} while(false);

return ret;
Expand Down
3 changes: 1 addition & 2 deletions lib/nfc/protocols/iso14443_3b/iso14443_3b_poller_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ Iso14443_3bError iso14443_3b_poller_halt(Iso14443_3bPoller* instance);
Iso14443_3bError iso14443_3b_poller_send_frame(
Iso14443_3bPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt);
BitBuffer* rx_buffer);

#ifdef __cplusplus
}
Expand Down
17 changes: 14 additions & 3 deletions lib/nfc/protocols/iso14443_4a/iso14443_4a.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
#define ISO14443_4A_TC1_KEY "TC(1)"
#define ISO14443_4A_T1_TK_KEY "T1...Tk"

#define ISO14443_4A_FDT_DEFAULT_FC ISO14443_3A_FDT_POLL_FC

typedef enum {
Iso14443_4aInterfaceByteTA1,
Iso14443_4aInterfaceByteTB1,
Expand Down Expand Up @@ -230,10 +232,19 @@ uint16_t iso14443_4a_get_frame_size_max(const Iso14443_4aData* data) {
uint32_t iso14443_4a_get_fwt_fc_max(const Iso14443_4aData* data) {
furi_assert(data);

if(!(data->ats_data.t0 & ISO14443_4A_ATS_T0_TB1)) return 0;
uint32_t fwt_fc_max = ISO14443_4A_FDT_DEFAULT_FC;

do {
if(!(data->ats_data.tl > 1)) break;
if(!(data->ats_data.t0 & ISO14443_4A_ATS_T0_TB1)) break;

const uint8_t fwi = data->ats_data.tb_1 >> 4;
if(fwi == 0x0F) break;

fwt_fc_max = 4096UL << fwi;
} while(false);

const uint8_t fwi = data->ats_data.tb_1 >> 4;
return fwi < 15 ? 4096UL << fwi : 0;
return fwt_fc_max;
}

const uint8_t* iso14443_4a_get_historical_bytes(const Iso14443_4aData* data, uint32_t* count) {
Expand Down
Loading

0 comments on commit f97ff5c

Please sign in to comment.