1
mirror of https://github.com/flipperdevices/flipperzero-firmware.git synced 2025-12-12 04:41:26 +04: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:
gornekich
2023-10-24 07:08:09 +04:00
committed by GitHub
parent 35c903494c
commit d92b0a82cc
514 changed files with 41488 additions and 68125 deletions

View File

@@ -6,6 +6,7 @@ env.Append(
],
SDK_HEADERS=[
File("digital_signal.h"),
File("digital_sequence.h"),
],
)

View File

@@ -0,0 +1,381 @@
#include "digital_sequence.h"
#include "digital_signal_i.h"
#include <furi.h>
#include <furi_hal_bus.h>
#include <stm32wbxx_ll_dma.h>
#include <stm32wbxx_ll_tim.h>
/**
* To enable debug output on an additional pin, set DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN to the required
* GpioPin variable. It can be passed at compile time via the --extra-define fbt switch.
* NOTE: This pin must be on the same GPIO port as the main pin.
*
* Example:
* ./fbt --extra-define=DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN=gpio_ext_pb3
*/
#define TAG "DigitalSequence"
/* Special value used to indicate the end of DMA ring buffer. */
#define DIGITAL_SEQUENCE_TIMER_MAX 0xFFFFFFFFUL
/* Time to wait in loops before returning */
#define DIGITAL_SEQUENCE_LOCK_WAIT_MS 10UL
#define DIGITAL_SEQUENCE_LOCK_WAIT_TICKS (DIGITAL_SEQUENCE_LOCK_WAIT_MS * 1000 * 64)
#define DIGITAL_SEQUENCE_GPIO_BUFFER_SIZE 2
/* Maximum capacity of the DMA ring buffer. */
#define DIGITAL_SEQUENCE_RING_BUFFER_SIZE 128
#define DIGITAL_SEQUENCE_RING_BUFFER_MIN_FREE_SIZE 2
/* Maximum amount of registered signals. */
#define DIGITAL_SEQUENCE_BANK_SIZE 32
typedef enum {
DigitalSequenceStateIdle,
DigitalSequenceStateActive,
} DigitalSequenceState;
typedef struct {
uint32_t data[DIGITAL_SEQUENCE_RING_BUFFER_SIZE];
uint32_t write_pos;
uint32_t read_pos;
} DigitalSequenceRingBuffer;
typedef uint32_t DigitalSequenceGpioBuffer[DIGITAL_SEQUENCE_GPIO_BUFFER_SIZE];
typedef const DigitalSignal* DigitalSequenceSignalBank[DIGITAL_SEQUENCE_BANK_SIZE];
struct DigitalSequence {
const GpioPin* gpio;
uint32_t size;
uint32_t max_size;
uint8_t* data;
LL_DMA_InitTypeDef dma_config_gpio;
LL_DMA_InitTypeDef dma_config_timer;
DigitalSequenceGpioBuffer gpio_buf;
DigitalSequenceRingBuffer timer_buf;
DigitalSequenceSignalBank signals;
DigitalSequenceState state;
};
DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio) {
furi_assert(size);
furi_assert(gpio);
DigitalSequence* sequence = malloc(sizeof(DigitalSequence));
sequence->gpio = gpio;
sequence->max_size = size;
sequence->data = malloc(sequence->max_size);
sequence->dma_config_gpio.PeriphOrM2MSrcAddress = (uint32_t)&gpio->port->BSRR;
sequence->dma_config_gpio.MemoryOrM2MDstAddress = (uint32_t)sequence->gpio_buf;
sequence->dma_config_gpio.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
sequence->dma_config_gpio.Mode = LL_DMA_MODE_CIRCULAR;
sequence->dma_config_gpio.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
sequence->dma_config_gpio.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
sequence->dma_config_gpio.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD;
sequence->dma_config_gpio.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD;
sequence->dma_config_gpio.NbData = DIGITAL_SEQUENCE_GPIO_BUFFER_SIZE;
sequence->dma_config_gpio.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP;
sequence->dma_config_gpio.Priority = LL_DMA_PRIORITY_VERYHIGH;
sequence->dma_config_timer.PeriphOrM2MSrcAddress = (uint32_t)&TIM2->ARR;
sequence->dma_config_timer.MemoryOrM2MDstAddress = (uint32_t)sequence->timer_buf.data;
sequence->dma_config_timer.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
sequence->dma_config_timer.Mode = LL_DMA_MODE_CIRCULAR;
sequence->dma_config_timer.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
sequence->dma_config_timer.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
sequence->dma_config_timer.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD;
sequence->dma_config_timer.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD;
sequence->dma_config_timer.NbData = DIGITAL_SEQUENCE_RING_BUFFER_SIZE;
sequence->dma_config_timer.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP;
sequence->dma_config_timer.Priority = LL_DMA_PRIORITY_HIGH;
return sequence;
}
void digital_sequence_free(DigitalSequence* sequence) {
furi_assert(sequence);
free(sequence->data);
free(sequence);
}
void digital_sequence_register_signal(
DigitalSequence* sequence,
uint8_t signal_index,
const DigitalSignal* signal) {
furi_assert(sequence);
furi_assert(signal);
furi_assert(signal_index < DIGITAL_SEQUENCE_BANK_SIZE);
sequence->signals[signal_index] = signal;
}
void digital_sequence_add_signal(DigitalSequence* sequence, uint8_t signal_index) {
furi_assert(sequence);
furi_assert(signal_index < DIGITAL_SEQUENCE_BANK_SIZE);
furi_assert(sequence->size < sequence->max_size);
sequence->data[sequence->size++] = signal_index;
}
static inline void digital_sequence_start_dma(DigitalSequence* sequence) {
furi_assert(sequence);
LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &sequence->dma_config_gpio);
LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &sequence->dma_config_timer);
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2);
}
static inline void digital_sequence_stop_dma() {
LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1);
LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2);
LL_DMA_ClearFlag_TC1(DMA1);
LL_DMA_ClearFlag_TC2(DMA1);
}
static inline void digital_sequence_start_timer() {
furi_hal_bus_enable(FuriHalBusTIM2);
LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP);
LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1);
LL_TIM_SetPrescaler(TIM2, 0);
LL_TIM_SetAutoReload(TIM2, DIGITAL_SEQUENCE_TIMER_MAX);
LL_TIM_SetCounter(TIM2, 0);
LL_TIM_EnableCounter(TIM2);
LL_TIM_EnableUpdateEvent(TIM2);
LL_TIM_EnableDMAReq_UPDATE(TIM2);
LL_TIM_GenerateEvent_UPDATE(TIM2);
}
static void digital_sequence_stop_timer() {
LL_TIM_DisableCounter(TIM2);
LL_TIM_DisableUpdateEvent(TIM2);
LL_TIM_DisableDMAReq_UPDATE(TIM2);
furi_hal_bus_disable(FuriHalBusTIM2);
}
static inline void digital_sequence_init_gpio_buffer(
DigitalSequence* sequence,
const DigitalSignal* first_signal) {
const uint32_t bit_set = sequence->gpio->pin << GPIO_BSRR_BS0_Pos
#ifdef DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN
| DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN.pin << GPIO_BSRR_BS0_Pos
#endif
;
const uint32_t bit_reset = sequence->gpio->pin << GPIO_BSRR_BR0_Pos
#ifdef DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN
| DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN.pin << GPIO_BSRR_BR0_Pos
#endif
;
if(first_signal->start_level) {
sequence->gpio_buf[0] = bit_set;
sequence->gpio_buf[1] = bit_reset;
} else {
sequence->gpio_buf[0] = bit_reset;
sequence->gpio_buf[1] = bit_set;
}
}
static inline void digital_sequence_finish(DigitalSequence* sequence) {
if(sequence->state == DigitalSequenceStateActive) {
const uint32_t prev_timer = DWT->CYCCNT;
do {
/* Special value has been loaded into the timer, signaling the end of transmission. */
if(TIM2->ARR == DIGITAL_SEQUENCE_TIMER_MAX) {
break;
}
if(DWT->CYCCNT - prev_timer > DIGITAL_SEQUENCE_LOCK_WAIT_TICKS) {
DigitalSequenceRingBuffer* dma_buffer = &sequence->timer_buf;
dma_buffer->read_pos = DIGITAL_SEQUENCE_RING_BUFFER_SIZE -
LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2);
FURI_LOG_D(
TAG,
"[SEQ] hung %lu ms in finish (ARR 0x%08lx, read %lu, write %lu)",
DIGITAL_SEQUENCE_LOCK_WAIT_MS,
TIM2->ARR,
dma_buffer->read_pos,
dma_buffer->write_pos);
break;
}
} while(true);
}
digital_sequence_stop_timer();
digital_sequence_stop_dma();
}
static inline void digital_sequence_enqueue_period(DigitalSequence* sequence, uint32_t length) {
DigitalSequenceRingBuffer* dma_buffer = &sequence->timer_buf;
if(sequence->state == DigitalSequenceStateActive) {
const uint32_t prev_timer = DWT->CYCCNT;
do {
dma_buffer->read_pos =
DIGITAL_SEQUENCE_RING_BUFFER_SIZE - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2);
const uint32_t size_free = (DIGITAL_SEQUENCE_RING_BUFFER_SIZE + dma_buffer->read_pos -
dma_buffer->write_pos) %
DIGITAL_SEQUENCE_RING_BUFFER_SIZE;
if(size_free > DIGITAL_SEQUENCE_RING_BUFFER_MIN_FREE_SIZE) {
break;
}
if(DWT->CYCCNT - prev_timer > DIGITAL_SEQUENCE_LOCK_WAIT_TICKS) {
FURI_LOG_D(
TAG,
"[SEQ] hung %lu ms in queue (ARR 0x%08lx, read %lu, write %lu)",
DIGITAL_SEQUENCE_LOCK_WAIT_MS,
TIM2->ARR,
dma_buffer->read_pos,
dma_buffer->write_pos);
break;
}
if(TIM2->ARR == DIGITAL_SEQUENCE_TIMER_MAX) {
FURI_LOG_D(
TAG,
"[SEQ] buffer underrun in queue (ARR 0x%08lx, read %lu, write %lu)",
TIM2->ARR,
dma_buffer->read_pos,
dma_buffer->write_pos);
break;
}
} while(true);
}
dma_buffer->data[dma_buffer->write_pos] = length;
dma_buffer->write_pos += 1;
dma_buffer->write_pos %= DIGITAL_SEQUENCE_RING_BUFFER_SIZE;
dma_buffer->data[dma_buffer->write_pos] = DIGITAL_SEQUENCE_TIMER_MAX;
}
static inline void digital_sequence_timer_buffer_reset(DigitalSequence* sequence) {
sequence->timer_buf.data[0] = DIGITAL_SEQUENCE_TIMER_MAX;
sequence->timer_buf.read_pos = 0;
sequence->timer_buf.write_pos = 0;
}
void digital_sequence_transmit(DigitalSequence* sequence) {
furi_assert(sequence);
furi_assert(sequence->size);
furi_assert(sequence->state == DigitalSequenceStateIdle);
FURI_CRITICAL_ENTER();
furi_hal_gpio_init(sequence->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
#ifdef DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN
furi_hal_gpio_init(
&DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
#endif
const DigitalSignal* signal_current = sequence->signals[sequence->data[0]];
digital_sequence_init_gpio_buffer(sequence, signal_current);
int32_t remainder_ticks = 0;
uint32_t reload_value_carry = 0;
uint32_t next_signal_index = 1;
for(;;) {
const DigitalSignal* signal_next =
(next_signal_index < sequence->size) ?
sequence->signals[sequence->data[next_signal_index++]] :
NULL;
for(uint32_t i = 0; i < signal_current->size; i++) {
const bool is_last_value = (i == signal_current->size - 1);
const uint32_t reload_value = signal_current->data[i] + reload_value_carry;
reload_value_carry = 0;
if(is_last_value) {
if(signal_next != NULL) {
/* Special case: signal boundary. Depending on whether the adjacent levels are equal or not,
* they will be combined to a single one or handled separately. */
const bool end_level = signal_current->start_level ^
((signal_current->size % 2) == 0);
/* If the adjacent levels are equal, carry the current period duration over to the next signal. */
if(end_level == signal_next->start_level) {
reload_value_carry = reload_value;
}
} else {
/** Special case: during the last period of the last signal, hold the output level indefinitely.
* @see digital_signal.h
*
* Setting reload_value_carry to a non-zero value will prevent the respective period from being
* added to the DMA ring buffer. */
reload_value_carry = 1;
}
}
/* A non-zero reload_value_carry means that the level was the same on the both sides of the signal boundary
* and the two respective periods were combined to one. */
if(reload_value_carry == 0) {
digital_sequence_enqueue_period(sequence, reload_value);
}
if(sequence->state == DigitalSequenceStateIdle) {
const bool is_buffer_filled = sequence->timer_buf.write_pos >=
(DIGITAL_SEQUENCE_RING_BUFFER_SIZE -
DIGITAL_SEQUENCE_RING_BUFFER_MIN_FREE_SIZE);
const bool is_end_of_data = (signal_next == NULL) && is_last_value;
if(is_buffer_filled || is_end_of_data) {
digital_sequence_start_dma(sequence);
digital_sequence_start_timer();
sequence->state = DigitalSequenceStateActive;
}
}
}
/* Exit the loop here when no further signals are available */
if(signal_next == NULL) break;
/* Prevent the rounding error from accumulating by distributing it across multiple periods. */
remainder_ticks += signal_current->remainder;
if(remainder_ticks >= DIGITAL_SIGNAL_T_TIM_DIV2) {
remainder_ticks -= DIGITAL_SIGNAL_T_TIM;
reload_value_carry += 1;
}
signal_current = signal_next;
};
digital_sequence_finish(sequence);
digital_sequence_timer_buffer_reset(sequence);
FURI_CRITICAL_EXIT();
sequence->state = DigitalSequenceStateIdle;
}
void digital_sequence_clear(DigitalSequence* sequence) {
furi_assert(sequence);
sequence->size = 0;
}

View File

@@ -0,0 +1,112 @@
/**
* @file digital_sequence.h
* @brief Fast and precise digital signal generation library.
*
* Each sequence is represented by one or more (up to 32) registered signals, which are addressed
* by their indices, and a list of signal indices to be transmitted.
*
* The registered signals must be set up prior to actually defining the sequence.
*
* Example: A sequence containing 4 registered signals and n indices to transmit.
*
* |Signal | Index |
* |:-----:|:-----:|
* | SOF | 0 |
* | EOF | 1 |
* | Zero | 2 |
* | One | 3 |
*
* ```
* Signal index | 0 | 3 | 2 | 2 | ... | 3 | 1 |
* 0 1 2 3 ... n - 2 n - 1
* ```
*
* The above sequence starts by transmitting the signal with index 0, which is SOF in this case,
* then it proceeds with indices 3, 2, 2, which are One, Zero, Zero and after n - 2 signals,
* it will conclude with indices 3 and 1 which are One and EOF respectively.
*
* This way, only the order in which the signals are sent is stored, while the signals themselves
* are not duplicated.
*/
#pragma once
#include <furi_hal_gpio.h>
#include <digital_signal/digital_signal.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct DigitalSequence DigitalSequence;
/**
* @brief Allocate a DigitalSequence instance of a given size which will operate on a set GPIO pin.
*
* @param[in] size maximum number of signal indices contained in the instance.
* @param[in] gpio the GPIO pin used to generate the signal.
* @returns pointer to the allocated DigitalSequence instance.
*/
DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio);
/**
* @brief Delete a previously allocated DigitalSequence instance.
*
* @param[in,out] sequence pointer to the instance to be deleted.
*/
void digital_sequence_free(DigitalSequence* sequence);
/**
* @brief Register a signal within a DigitalSequence instance by its index.
*
* This function must be called for each signal to be used in the sequence. The DigitalSequence
* instance does not own the signals, therefore, their lifetime must be no less than the instance's.
*
* The user is responsible for creation and deletion of DigitalSignal instances and
* also for keeping track of their respective indices.
*
* @param[in,out] sequence pointer to the instance to be modified.
* @param[in] signal_index index to register the signal under (must be less than 32).
* @param[in] signal pointer to the DigitalSignal instance to be registered.
*/
void digital_sequence_register_signal(
DigitalSequence* sequence,
uint8_t signal_index,
const DigitalSignal* signal);
/**
* @brief Append a signal index to a DigitalSequence instance.
*
* The signal under the index must be registered beforehand by calling digital_sequence_set_signal().
*
* @param[in,out] sequence pointer to the instance to be modified.
* @param[in] signal_index signal index to be appended to the sequence (must be less than 32).
*/
void digital_sequence_add_signal(DigitalSequence* sequence, uint8_t signal_index);
/**
* @brief Transmit the sequence contained in the DigitalSequence instance.
*
* Must contain at least one registered signal and one signal index.
*
* NOTE: The current implementation will properly initialise the GPIO provided during construction,
* but it is the caller's responsibility to reconfigure it back before reusing for other purposes.
* This is due to performance reasons.
*
* @param[in] sequence pointer to the sequence to be transmitted.
*/
void digital_sequence_transmit(DigitalSequence* sequence);
/**
* @brief Clear the signal sequence in a DigitalSequence instance.
*
* Calling this function does not un-register the registered signals, so it is
* safe to call digital_sequence_add_signal() right afterwards.
*
* @param[in,out] sequence pointer to the instance to be cleared.
*/
void digital_sequence_clear(DigitalSequence* sequence);
#ifdef __cplusplus
}
#endif

View File

@@ -1,106 +1,15 @@
#include "digital_signal.h"
#include "digital_signal_i.h"
#include <furi.h>
#include <furi_hal.h>
#include <furi_hal_resources.h>
#include <math.h>
#include <stm32wbxx_ll_dma.h>
#include <stm32wbxx_ll_tim.h>
/* must be on bank B */
// For debugging purposes use `--extra-define=DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN=gpio_ext_pb3` fbt option
struct ReloadBuffer {
uint32_t* buffer; /* DMA ringbuffer */
uint32_t size; /* maximum entry count of the ring buffer */
uint32_t write_pos; /* current buffer write index */
uint32_t read_pos; /* current buffer read index */
bool dma_active;
};
struct DigitalSequence {
uint8_t signals_size;
bool bake;
uint32_t sequence_used;
uint32_t sequence_size;
DigitalSignal** signals;
uint8_t* sequence;
const GpioPin* gpio;
uint32_t send_time;
bool send_time_active;
LL_DMA_InitTypeDef dma_config_gpio;
LL_DMA_InitTypeDef dma_config_timer;
uint32_t* gpio_buff;
struct ReloadBuffer* dma_buffer;
};
struct DigitalSignalInternals {
uint64_t factor;
uint32_t reload_reg_entries;
uint32_t reload_reg_remainder;
uint32_t gpio_buff[2];
const GpioPin* gpio;
LL_DMA_InitTypeDef dma_config_gpio;
LL_DMA_InitTypeDef dma_config_timer;
};
#define TAG "DigitalSignal"
#define F_TIM (64000000.0)
#define T_TIM 1562 /* 15.625 ns *100 */
#define T_TIM_DIV2 781 /* 15.625 ns / 2 *100 */
/* end marker in DMA ringbuffer, will get written into timer register at the end */
#define SEQ_TIMER_MAX 0xFFFFFFFF
/* time to wait in loops before returning */
#define SEQ_LOCK_WAIT_MS 10UL
#define SEQ_LOCK_WAIT_TICKS (SEQ_LOCK_WAIT_MS * 1000 * 64)
/* maximum entry count of the sequence dma ring buffer */
#define RINGBUFFER_SIZE 128
/* maximum number of DigitalSignals in a sequence */
#define SEQUENCE_SIGNALS_SIZE 32
/*
* if sequence size runs out from the initial value passed to digital_sequence_alloc
* the size will be increased by this amount and reallocated
*/
#define SEQUENCE_SIZE_REALLOCATE_INCREMENT 256
DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt) {
DigitalSignal* digital_signal_alloc(uint32_t max_size) {
DigitalSignal* signal = malloc(sizeof(DigitalSignal));
signal->start_level = true;
signal->edges_max_cnt = max_edges_cnt;
signal->edge_timings = malloc(signal->edges_max_cnt * sizeof(uint32_t));
signal->edge_cnt = 0;
signal->reload_reg_buff = malloc(signal->edges_max_cnt * sizeof(uint32_t));
signal->internals = malloc(sizeof(DigitalSignalInternals));
DigitalSignalInternals* internals = signal->internals;
internals->factor = 1024 * 1024;
internals->dma_config_gpio.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
internals->dma_config_gpio.Mode = LL_DMA_MODE_CIRCULAR;
internals->dma_config_gpio.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
internals->dma_config_gpio.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
internals->dma_config_gpio.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD;
internals->dma_config_gpio.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD;
internals->dma_config_gpio.NbData = 2;
internals->dma_config_gpio.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP;
internals->dma_config_gpio.Priority = LL_DMA_PRIORITY_VERYHIGH;
internals->dma_config_timer.PeriphOrM2MSrcAddress = (uint32_t) & (TIM2->ARR);
internals->dma_config_timer.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
internals->dma_config_timer.Mode = LL_DMA_MODE_NORMAL;
internals->dma_config_timer.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
internals->dma_config_timer.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
internals->dma_config_timer.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD;
internals->dma_config_timer.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD;
internals->dma_config_timer.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP;
internals->dma_config_timer.Priority = LL_DMA_PRIORITY_HIGH;
signal->max_size = max_size;
signal->data = malloc(max_size * sizeof(uint32_t));
return signal;
}
@@ -108,559 +17,81 @@ DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt) {
void digital_signal_free(DigitalSignal* signal) {
furi_assert(signal);
free(signal->edge_timings);
free(signal->reload_reg_buff);
free(signal->internals);
free(signal->data);
free(signal);
}
bool digital_signal_append(DigitalSignal* signal_a, DigitalSignal* signal_b) {
furi_assert(signal_a);
furi_assert(signal_b);
if(signal_a->edges_max_cnt < signal_a->edge_cnt + signal_b->edge_cnt) {
return false;
}
/* in case there are no edges in our target signal, the signal to append makes the rules */
if(!signal_a->edge_cnt) {
signal_a->start_level = signal_b->start_level;
}
bool end_level = signal_a->start_level;
if(signal_a->edge_cnt) {
end_level = signal_a->start_level ^ !(signal_a->edge_cnt % 2);
}
uint8_t start_copy = 0;
if(end_level == signal_b->start_level) {
if(signal_a->edge_cnt) {
signal_a->edge_timings[signal_a->edge_cnt - 1] += signal_b->edge_timings[0];
start_copy += 1;
} else {
signal_a->edge_timings[signal_a->edge_cnt] += signal_b->edge_timings[0];
}
}
for(size_t i = 0; i < signal_b->edge_cnt - start_copy; i++) {
signal_a->edge_timings[signal_a->edge_cnt + i] = signal_b->edge_timings[start_copy + i];
}
signal_a->edge_cnt += signal_b->edge_cnt - start_copy;
return true;
}
bool digital_signal_get_start_level(DigitalSignal* signal) {
bool digital_signal_get_start_level(const DigitalSignal* signal) {
furi_assert(signal);
return signal->start_level;
}
uint32_t digital_signal_get_edges_cnt(DigitalSignal* signal) {
void digital_signal_set_start_level(DigitalSignal* signal, bool level) {
furi_assert(signal);
return signal->edge_cnt;
signal->start_level = level;
}
void digital_signal_add(DigitalSignal* signal, uint32_t ticks) {
uint32_t digital_signal_get_size(const DigitalSignal* signal) {
furi_assert(signal);
furi_assert(signal->edge_cnt < signal->edges_max_cnt);
signal->edge_timings[signal->edge_cnt++] = ticks;
return signal->size;
}
void digital_signal_add_pulse(DigitalSignal* signal, uint32_t ticks, bool level) {
void digital_signal_add_period(DigitalSignal* signal, uint32_t ticks) {
furi_assert(signal);
furi_assert(signal->edge_cnt < signal->edges_max_cnt);
furi_assert(signal->size < signal->max_size);
/* virgin signal? add it as the only level */
if(signal->edge_cnt == 0) {
const uint32_t duration = ticks + signal->remainder;
uint32_t reload_value = duration / DIGITAL_SIGNAL_T_TIM;
int32_t remainder = duration - reload_value * DIGITAL_SIGNAL_T_TIM;
if(remainder >= DIGITAL_SIGNAL_T_TIM_DIV2) {
reload_value += 1;
remainder -= DIGITAL_SIGNAL_T_TIM;
}
furi_check(reload_value > 1);
signal->data[signal->size++] = reload_value - 1;
signal->remainder = remainder;
}
static void digital_signal_extend_last_period(DigitalSignal* signal, uint32_t ticks) {
furi_assert(signal->size <= signal->max_size);
const uint32_t reload_value_old = signal->data[signal->size - 1] + 1;
const uint32_t duration = ticks + signal->remainder + reload_value_old * DIGITAL_SIGNAL_T_TIM;
uint32_t reload_value = duration / DIGITAL_SIGNAL_T_TIM;
int32_t remainder = duration - reload_value * DIGITAL_SIGNAL_T_TIM;
if(remainder >= DIGITAL_SIGNAL_T_TIM_DIV2) {
reload_value += 1;
remainder -= DIGITAL_SIGNAL_T_TIM;
}
furi_check(reload_value > 1);
signal->data[signal->size - 1] = reload_value - 1;
signal->remainder = remainder;
}
void digital_signal_add_period_with_level(DigitalSignal* signal, uint32_t ticks, bool level) {
furi_assert(signal);
if(signal->size == 0) {
signal->start_level = level;
signal->edge_timings[signal->edge_cnt++] = ticks;
digital_signal_add_period(signal, ticks);
} else {
bool end_level = signal->start_level ^ !(signal->edge_cnt % 2);
const bool end_level = signal->start_level ^ !(signal->size % 2);
if(level != end_level) {
signal->edge_timings[signal->edge_cnt++] = ticks;
digital_signal_add_period(signal, ticks);
} else {
signal->edge_timings[signal->edge_cnt - 1] += ticks;
}
}
}
uint32_t digital_signal_get_edge(DigitalSignal* signal, uint32_t edge_num) {
furi_assert(signal);
furi_assert(edge_num < signal->edge_cnt);
return signal->edge_timings[edge_num];
}
void digital_signal_prepare_arr(DigitalSignal* signal) {
furi_assert(signal);
DigitalSignalInternals* internals = signal->internals;
/* set up signal polarities */
if(internals->gpio) {
uint32_t bit_set = internals->gpio->pin;
uint32_t bit_reset = internals->gpio->pin << 16;
#ifdef DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN
bit_set |= DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN.pin;
bit_reset |= DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN.pin << 16;
#endif
if(signal->start_level) {
internals->gpio_buff[0] = bit_set;
internals->gpio_buff[1] = bit_reset;
} else {
internals->gpio_buff[0] = bit_reset;
internals->gpio_buff[1] = bit_set;
}
}
/* set up edge timings */
internals->reload_reg_entries = 0;
for(size_t pos = 0; pos < signal->edge_cnt; pos++) {
uint32_t pulse_duration = signal->edge_timings[pos] + internals->reload_reg_remainder;
if(pulse_duration < 10 || pulse_duration > 10000000) {
FURI_LOG_D(
TAG,
"[prepare] pulse_duration out of range: %lu = %lu * %llu",
pulse_duration,
signal->edge_timings[pos],
internals->factor);
pulse_duration = 100;
}
uint32_t pulse_ticks = (pulse_duration + T_TIM_DIV2) / T_TIM;
internals->reload_reg_remainder = pulse_duration - (pulse_ticks * T_TIM);
if(pulse_ticks > 1) {
signal->reload_reg_buff[internals->reload_reg_entries++] = pulse_ticks - 1;
}
}
}
static void digital_signal_stop_dma() {
LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1);
LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2);
LL_DMA_ClearFlag_TC1(DMA1);
LL_DMA_ClearFlag_TC2(DMA1);
}
static void digital_signal_stop_timer() {
LL_TIM_DisableCounter(TIM2);
LL_TIM_DisableUpdateEvent(TIM2);
LL_TIM_DisableDMAReq_UPDATE(TIM2);
furi_hal_bus_disable(FuriHalBusTIM2);
}
static void digital_signal_setup_timer() {
furi_hal_bus_enable(FuriHalBusTIM2);
LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP);
LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1);
LL_TIM_SetPrescaler(TIM2, 0);
LL_TIM_SetAutoReload(TIM2, SEQ_TIMER_MAX);
LL_TIM_SetCounter(TIM2, 0);
}
static void digital_signal_start_timer() {
LL_TIM_EnableCounter(TIM2);
LL_TIM_EnableUpdateEvent(TIM2);
LL_TIM_EnableDMAReq_UPDATE(TIM2);
LL_TIM_GenerateEvent_UPDATE(TIM2);
}
static bool digital_signal_setup_dma(DigitalSignal* signal) {
furi_assert(signal);
DigitalSignalInternals* internals = signal->internals;
if(!signal->internals->reload_reg_entries) {
return false;
}
digital_signal_stop_dma();
internals->dma_config_gpio.MemoryOrM2MDstAddress = (uint32_t)internals->gpio_buff;
internals->dma_config_gpio.PeriphOrM2MSrcAddress = (uint32_t) & (internals->gpio->port->BSRR);
internals->dma_config_timer.MemoryOrM2MDstAddress = (uint32_t)signal->reload_reg_buff;
internals->dma_config_timer.NbData = signal->internals->reload_reg_entries;
/* set up DMA channel 1 and 2 for GPIO and timer copy operations */
LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &internals->dma_config_gpio);
LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &internals->dma_config_timer);
/* enable both DMA channels */
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2);
return true;
}
void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) {
furi_assert(signal);
if(!signal->edge_cnt) {
return;
}
/* Configure gpio as output */
signal->internals->gpio = gpio;
furi_hal_gpio_init(
signal->internals->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
digital_signal_prepare_arr(signal);
digital_signal_setup_dma(signal);
digital_signal_setup_timer();
digital_signal_start_timer();
while(!LL_DMA_IsActiveFlag_TC2(DMA1)) {
}
digital_signal_stop_timer();
digital_signal_stop_dma();
}
static void digital_sequence_alloc_signals(DigitalSequence* sequence, uint32_t size) {
sequence->signals_size = size;
sequence->signals = malloc(sequence->signals_size * sizeof(DigitalSignal*));
}
static void digital_sequence_alloc_sequence(DigitalSequence* sequence, uint32_t size) {
sequence->sequence_used = 0;
sequence->sequence_size = size;
sequence->sequence = malloc(sequence->sequence_size);
sequence->send_time = 0;
sequence->send_time_active = false;
}
DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio) {
furi_assert(gpio);
DigitalSequence* sequence = malloc(sizeof(DigitalSequence));
sequence->gpio = gpio;
sequence->bake = false;
sequence->dma_buffer = malloc(sizeof(struct ReloadBuffer));
sequence->dma_buffer->size = RINGBUFFER_SIZE;
sequence->dma_buffer->buffer = malloc(sequence->dma_buffer->size * sizeof(uint32_t));
sequence->dma_config_gpio.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
sequence->dma_config_gpio.Mode = LL_DMA_MODE_CIRCULAR;
sequence->dma_config_gpio.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
sequence->dma_config_gpio.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
sequence->dma_config_gpio.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD;
sequence->dma_config_gpio.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD;
sequence->dma_config_gpio.NbData = 2;
sequence->dma_config_gpio.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP;
sequence->dma_config_gpio.Priority = LL_DMA_PRIORITY_VERYHIGH;
sequence->dma_config_timer.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
sequence->dma_config_timer.Mode = LL_DMA_MODE_CIRCULAR;
sequence->dma_config_timer.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
sequence->dma_config_timer.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
sequence->dma_config_timer.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD;
sequence->dma_config_timer.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD;
sequence->dma_config_timer.PeriphOrM2MSrcAddress = (uint32_t) & (TIM2->ARR);
sequence->dma_config_timer.MemoryOrM2MDstAddress = (uint32_t)sequence->dma_buffer->buffer;
sequence->dma_config_timer.NbData = sequence->dma_buffer->size;
sequence->dma_config_timer.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP;
sequence->dma_config_timer.Priority = LL_DMA_PRIORITY_HIGH;
digital_sequence_alloc_signals(sequence, SEQUENCE_SIGNALS_SIZE);
digital_sequence_alloc_sequence(sequence, size);
return sequence;
}
void digital_sequence_free(DigitalSequence* sequence) {
furi_assert(sequence);
free(sequence->signals);
free(sequence->sequence);
free(sequence->dma_buffer->buffer);
free(sequence->dma_buffer);
free(sequence);
}
void digital_sequence_set_signal(
DigitalSequence* sequence,
uint8_t signal_index,
DigitalSignal* signal) {
furi_assert(sequence);
furi_assert(signal);
furi_assert(signal_index < sequence->signals_size);
sequence->signals[signal_index] = signal;
signal->internals->gpio = sequence->gpio;
signal->internals->reload_reg_remainder = 0;
digital_signal_prepare_arr(signal);
}
void digital_sequence_set_sendtime(DigitalSequence* sequence, uint32_t send_time) {
furi_assert(sequence);
sequence->send_time = send_time;
sequence->send_time_active = true;
}
void digital_sequence_add(DigitalSequence* sequence, uint8_t signal_index) {
furi_assert(sequence);
furi_assert(signal_index < sequence->signals_size);
if(sequence->sequence_used >= sequence->sequence_size) {
sequence->sequence_size += SEQUENCE_SIZE_REALLOCATE_INCREMENT;
sequence->sequence = realloc(sequence->sequence, sequence->sequence_size); //-V701
furi_assert(sequence->sequence);
}
sequence->sequence[sequence->sequence_used++] = signal_index;
}
static bool digital_sequence_setup_dma(DigitalSequence* sequence) {
furi_assert(sequence);
digital_signal_stop_dma();
sequence->dma_config_gpio.MemoryOrM2MDstAddress = (uint32_t)sequence->gpio_buff;
sequence->dma_config_gpio.PeriphOrM2MSrcAddress = (uint32_t) & (sequence->gpio->port->BSRR);
/* set up DMA channel 1 and 2 for GPIO and timer copy operations */
LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &sequence->dma_config_gpio);
LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &sequence->dma_config_timer);
/* enable both DMA channels */
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2);
return true;
}
static DigitalSignal* digital_sequence_bake(DigitalSequence* sequence) {
furi_assert(sequence);
uint32_t edges = 0;
for(uint32_t pos = 0; pos < sequence->sequence_used; pos++) {
uint8_t signal_index = sequence->sequence[pos];
DigitalSignal* sig = sequence->signals[signal_index];
edges += sig->edge_cnt;
}
DigitalSignal* ret = digital_signal_alloc(edges);
for(uint32_t pos = 0; pos < sequence->sequence_used; pos++) {
uint8_t signal_index = sequence->sequence[pos];
DigitalSignal* sig = sequence->signals[signal_index];
digital_signal_append(ret, sig);
}
return ret;
}
static void digital_sequence_finish(DigitalSequence* sequence) {
struct ReloadBuffer* dma_buffer = sequence->dma_buffer;
if(dma_buffer->dma_active) {
uint32_t prev_timer = DWT->CYCCNT;
do {
/* we are finished, when the DMA transferred the SEQ_TIMER_MAX marker */
if(TIM2->ARR == SEQ_TIMER_MAX) {
break;
}
if(DWT->CYCCNT - prev_timer > SEQ_LOCK_WAIT_TICKS) {
dma_buffer->read_pos =
RINGBUFFER_SIZE - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2);
FURI_LOG_D(
TAG,
"[SEQ] hung %lu ms in finish (ARR 0x%08lx, read %lu, write %lu)",
SEQ_LOCK_WAIT_MS,
TIM2->ARR,
dma_buffer->read_pos,
dma_buffer->write_pos);
break;
}
} while(1);
}
digital_signal_stop_timer();
digital_signal_stop_dma();
}
static void digital_sequence_queue_pulse(DigitalSequence* sequence, uint32_t length) {
struct ReloadBuffer* dma_buffer = sequence->dma_buffer;
if(dma_buffer->dma_active) {
uint32_t prev_timer = DWT->CYCCNT;
do {
dma_buffer->read_pos = RINGBUFFER_SIZE - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2);
uint32_t free =
(RINGBUFFER_SIZE + dma_buffer->read_pos - dma_buffer->write_pos) % RINGBUFFER_SIZE;
if(free > 2) {
break;
}
if(DWT->CYCCNT - prev_timer > SEQ_LOCK_WAIT_TICKS) {
FURI_LOG_D(
TAG,
"[SEQ] hung %lu ms in queue (ARR 0x%08lx, read %lu, write %lu)",
SEQ_LOCK_WAIT_MS,
TIM2->ARR,
dma_buffer->read_pos,
dma_buffer->write_pos);
break;
}
if(TIM2->ARR == SEQ_TIMER_MAX) {
FURI_LOG_D(
TAG,
"[SEQ] buffer underrun in queue (ARR 0x%08lx, read %lu, write %lu)",
TIM2->ARR,
dma_buffer->read_pos,
dma_buffer->write_pos);
break;
}
} while(1);
}
dma_buffer->buffer[dma_buffer->write_pos] = length;
dma_buffer->write_pos++;
dma_buffer->write_pos %= RINGBUFFER_SIZE;
dma_buffer->buffer[dma_buffer->write_pos] = SEQ_TIMER_MAX;
}
bool digital_sequence_send(DigitalSequence* sequence) {
furi_assert(sequence);
struct ReloadBuffer* dma_buffer = sequence->dma_buffer;
furi_hal_gpio_init(sequence->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
#ifdef DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN
furi_hal_gpio_init(
&DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
#endif
if(sequence->bake) {
DigitalSignal* sig = digital_sequence_bake(sequence);
digital_signal_send(sig, sequence->gpio);
digital_signal_free(sig);
return true;
}
if(!sequence->sequence_used) {
return false;
}
int32_t remainder = 0;
uint32_t trade_for_next = 0;
uint32_t seq_pos_next = 1;
dma_buffer->dma_active = false;
dma_buffer->buffer[0] = SEQ_TIMER_MAX;
dma_buffer->read_pos = 0;
dma_buffer->write_pos = 0;
/* already prepare the current signal pointer */
DigitalSignal* sig = sequence->signals[sequence->sequence[0]];
DigitalSignal* sig_next = NULL;
/* re-use the GPIO buffer from the first signal */
sequence->gpio_buff = sig->internals->gpio_buff;
FURI_CRITICAL_ENTER();
while(sig) {
bool last_signal = (seq_pos_next >= sequence->sequence_used);
if(!last_signal) {
sig_next = sequence->signals[sequence->sequence[seq_pos_next++]];
}
for(uint32_t pulse_pos = 0; pulse_pos < sig->internals->reload_reg_entries; pulse_pos++) {
bool last_pulse = ((pulse_pos + 1) >= sig->internals->reload_reg_entries);
uint32_t pulse_length = sig->reload_reg_buff[pulse_pos] + trade_for_next;
trade_for_next = 0;
/* when we are too late more than half a tick, make the first edge temporarily longer */
if(remainder >= T_TIM_DIV2) {
remainder -= T_TIM;
pulse_length += 1;
}
/* last pulse in current signal and have a next signal? */
if(last_pulse && sig_next) {
/* when a signal ends with the same level as the next signal begins, let the next signal generate the whole pulse.
beware, we do not want the level after the last edge, but the last level before that edge */
bool end_level = sig->start_level ^ ((sig->edge_cnt % 2) == 0);
/* if they have the same level, pass the duration to the next pulse(s) */
if(end_level == sig_next->start_level) {
trade_for_next = pulse_length;
}
}
/* if it was decided, that the next signal's first pulse shall also handle our "length", then do not queue here */
if(!trade_for_next) {
digital_sequence_queue_pulse(sequence, pulse_length);
if(!dma_buffer->dma_active) {
/* start transmission when buffer was filled enough */
bool start_send = sequence->dma_buffer->write_pos >= (RINGBUFFER_SIZE - 2);
/* or it was the last pulse */
if(last_pulse && last_signal) {
start_send = true;
}
/* start transmission */
if(start_send) {
digital_sequence_setup_dma(sequence);
digital_signal_setup_timer();
/* if the send time is specified, wait till the core timer passed beyond that time */
if(sequence->send_time_active) {
sequence->send_time_active = false;
while(sequence->send_time - DWT->CYCCNT < 0x80000000) {
}
}
digital_signal_start_timer();
dma_buffer->dma_active = true;
}
}
}
}
remainder += sig->internals->reload_reg_remainder;
sig = sig_next;
sig_next = NULL;
}
/* wait until last dma transaction was finished */
FURI_CRITICAL_EXIT();
digital_sequence_finish(sequence);
return true;
}
void digital_sequence_clear(DigitalSequence* sequence) {
furi_assert(sequence);
sequence->sequence_used = 0;
}
void digital_sequence_timebase_correction(DigitalSequence* sequence, float factor) {
for(uint32_t sig_pos = 0; sig_pos < sequence->signals_size; sig_pos++) {
DigitalSignal* signal = sequence->signals[sig_pos];
if(signal) {
signal->internals->factor = (uint32_t)(1024 * 1024 * factor);
digital_signal_prepare_arr(signal);
digital_signal_extend_last_period(signal, ticks);
}
}
}

View File

@@ -1,74 +1,121 @@
/**
* @file digital_signal.h
* @brief Simple digital signal container for the DigitalSequence library.
*
* Each signal is represented by its start level (high or low) and one or more periods.
* The output will transition to its inverse value on each period boundary.
*
* Example: A signal with n periods and HIGH start level.
*
* ```
* ----+ +------+ +- ... -+
* t0 | t1 | t2 | t3 | | tn - 1
* +--------+ +----+ +--------
* ```
*
*/
#pragma once
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <furi_hal_gpio.h>
#ifdef __cplusplus
extern "C" {
#endif
/* helper for easier signal generation */
// DigitalSignal uses 10 picosecond time units (1 tick = 10 ps).
// Use the macros below to convert the time from other units.
#define DIGITAL_SIGNAL_MS(x) ((x)*100000000UL)
#define DIGITAL_SIGNAL_US(x) ((x)*100000UL)
#define DIGITAL_SIGNAL_NS(x) ((x)*100UL)
#define DIGITAL_SIGNAL_PS(x) ((x) / 10UL)
/* using an anonymous type for the internals */
typedef struct DigitalSignalInternals DigitalSignalInternals;
typedef struct DigitalSignal DigitalSignal;
/* and a public one for accessing user-side fields */
typedef struct DigitalSignal {
bool start_level;
uint32_t edge_cnt;
uint32_t edges_max_cnt;
uint32_t* edge_timings;
uint32_t* reload_reg_buff; /* internal, but used by unit tests */
DigitalSignalInternals* internals;
} DigitalSignal;
typedef struct DigitalSequence DigitalSequence;
DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt);
/**
* @brief Allocate a DigitalSignal instance with a defined maximum size.
*
* @param[in] max_size the maximum number of periods the instance will be able to contain.
* @returns pointer to the allocated instance.
*/
DigitalSignal* digital_signal_alloc(uint32_t max_size);
/**
* @brief Delete a previously allocated DigitalSignal instance.
*
* @param[in,out] signal pointer to the instance to be deleted.
*/
void digital_signal_free(DigitalSignal* signal);
void digital_signal_add(DigitalSignal* signal, uint32_t ticks);
/**
* @brief Append one period to the end of the DigitalSignal instance.
*
* @param[in,out] signal pointer to a the instance to append to.
* @param[in] ticks the period length, in 10 picosecond units.
*/
void digital_signal_add_period(DigitalSignal* signal, uint32_t ticks);
void digital_signal_add_pulse(DigitalSignal* signal, uint32_t ticks, bool level);
/**
* @brief Append one period to the end of the DigitalSignal instance, with the level specified.
*
* If the level is the same as the last level contained in the instance, then it is extened
* by the given ticks value. Otherwise, the behaviour is identical to digital_signal_add_period().
*
* Example 1: add tc with HIGH level
* ```
* before:
* ... ------+
* ta | tb
* +-------
* after:
* ... ------+ +-------
* ta | tb | tc
* +------+
* ```
* Example 2: add tc with LOW level
* ```
* before:
* ... ------+
* ta | tb
* +-------
* after:
* ... ------+
* ta | tb + tc
* +--------------
* ```
*
* @param[in,out] signal pointer to the instance to append to.
* @param[in] ticks the period length, in 10 picosecond units.
* @param[in] level the level to be set during the period.
*/
void digital_signal_add_period_with_level(DigitalSignal* signal, uint32_t ticks, bool level);
bool digital_signal_append(DigitalSignal* signal_a, DigitalSignal* signal_b);
/**
* @brief Get the current start level contained in the DigitalSignal instance.
*
* If not explicitly set with digital_signal_set_start_level(), it defaults to false.
*
* @param[in] signal pointer to the instance to be queried.
* @returns the start level value.
*/
bool digital_signal_get_start_level(const DigitalSignal* signal);
void digital_signal_prepare_arr(DigitalSignal* signal);
/**
* @brief Set the start level contained in the DigitalSignal instance.
*
* @param[in,out] signal pointer to the instance to be modified.
* @param[in] level signal level to be set as the start level.
*/
void digital_signal_set_start_level(DigitalSignal* signal, bool level);
bool digital_signal_get_start_level(DigitalSignal* signal);
uint32_t digital_signal_get_edges_cnt(DigitalSignal* signal);
uint32_t digital_signal_get_edge(DigitalSignal* signal, uint32_t edge_num);
void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio);
DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio);
void digital_sequence_free(DigitalSequence* sequence);
void digital_sequence_set_signal(
DigitalSequence* sequence,
uint8_t signal_index,
DigitalSignal* signal);
void digital_sequence_set_sendtime(DigitalSequence* sequence, uint32_t send_time);
void digital_sequence_add(DigitalSequence* sequence, uint8_t signal_index);
bool digital_sequence_send(DigitalSequence* sequence);
void digital_sequence_clear(DigitalSequence* sequence);
void digital_sequence_timebase_correction(DigitalSequence* sequence, float factor);
/**
* @brief Get the number of periods currently stored in a DigitalSignal instance.
*
* @param[in] signal pointer to the instance to be queried.
* @return the number of periods stored in the instance.
*/
uint32_t digital_signal_get_size(const DigitalSignal* signal);
#ifdef __cplusplus
}

View File

@@ -0,0 +1,23 @@
/**
* @file digital_signal_i.h
* @brief DigitalSignal private definitions.
*
* This file is an implementation detail. It must not be included in
* any public API-related headers.
*/
#include <stdint.h>
#include <stdbool.h>
#define DIGITAL_SIGNAL_T_TIM 1562 /**< 15.625 ns *100 */
#define DIGITAL_SIGNAL_T_TIM_DIV2 (DIGITAL_SIGNAL_T_TIM / 2) /**< 15.625 ns / 2 *100 */
/**
* @brief DigitalSignal structure type.
*/
struct DigitalSignal {
bool start_level; /**< The level to begin the signal with. */
uint32_t size; /**< Current period count contained in the instance. */
uint32_t max_size; /**< Maximum period count this instance can hold. */
uint32_t* data; /**< Pointer to the array of time periods. */
int32_t remainder; /**< Remainder left after converting all periods into timer ticks. */
};

View File

@@ -0,0 +1,139 @@
#include "iso14443_3a_signal.h"
#include <digital_signal/digital_sequence.h>
#define BITS_IN_BYTE (8)
#define ISO14443_3A_SIGNAL_BIT_MAX_EDGES (10)
#define ISO14443_3A_SIGNAL_MAX_EDGES (1350)
#define ISO14443_3A_SIGNAL_SEQUENCE_SIZE \
(ISO14443_3A_SIGNAL_MAX_EDGES / (ISO14443_3A_SIGNAL_BIT_MAX_EDGES - 2))
#define ISO14443_3A_SIGNAL_F_SIG (13560000.0)
#define ISO14443_3A_SIGNAL_T_SIG 7374 //73.746ns*100
#define ISO14443_3A_SIGNAL_T_SIG_X8 58992 //T_SIG*8
#define ISO14443_3A_SIGNAL_T_SIG_X8_X8 471936 //T_SIG*8*8
#define ISO14443_3A_SIGNAL_T_SIG_X8_X9 530928 //T_SIG*8*9
typedef enum {
Iso14443_3aSignalIndexZero,
Iso14443_3aSignalIndexOne,
Iso14443_3aSignalIndexCount,
} Iso14443_3aSignalIndex;
typedef DigitalSignal* Iso14443_3aSignalBank[Iso14443_3aSignalIndexCount];
struct Iso14443_3aSignal {
DigitalSequence* tx_sequence;
Iso14443_3aSignalBank signals;
};
static void iso14443_3a_signal_add_byte(Iso14443_3aSignal* instance, uint8_t byte, bool parity) {
for(size_t i = 0; i < BITS_IN_BYTE; i++) {
digital_sequence_add_signal(
instance->tx_sequence,
FURI_BIT(byte, i) ? Iso14443_3aSignalIndexOne : Iso14443_3aSignalIndexZero);
}
digital_sequence_add_signal(
instance->tx_sequence, parity ? Iso14443_3aSignalIndexOne : Iso14443_3aSignalIndexZero);
}
static void iso14443_3a_signal_encode(
Iso14443_3aSignal* instance,
const uint8_t* tx_data,
const uint8_t* tx_parity,
size_t tx_bits) {
furi_assert(instance);
furi_assert(tx_data);
furi_assert(tx_parity);
// Start of frame
digital_sequence_add_signal(instance->tx_sequence, Iso14443_3aSignalIndexOne);
if(tx_bits < BITS_IN_BYTE) {
for(size_t i = 0; i < tx_bits; i++) {
digital_sequence_add_signal(
instance->tx_sequence,
FURI_BIT(tx_data[0], i) ? Iso14443_3aSignalIndexOne : Iso14443_3aSignalIndexZero);
}
} else {
for(size_t i = 0; i < tx_bits / BITS_IN_BYTE; i++) {
bool parity = FURI_BIT(tx_parity[i / BITS_IN_BYTE], i % BITS_IN_BYTE);
iso14443_3a_signal_add_byte(instance, tx_data[i], parity);
}
}
}
static inline void iso14443_3a_signal_set_bit(DigitalSignal* signal, bool bit) {
digital_signal_set_start_level(signal, bit);
if(bit) {
for(uint32_t i = 0; i < 7; ++i) {
digital_signal_add_period(signal, ISO14443_3A_SIGNAL_T_SIG_X8);
}
digital_signal_add_period(signal, ISO14443_3A_SIGNAL_T_SIG_X8_X9);
} else {
digital_signal_add_period(signal, ISO14443_3A_SIGNAL_T_SIG_X8_X8);
for(uint32_t i = 0; i < 8; ++i) {
digital_signal_add_period(signal, ISO14443_3A_SIGNAL_T_SIG_X8);
}
}
}
static inline void iso14443_3a_signal_bank_fill(Iso14443_3aSignalBank bank) {
for(uint32_t i = 0; i < Iso14443_3aSignalIndexCount; ++i) {
bank[i] = digital_signal_alloc(ISO14443_3A_SIGNAL_BIT_MAX_EDGES);
iso14443_3a_signal_set_bit(bank[i], i % Iso14443_3aSignalIndexCount != 0);
}
}
static inline void iso14443_3a_signal_bank_clear(Iso14443_3aSignalBank bank) {
for(uint32_t i = 0; i < Iso14443_3aSignalIndexCount; ++i) {
digital_signal_free(bank[i]);
}
}
static inline void
iso14443_3a_signal_bank_register(Iso14443_3aSignalBank bank, DigitalSequence* sequence) {
for(uint32_t i = 0; i < Iso14443_3aSignalIndexCount; ++i) {
digital_sequence_register_signal(sequence, i, bank[i]);
}
}
Iso14443_3aSignal* iso14443_3a_signal_alloc(const GpioPin* pin) {
furi_assert(pin);
Iso14443_3aSignal* instance = malloc(sizeof(Iso14443_3aSignal));
instance->tx_sequence = digital_sequence_alloc(ISO14443_3A_SIGNAL_SEQUENCE_SIZE, pin);
iso14443_3a_signal_bank_fill(instance->signals);
iso14443_3a_signal_bank_register(instance->signals, instance->tx_sequence);
return instance;
}
void iso14443_3a_signal_free(Iso14443_3aSignal* instance) {
furi_assert(instance);
furi_assert(instance->tx_sequence);
iso14443_3a_signal_bank_clear(instance->signals);
digital_sequence_free(instance->tx_sequence);
free(instance);
}
void iso14443_3a_signal_tx(
Iso14443_3aSignal* instance,
const uint8_t* tx_data,
const uint8_t* tx_parity,
size_t tx_bits) {
furi_assert(instance);
furi_assert(tx_data);
furi_assert(tx_parity);
FURI_CRITICAL_ENTER();
digital_sequence_clear(instance->tx_sequence);
iso14443_3a_signal_encode(instance, tx_data, tx_parity, tx_bits);
digital_sequence_transmit(instance->tx_sequence);
FURI_CRITICAL_EXIT();
}

View File

@@ -0,0 +1,51 @@
/**
* @file iso14443_3a_signal.h
* @brief DigitalSequence preset for generating ISO14443-3A compliant signals.
*/
#pragma once
#include <furi_hal_resources.h>
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct Iso14443_3aSignal Iso14443_3aSignal;
/**
* @brief Allocate an Iso14443_3aSignal instance with a set GPIO pin.
*
* @param[in] pin GPIO pin to use during transmission.
* @returns pointer to the allocated instance.
*/
Iso14443_3aSignal* iso14443_3a_signal_alloc(const GpioPin* pin);
/**
* @brief Delete an Iso14443_3aSignal instance.
*
* @param[in,out] instance pointer to the instance to be deleted.
*/
void iso14443_3a_signal_free(Iso14443_3aSignal* instance);
/**
* @brief Transmit arbitrary bytes using an Iso14443_3aSignal instance.
*
* This function will block until the transmisson has been completed.
*
* @param[in] instance pointer to the instance used in transmission.
* @param[in] tx_data pointer to the data to be transmitted.
* @param[in] tx_parity pointer to the bit-packed parity array.
* @param[in] tx_bits size of the data to be transmitted in bits.
*/
void iso14443_3a_signal_tx(
Iso14443_3aSignal* instance,
const uint8_t* tx_data,
const uint8_t* tx_parity,
size_t tx_bits);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,204 @@
#include "iso15693_signal.h"
#include <digital_signal/digital_sequence.h>
#define BITS_IN_BYTE (8U)
#define ISO15693_SIGNAL_COEFF_HI (1U)
#define ISO15693_SIGNAL_COEFF_LO (4U)
#define ISO15693_SIGNAL_ZERO_EDGES (16U)
#define ISO15693_SIGNAL_ONE_EDGES (ISO15693_SIGNAL_ZERO_EDGES + 1U)
#define ISO15693_SIGNAL_EOF_EDGES (64U)
#define ISO15693_SIGNAL_SOF_EDGES (ISO15693_SIGNAL_EOF_EDGES + 1U)
#define ISO15693_SIGNAL_EDGES (1350U)
#define ISO15693_SIGNAL_FC (13.56e6)
#define ISO15693_SIGNAL_FC_16 (16.0e11 / ISO15693_SIGNAL_FC)
#define ISO15693_SIGNAL_FC_256 (256.0e11 / ISO15693_SIGNAL_FC)
#define ISO15693_SIGNAL_FC_768 (768.0e11 / ISO15693_SIGNAL_FC)
typedef enum {
Iso15693SignalIndexSof,
Iso15693SignalIndexEof,
Iso15693SignalIndexOne,
Iso15693SignalIndexZero,
Iso15693SignalIndexNum,
} Iso15693SignalIndex;
typedef DigitalSignal* Iso15693SignalBank[Iso15693SignalIndexNum];
struct Iso15693Signal {
DigitalSequence* tx_sequence;
Iso15693SignalBank banks[Iso15693SignalDataRateNum];
};
// Add an unmodulated signal for the length of Fc / 256 * k (where k = 1 or 4)
static void iso15693_add_silence(DigitalSignal* signal, Iso15693SignalDataRate data_rate) {
const uint32_t k = data_rate == Iso15693SignalDataRateHi ? ISO15693_SIGNAL_COEFF_HI :
ISO15693_SIGNAL_COEFF_LO;
digital_signal_add_period_with_level(signal, ISO15693_SIGNAL_FC_256 * k, false);
}
// Add 8 * k subcarrier pulses of Fc / 16 (where k = 1 or 4)
static void iso15693_add_subcarrier(DigitalSignal* signal, Iso15693SignalDataRate data_rate) {
const uint32_t k = data_rate == Iso15693SignalDataRateHi ? ISO15693_SIGNAL_COEFF_HI :
ISO15693_SIGNAL_COEFF_LO;
for(uint32_t i = 0; i < ISO15693_SIGNAL_ZERO_EDGES * k; ++i) {
digital_signal_add_period_with_level(signal, ISO15693_SIGNAL_FC_16, !(i % 2));
}
}
static void iso15693_add_bit(DigitalSignal* signal, Iso15693SignalDataRate data_rate, bool bit) {
if(bit) {
iso15693_add_silence(signal, data_rate);
iso15693_add_subcarrier(signal, data_rate);
} else {
iso15693_add_subcarrier(signal, data_rate);
iso15693_add_silence(signal, data_rate);
}
}
static inline void iso15693_add_sof(DigitalSignal* signal, Iso15693SignalDataRate data_rate) {
// Not adding silence since it only increases response time
for(uint32_t i = 0; i < ISO15693_SIGNAL_FC_768 / ISO15693_SIGNAL_FC_256; ++i) {
iso15693_add_subcarrier(signal, data_rate);
}
iso15693_add_bit(signal, data_rate, true);
}
static inline void iso15693_add_eof(DigitalSignal* signal, Iso15693SignalDataRate data_rate) {
iso15693_add_bit(signal, data_rate, false);
for(uint32_t i = 0; i < ISO15693_SIGNAL_FC_768 / ISO15693_SIGNAL_FC_256; ++i) {
iso15693_add_subcarrier(signal, data_rate);
}
// Not adding silence since it does nothing here
}
static inline uint32_t
iso15693_get_sequence_index(Iso15693SignalIndex index, Iso15693SignalDataRate data_rate) {
return index + data_rate * Iso15693SignalIndexNum;
}
static inline void
iso15693_add_byte(Iso15693Signal* instance, Iso15693SignalDataRate data_rate, uint8_t byte) {
for(size_t i = 0; i < BITS_IN_BYTE; i++) {
const uint8_t bit = byte & (1U << i);
digital_sequence_add_signal(
instance->tx_sequence,
iso15693_get_sequence_index(
bit ? Iso15693SignalIndexOne : Iso15693SignalIndexZero, data_rate));
}
}
static inline void iso15693_signal_encode(
Iso15693Signal* instance,
Iso15693SignalDataRate data_rate,
const uint8_t* tx_data,
size_t tx_data_size) {
digital_sequence_add_signal(
instance->tx_sequence, iso15693_get_sequence_index(Iso15693SignalIndexSof, data_rate));
for(size_t i = 0; i < tx_data_size; i++) {
iso15693_add_byte(instance, data_rate, tx_data[i]);
}
digital_sequence_add_signal(
instance->tx_sequence, iso15693_get_sequence_index(Iso15693SignalIndexEof, data_rate));
}
static void iso15693_signal_bank_fill(Iso15693Signal* instance, Iso15693SignalDataRate data_rate) {
const uint32_t k = data_rate == Iso15693SignalDataRateHi ? ISO15693_SIGNAL_COEFF_HI :
ISO15693_SIGNAL_COEFF_LO;
DigitalSignal** bank = instance->banks[data_rate];
bank[Iso15693SignalIndexSof] = digital_signal_alloc(ISO15693_SIGNAL_SOF_EDGES * k);
bank[Iso15693SignalIndexEof] = digital_signal_alloc(ISO15693_SIGNAL_EOF_EDGES * k);
bank[Iso15693SignalIndexOne] = digital_signal_alloc(ISO15693_SIGNAL_ONE_EDGES * k);
bank[Iso15693SignalIndexZero] = digital_signal_alloc(ISO15693_SIGNAL_ZERO_EDGES * k);
iso15693_add_sof(bank[Iso15693SignalIndexSof], data_rate);
iso15693_add_eof(bank[Iso15693SignalIndexEof], data_rate);
iso15693_add_bit(bank[Iso15693SignalIndexOne], data_rate, true);
iso15693_add_bit(bank[Iso15693SignalIndexZero], data_rate, false);
}
static void
iso15693_signal_bank_clear(Iso15693Signal* instance, Iso15693SignalDataRate data_rate) {
DigitalSignal** bank = instance->banks[data_rate];
for(uint32_t i = 0; i < Iso15693SignalIndexNum; ++i) {
digital_signal_free(bank[i]);
}
}
static void
iso15693_signal_bank_register(Iso15693Signal* instance, Iso15693SignalDataRate data_rate) {
for(uint32_t i = 0; i < Iso15693SignalIndexNum; ++i) {
digital_sequence_register_signal(
instance->tx_sequence,
iso15693_get_sequence_index(i, data_rate),
instance->banks[data_rate][i]);
}
}
Iso15693Signal* iso15693_signal_alloc(const GpioPin* pin) {
furi_assert(pin);
Iso15693Signal* instance = malloc(sizeof(Iso15693Signal));
instance->tx_sequence = digital_sequence_alloc(BITS_IN_BYTE * 255 + 2, pin);
for(uint32_t i = 0; i < Iso15693SignalDataRateNum; ++i) {
iso15693_signal_bank_fill(instance, i);
iso15693_signal_bank_register(instance, i);
}
return instance;
}
void iso15693_signal_free(Iso15693Signal* instance) {
furi_assert(instance);
digital_sequence_free(instance->tx_sequence);
for(uint32_t i = 0; i < Iso15693SignalDataRateNum; ++i) {
iso15693_signal_bank_clear(instance, i);
}
free(instance);
}
void iso15693_signal_tx(
Iso15693Signal* instance,
Iso15693SignalDataRate data_rate,
const uint8_t* tx_data,
size_t tx_data_size) {
furi_assert(instance);
furi_assert(data_rate < Iso15693SignalDataRateNum);
furi_assert(tx_data);
FURI_CRITICAL_ENTER();
digital_sequence_clear(instance->tx_sequence);
iso15693_signal_encode(instance, data_rate, tx_data, tx_data_size);
digital_sequence_transmit(instance->tx_sequence);
FURI_CRITICAL_EXIT();
}
void iso15693_signal_tx_sof(Iso15693Signal* instance, Iso15693SignalDataRate data_rate) {
furi_assert(instance);
furi_assert(data_rate < Iso15693SignalDataRateNum);
FURI_CRITICAL_ENTER();
digital_sequence_clear(instance->tx_sequence);
digital_sequence_add_signal(
instance->tx_sequence, iso15693_get_sequence_index(Iso15693SignalIndexSof, data_rate));
digital_sequence_transmit(instance->tx_sequence);
FURI_CRITICAL_EXIT();
}

View File

@@ -0,0 +1,73 @@
/**
* @file iso15693_signal.h
* @brief DigitalSequence preset for generating ISO15693-compliant signals.
*
*/
#pragma once
#include <furi_hal_resources.h>
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct Iso15693Signal Iso15693Signal;
/**
* @brief Supported data rates.
*/
typedef enum {
Iso15693SignalDataRateHi, /**< High data rate. */
Iso15693SignalDataRateLo, /**< Low data rate. */
Iso15693SignalDataRateNum, /**< Data rate mode count. Internal use. */
} Iso15693SignalDataRate;
/**
* @brief Allocate an Iso15693Signal instance with a set GPIO pin.
*
* @param[in] pin GPIO pin to use during transmission.
* @returns pointer to the allocated instance.
*/
Iso15693Signal* iso15693_signal_alloc(const GpioPin* pin);
/**
* @brief Delete an Iso15693Signal instance.
*
* @param[in,out] instance pointer to the instance to be deleted.
*/
void iso15693_signal_free(Iso15693Signal* instance);
/**
* @brief Transmit arbitrary bytes using an Iso15693Signal instance.
* @see Iso15693SignalDataRate
*
* This function will block until the transmisson has been completed.
*
* @param[in] instance pointer to the instance used in transmission.
* @param[in] data_rate data rate to transmit at.
* @param[in] tx_data pointer to the data to be transmitted.
* @param[in] tx_data_size size of the data to be transmitted in bytes.
*/
void iso15693_signal_tx(
Iso15693Signal* instance,
Iso15693SignalDataRate data_rate,
const uint8_t* tx_data,
size_t tx_data_size);
/**
* @brief Transmit Start of Frame using an Iso15693Signal instance.
* @see Iso15693SignalDataRate
*
* This function will block until the transmisson has been completed.
*
* @param[in] instance pointer to the instance used in transmission.
* @param[in] data_rate data rate to transmit at.
*/
void iso15693_signal_tx_sof(Iso15693Signal* instance, Iso15693SignalDataRate data_rate);
#ifdef __cplusplus
}
#endif