This commit is contained in:
Willy-JL
2023-05-02 14:28:51 +01:00
3 changed files with 162 additions and 11 deletions
@@ -1,10 +1,12 @@
#include <furi.h>
#include <furi_hal.h>
#include <stm32wbxx_ll_tim.h>
#include <storage/storage.h>
#include <lib/flipper_format/flipper_format.h>
#include <lib/nfc/protocols/nfca.h>
#include <lib/nfc/helpers/mf_classic_dict.h>
#include <lib/digital_signal/digital_signal.h>
#include <lib/pulse_reader/pulse_reader.h>
#include <lib/nfc/nfc_device.h>
#include <lib/nfc/helpers/nfc_generators.h>
@@ -179,6 +181,153 @@ MU_TEST(nfc_digital_signal_test) {
"NFC long digital signal test failed\r\n");
}
static bool nfc_test_pulse_reader_toggle(
uint32_t usec_low,
uint32_t usec_high,
uint32_t period_count,
uint32_t tolerance) {
furi_assert(nfc_test);
bool success = false;
uint32_t pulses = 0;
const GpioPin* gpio_in = &gpio_ext_pa6;
const GpioPin* gpio_out = &gpio_ext_pa7;
PulseReader* reader = NULL;
do {
reader = pulse_reader_alloc(gpio_in, 512);
if(!reader) {
FURI_LOG_E(TAG, "failed to allocate pulse reader");
break;
}
/* use TIM1 to create a specific number of pulses with defined duty cycle
but first set the IO to high, so the low/high pulse can get detected */
furi_hal_gpio_init(gpio_out, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
furi_hal_gpio_write(gpio_out, true);
LL_TIM_DeInit(TIM1);
LL_TIM_SetCounterMode(TIM1, LL_TIM_COUNTERMODE_UP);
LL_TIM_SetRepetitionCounter(TIM1, 0);
LL_TIM_SetClockDivision(TIM1, LL_TIM_CLOCKDIVISION_DIV1);
LL_TIM_SetClockSource(TIM1, LL_TIM_CLOCKSOURCE_INTERNAL);
LL_TIM_DisableARRPreload(TIM1);
LL_TIM_OC_DisablePreload(TIM1, LL_TIM_CHANNEL_CH1);
LL_TIM_OC_SetMode(TIM1, LL_TIM_CHANNEL_CH1, LL_TIM_OCMODE_PWM2);
LL_TIM_OC_SetPolarity(TIM1, LL_TIM_CHANNEL_CH1N, LL_TIM_OCPOLARITY_HIGH);
LL_TIM_OC_DisableFast(TIM1, LL_TIM_CHANNEL_CH1);
LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH1N);
LL_TIM_EnableAllOutputs(TIM1);
/* now calculate the TIM1 period and compare values */
uint32_t freq_div = 64 * (usec_low + usec_high);
uint32_t prescaler = freq_div / 0x10000LU;
uint32_t period = freq_div / (prescaler + 1);
uint32_t compare = 64 * usec_low / (prescaler + 1);
LL_TIM_SetPrescaler(TIM1, prescaler);
LL_TIM_SetAutoReload(TIM1, period - 1);
LL_TIM_SetCounter(TIM1, period - 1);
LL_TIM_OC_SetCompareCH1(TIM1, compare);
/* timer is ready to launch, now start the pulse reader */
pulse_reader_set_timebase(reader, PulseReaderUnitMicrosecond);
pulse_reader_start(reader);
/* and quickly enable and switch over the GPIO to the generated signal */
LL_TIM_EnableCounter(TIM1);
furi_hal_gpio_init_ex(
gpio_out, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedVeryHigh, GpioAltFn1TIM1);
/* now it's time to parse the pulses received by the reader */
uint32_t timer_pulses = period_count;
uint32_t prev_cnt = 0;
while(timer_pulses > 0) {
/* whenever the counter gets reset, we went through a full period */
uint32_t cur_cnt = LL_TIM_GetCounter(TIM1);
if(cur_cnt < prev_cnt) {
timer_pulses--;
}
prev_cnt = cur_cnt;
}
/* quickly halt the counter to keep a static signal */
LL_TIM_DisableCounter(TIM1);
do {
/* as all edges were sampled asynchronously, the timeout can be zero */
uint32_t length = pulse_reader_receive(reader, 0);
/* in the last pulse, we expect a "no edge" return value. if seen that, test succeeded. */
if(pulses > period_count * 2) {
if(length != PULSE_READER_NO_EDGE) {
FURI_LOG_E(
TAG,
"last pulse expected to be PULSE_READER_NO_EDGE, but was %lu.",
length);
break;
}
success = true;
break;
}
/* else we shall never see "no edge" or "lost edge" */
if(length == PULSE_READER_NO_EDGE) {
FURI_LOG_E(TAG, "%lu. pulse not expected to be PULSE_READER_NO_EDGE", pulses);
break;
}
if(length == PULSE_READER_LOST_EDGE) {
FURI_LOG_E(TAG, "%lu. pulse not expected to be PULSE_READER_LOST_EDGE", pulses);
break;
}
if(pulses > 0) {
/* throw away the first pulse, which is the 1->0 from the first start and will be irrelevant for our test */
bool phase = ((pulses - 1) % 2) == 1;
uint32_t expected = phase ? usec_high : usec_low;
uint32_t deviation = abs((int32_t)length - (int32_t)expected);
if(deviation > tolerance) {
FURI_LOG_E(
TAG,
"%lu. pulse expected %lu, but pulse was %lu.",
pulses,
expected,
length);
break;
}
}
pulses++;
} while(true);
} while(false);
if(reader != NULL) {
pulse_reader_stop(reader);
pulse_reader_free(reader);
}
LL_TIM_DeInit(TIM1);
furi_hal_gpio_init_simple(gpio_in, GpioModeAnalog);
furi_hal_gpio_init_simple(gpio_out, GpioModeAnalog);
return success;
}
MU_TEST(nfc_pulse_reader_test) {
mu_assert(nfc_test_pulse_reader_toggle(1500, 2500, 50, 10), "1 ms signal failed\r\n");
mu_assert(nfc_test_pulse_reader_toggle(10000, 10000, 10, 10), "10 ms signal failed\r\n");
mu_assert(nfc_test_pulse_reader_toggle(100000, 100000, 5, 50), "100 ms signal failed\r\n");
mu_assert(nfc_test_pulse_reader_toggle(100, 900, 50, 10), "1 ms asymmetric signal failed\r\n");
mu_assert(
nfc_test_pulse_reader_toggle(3333, 6666, 10, 10), "10 ms asymmetric signal failed\r\n");
mu_assert(
nfc_test_pulse_reader_toggle(25000, 75000, 5, 10), "100 ms asymmetric signal failed\r\n");
}
MU_TEST(mf_classic_dict_test) {
MfClassicDict* instance = NULL;
uint64_t key = 0;
@@ -513,6 +662,7 @@ MU_TEST(mf_classic_4k_7b_file_test) {
MU_TEST_SUITE(nfc) {
nfc_test_alloc();
MU_RUN_TEST(nfc_pulse_reader_test);
MU_RUN_TEST(nfca_file_test);
MU_RUN_TEST(mf_mini_file_test);
MU_RUN_TEST(mf_classic_1k_4b_file_test);
+8 -9
View File
@@ -1401,18 +1401,17 @@ bool nfc_device_save(NfcDevice* dev, const char* dev_name) {
break;
nfc_device_prepare_format_string(dev, temp_str);
if(!flipper_format_write_string(file, "Device type", temp_str)) break;
// Write UID, ATQA, SAK
if(!flipper_format_write_comment_cstr(file, "UID, ATQA and SAK are common for all formats"))
break;
// Write UID
if(!flipper_format_write_comment_cstr(file, "UID is common for all formats")) break;
if(!flipper_format_write_hex(file, "UID", data->uid, data->uid_len)) break;
if(dev->format != NfcDeviceSaveFormatNfcV) {
// Write ATQA, SAK
if(!flipper_format_write_comment_cstr(file, "ISO14443 specific fields")) break;
// Save ATQA in MSB order for correct companion apps display
uint8_t atqa[2] = {data->a_data.atqa[1], data->a_data.atqa[0]};
uint8_t atqa[2] = {data->atqa[1], data->atqa[0]};
if(!flipper_format_write_hex(file, "ATQA", atqa, 2)) break;
if(!flipper_format_write_hex(file, "SAK", &data->a_data.sak, 1)) break;
if(!flipper_format_write_hex(file, "SAK", &data->sak, 1)) break;
}
// Save more data if necessary
@@ -1503,14 +1502,14 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia
if(!flipper_format_read_hex(file, "UID", data->uid, data->uid_len)) break;
if(dev->format != NfcDeviceSaveFormatNfcV) {
if(version == version_with_lsb_atqa) {
if(!flipper_format_read_hex(file, "ATQA", data->a_data.atqa, 2)) break;
if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break;
} else {
uint8_t atqa[2] = {};
if(!flipper_format_read_hex(file, "ATQA", atqa, 2)) break;
data->a_data.atqa[0] = atqa[1];
data->a_data.atqa[1] = atqa[0];
data->atqa[0] = atqa[1];
data->atqa[1] = atqa[0];
}
if(!flipper_format_read_hex(file, "SAK", &data->a_data.sak, 1)) break;
if(!flipper_format_read_hex(file, "SAK", &data->sak, 1)) break;
}
// Load CUID
uint8_t* cuid_start = data->uid;
+4 -2
View File
@@ -1310,18 +1310,19 @@ bool nfcv_emu_loop(
}
nfcv_data->emu_protocol_handler(tx_rx, nfc_data, nfcv_data);
/* determine readers fc by analyzing transmission duration */
uint32_t duration = eof_timestamp - sof_timestamp;
float fc_1024 = (4.0f * duration) / (4 * (frame_pos * 4 + 1) + 1);
/* it should be 1024/fc in 64MHz ticks */
float fact = fc_1024 / ((1000000.0f * 64.0f * 1024.0f) / NFCV_FC);
FURI_LOG_D(TAG, "1024/fc: %f -> %f %%", (double)fc_1024, (double)fact * 100);
FURI_LOG_D(TAG, "1024/fc: %f -> %f %%", (double)fc_1024, (double)(fact * 100));
#if 0
if(fact > 0.99f && fact < 1.01f) {
static float avg_err = 0.0f;
avg_err = (avg_err * 15.0f + (fact - 1.0f)) / 16.0f;
FURI_LOG_D(TAG, " ==> set %f %%", (1.0f + avg_err) * 100);
FURI_LOG_D(TAG, " ==> set %f %%", (double)((1.0f + avg_err) * 100));
digital_sequence_timebase_correction(nfcv_data->emu_air.nfcv_signal, 1.0f + avg_err);
}
#endif
@@ -1345,3 +1346,4 @@ bool nfcv_emu_loop(
return ret;
}