mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-07-21 00:58:10 -07:00
NFC refactoring (#3050)
"A long time ago in a galaxy far, far away...." we started NFC subsystem refactoring. Starring: - @gornekich - NFC refactoring project lead, architect, senior developer - @gsurkov - architect, senior developer - @RebornedBrain - senior developer Supporting roles: - @skotopes, @DrZlo13, @hedger - general architecture advisors, code review - @Astrrra, @doomwastaken, @Hellitron, @ImagineVagon333 - quality assurance Special thanks: @bettse, @pcunning, @nxv, @noproto, @AloneLiberty and everyone else who has been helping us all this time and contributing valuable knowledges, ideas and source code.
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
Import("env")
|
||||
|
||||
env.Append(
|
||||
CPPPATH=[
|
||||
"#/lib/signal_reader",
|
||||
],
|
||||
SDK_HEADERS=[
|
||||
File("signal_reader.h"),
|
||||
],
|
||||
)
|
||||
|
||||
libenv = env.Clone(FW_LIB_NAME="signal_reader")
|
||||
libenv.ApplyLibFlags()
|
||||
libenv.Append(CCFLAGS=["-O3", "-funroll-loops", "-Ofast"])
|
||||
|
||||
sources = libenv.GlobRecursive("*.c*")
|
||||
|
||||
lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources)
|
||||
libenv.Install("${LIB_DIST_DIR}", lib)
|
||||
Return("lib")
|
||||
@@ -0,0 +1,300 @@
|
||||
#include "iso15693_parser.h"
|
||||
|
||||
#include <toolbox/bit_buffer.h>
|
||||
|
||||
#include <furi/furi.h>
|
||||
|
||||
#define ISO15693_PARSER_SIGNAL_READER_BUFF_SIZE (2)
|
||||
#define ISO15693_PARSER_BITSTREAM_BUFF_SIZE (32)
|
||||
#define ISO15693_PARSER_BITRATE_F64MHZ (603U)
|
||||
|
||||
#define TAG "Iso15693Parser"
|
||||
|
||||
typedef enum {
|
||||
Iso15693ParserStateParseSoF,
|
||||
Iso15693ParserStateParseFrame,
|
||||
Iso15693ParserStateFail,
|
||||
} Iso15693ParserState;
|
||||
|
||||
typedef enum {
|
||||
Iso15693ParserMode1OutOf4,
|
||||
Iso15693ParserMode1OutOf256,
|
||||
|
||||
Iso15693ParserModeNum,
|
||||
} Iso15693ParserMode;
|
||||
|
||||
struct Iso15693Parser {
|
||||
Iso15693ParserState state;
|
||||
Iso15693ParserMode mode;
|
||||
|
||||
SignalReader* signal_reader;
|
||||
|
||||
uint8_t bitstream_buff[ISO15693_PARSER_BITSTREAM_BUFF_SIZE];
|
||||
size_t bitstream_idx;
|
||||
uint8_t last_byte;
|
||||
|
||||
bool signal_detected;
|
||||
bool bit_offset_calculated;
|
||||
uint8_t bit_offset;
|
||||
size_t byte_idx;
|
||||
size_t bytes_to_process;
|
||||
|
||||
uint8_t next_byte;
|
||||
uint16_t next_byte_part;
|
||||
bool zero_found;
|
||||
|
||||
BitBuffer* parsed_frame;
|
||||
bool eof_received;
|
||||
bool frame_parsed;
|
||||
|
||||
Iso15693ParserCallback callback;
|
||||
void* context;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
Iso15693ParserCommandProcessed,
|
||||
Iso15693ParserCommandWaitData,
|
||||
Iso15693ParserCommandFail,
|
||||
Iso15693ParserCommandSuccess,
|
||||
} Iso15693ParserCommand;
|
||||
|
||||
typedef Iso15693ParserCommand (*Iso15693ParserStateHandler)(Iso15693Parser* instance);
|
||||
|
||||
Iso15693Parser* iso15693_parser_alloc(const GpioPin* pin, size_t max_frame_size) {
|
||||
Iso15693Parser* instance = malloc(sizeof(Iso15693Parser));
|
||||
instance->parsed_frame = bit_buffer_alloc(max_frame_size);
|
||||
|
||||
instance->signal_reader = signal_reader_alloc(pin, ISO15693_PARSER_SIGNAL_READER_BUFF_SIZE);
|
||||
signal_reader_set_sample_rate(
|
||||
instance->signal_reader, SignalReaderTimeUnit64Mhz, ISO15693_PARSER_BITRATE_F64MHZ);
|
||||
signal_reader_set_pull(instance->signal_reader, GpioPullDown);
|
||||
signal_reader_set_polarity(instance->signal_reader, SignalReaderPolarityInverted);
|
||||
signal_reader_set_trigger(instance->signal_reader, SignalReaderTriggerRisingFallingEdge);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void iso15693_parser_free(Iso15693Parser* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
bit_buffer_free(instance->parsed_frame);
|
||||
signal_reader_free(instance->signal_reader);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void iso15693_parser_reset(Iso15693Parser* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
instance->state = Iso15693ParserStateParseSoF;
|
||||
instance->mode = Iso15693ParserMode1OutOf4;
|
||||
memset(instance->bitstream_buff, 0x00, sizeof(instance->bitstream_buff));
|
||||
instance->bitstream_idx = 0;
|
||||
|
||||
instance->next_byte = 0;
|
||||
instance->next_byte_part = 0;
|
||||
|
||||
instance->bit_offset = 0;
|
||||
instance->byte_idx = 0;
|
||||
instance->bytes_to_process = 0;
|
||||
instance->signal_detected = false;
|
||||
instance->bit_offset_calculated = false;
|
||||
|
||||
instance->last_byte = 0x00;
|
||||
instance->zero_found = false;
|
||||
instance->eof_received = false;
|
||||
|
||||
bit_buffer_reset(instance->parsed_frame);
|
||||
instance->frame_parsed = false;
|
||||
}
|
||||
|
||||
static void signal_reader_callback(SignalReaderEvent event, void* context) {
|
||||
furi_assert(context);
|
||||
furi_assert(event.data->data);
|
||||
furi_assert(event.data->len == ISO15693_PARSER_SIGNAL_READER_BUFF_SIZE / 2);
|
||||
|
||||
Iso15693Parser* instance = context;
|
||||
furi_assert(instance->callback);
|
||||
|
||||
const uint8_t sof_1_out_of_4 = 0x21;
|
||||
const uint8_t sof_1_out_of_256 = 0x81;
|
||||
const uint8_t eof_single = 0x01;
|
||||
const uint8_t eof = 0x04;
|
||||
|
||||
if(instance->state == Iso15693ParserStateParseSoF) {
|
||||
if(event.data->data[0] == sof_1_out_of_4) {
|
||||
instance->mode = Iso15693ParserMode1OutOf4;
|
||||
instance->state = Iso15693ParserStateParseFrame;
|
||||
} else if(event.data->data[0] == sof_1_out_of_256) {
|
||||
instance->mode = Iso15693ParserMode1OutOf256;
|
||||
instance->state = Iso15693ParserStateParseFrame;
|
||||
} else if(event.data->data[0] == eof_single) {
|
||||
instance->eof_received = true;
|
||||
instance->callback(Iso15693ParserEventDataReceived, instance->context);
|
||||
} else {
|
||||
instance->state = Iso15693ParserStateFail;
|
||||
instance->callback(Iso15693ParserEventDataReceived, instance->context);
|
||||
}
|
||||
} else {
|
||||
if(instance->mode == Iso15693ParserMode1OutOf4) {
|
||||
if(event.data->data[0] == eof) {
|
||||
instance->eof_received = true;
|
||||
instance->callback(Iso15693ParserEventDataReceived, instance->context);
|
||||
} else {
|
||||
instance->bitstream_buff[instance->bytes_to_process] = event.data->data[0];
|
||||
instance->bytes_to_process++;
|
||||
if(instance->bytes_to_process == ISO15693_PARSER_BITSTREAM_BUFF_SIZE) {
|
||||
instance->callback(Iso15693ParserEventDataReceived, instance->context);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
instance->bitstream_buff[instance->bytes_to_process] = event.data->data[0];
|
||||
instance->bytes_to_process++;
|
||||
if(instance->bytes_to_process == ISO15693_PARSER_BITSTREAM_BUFF_SIZE) {
|
||||
instance->callback(Iso15693ParserEventDataReceived, instance->context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void iso15693_parser_start_signal_reader(Iso15693Parser* instance) {
|
||||
iso15693_parser_reset(instance);
|
||||
signal_reader_start(instance->signal_reader, signal_reader_callback, instance);
|
||||
}
|
||||
|
||||
void iso15693_parser_start(
|
||||
Iso15693Parser* instance,
|
||||
Iso15693ParserCallback callback,
|
||||
void* context) {
|
||||
furi_assert(instance);
|
||||
furi_assert(callback);
|
||||
|
||||
instance->callback = callback;
|
||||
instance->context = context;
|
||||
iso15693_parser_start_signal_reader(instance);
|
||||
}
|
||||
|
||||
void iso15693_parser_stop(Iso15693Parser* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
signal_reader_stop(instance->signal_reader);
|
||||
}
|
||||
|
||||
static Iso15693ParserCommand iso15693_parser_parse_1_out_of_4(Iso15693Parser* instance) {
|
||||
Iso15693ParserCommand command = Iso15693ParserCommandWaitData;
|
||||
const uint8_t bit_patterns_1_out_of_4[] = {0x02, 0x08, 0x20, 0x80};
|
||||
|
||||
for(size_t i = 0; i < instance->bytes_to_process; i++) {
|
||||
// Check next pattern
|
||||
size_t j = 0;
|
||||
for(j = 0; j < COUNT_OF(bit_patterns_1_out_of_4); j++) {
|
||||
if(instance->bitstream_buff[i] == bit_patterns_1_out_of_4[j]) {
|
||||
instance->next_byte |= j << (instance->next_byte_part * 2);
|
||||
instance->next_byte_part++;
|
||||
if(instance->next_byte_part == 4) {
|
||||
instance->next_byte_part = 0;
|
||||
bit_buffer_append_byte(instance->parsed_frame, instance->next_byte);
|
||||
instance->next_byte = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(j == COUNT_OF(bit_patterns_1_out_of_4)) {
|
||||
command = Iso15693ParserCommandFail;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(command != Iso15693ParserCommandFail) {
|
||||
if(instance->eof_received) {
|
||||
command = Iso15693ParserCommandSuccess;
|
||||
instance->frame_parsed = true;
|
||||
}
|
||||
}
|
||||
|
||||
instance->bytes_to_process = 0;
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
static Iso15693ParserCommand iso15693_parser_parse_1_out_of_256(Iso15693Parser* instance) {
|
||||
Iso15693ParserCommand command = Iso15693ParserCommandWaitData;
|
||||
const uint8_t eof = 0x04;
|
||||
|
||||
for(size_t i = instance->byte_idx; i < instance->bytes_to_process; i++) {
|
||||
// Check EoF
|
||||
if(instance->next_byte_part == 0) {
|
||||
if(instance->bitstream_buff[i] == eof) {
|
||||
instance->frame_parsed = true;
|
||||
command = Iso15693ParserCommandSuccess;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(instance->zero_found) {
|
||||
if(instance->bitstream_buff[i] != 0x00) {
|
||||
command = Iso15693ParserCommandFail;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if(instance->bitstream_buff[i] != 0x00) {
|
||||
for(size_t j = 0; j < 8; j++) {
|
||||
if(FURI_BIT(instance->bitstream_buff[i], j) == 1) {
|
||||
bit_buffer_append_byte(
|
||||
instance->parsed_frame, instance->next_byte_part * 4 + j / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
instance->next_byte_part = (instance->next_byte_part + 1) % 64;
|
||||
}
|
||||
instance->bytes_to_process = 0;
|
||||
instance->byte_idx = 0;
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
static const Iso15693ParserStateHandler iso15693_parser_state_handlers[Iso15693ParserModeNum] = {
|
||||
[Iso15693ParserMode1OutOf4] = iso15693_parser_parse_1_out_of_4,
|
||||
[Iso15693ParserMode1OutOf256] = iso15693_parser_parse_1_out_of_256,
|
||||
};
|
||||
|
||||
bool iso15693_parser_run(Iso15693Parser* instance) {
|
||||
if(instance->state == Iso15693ParserStateFail) {
|
||||
iso15693_parser_stop(instance);
|
||||
iso15693_parser_start_signal_reader(instance);
|
||||
} else if((instance->state == Iso15693ParserStateParseSoF) && (instance->eof_received)) {
|
||||
instance->frame_parsed = true;
|
||||
} else if(instance->bytes_to_process) {
|
||||
Iso15693ParserCommand command = Iso15693ParserCommandProcessed;
|
||||
while(command == Iso15693ParserCommandProcessed) {
|
||||
command = iso15693_parser_state_handlers[instance->mode](instance);
|
||||
}
|
||||
|
||||
if(command == Iso15693ParserCommandFail) {
|
||||
iso15693_parser_stop(instance);
|
||||
iso15693_parser_start_signal_reader(instance);
|
||||
FURI_LOG_D(TAG, "Frame parse failed");
|
||||
}
|
||||
}
|
||||
|
||||
return instance->frame_parsed;
|
||||
}
|
||||
|
||||
size_t iso15693_parser_get_data_size_bytes(Iso15693Parser* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
return bit_buffer_get_size_bytes(instance->parsed_frame);
|
||||
}
|
||||
|
||||
void iso15693_parser_get_data(
|
||||
Iso15693Parser* instance,
|
||||
uint8_t* buff,
|
||||
size_t buff_size,
|
||||
size_t* data_bits) {
|
||||
furi_assert(instance);
|
||||
furi_assert(buff);
|
||||
furi_assert(data_bits);
|
||||
|
||||
bit_buffer_write_bytes(instance->parsed_frame, buff, buff_size);
|
||||
*data_bits = bit_buffer_get_size(instance->parsed_frame);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../signal_reader.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct Iso15693Parser Iso15693Parser;
|
||||
|
||||
typedef enum {
|
||||
Iso15693ParserEventDataReceived,
|
||||
} Iso15693ParserEvent;
|
||||
|
||||
typedef void (*Iso15693ParserCallback)(Iso15693ParserEvent event, void* context);
|
||||
|
||||
Iso15693Parser* iso15693_parser_alloc(const GpioPin* pin, size_t max_frame_size);
|
||||
|
||||
void iso15693_parser_free(Iso15693Parser* instance);
|
||||
|
||||
void iso15693_parser_reset(Iso15693Parser* instance);
|
||||
|
||||
void iso15693_parser_start(
|
||||
Iso15693Parser* instance,
|
||||
Iso15693ParserCallback callback,
|
||||
void* context);
|
||||
|
||||
void iso15693_parser_stop(Iso15693Parser* instance);
|
||||
|
||||
bool iso15693_parser_run(Iso15693Parser* instance);
|
||||
|
||||
size_t iso15693_parser_get_data_size_bytes(Iso15693Parser* instance);
|
||||
|
||||
void iso15693_parser_get_data(
|
||||
Iso15693Parser* instance,
|
||||
uint8_t* buff,
|
||||
size_t buff_size,
|
||||
size_t* data_bits);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,317 @@
|
||||
#include "signal_reader.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <furi_hal_gpio.h>
|
||||
|
||||
#include <stm32wbxx_ll_dma.h>
|
||||
#include <stm32wbxx_ll_dmamux.h>
|
||||
#include <stm32wbxx_ll_tim.h>
|
||||
#include <stm32wbxx_ll_exti.h>
|
||||
|
||||
#include <furi_hal_bus.h>
|
||||
|
||||
#define SIGNAL_READER_DMA DMA2
|
||||
|
||||
#define SIGNAL_READER_CAPTURE_TIM (TIM16)
|
||||
#define SIGNAL_READER_CAPTURE_TIM_CHANNEL LL_TIM_CHANNEL_CH1
|
||||
|
||||
#define SIGNAL_READER_DMA_GPIO LL_DMA_CHANNEL_2
|
||||
#define SIGNAL_READER_DMA_GPIO_IRQ FuriHalInterruptIdDma2Ch2
|
||||
#define SIGNAL_READER_DMA_GPIO_DEF SIGNAL_READER_DMA, SIGNAL_READER_DMA_GPIO
|
||||
|
||||
#define SIGNAL_READER_DMA_TRIGGER LL_DMA_CHANNEL_3
|
||||
#define SIGNAL_READER_DMA_TRIGGER_IRQ FuriHalInterruptIdDma2Ch3
|
||||
#define SIGNAL_READER_DMA_TRIGGER_DEF SIGNAL_READER_DMA, SIGNAL_READER_DMA_TRIGGER
|
||||
|
||||
#define SIGNAL_READER_DMA_CNT_SYNC LL_DMA_CHANNEL_5
|
||||
#define SIGNAL_READER_DMA_CNT_SYNC_IRQ FuriHalInterruptIdDma2Ch5
|
||||
#define SIGNAL_READER_DMA_CNT_SYNC_DEF SIGNAL_READER_DMA, SIGNAL_READER_DMA_CNT_SYNC
|
||||
|
||||
struct SignalReader {
|
||||
size_t buffer_size;
|
||||
const GpioPin* pin;
|
||||
GpioPull pull;
|
||||
SignalReaderPolarity polarity;
|
||||
SignalReaderTrigger trigger;
|
||||
|
||||
uint16_t* gpio_buffer;
|
||||
uint8_t* bitstream_buffer;
|
||||
uint32_t cnt_en;
|
||||
|
||||
uint32_t tim_cnt_compensation;
|
||||
uint32_t tim_arr;
|
||||
|
||||
SignalReaderEvent event;
|
||||
SignalReaderEventData event_data;
|
||||
|
||||
SignalReaderCallback callback;
|
||||
void* context;
|
||||
};
|
||||
|
||||
#define GPIO_PIN_MAP(pin, prefix) \
|
||||
(((pin) == (LL_GPIO_PIN_0)) ? prefix##0 : \
|
||||
((pin) == (LL_GPIO_PIN_1)) ? prefix##1 : \
|
||||
((pin) == (LL_GPIO_PIN_2)) ? prefix##2 : \
|
||||
((pin) == (LL_GPIO_PIN_3)) ? prefix##3 : \
|
||||
((pin) == (LL_GPIO_PIN_4)) ? prefix##4 : \
|
||||
((pin) == (LL_GPIO_PIN_5)) ? prefix##5 : \
|
||||
((pin) == (LL_GPIO_PIN_6)) ? prefix##6 : \
|
||||
((pin) == (LL_GPIO_PIN_7)) ? prefix##7 : \
|
||||
((pin) == (LL_GPIO_PIN_8)) ? prefix##8 : \
|
||||
((pin) == (LL_GPIO_PIN_9)) ? prefix##9 : \
|
||||
((pin) == (LL_GPIO_PIN_10)) ? prefix##10 : \
|
||||
((pin) == (LL_GPIO_PIN_11)) ? prefix##11 : \
|
||||
((pin) == (LL_GPIO_PIN_12)) ? prefix##12 : \
|
||||
((pin) == (LL_GPIO_PIN_13)) ? prefix##13 : \
|
||||
((pin) == (LL_GPIO_PIN_14)) ? prefix##14 : \
|
||||
prefix##15)
|
||||
|
||||
#define GET_DMAMUX_EXTI_LINE(pin) GPIO_PIN_MAP(pin, LL_DMAMUX_REQ_GEN_EXTI_LINE)
|
||||
|
||||
SignalReader* signal_reader_alloc(const GpioPin* gpio_pin, uint32_t size) {
|
||||
SignalReader* instance = malloc(sizeof(SignalReader));
|
||||
|
||||
instance->pin = gpio_pin;
|
||||
instance->pull = GpioPullNo;
|
||||
|
||||
instance->buffer_size = size;
|
||||
instance->gpio_buffer = malloc(sizeof(uint16_t) * size * 8);
|
||||
instance->bitstream_buffer = malloc(size);
|
||||
|
||||
instance->event.data = &instance->event_data;
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void signal_reader_free(SignalReader* instance) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->gpio_buffer);
|
||||
furi_assert(instance->bitstream_buffer);
|
||||
|
||||
free(instance->gpio_buffer);
|
||||
free(instance->bitstream_buffer);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void signal_reader_set_pull(SignalReader* instance, GpioPull pull) {
|
||||
furi_assert(instance);
|
||||
|
||||
instance->pull = pull;
|
||||
}
|
||||
|
||||
void signal_reader_set_polarity(SignalReader* instance, SignalReaderPolarity polarity) {
|
||||
furi_assert(instance);
|
||||
|
||||
instance->polarity = polarity;
|
||||
}
|
||||
|
||||
void signal_reader_set_sample_rate(
|
||||
SignalReader* instance,
|
||||
SignalReaderTimeUnit time_unit,
|
||||
uint32_t time) {
|
||||
furi_assert(instance);
|
||||
UNUSED(time_unit);
|
||||
|
||||
instance->tim_arr = time;
|
||||
}
|
||||
|
||||
void signal_reader_set_trigger(SignalReader* instance, SignalReaderTrigger trigger) {
|
||||
furi_assert(instance);
|
||||
|
||||
instance->trigger = trigger;
|
||||
}
|
||||
|
||||
static void furi_hal_sw_digital_pin_dma_rx_isr(void* context) {
|
||||
SignalReader* instance = context;
|
||||
|
||||
uint16_t* gpio_buff_start = NULL;
|
||||
uint8_t* bitstream_buff_start = NULL;
|
||||
|
||||
if(LL_DMA_IsActiveFlag_HT2(SIGNAL_READER_DMA)) {
|
||||
LL_DMA_ClearFlag_HT2(SIGNAL_READER_DMA);
|
||||
instance->event.type = SignalReaderEventTypeHalfBufferFilled;
|
||||
gpio_buff_start = instance->gpio_buffer;
|
||||
bitstream_buff_start = instance->bitstream_buffer;
|
||||
|
||||
if(instance->callback) {
|
||||
furi_assert(gpio_buff_start);
|
||||
furi_assert(bitstream_buff_start);
|
||||
|
||||
for(size_t i = 0; i < instance->buffer_size * 4; i++) {
|
||||
if((i % 8) == 0) {
|
||||
bitstream_buff_start[i / 8] = 0;
|
||||
}
|
||||
uint8_t bit = 0;
|
||||
if(instance->polarity == SignalReaderPolarityNormal) {
|
||||
bit = (gpio_buff_start[i] & instance->pin->pin) == instance->pin->pin;
|
||||
} else {
|
||||
bit = (gpio_buff_start[i] & instance->pin->pin) == 0;
|
||||
}
|
||||
bitstream_buff_start[i / 8] |= bit << (i % 8);
|
||||
}
|
||||
instance->event_data.data = bitstream_buff_start;
|
||||
instance->event_data.len = instance->buffer_size / 2;
|
||||
instance->callback(instance->event, instance->context);
|
||||
}
|
||||
}
|
||||
if(LL_DMA_IsActiveFlag_TC2(SIGNAL_READER_DMA)) {
|
||||
LL_DMA_ClearFlag_TC2(SIGNAL_READER_DMA);
|
||||
instance->event.type = SignalReaderEventTypeFullBufferFilled;
|
||||
gpio_buff_start = &instance->gpio_buffer[instance->buffer_size * 4];
|
||||
bitstream_buff_start = &instance->bitstream_buffer[instance->buffer_size / 2];
|
||||
|
||||
if(instance->callback) {
|
||||
furi_assert(gpio_buff_start);
|
||||
furi_assert(bitstream_buff_start);
|
||||
|
||||
for(size_t i = 0; i < instance->buffer_size * 4; i++) {
|
||||
if((i % 8) == 0) {
|
||||
bitstream_buff_start[i / 8] = 0;
|
||||
}
|
||||
uint8_t bit = 0;
|
||||
if(instance->polarity == SignalReaderPolarityNormal) {
|
||||
bit = (gpio_buff_start[i] & instance->pin->pin) == instance->pin->pin;
|
||||
} else {
|
||||
bit = (gpio_buff_start[i] & instance->pin->pin) == 0;
|
||||
}
|
||||
bitstream_buff_start[i / 8] |= bit << (i % 8);
|
||||
}
|
||||
instance->event_data.data = bitstream_buff_start;
|
||||
instance->event_data.len = instance->buffer_size / 2;
|
||||
instance->callback(instance->event, instance->context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void signal_reader_start(SignalReader* instance, SignalReaderCallback callback, void* context) {
|
||||
furi_assert(instance);
|
||||
furi_assert(callback);
|
||||
|
||||
instance->callback = callback;
|
||||
instance->context = context;
|
||||
|
||||
// EXTI delay compensation
|
||||
instance->tim_cnt_compensation = 9;
|
||||
instance->cnt_en = SIGNAL_READER_CAPTURE_TIM->CR1;
|
||||
instance->cnt_en |= TIM_CR1_CEN;
|
||||
|
||||
furi_hal_bus_enable(FuriHalBusTIM16);
|
||||
|
||||
// Capture timer config
|
||||
LL_TIM_SetPrescaler(SIGNAL_READER_CAPTURE_TIM, 0);
|
||||
LL_TIM_SetCounterMode(SIGNAL_READER_CAPTURE_TIM, LL_TIM_COUNTERMODE_UP);
|
||||
LL_TIM_SetAutoReload(SIGNAL_READER_CAPTURE_TIM, instance->tim_arr);
|
||||
LL_TIM_SetClockDivision(SIGNAL_READER_CAPTURE_TIM, LL_TIM_CLOCKDIVISION_DIV1);
|
||||
|
||||
LL_TIM_DisableARRPreload(SIGNAL_READER_CAPTURE_TIM);
|
||||
LL_TIM_SetClockSource(SIGNAL_READER_CAPTURE_TIM, LL_TIM_CLOCKSOURCE_INTERNAL);
|
||||
|
||||
// Configure TIM channel CC1
|
||||
LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {};
|
||||
TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_FROZEN;
|
||||
TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_DISABLE;
|
||||
TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_DISABLE;
|
||||
TIM_OC_InitStruct.CompareValue = (instance->tim_arr / 2);
|
||||
TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_HIGH;
|
||||
LL_TIM_OC_Init(
|
||||
SIGNAL_READER_CAPTURE_TIM, SIGNAL_READER_CAPTURE_TIM_CHANNEL, &TIM_OC_InitStruct);
|
||||
LL_TIM_OC_DisableFast(SIGNAL_READER_CAPTURE_TIM, SIGNAL_READER_CAPTURE_TIM_CHANNEL);
|
||||
|
||||
LL_TIM_SetTriggerOutput(SIGNAL_READER_CAPTURE_TIM, LL_TIM_TRGO_RESET);
|
||||
LL_TIM_DisableMasterSlaveMode(SIGNAL_READER_CAPTURE_TIM);
|
||||
|
||||
// Start
|
||||
LL_TIM_GenerateEvent_UPDATE(SIGNAL_READER_CAPTURE_TIM);
|
||||
|
||||
/* We need the EXTI to be configured as interrupt generating line, but no ISR registered */
|
||||
furi_hal_gpio_init(
|
||||
instance->pin, GpioModeInterruptRiseFall, instance->pull, GpioSpeedVeryHigh);
|
||||
|
||||
/* Set DMAMUX request generation signal ID on specified DMAMUX channel */
|
||||
LL_DMAMUX_SetRequestSignalID(
|
||||
DMAMUX1, LL_DMAMUX_REQ_GEN_0, GET_DMAMUX_EXTI_LINE(instance->pin->pin));
|
||||
/* Set the polarity of the signal on which the DMA request is generated */
|
||||
LL_DMAMUX_SetRequestGenPolarity(DMAMUX1, LL_DMAMUX_REQ_GEN_0, LL_DMAMUX_REQ_GEN_POL_RISING);
|
||||
/* Set the number of DMA requests that will be authorized after a generation event */
|
||||
LL_DMAMUX_SetGenRequestNb(DMAMUX1, LL_DMAMUX_REQ_GEN_0, 1);
|
||||
|
||||
// Configure DMA Sync
|
||||
LL_DMA_SetMemoryAddress(
|
||||
SIGNAL_READER_DMA_CNT_SYNC_DEF, (uint32_t)&instance->tim_cnt_compensation);
|
||||
LL_DMA_SetPeriphAddress(
|
||||
SIGNAL_READER_DMA_CNT_SYNC_DEF, (uint32_t) & (SIGNAL_READER_CAPTURE_TIM->CNT));
|
||||
LL_DMA_ConfigTransfer(
|
||||
SIGNAL_READER_DMA_CNT_SYNC_DEF,
|
||||
LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT |
|
||||
LL_DMA_MEMORY_NOINCREMENT | LL_DMA_PDATAALIGN_HALFWORD | LL_DMA_MDATAALIGN_HALFWORD |
|
||||
LL_DMA_PRIORITY_VERYHIGH);
|
||||
LL_DMA_SetDataLength(SIGNAL_READER_DMA_CNT_SYNC_DEF, 1);
|
||||
LL_DMA_SetPeriphRequest(SIGNAL_READER_DMA_CNT_SYNC_DEF, LL_DMAMUX_REQ_GENERATOR0);
|
||||
|
||||
// Configure DMA Sync
|
||||
LL_DMA_SetMemoryAddress(SIGNAL_READER_DMA_TRIGGER_DEF, (uint32_t)&instance->cnt_en);
|
||||
LL_DMA_SetPeriphAddress(
|
||||
SIGNAL_READER_DMA_TRIGGER_DEF, (uint32_t) & (SIGNAL_READER_CAPTURE_TIM->CR1));
|
||||
LL_DMA_ConfigTransfer(
|
||||
SIGNAL_READER_DMA_TRIGGER_DEF,
|
||||
LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_PERIPH_NOINCREMENT | LL_DMA_MEMORY_NOINCREMENT |
|
||||
LL_DMA_PDATAALIGN_HALFWORD | LL_DMA_MDATAALIGN_HALFWORD | LL_DMA_PRIORITY_VERYHIGH);
|
||||
LL_DMA_SetDataLength(SIGNAL_READER_DMA_TRIGGER_DEF, 1);
|
||||
LL_DMA_SetPeriphRequest(SIGNAL_READER_DMA_TRIGGER_DEF, LL_DMAMUX_REQ_GENERATOR0);
|
||||
|
||||
// Configure DMA Rx pin
|
||||
LL_DMA_SetMemoryAddress(SIGNAL_READER_DMA_GPIO_DEF, (uint32_t)instance->gpio_buffer);
|
||||
LL_DMA_SetPeriphAddress(SIGNAL_READER_DMA_GPIO_DEF, (uint32_t) & (instance->pin->port->IDR));
|
||||
LL_DMA_ConfigTransfer(
|
||||
SIGNAL_READER_DMA_GPIO_DEF,
|
||||
LL_DMA_DIRECTION_PERIPH_TO_MEMORY | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT |
|
||||
LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_HALFWORD | LL_DMA_MDATAALIGN_HALFWORD |
|
||||
LL_DMA_PRIORITY_HIGH);
|
||||
LL_DMA_SetDataLength(SIGNAL_READER_DMA_GPIO_DEF, instance->buffer_size * 8);
|
||||
LL_DMA_SetPeriphRequest(SIGNAL_READER_DMA_GPIO_DEF, LL_DMAMUX_REQ_TIM16_CH1);
|
||||
|
||||
// Configure DMA Channel CC1
|
||||
LL_TIM_EnableDMAReq_CC1(SIGNAL_READER_CAPTURE_TIM);
|
||||
LL_TIM_CC_EnableChannel(SIGNAL_READER_CAPTURE_TIM, SIGNAL_READER_CAPTURE_TIM_CHANNEL);
|
||||
|
||||
// Start DMA irq, higher priority than normal
|
||||
furi_hal_interrupt_set_isr_ex(
|
||||
SIGNAL_READER_DMA_GPIO_IRQ, 14, furi_hal_sw_digital_pin_dma_rx_isr, instance);
|
||||
|
||||
// Start DMA Sync timer
|
||||
LL_DMA_EnableChannel(SIGNAL_READER_DMA_CNT_SYNC_DEF);
|
||||
|
||||
// Start DMA Rx pin
|
||||
LL_DMA_EnableChannel(SIGNAL_READER_DMA_GPIO_DEF);
|
||||
// Strat timer
|
||||
LL_TIM_SetCounter(SIGNAL_READER_CAPTURE_TIM, 0);
|
||||
if(instance->trigger == SignalReaderTriggerNone) {
|
||||
LL_TIM_EnableCounter(SIGNAL_READER_CAPTURE_TIM);
|
||||
} else {
|
||||
LL_DMA_EnableChannel(SIGNAL_READER_DMA_TRIGGER_DEF);
|
||||
}
|
||||
|
||||
LL_DMAMUX_EnableRequestGen(DMAMUX1, LL_DMAMUX_REQ_GEN_0);
|
||||
// Need to clear flags before enabling DMA !!!!
|
||||
if(LL_DMA_IsActiveFlag_TC2(SIGNAL_READER_DMA)) LL_DMA_ClearFlag_TC1(SIGNAL_READER_DMA);
|
||||
if(LL_DMA_IsActiveFlag_TE2(SIGNAL_READER_DMA)) LL_DMA_ClearFlag_TE1(SIGNAL_READER_DMA);
|
||||
LL_DMA_EnableIT_TC(SIGNAL_READER_DMA_GPIO_DEF);
|
||||
LL_DMA_EnableIT_HT(SIGNAL_READER_DMA_GPIO_DEF);
|
||||
}
|
||||
|
||||
void signal_reader_stop(SignalReader* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
furi_hal_interrupt_set_isr(SIGNAL_READER_DMA_GPIO_IRQ, NULL, NULL);
|
||||
|
||||
// Deinit DMA Rx pin
|
||||
LL_DMA_DeInit(SIGNAL_READER_DMA_GPIO_DEF);
|
||||
// Deinit DMA Sync timer
|
||||
LL_DMA_DeInit(SIGNAL_READER_DMA_CNT_SYNC_DEF);
|
||||
// Deinit DMA Trigger timer
|
||||
LL_DMA_DeInit(SIGNAL_READER_DMA_TRIGGER_DEF);
|
||||
|
||||
furi_hal_bus_disable(FuriHalBusTIM16);
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <furi_hal_gpio.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
SignalReaderEventTypeHalfBufferFilled,
|
||||
SignalReaderEventTypeFullBufferFilled,
|
||||
} SignalReaderEventType;
|
||||
|
||||
typedef struct {
|
||||
uint8_t* data;
|
||||
size_t len;
|
||||
} SignalReaderEventData;
|
||||
|
||||
typedef struct {
|
||||
SignalReaderEventType type;
|
||||
SignalReaderEventData* data;
|
||||
} SignalReaderEvent;
|
||||
|
||||
typedef enum {
|
||||
SignalReaderTimeUnit64Mhz,
|
||||
} SignalReaderTimeUnit;
|
||||
|
||||
typedef enum {
|
||||
SignalReaderPolarityNormal,
|
||||
SignalReaderPolarityInverted,
|
||||
} SignalReaderPolarity;
|
||||
|
||||
typedef enum {
|
||||
SignalReaderTriggerNone,
|
||||
SignalReaderTriggerRisingFallingEdge,
|
||||
} SignalReaderTrigger;
|
||||
|
||||
typedef void (*SignalReaderCallback)(SignalReaderEvent event, void* context);
|
||||
|
||||
typedef struct SignalReader SignalReader;
|
||||
|
||||
SignalReader* signal_reader_alloc(const GpioPin* gpio_pin, uint32_t size);
|
||||
|
||||
void signal_reader_free(SignalReader* instance);
|
||||
|
||||
void signal_reader_set_pull(SignalReader* instance, GpioPull pull);
|
||||
|
||||
void signal_reader_set_polarity(SignalReader* instance, SignalReaderPolarity polarity);
|
||||
|
||||
void signal_reader_set_sample_rate(
|
||||
SignalReader* instance,
|
||||
SignalReaderTimeUnit time_unit,
|
||||
uint32_t time);
|
||||
|
||||
void signal_reader_set_trigger(SignalReader* instance, SignalReaderTrigger trigger);
|
||||
|
||||
void signal_reader_start(SignalReader* instance, SignalReaderCallback callback, void* context);
|
||||
|
||||
void signal_reader_stop(SignalReader* instance);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user