From 3846e811579ed0586ba641a5e5863747d715f760 Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Thu, 1 Dec 2022 01:37:50 +0100 Subject: [PATCH 01/29] added DigitalSequence to chain multiple DigitalSignals added PulseReader for hardware assisted digital signal sampling --- lib/SConscript | 1 + lib/digital_signal/digital_signal.c | 451 +++++++++++++++++++++++----- lib/digital_signal/digital_signal.h | 43 ++- lib/pulse_reader/pulse_reader.c | 202 +++++++++++++ lib/pulse_reader/pulse_reader.h | 140 +++++++++ 5 files changed, 761 insertions(+), 76 deletions(-) create mode 100644 lib/pulse_reader/pulse_reader.c create mode 100644 lib/pulse_reader/pulse_reader.h diff --git a/lib/SConscript b/lib/SConscript index f5d4689f1..51f6f7544 100644 --- a/lib/SConscript +++ b/lib/SConscript @@ -4,6 +4,7 @@ env.Append( LINT_SOURCES=[ Dir("app-scened-template"), Dir("digital_signal"), + Dir("pulse_reader"), Dir("drivers"), Dir("flipper_format"), Dir("infrared"), diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 46ca307a7..c7e8bbca5 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -1,23 +1,46 @@ #include "digital_signal.h" #include -#include -#include +#include #include -#pragma GCC optimize("O3,unroll-loops,Ofast") +#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 */ -#define F_TIM (64000000.0) -#define T_TIM 1562 //15.625 ns *100 -#define T_TIM_DIV2 781 //15.625 ns / 2 *100 DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt) { DigitalSignal* signal = malloc(sizeof(DigitalSignal)); signal->start_level = true; signal->edges_max_cnt = max_edges_cnt; - signal->edge_timings = malloc(max_edges_cnt * sizeof(uint32_t)); - signal->reload_reg_buff = malloc(max_edges_cnt * sizeof(uint32_t)); + 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->reload_reg_entries = 0; + signal->reload_reg_remainder = 0; + + signal->dma_config_gpio.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + signal->dma_config_gpio.Mode = LL_DMA_MODE_CIRCULAR; + signal->dma_config_gpio.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + signal->dma_config_gpio.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + signal->dma_config_gpio.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + signal->dma_config_gpio.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + signal->dma_config_gpio.NbData = 2; + signal->dma_config_gpio.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + signal->dma_config_gpio.Priority = LL_DMA_PRIORITY_VERYHIGH; + + signal->dma_config_timer.PeriphOrM2MSrcAddress = (uint32_t) &(TIM2->ARR); + signal->dma_config_timer.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + signal->dma_config_timer.Mode = LL_DMA_MODE_NORMAL; + signal->dma_config_timer.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + signal->dma_config_timer.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + signal->dma_config_timer.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + signal->dma_config_timer.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + signal->dma_config_timer.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + signal->dma_config_timer.Priority = LL_DMA_PRIORITY_HIGH; return signal; } @@ -37,7 +60,10 @@ bool digital_signal_append(DigitalSignal* signal_a, DigitalSignal* 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); @@ -72,6 +98,32 @@ uint32_t digital_signal_get_edges_cnt(DigitalSignal* signal) { return signal->edge_cnt; } +void digital_signal_add(DigitalSignal* signal, uint32_t ticks) { + furi_assert(signal); + furi_assert(signal->edge_cnt < signal->edges_max_cnt); + + signal->edge_timings[signal->edge_cnt++] = ticks; +} + +void digital_signal_add_pulse(DigitalSignal* signal, uint32_t ticks, bool level) { + furi_assert(signal); + furi_assert(signal->edge_cnt < signal->edges_max_cnt); + + /* virgin signal? add it as the only level */ + if(signal->edge_cnt == 0) { + signal->start_level = level; + signal->edge_timings[signal->edge_cnt++] = ticks; + } else { + bool end_level = signal->start_level ^ !(signal->edge_cnt % 2); + + if(level != end_level) { + signal->edge_timings[signal->edge_cnt++] = 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); @@ -79,76 +131,76 @@ uint32_t digital_signal_get_edge(DigitalSignal* signal, uint32_t edge_num) { return signal->edge_timings[edge_num]; } -void digital_signal_prepare_arr(DigitalSignal* signal) { - uint32_t t_signal_rest = signal->edge_timings[0]; - uint32_t r_count_tick_arr = 0; - uint32_t r_rest_div = 0; +void digital_signal_prepare(DigitalSignal* signal) { + furi_assert(signal); + furi_assert(signal->gpio); + furi_assert(signal->gpio->pin); + + /* set up signal polarities */ + uint32_t bit_set = signal->gpio->pin; + uint32_t bit_reset = signal->gpio->pin << 16; - for(size_t i = 0; i < signal->edge_cnt - 1; i++) { - r_count_tick_arr = t_signal_rest / T_TIM; - r_rest_div = t_signal_rest % T_TIM; - t_signal_rest = signal->edge_timings[i + 1] + r_rest_div; + if(signal->start_level) { + signal->gpio_buff[0] = bit_set; + signal->gpio_buff[1] = bit_reset; + } else { + signal->gpio_buff[0] = bit_reset; + signal->gpio_buff[1] = bit_set; + } - if(r_rest_div < T_TIM_DIV2) { - signal->reload_reg_buff[i] = r_count_tick_arr - 1; - } else { - signal->reload_reg_buff[i] = r_count_tick_arr; - t_signal_rest -= T_TIM; + /* set up edge timings */ + signal->reload_reg_entries = 0; + + for(size_t pos = 0; pos < signal->edge_cnt; pos++) { + uint32_t pulse_duration = signal->edge_timings[pos] + signal->reload_reg_remainder; + uint32_t pulse_ticks = (pulse_duration + T_TIM_DIV2) / T_TIM; + signal->reload_reg_remainder = pulse_duration - (pulse_ticks * T_TIM); + + if(pulse_ticks > 1) { + signal->reload_reg_buff[signal->reload_reg_entries++] = pulse_ticks - 1; } } } -void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) { +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_SetCounter(TIM2, 0); +} + +static bool digital_signal_setup_dma(DigitalSignal* signal) { furi_assert(signal); - furi_assert(gpio); - // Configure gpio as output - furi_hal_gpio_init(gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - - // Init gpio buffer and DMA channel - uint16_t gpio_reg = gpio->port->ODR; - uint16_t gpio_buff[2]; - if(signal->start_level) { - gpio_buff[0] = gpio_reg | gpio->pin; - gpio_buff[1] = gpio_reg & ~(gpio->pin); - } else { - gpio_buff[0] = gpio_reg & ~(gpio->pin); - gpio_buff[1] = gpio_reg | gpio->pin; + if(!signal->reload_reg_entries) { + return false; } - LL_DMA_InitTypeDef dma_config = {}; - dma_config.MemoryOrM2MDstAddress = (uint32_t)gpio_buff; - dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (gpio->port->ODR); - dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; - dma_config.Mode = LL_DMA_MODE_CIRCULAR; - dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; - dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; - dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_HALFWORD; - dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_HALFWORD; - dma_config.NbData = 2; - dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; - dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH; - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config); - LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, 2); - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); - // Init timer arr register buffer and DMA channel - digital_signal_prepare_arr(signal); - dma_config.MemoryOrM2MDstAddress = (uint32_t)signal->reload_reg_buff; - dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (TIM2->ARR); - dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; - dma_config.Mode = LL_DMA_MODE_NORMAL; - dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; - dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; - dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; - dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; - dma_config.NbData = signal->edge_cnt - 2; - dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; - dma_config.Priority = LL_DMA_PRIORITY_HIGH; - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config); - LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, signal->edge_cnt - 2); + signal->dma_config_gpio.MemoryOrM2MDstAddress = (uint32_t) signal->gpio_buff; + signal->dma_config_gpio.PeriphOrM2MSrcAddress = (uint32_t) &(signal->gpio->port->BSRR); + signal->dma_config_timer.MemoryOrM2MDstAddress = (uint32_t)signal->reload_reg_buff; + signal->dma_config_timer.NbData = signal->reload_reg_entries; + + /* set up DMA channel 1 and 2 for GPIO and timer copy operations */ + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &signal->dma_config_gpio); + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &signal->dma_config_timer); + + /* enable both DMA channels */ + LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); - // Set up timer + return true; +} + +static void digital_signal_setup_timer() { + + digital_signal_stop_timer(); + LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP); LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1); LL_TIM_SetPrescaler(TIM2, 0); @@ -156,18 +208,267 @@ void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) { LL_TIM_SetCounter(TIM2, 0); LL_TIM_EnableUpdateEvent(TIM2); LL_TIM_EnableDMAReq_UPDATE(TIM2); +} - // Start transactions - LL_TIM_GenerateEvent_UPDATE(TIM2); // Do we really need it? +static void digital_signal_start_timer() { + LL_TIM_GenerateEvent_UPDATE(TIM2); LL_TIM_EnableCounter(TIM2); +} - while(!LL_DMA_IsActiveFlag_TC2(DMA1)) - ; +void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) { + furi_assert(signal); + + if(!signal->edge_cnt) { + return; + } + + /* Configure gpio as output */ + signal->gpio = gpio; + furi_hal_gpio_init(signal->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + + /* single signal, add a temporary, terminating edge at the end */ + signal->edge_timings[signal->edge_cnt++] = 10; + digital_signal_prepare(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(); + + signal->edge_cnt--; +} + +void digital_sequence_alloc_signals(DigitalSequence* sequence, uint32_t size) { + sequence->signals_size = size; + sequence->signals = malloc(sequence->signals_size * sizeof(DigitalSignal*)); + sequence->signals_prolonged = malloc(sequence->signals_size * sizeof(bool)); +} + +void digital_sequence_alloc_sequence(DigitalSequence* sequence, uint32_t size) { + sequence->sequence_used = 0; + sequence->sequence_size = size; + sequence->sequence = malloc(sequence->sequence_size); +} + +DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio) { + + DigitalSequence* sequence = malloc(sizeof(DigitalSequence)); + + sequence->gpio = gpio; + sequence->bake = false; + + digital_sequence_alloc_signals(sequence, 32); + 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); +} + +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->gpio = sequence->gpio; + signal->reload_reg_remainder = 0; + + digital_signal_prepare(signal); +} + +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 += 256; + sequence->sequence = realloc(sequence->sequence, sequence->sequence_size); + } + + sequence->sequence[sequence->sequence_used++] = signal_index; +} + +void digital_signal_update_dma(DigitalSignal* signal) { + + volatile uint32_t dma1_data[] = { + /* R6 */ (uint32_t)&(DMA1_Channel1->CCR), + /* R7 */ DMA1_Channel1->CCR & ~DMA_CCR_EN, + /* R8 */ 2, + /* R9 */ (uint32_t)&(signal->gpio->port->BSRR), + /* R10 */ (uint32_t)signal->gpio_buff, + /* R11 */ DMA1_Channel1->CCR | DMA_CCR_EN }; + + volatile uint32_t dma2_data[] = { + /* R0 */ (uint32_t)&(DMA1_Channel2->CCR), + /* R1 */ DMA1_Channel2->CCR & ~DMA_CCR_EN, + /* R2 */ (uint32_t)signal->reload_reg_entries, + /* R3 */ (uint32_t)&(TIM2->ARR), + /* R4 */ (uint32_t)signal->reload_reg_buff, + /* R5 */ DMA1_Channel2->CCR | DMA_CCR_EN }; + + + /* hurry when setting up next transfer */ + asm volatile("\t" + "MOV r6, %[data1]\n\t" + "MOV r7, %[data2]\n\t" + + "PUSH {r0-r12}\n\t" + + "LDM r7, {r0-r5}\n\t" + "LDM r6, {r6-r11}\n\t" + + "loop:\n\t" + "LDR r12, [r0, #4]\n\t" + "CMP r12, #0\n\t" + "BNE loop\n\t" + + "STM r6, {r7-r10}\n\t" /* disable channel and set up new parameters */ + "STR r11, [r6, #0]\n\t" /* enable channel again */ + "STM r0, {r1-r4}\n\t" /* disable channel and set up new parameters */ + "STR r5, [r0, #0]\n\t" /* enable channel again */ + + "POP {r0-r12}\n\t" + + : /* no outputs*/ + : /* inputs */ + [data1] "r" (dma1_data), + [data2] "r" (dma2_data) + : "r6", "r7" ); + LL_DMA_ClearFlag_TC1(DMA1); LL_DMA_ClearFlag_TC2(DMA1); - LL_TIM_DisableCounter(TIM2); - LL_TIM_SetCounter(TIM2, 0); - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); +} + +static bool digital_sequence_send_signal(DigitalSignal* signal) { + furi_assert(signal); + + /* the first iteration has to set up the whole machinery */ + if(!LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_1)) { + if(!digital_signal_setup_dma(signal)) { + FURI_LOG_D(TAG, "digital_sequence_send_signal: Signal has no entries, aborting"); + return false; + } + digital_signal_setup_timer(); + digital_signal_start_timer(); + } else { + /* configure next polarities and timings */ + digital_signal_update_dma(signal); + } + + return true; +} + +DigitalSignal* digital_sequence_bake(DigitalSequence* 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; +} + +bool digital_sequence_send(DigitalSequence* sequence) { + furi_assert(sequence); + + furi_hal_gpio_init(sequence->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + + if(sequence->bake) { + DigitalSignal* sig = digital_sequence_bake(sequence); + + digital_signal_send(sig, sequence->gpio); + digital_signal_free(sig); + return true; + } + + int32_t remainder = 0; + FURI_CRITICAL_ENTER(); + + for(uint32_t pos = 0; pos < sequence->sequence_used; pos++) { + uint8_t signal_index = sequence->sequence[pos]; + DigitalSignal *sig = sequence->signals[signal_index]; + + if(!sig) { + FURI_LOG_D(TAG, "digital_sequence_send: Signal at index %u, used at pos %lu is NULL, aborting", signal_index, pos); + break; + } + + /* when we are too late more than half a tick, make the first edge temporarily longer */ + bool needs_prolongation = false; + + if(remainder >= T_TIM_DIV2) { + remainder -= T_TIM; + needs_prolongation = true; + } + + /* update the total remainder */ + remainder += sig->reload_reg_remainder; + + /* do we need to update the prolongation? */ + if(needs_prolongation != sequence->signals_prolonged[signal_index]) { + if(needs_prolongation) { + sig->edge_timings[0]++; + } else { + sig->edge_timings[0]--; + } + sequence->signals_prolonged[signal_index] = needs_prolongation; + } + + bool success = digital_sequence_send_signal(sig); + + if(!success) { + break; + } + } + FURI_CRITICAL_EXIT(); + + while(LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2)) { + } + + digital_signal_stop_timer(); + digital_signal_stop_dma(); + + /* undo previously prolonged edges */ + for(uint32_t pos = 0; pos < sequence->signals_size; pos++) { + DigitalSignal *sig = sequence->signals[pos]; + + if(sig && sequence->signals_prolonged[pos]) { + sig->edge_timings[0]--; + sequence->signals_prolonged[pos] = false; + } + } + + return true; +} + +void digital_sequence_clear(DigitalSequence* sequence) { + furi_assert(sequence); + + sequence->sequence_used = 0; } diff --git a/lib/digital_signal/digital_signal.h b/lib/digital_signal/digital_signal.h index 90905d74b..8f6142258 100644 --- a/lib/digital_signal/digital_signal.h +++ b/lib/digital_signal/digital_signal.h @@ -5,26 +5,57 @@ #include #include +#include +#include #ifdef __cplusplus extern "C" { #endif +/* helper for easier signal generation */ +#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) + + typedef struct { bool start_level; uint32_t edge_cnt; uint32_t edges_max_cnt; uint32_t* edge_timings; uint32_t* reload_reg_buff; + 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; } DigitalSignal; +typedef struct { + uint8_t signals_size; + bool bake; + uint32_t sequence_used; + uint32_t sequence_size; + DigitalSignal** signals; + bool* signals_prolonged; + uint8_t* sequence; + const GpioPin* gpio; +} DigitalSequence; + + DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt); void digital_signal_free(DigitalSignal* signal); +void digital_signal_add(DigitalSignal* signal, uint32_t ticks); + +void digital_signal_add_pulse(DigitalSignal* signal, uint32_t ticks, bool level); + bool digital_signal_append(DigitalSignal* signal_a, DigitalSignal* signal_b); -void digital_signal_prepare_arr(DigitalSignal* signal); +void digital_signal_prepare(DigitalSignal* signal); bool digital_signal_get_start_level(DigitalSignal* signal); @@ -34,6 +65,16 @@ 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_add(DigitalSequence* sequence, uint8_t signal_index); +bool digital_sequence_send(DigitalSequence* sequence); +void digital_sequence_clear(DigitalSequence* sequence); + + + #ifdef __cplusplus } #endif diff --git a/lib/pulse_reader/pulse_reader.c b/lib/pulse_reader/pulse_reader.c new file mode 100644 index 000000000..18e7ee98d --- /dev/null +++ b/lib/pulse_reader/pulse_reader.c @@ -0,0 +1,202 @@ +#include +#include +#include +#include + +#include "pulse_reader.h" + + +#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) + + + +PulseReader* pulse_reader_alloc(const GpioPin* gpio, uint32_t size) { + + PulseReader* signal = malloc(sizeof(PulseReader)); + signal->timer_buffer = malloc(size * sizeof(uint32_t)); + signal->gpio_buffer = malloc(size * sizeof(uint32_t)); + signal->dma_channel = LL_DMA_CHANNEL_4; + signal->gpio = gpio; + signal->size = size; + signal->timer_value = 0; + signal->pos = 0; + + pulse_reader_set_timebase(signal, PulseReaderUnit64MHz); + pulse_reader_set_bittime(signal, 1); + + signal->dma_config_timer.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY; + signal->dma_config_timer.PeriphOrM2MSrcAddress = (uint32_t) &(TIM2->CNT); + signal->dma_config_timer.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + signal->dma_config_timer.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + signal->dma_config_timer.MemoryOrM2MDstAddress = (uint32_t) signal->timer_buffer; + signal->dma_config_timer.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + signal->dma_config_timer.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + signal->dma_config_timer.Mode = LL_DMA_MODE_CIRCULAR; + signal->dma_config_timer.PeriphRequest = LL_DMAMUX_REQ_GENERATOR0; /* executes LL_DMA_SetPeriphRequest */ + signal->dma_config_timer.Priority = LL_DMA_PRIORITY_VERYHIGH; + + signal->dma_config_gpio.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY; + signal->dma_config_gpio.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + signal->dma_config_gpio.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + signal->dma_config_gpio.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + signal->dma_config_gpio.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + signal->dma_config_gpio.Mode = LL_DMA_MODE_CIRCULAR; + signal->dma_config_gpio.PeriphRequest = LL_DMAMUX_REQ_GENERATOR0; /* executes LL_DMA_SetPeriphRequest */ + signal->dma_config_gpio.Priority = LL_DMA_PRIORITY_VERYHIGH; + + return signal; +} + +void pulse_reader_set_timebase(PulseReader* signal, PulseReaderUnit unit) { + switch(unit) { + case PulseReaderUnit64MHz: + signal->unit_multiplier = 1; + signal->unit_divider = 1; + break; + case PulseReaderUnitPicosecond: + signal->unit_multiplier = 15625; + signal->unit_divider = 1; + break; + case PulseReaderUnitNanosecond: + signal->unit_multiplier = 15625; + signal->unit_divider = 1000; + break; + case PulseReaderUnitMicrosecond: + signal->unit_multiplier = 15625; + signal->unit_divider = 1000000; + break; + } +} + +void pulse_reader_set_bittime(PulseReader* signal, uint32_t bit_time) { + signal->bit_time = bit_time; +} + +void pulse_reader_free(PulseReader* signal) { + free(signal->timer_buffer); + free(signal->gpio_buffer); + free(signal); +} + +uint32_t pulse_reader_samples(PulseReader* signal) { + uint32_t dma_pos = signal->size - (uint32_t)LL_DMA_GetDataLength(DMA1, signal->dma_channel); + + return ((signal->pos + signal->size) - dma_pos) % signal->size; +} + +void pulse_reader_stop(PulseReader* signal) { + LL_DMA_DisableChannel(DMA1, signal->dma_channel); + LL_DMA_DisableChannel(DMA1, signal->dma_channel+1); + LL_DMAMUX_DisableRequestGen(NULL, LL_DMAMUX_REQ_GEN_0); + LL_TIM_DisableCounter(TIM2); +} + +void pulse_reader_start(PulseReader* signal) { + /* configure DMA to read from a timer peripheral */ + signal->dma_config_timer.NbData = signal->size; + + signal->dma_config_gpio.PeriphOrM2MSrcAddress = (uint32_t) &(signal->gpio->port->IDR); + signal->dma_config_gpio.MemoryOrM2MDstAddress = (uint32_t) signal->gpio_buffer; + signal->dma_config_gpio.NbData = signal->size; + + /* start counter */ + 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, 0xFFFFFFFF); + LL_TIM_SetCounter(TIM2, 0); + LL_TIM_EnableCounter(TIM2); + + /* generator 0 gets fed by EXTI_LINEn */ + LL_DMAMUX_SetRequestSignalID(NULL, LL_DMAMUX_REQ_GEN_0, GET_DMAMUX_EXTI_LINE(signal->gpio->pin)); + /* trigger on rising edge of the interrupt */ + LL_DMAMUX_SetRequestGenPolarity(NULL, LL_DMAMUX_REQ_GEN_0, LL_DMAMUX_REQ_GEN_POL_RISING); + /* now enable request generation again */ + LL_DMAMUX_EnableRequestGen(NULL, LL_DMAMUX_REQ_GEN_0); + + /* we need the EXTI to be configured as interrupt generating line, but no ISR registered */ + furi_hal_gpio_init_ex(signal->gpio, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedVeryHigh, GpioAltFnUnused); + + /* capture current timer */ + signal->pos = 0; + signal->start_level = furi_hal_gpio_read(signal->gpio); + signal->timer_value = TIM2->CNT; + signal->gpio_mask = signal->gpio->pin; + + /* now set up DMA with these settings */ + LL_DMA_Init(DMA1, signal->dma_channel, &signal->dma_config_timer); + LL_DMA_Init(DMA1, signal->dma_channel + 1, &signal->dma_config_gpio); + LL_DMA_EnableChannel(DMA1, signal->dma_channel); + LL_DMA_EnableChannel(DMA1, signal->dma_channel + 1); +} + +uint32_t pulse_reader_receive(PulseReader* signal, int timeout_us) { + + uint32_t start_time = DWT->CYCCNT; + uint32_t timeout_ticks = timeout_us * (F_TIM2/1000000); + + do { + /* get the DMA's next write position by reading "remaining length" register */ + uint32_t dma_pos = signal->size - (uint32_t)LL_DMA_GetDataLength(DMA1, signal->dma_channel); + + /* the DMA has advanced in the ringbuffer */ + if(dma_pos != signal->pos) { + + uint32_t delta = signal->timer_buffer[signal->pos] - signal->timer_value; + uint32_t last_gpio_value = signal->gpio_value; + + signal->gpio_value = signal->gpio_buffer[signal->pos]; + + /* check if the GPIO really toggled. if not, we lost an edge :( */ + if(((last_gpio_value ^ signal->gpio_value) & signal->gpio_mask) != signal->gpio_mask) { + signal->gpio_value ^= signal->gpio_mask; + return PULSE_READER_LOST_EDGE; + } + signal->timer_value = signal->timer_buffer[signal->pos]; + + signal->pos++; + signal->pos %= signal->size; + + uint32_t delta_unit = 0; + + /* probably larger values, so choose a wider data type */ + if(signal->unit_divider > 1) { + delta_unit = (uint32_t)((uint64_t)delta * (uint64_t)signal->unit_multiplier / signal->unit_divider); + } else { + delta_unit = delta * signal->unit_multiplier; + } + + /* if to be scaled to bit times, save a few instructions. should be faster */ + if(signal->bit_time > 1) { + return (delta_unit + signal->bit_time / 2) / signal->bit_time; + } + + return delta_unit; + } + + /* check for timeout */ + uint32_t elapsed = DWT->CYCCNT - start_time; + + if(elapsed > timeout_ticks) { + return PULSE_READER_NO_EDGE; + } + } while(true); +} diff --git a/lib/pulse_reader/pulse_reader.h b/lib/pulse_reader/pulse_reader.h new file mode 100644 index 000000000..f55a65931 --- /dev/null +++ b/lib/pulse_reader/pulse_reader.h @@ -0,0 +1,140 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define PULSE_READER_NO_EDGE 0xFFFFFFFFUL +#define PULSE_READER_LOST_EDGE 0xFFFFFFFEUL +#define F_TIM2 64000000UL + +/** + * unit of the edge durations to return + */ +typedef enum { + PulseReaderUnit64MHz, + PulseReaderUnitPicosecond, + PulseReaderUnitNanosecond, + PulseReaderUnitMicrosecond, +} PulseReaderUnit; + + +typedef struct { + bool start_level; + uint32_t* timer_buffer; + uint32_t* gpio_buffer; + uint32_t size; + uint32_t pos; + uint32_t timer_value; + uint32_t gpio_value; + uint32_t gpio_mask; + uint32_t unit_multiplier; + uint32_t unit_divider; + uint32_t bit_time; + uint32_t dma_channel; + const GpioPin* gpio; + LL_DMA_InitTypeDef dma_config_timer; + LL_DMA_InitTypeDef dma_config_gpio; +} PulseReader; + + +/** Allocate a PulseReader object + * + * Allocates memory for a ringbuffer and initalizes the object + * + * @param[in] gpio the GPIO to use. will get configured as input. + * @param[in] size number of edges to buffer + */ +PulseReader* pulse_reader_alloc(const GpioPin* gpio, uint32_t size); + + +/** Free a PulseReader object + * + * Frees all memory of the given object + * + * @param[in] signal previously allocated PulseReader object. + */ +void pulse_reader_free(PulseReader* signal); + + +/** Start signal capturing + * + * Initializes DMA1, TIM2 and DMAMUX_REQ_GEN_0 to automatically capture timer values + * + * @param[in] signal previously allocated PulseReader object. + */ +void pulse_reader_start(PulseReader* signal); + + +/** Stop signal capturing + * + * Frees DMA1, TIM2 and DMAMUX_REQ_GEN_0 + * + * @param[in] signal previously allocated PulseReader object. + */ +void pulse_reader_stop(PulseReader* signal); + + +/** Recevie a sample from ringbuffer + * + * Waits for the specified time until a new edge gets detected. + * If not configured otherwise, the pulse duration will be in picosecond resolution. + * If a bittime was configured, the return value will contain the properly rounded + * number of bit times measured. + * + * @param[in] signal previously allocated PulseReader object. + * @param[in] timeout_us time to wait for a signal [µs] + * + * @returns the scaled value of the pulse duration + */ +uint32_t pulse_reader_receive(PulseReader* signal, int timeout_us); + + +/** Get available samples + * + * Get the number of available samples in the ringbuffer + * + * @param[in] signal previously allocated PulseReader object. + * + * @returns the number of samples in buffer + */ +uint32_t pulse_reader_samples(PulseReader* signal); + + +/** Set timebase + * + * Set the timebase to be used when returning pulse duration. + * + * @param[in] signal previously allocated PulseReader object. + * @param[in] unit PulseReaderUnit64MHz or PulseReaderUnitPicosecond + */ +void pulse_reader_set_timebase(PulseReader* signal, PulseReaderUnit unit); + + +/** Set bit time + * + * Set the number of timebase units per bit. + * When set, the pulse_reader_receive() will return an already rounded + * bit count value instead of the raw duration. + * + * Set to 1 to return duration again. + * + * @param[in] signal previously allocated PulseReader object. + * @param[in] bit_time + */ +void pulse_reader_set_bittime(PulseReader* signal, uint32_t bit_time); + + +#ifdef __cplusplus +} +#endif From 26c4e886777e23d39218102d695a43f99d30aaa9 Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Thu, 1 Dec 2022 18:43:22 +0100 Subject: [PATCH 02/29] added send_time option to start a signal at a specific DWT->CYCCNT value --- lib/digital_signal/digital_signal.c | 21 +++++++++++++++++++-- lib/digital_signal/digital_signal.h | 2 ++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index c7e8bbca5..727b64ca4 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -253,6 +253,7 @@ 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; } DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio) { @@ -288,6 +289,10 @@ void digital_sequence_set_signal(DigitalSequence* sequence, uint8_t signal_index digital_signal_prepare(signal); } +void digital_sequence_set_sendtime(DigitalSequence* sequence, uint32_t send_time) { + sequence->send_time = send_time; +} + void digital_sequence_add(DigitalSequence* sequence, uint8_t signal_index) { furi_assert(sequence); furi_assert(signal_index < sequence->signals_size); @@ -352,7 +357,8 @@ void digital_signal_update_dma(DigitalSignal* signal) { LL_DMA_ClearFlag_TC2(DMA1); } -static bool digital_sequence_send_signal(DigitalSignal* signal) { +static bool digital_sequence_send_signal(DigitalSequence* sequence, DigitalSignal* signal) { + furi_assert(sequence); furi_assert(signal); /* the first iteration has to set up the whole machinery */ @@ -362,6 +368,17 @@ static bool digital_sequence_send_signal(DigitalSignal* signal) { return false; } digital_signal_setup_timer(); + + /* if the send time is specified, wait till the core timer passed beyond that time */ + if(sequence->send_time != 0) { + while(true) { + uint32_t delta = sequence->send_time - DWT->CYCCNT; + /* yeah, it's making use of underflows... */ + if(delta > 0x80000000) { + break; + } + } + } digital_signal_start_timer(); } else { /* configure next polarities and timings */ @@ -440,7 +457,7 @@ bool digital_sequence_send(DigitalSequence* sequence) { sequence->signals_prolonged[signal_index] = needs_prolongation; } - bool success = digital_sequence_send_signal(sig); + bool success = digital_sequence_send_signal(sequence, sig); if(!success) { break; diff --git a/lib/digital_signal/digital_signal.h b/lib/digital_signal/digital_signal.h index 8f6142258..94df13a60 100644 --- a/lib/digital_signal/digital_signal.h +++ b/lib/digital_signal/digital_signal.h @@ -42,6 +42,7 @@ typedef struct { bool* signals_prolonged; uint8_t* sequence; const GpioPin* gpio; + uint32_t send_time; } DigitalSequence; @@ -69,6 +70,7 @@ 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); From 0a714fc1648cb70bfdc8985d33494650dbf7eb41 Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Mon, 5 Dec 2022 09:36:45 +0100 Subject: [PATCH 03/29] fixed linter errors and undone function renaming --- lib/digital_signal/digital_signal.c | 104 ++++++++++++++-------------- lib/digital_signal/digital_signal.h | 18 +++-- lib/pulse_reader/pulse_reader.c | 68 +++++++++--------- lib/pulse_reader/pulse_reader.h | 16 +---- 4 files changed, 96 insertions(+), 110 deletions(-) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 727b64ca4..b78daab3d 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -6,11 +6,9 @@ #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 */ - +#define F_TIM (64000000.0) +#define T_TIM 1562 /* 15.625 ns *100 */ +#define T_TIM_DIV2 781 /* 15.625 ns / 2 *100 */ DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt) { DigitalSignal* signal = malloc(sizeof(DigitalSignal)); @@ -21,7 +19,7 @@ DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt) { signal->reload_reg_buff = malloc(signal->edges_max_cnt * sizeof(uint32_t)); signal->reload_reg_entries = 0; signal->reload_reg_remainder = 0; - + signal->dma_config_gpio.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; signal->dma_config_gpio.Mode = LL_DMA_MODE_CIRCULAR; signal->dma_config_gpio.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; @@ -32,7 +30,7 @@ DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt) { signal->dma_config_gpio.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; signal->dma_config_gpio.Priority = LL_DMA_PRIORITY_VERYHIGH; - signal->dma_config_timer.PeriphOrM2MSrcAddress = (uint32_t) &(TIM2->ARR); + signal->dma_config_timer.PeriphOrM2MSrcAddress = (uint32_t) & (TIM2->ARR); signal->dma_config_timer.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; signal->dma_config_timer.Mode = LL_DMA_MODE_NORMAL; signal->dma_config_timer.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; @@ -131,11 +129,11 @@ uint32_t digital_signal_get_edge(DigitalSignal* signal, uint32_t edge_num) { return signal->edge_timings[edge_num]; } -void digital_signal_prepare(DigitalSignal* signal) { +void digital_signal_prepare_arr(DigitalSignal* signal) { furi_assert(signal); furi_assert(signal->gpio); furi_assert(signal->gpio->pin); - + /* set up signal polarities */ uint32_t bit_set = signal->gpio->pin; uint32_t bit_reset = signal->gpio->pin << 16; @@ -181,8 +179,8 @@ static bool digital_signal_setup_dma(DigitalSignal* signal) { return false; } - signal->dma_config_gpio.MemoryOrM2MDstAddress = (uint32_t) signal->gpio_buff; - signal->dma_config_gpio.PeriphOrM2MSrcAddress = (uint32_t) &(signal->gpio->port->BSRR); + signal->dma_config_gpio.MemoryOrM2MDstAddress = (uint32_t)signal->gpio_buff; + signal->dma_config_gpio.PeriphOrM2MSrcAddress = (uint32_t) & (signal->gpio->port->BSRR); signal->dma_config_timer.MemoryOrM2MDstAddress = (uint32_t)signal->reload_reg_buff; signal->dma_config_timer.NbData = signal->reload_reg_entries; @@ -198,7 +196,6 @@ static bool digital_signal_setup_dma(DigitalSignal* signal) { } static void digital_signal_setup_timer() { - digital_signal_stop_timer(); LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP); @@ -228,7 +225,7 @@ void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) { /* single signal, add a temporary, terminating edge at the end */ signal->edge_timings[signal->edge_cnt++] = 10; - digital_signal_prepare(signal); + digital_signal_prepare_arr(signal); digital_signal_setup_dma(signal); digital_signal_setup_timer(); @@ -257,7 +254,6 @@ void digital_sequence_alloc_sequence(DigitalSequence* sequence, uint32_t size) { } DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio) { - DigitalSequence* sequence = malloc(sizeof(DigitalSequence)); sequence->gpio = gpio; @@ -277,7 +273,10 @@ void digital_sequence_free(DigitalSequence* sequence) { free(sequence); } -void digital_sequence_set_signal(DigitalSequence* sequence, uint8_t signal_index, DigitalSignal* signal) { +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); @@ -286,7 +285,7 @@ void digital_sequence_set_signal(DigitalSequence* sequence, uint8_t signal_index signal->gpio = sequence->gpio; signal->reload_reg_remainder = 0; - digital_signal_prepare(signal); + digital_signal_prepare_arr(signal); } void digital_sequence_set_sendtime(DigitalSequence* sequence, uint32_t send_time) { @@ -306,52 +305,48 @@ void digital_sequence_add(DigitalSequence* sequence, uint8_t signal_index) { } void digital_signal_update_dma(DigitalSignal* signal) { - volatile uint32_t dma1_data[] = { - /* R6 */ (uint32_t)&(DMA1_Channel1->CCR), + /* R6 */ (uint32_t) & (DMA1_Channel1->CCR), /* R7 */ DMA1_Channel1->CCR & ~DMA_CCR_EN, /* R8 */ 2, - /* R9 */ (uint32_t)&(signal->gpio->port->BSRR), + /* R9 */ (uint32_t) & (signal->gpio->port->BSRR), /* R10 */ (uint32_t)signal->gpio_buff, - /* R11 */ DMA1_Channel1->CCR | DMA_CCR_EN }; + /* R11 */ DMA1_Channel1->CCR | DMA_CCR_EN}; volatile uint32_t dma2_data[] = { - /* R0 */ (uint32_t)&(DMA1_Channel2->CCR), + /* R0 */ (uint32_t) & (DMA1_Channel2->CCR), /* R1 */ DMA1_Channel2->CCR & ~DMA_CCR_EN, /* R2 */ (uint32_t)signal->reload_reg_entries, - /* R3 */ (uint32_t)&(TIM2->ARR), + /* R3 */ (uint32_t) & (TIM2->ARR), /* R4 */ (uint32_t)signal->reload_reg_buff, - /* R5 */ DMA1_Channel2->CCR | DMA_CCR_EN }; + /* R5 */ DMA1_Channel2->CCR | DMA_CCR_EN}; - /* hurry when setting up next transfer */ asm volatile("\t" - "MOV r6, %[data1]\n\t" - "MOV r7, %[data2]\n\t" + "MOV r6, %[data1]\n\t" + "MOV r7, %[data2]\n\t" - "PUSH {r0-r12}\n\t" + "PUSH {r0-r12}\n\t" - "LDM r7, {r0-r5}\n\t" - "LDM r6, {r6-r11}\n\t" + "LDM r7, {r0-r5}\n\t" + "LDM r6, {r6-r11}\n\t" - "loop:\n\t" - "LDR r12, [r0, #4]\n\t" - "CMP r12, #0\n\t" - "BNE loop\n\t" + "loop:\n\t" + "LDR r12, [r0, #4]\n\t" + "CMP r12, #0\n\t" + "BNE loop\n\t" - "STM r6, {r7-r10}\n\t" /* disable channel and set up new parameters */ - "STR r11, [r6, #0]\n\t" /* enable channel again */ - "STM r0, {r1-r4}\n\t" /* disable channel and set up new parameters */ - "STR r5, [r0, #0]\n\t" /* enable channel again */ + "STM r6, {r7-r10}\n\t" /* disable channel and set up new parameters */ + "STR r11, [r6, #0]\n\t" /* enable channel again */ + "STM r0, {r1-r4}\n\t" /* disable channel and set up new parameters */ + "STR r5, [r0, #0]\n\t" /* enable channel again */ - "POP {r0-r12}\n\t" + "POP {r0-r12}\n\t" - : /* no outputs*/ - : /* inputs */ - [data1] "r" (dma1_data), - [data2] "r" (dma2_data) - : "r6", "r7" ); - + : /* no outputs*/ + : /* inputs */ + [data1] "r"(dma1_data), [data2] "r"(dma2_data) + : "r6", "r7"); LL_DMA_ClearFlag_TC1(DMA1); LL_DMA_ClearFlag_TC2(DMA1); @@ -360,7 +355,7 @@ void digital_signal_update_dma(DigitalSignal* signal) { static bool digital_sequence_send_signal(DigitalSequence* sequence, DigitalSignal* signal) { furi_assert(sequence); furi_assert(signal); - + /* the first iteration has to set up the whole machinery */ if(!LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_1)) { if(!digital_signal_setup_dma(signal)) { @@ -389,21 +384,20 @@ static bool digital_sequence_send_signal(DigitalSequence* sequence, DigitalSigna } DigitalSignal* digital_sequence_bake(DigitalSequence* 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]; + 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]; + DigitalSignal* sig = sequence->signals[signal_index]; digital_signal_append(ret, sig); } @@ -429,10 +423,14 @@ bool digital_sequence_send(DigitalSequence* sequence) { for(uint32_t pos = 0; pos < sequence->sequence_used; pos++) { uint8_t signal_index = sequence->sequence[pos]; - DigitalSignal *sig = sequence->signals[signal_index]; + DigitalSignal* sig = sequence->signals[signal_index]; if(!sig) { - FURI_LOG_D(TAG, "digital_sequence_send: Signal at index %u, used at pos %lu is NULL, aborting", signal_index, pos); + FURI_LOG_D( + TAG, + "digital_sequence_send: Signal at index %u, used at pos %lu is NULL, aborting", + signal_index, + pos); break; } @@ -470,10 +468,10 @@ bool digital_sequence_send(DigitalSequence* sequence) { digital_signal_stop_timer(); digital_signal_stop_dma(); - + /* undo previously prolonged edges */ for(uint32_t pos = 0; pos < sequence->signals_size; pos++) { - DigitalSignal *sig = sequence->signals[pos]; + DigitalSignal* sig = sequence->signals[pos]; if(sig && sequence->signals_prolonged[pos]) { sig->edge_timings[0]--; diff --git a/lib/digital_signal/digital_signal.h b/lib/digital_signal/digital_signal.h index 94df13a60..2e1a05522 100644 --- a/lib/digital_signal/digital_signal.h +++ b/lib/digital_signal/digital_signal.h @@ -13,11 +13,10 @@ extern "C" { #endif /* helper for easier signal generation */ -#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) - +#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) typedef struct { bool start_level; @@ -45,7 +44,6 @@ typedef struct { uint32_t send_time; } DigitalSequence; - DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt); void digital_signal_free(DigitalSignal* signal); @@ -66,17 +64,17 @@ 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_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); - - #ifdef __cplusplus } #endif diff --git a/lib/pulse_reader/pulse_reader.c b/lib/pulse_reader/pulse_reader.c index 18e7ee98d..20f993844 100644 --- a/lib/pulse_reader/pulse_reader.c +++ b/lib/pulse_reader/pulse_reader.c @@ -5,7 +5,6 @@ #include "pulse_reader.h" - #define GPIO_PIN_MAP(pin, prefix) \ (((pin) == (LL_GPIO_PIN_0)) ? prefix##0 : \ ((pin) == (LL_GPIO_PIN_1)) ? prefix##1 : \ @@ -26,10 +25,7 @@ #define GET_DMAMUX_EXTI_LINE(pin) GPIO_PIN_MAP(pin, LL_DMAMUX_REQ_GEN_EXTI_LINE) - - PulseReader* pulse_reader_alloc(const GpioPin* gpio, uint32_t size) { - PulseReader* signal = malloc(sizeof(PulseReader)); signal->timer_buffer = malloc(size * sizeof(uint32_t)); signal->gpio_buffer = malloc(size * sizeof(uint32_t)); @@ -43,14 +39,15 @@ PulseReader* pulse_reader_alloc(const GpioPin* gpio, uint32_t size) { pulse_reader_set_bittime(signal, 1); signal->dma_config_timer.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY; - signal->dma_config_timer.PeriphOrM2MSrcAddress = (uint32_t) &(TIM2->CNT); + signal->dma_config_timer.PeriphOrM2MSrcAddress = (uint32_t) & (TIM2->CNT); signal->dma_config_timer.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; signal->dma_config_timer.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; - signal->dma_config_timer.MemoryOrM2MDstAddress = (uint32_t) signal->timer_buffer; + signal->dma_config_timer.MemoryOrM2MDstAddress = (uint32_t)signal->timer_buffer; signal->dma_config_timer.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; signal->dma_config_timer.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; signal->dma_config_timer.Mode = LL_DMA_MODE_CIRCULAR; - signal->dma_config_timer.PeriphRequest = LL_DMAMUX_REQ_GENERATOR0; /* executes LL_DMA_SetPeriphRequest */ + signal->dma_config_timer.PeriphRequest = + LL_DMAMUX_REQ_GENERATOR0; /* executes LL_DMA_SetPeriphRequest */ signal->dma_config_timer.Priority = LL_DMA_PRIORITY_VERYHIGH; signal->dma_config_gpio.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY; @@ -59,7 +56,8 @@ PulseReader* pulse_reader_alloc(const GpioPin* gpio, uint32_t size) { signal->dma_config_gpio.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; signal->dma_config_gpio.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; signal->dma_config_gpio.Mode = LL_DMA_MODE_CIRCULAR; - signal->dma_config_gpio.PeriphRequest = LL_DMAMUX_REQ_GENERATOR0; /* executes LL_DMA_SetPeriphRequest */ + signal->dma_config_gpio.PeriphRequest = + LL_DMAMUX_REQ_GENERATOR0; /* executes LL_DMA_SetPeriphRequest */ signal->dma_config_gpio.Priority = LL_DMA_PRIORITY_VERYHIGH; return signal; @@ -67,22 +65,22 @@ PulseReader* pulse_reader_alloc(const GpioPin* gpio, uint32_t size) { void pulse_reader_set_timebase(PulseReader* signal, PulseReaderUnit unit) { switch(unit) { - case PulseReaderUnit64MHz: - signal->unit_multiplier = 1; - signal->unit_divider = 1; - break; - case PulseReaderUnitPicosecond: - signal->unit_multiplier = 15625; - signal->unit_divider = 1; - break; - case PulseReaderUnitNanosecond: - signal->unit_multiplier = 15625; - signal->unit_divider = 1000; - break; - case PulseReaderUnitMicrosecond: - signal->unit_multiplier = 15625; - signal->unit_divider = 1000000; - break; + case PulseReaderUnit64MHz: + signal->unit_multiplier = 1; + signal->unit_divider = 1; + break; + case PulseReaderUnitPicosecond: + signal->unit_multiplier = 15625; + signal->unit_divider = 1; + break; + case PulseReaderUnitNanosecond: + signal->unit_multiplier = 15625; + signal->unit_divider = 1000; + break; + case PulseReaderUnitMicrosecond: + signal->unit_multiplier = 15625; + signal->unit_divider = 1000000; + break; } } @@ -104,7 +102,7 @@ uint32_t pulse_reader_samples(PulseReader* signal) { void pulse_reader_stop(PulseReader* signal) { LL_DMA_DisableChannel(DMA1, signal->dma_channel); - LL_DMA_DisableChannel(DMA1, signal->dma_channel+1); + LL_DMA_DisableChannel(DMA1, signal->dma_channel + 1); LL_DMAMUX_DisableRequestGen(NULL, LL_DMAMUX_REQ_GEN_0); LL_TIM_DisableCounter(TIM2); } @@ -113,8 +111,8 @@ void pulse_reader_start(PulseReader* signal) { /* configure DMA to read from a timer peripheral */ signal->dma_config_timer.NbData = signal->size; - signal->dma_config_gpio.PeriphOrM2MSrcAddress = (uint32_t) &(signal->gpio->port->IDR); - signal->dma_config_gpio.MemoryOrM2MDstAddress = (uint32_t) signal->gpio_buffer; + signal->dma_config_gpio.PeriphOrM2MSrcAddress = (uint32_t) & (signal->gpio->port->IDR); + signal->dma_config_gpio.MemoryOrM2MDstAddress = (uint32_t)signal->gpio_buffer; signal->dma_config_gpio.NbData = signal->size; /* start counter */ @@ -126,14 +124,16 @@ void pulse_reader_start(PulseReader* signal) { LL_TIM_EnableCounter(TIM2); /* generator 0 gets fed by EXTI_LINEn */ - LL_DMAMUX_SetRequestSignalID(NULL, LL_DMAMUX_REQ_GEN_0, GET_DMAMUX_EXTI_LINE(signal->gpio->pin)); + LL_DMAMUX_SetRequestSignalID( + NULL, LL_DMAMUX_REQ_GEN_0, GET_DMAMUX_EXTI_LINE(signal->gpio->pin)); /* trigger on rising edge of the interrupt */ LL_DMAMUX_SetRequestGenPolarity(NULL, LL_DMAMUX_REQ_GEN_0, LL_DMAMUX_REQ_GEN_POL_RISING); /* now enable request generation again */ LL_DMAMUX_EnableRequestGen(NULL, LL_DMAMUX_REQ_GEN_0); /* we need the EXTI to be configured as interrupt generating line, but no ISR registered */ - furi_hal_gpio_init_ex(signal->gpio, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedVeryHigh, GpioAltFnUnused); + furi_hal_gpio_init_ex( + signal->gpio, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedVeryHigh, GpioAltFnUnused); /* capture current timer */ signal->pos = 0; @@ -149,17 +149,16 @@ void pulse_reader_start(PulseReader* signal) { } uint32_t pulse_reader_receive(PulseReader* signal, int timeout_us) { - uint32_t start_time = DWT->CYCCNT; - uint32_t timeout_ticks = timeout_us * (F_TIM2/1000000); + uint32_t timeout_ticks = timeout_us * (F_TIM2 / 1000000); do { /* get the DMA's next write position by reading "remaining length" register */ - uint32_t dma_pos = signal->size - (uint32_t)LL_DMA_GetDataLength(DMA1, signal->dma_channel); + uint32_t dma_pos = + signal->size - (uint32_t)LL_DMA_GetDataLength(DMA1, signal->dma_channel); /* the DMA has advanced in the ringbuffer */ if(dma_pos != signal->pos) { - uint32_t delta = signal->timer_buffer[signal->pos] - signal->timer_value; uint32_t last_gpio_value = signal->gpio_value; @@ -179,7 +178,8 @@ uint32_t pulse_reader_receive(PulseReader* signal, int timeout_us) { /* probably larger values, so choose a wider data type */ if(signal->unit_divider > 1) { - delta_unit = (uint32_t)((uint64_t)delta * (uint64_t)signal->unit_multiplier / signal->unit_divider); + delta_unit = + (uint32_t)((uint64_t)delta * (uint64_t)signal->unit_multiplier / signal->unit_divider); } else { delta_unit = delta * signal->unit_multiplier; } diff --git a/lib/pulse_reader/pulse_reader.h b/lib/pulse_reader/pulse_reader.h index f55a65931..564bf1063 100644 --- a/lib/pulse_reader/pulse_reader.h +++ b/lib/pulse_reader/pulse_reader.h @@ -14,9 +14,9 @@ extern "C" { #endif -#define PULSE_READER_NO_EDGE 0xFFFFFFFFUL -#define PULSE_READER_LOST_EDGE 0xFFFFFFFEUL -#define F_TIM2 64000000UL +#define PULSE_READER_NO_EDGE 0xFFFFFFFFUL +#define PULSE_READER_LOST_EDGE 0xFFFFFFFEUL +#define F_TIM2 64000000UL /** * unit of the edge durations to return @@ -28,7 +28,6 @@ typedef enum { PulseReaderUnitMicrosecond, } PulseReaderUnit; - typedef struct { bool start_level; uint32_t* timer_buffer; @@ -47,7 +46,6 @@ typedef struct { LL_DMA_InitTypeDef dma_config_gpio; } PulseReader; - /** Allocate a PulseReader object * * Allocates memory for a ringbuffer and initalizes the object @@ -57,7 +55,6 @@ typedef struct { */ PulseReader* pulse_reader_alloc(const GpioPin* gpio, uint32_t size); - /** Free a PulseReader object * * Frees all memory of the given object @@ -66,7 +63,6 @@ PulseReader* pulse_reader_alloc(const GpioPin* gpio, uint32_t size); */ void pulse_reader_free(PulseReader* signal); - /** Start signal capturing * * Initializes DMA1, TIM2 and DMAMUX_REQ_GEN_0 to automatically capture timer values @@ -75,7 +71,6 @@ void pulse_reader_free(PulseReader* signal); */ void pulse_reader_start(PulseReader* signal); - /** Stop signal capturing * * Frees DMA1, TIM2 and DMAMUX_REQ_GEN_0 @@ -84,7 +79,6 @@ void pulse_reader_start(PulseReader* signal); */ void pulse_reader_stop(PulseReader* signal); - /** Recevie a sample from ringbuffer * * Waits for the specified time until a new edge gets detected. @@ -99,7 +93,6 @@ void pulse_reader_stop(PulseReader* signal); */ uint32_t pulse_reader_receive(PulseReader* signal, int timeout_us); - /** Get available samples * * Get the number of available samples in the ringbuffer @@ -110,7 +103,6 @@ uint32_t pulse_reader_receive(PulseReader* signal, int timeout_us); */ uint32_t pulse_reader_samples(PulseReader* signal); - /** Set timebase * * Set the timebase to be used when returning pulse duration. @@ -120,7 +112,6 @@ uint32_t pulse_reader_samples(PulseReader* signal); */ void pulse_reader_set_timebase(PulseReader* signal, PulseReaderUnit unit); - /** Set bit time * * Set the number of timebase units per bit. @@ -134,7 +125,6 @@ void pulse_reader_set_timebase(PulseReader* signal, PulseReaderUnit unit); */ void pulse_reader_set_bittime(PulseReader* signal, uint32_t bit_time); - #ifdef __cplusplus } #endif From 9a6a796804ac07999216fcb02b182147ebf7439a Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Mon, 5 Dec 2022 10:32:44 +0100 Subject: [PATCH 04/29] fixed renaming --- lib/digital_signal/digital_signal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/digital_signal/digital_signal.h b/lib/digital_signal/digital_signal.h index 2e1a05522..9a91c8b45 100644 --- a/lib/digital_signal/digital_signal.h +++ b/lib/digital_signal/digital_signal.h @@ -54,7 +54,7 @@ void digital_signal_add_pulse(DigitalSignal* signal, uint32_t ticks, bool level) bool digital_signal_append(DigitalSignal* signal_a, DigitalSignal* signal_b); -void digital_signal_prepare(DigitalSignal* signal); +void digital_signal_prepare_arr(DigitalSignal* signal); bool digital_signal_get_start_level(DigitalSignal* signal); From b24c0cd97fbaa1c41758e5a4e9af6185c33472df Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Mon, 5 Dec 2022 10:36:15 +0100 Subject: [PATCH 05/29] flagged functions in api_symbols.csv --- firmware/targets/f7/api_symbols.csv | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index d0c6b36ad..63fb64a00 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -736,6 +736,15 @@ Function,+,dialog_message_set_text,void,"DialogMessage*, const char*, uint8_t, u Function,+,dialog_message_show,DialogMessageButton,"DialogsApp*, const DialogMessage*" Function,+,dialog_message_show_storage_error,void,"DialogsApp*, const char*" Function,-,difftime,double,"time_t, time_t" +Function,-,digital_sequence_add,void,"DigitalSequence*, uint8_t" +Function,-,digital_sequence_alloc,DigitalSequence*,"uint32_t, const GpioPin*" +Function,-,digital_sequence_clear,void,DigitalSequence* +Function,-,digital_sequence_free,void,DigitalSequence* +Function,-,digital_sequence_send,_Bool,DigitalSequence* +Function,-,digital_sequence_set_sendtime,void,"DigitalSequence*, uint32_t" +Function,-,digital_sequence_set_signal,void,"DigitalSequence*, uint8_t, DigitalSignal*" +Function,-,digital_signal_add,void,"DigitalSignal*, uint32_t" +Function,-,digital_signal_add_pulse,void,"DigitalSignal*, uint32_t, _Bool" Function,-,digital_signal_alloc,DigitalSignal*,uint32_t Function,-,digital_signal_append,_Bool,"DigitalSignal*, DigitalSignal*" Function,-,digital_signal_free,void,DigitalSignal* From 302739d543ebb95f17056b3cfaed6ac92d5beea3 Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Mon, 5 Dec 2022 12:19:05 +0100 Subject: [PATCH 06/29] allow gpio field to stay uninitialized in digital_signal_prepare_arr() --- lib/digital_signal/digital_signal.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index b78daab3d..9ba5cc901 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -131,19 +131,19 @@ uint32_t digital_signal_get_edge(DigitalSignal* signal, uint32_t edge_num) { void digital_signal_prepare_arr(DigitalSignal* signal) { furi_assert(signal); - furi_assert(signal->gpio); - furi_assert(signal->gpio->pin); /* set up signal polarities */ - uint32_t bit_set = signal->gpio->pin; - uint32_t bit_reset = signal->gpio->pin << 16; + if(signal->gpio) { + uint32_t bit_set = signal->gpio->pin; + uint32_t bit_reset = signal->gpio->pin << 16; - if(signal->start_level) { - signal->gpio_buff[0] = bit_set; - signal->gpio_buff[1] = bit_reset; - } else { - signal->gpio_buff[0] = bit_reset; - signal->gpio_buff[1] = bit_set; + if(signal->start_level) { + signal->gpio_buff[0] = bit_set; + signal->gpio_buff[1] = bit_reset; + } else { + signal->gpio_buff[0] = bit_reset; + signal->gpio_buff[1] = bit_set; + } } /* set up edge timings */ From 035d630ff90f9bf29f29336fc78465b3517fa106 Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Thu, 15 Dec 2022 15:29:16 +0100 Subject: [PATCH 07/29] fix test cases to match (expected) implementation --- assets/unit_tests/nfc/nfc_nfca_signal_long.nfc | 2 +- assets/unit_tests/nfc/nfc_nfca_signal_short.nfc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/unit_tests/nfc/nfc_nfca_signal_long.nfc b/assets/unit_tests/nfc/nfc_nfca_signal_long.nfc index fae69cb5c..dd6a2ff8e 100644 --- a/assets/unit_tests/nfc/nfc_nfca_signal_long.nfc +++ b/assets/unit_tests/nfc/nfc_nfca_signal_long.nfc @@ -3,4 +3,4 @@ Version: 1 Data length: 18 Plain data: f1 99 41 43 a1 2f 23 01 de f3 c5 8d 91 4b 1e 50 4a c9 Timings length: 1304 -Timings: 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 640 37 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 37 640 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 37 36 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 37 36 37 641 37 36 37 37 37 36 37 37 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 641 37 37 36 37 37 37 36 37 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 641 37 36 37 37 37 36 37 37 37 36 37 37 37 37 36 641 37 37 36 37 37 37 36 37 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 641 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 37 37 37 36 37 37 37 36 641 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 0 +Timings: 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 640 37 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 37 640 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 37 36 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 37 36 37 641 37 36 37 37 37 36 37 37 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 641 37 37 36 37 37 37 36 37 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 641 37 36 37 37 37 36 37 37 37 36 37 37 37 37 36 641 37 37 36 37 37 37 36 37 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 641 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 37 37 37 36 37 37 37 36 641 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 diff --git a/assets/unit_tests/nfc/nfc_nfca_signal_short.nfc b/assets/unit_tests/nfc/nfc_nfca_signal_short.nfc index 3b7e2d9e9..f447fca26 100644 --- a/assets/unit_tests/nfc/nfc_nfca_signal_short.nfc +++ b/assets/unit_tests/nfc/nfc_nfca_signal_short.nfc @@ -3,4 +3,4 @@ Version: 1 Data length: 4 Plain data: 14 d8 a0 c9 Timings length: 296 -Timings: 37 37 36 37 37 37 36 641 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 338 37 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 339 37 37 36 37 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 641 36 37 37 37 37 36 37 0 +Timings: 37 37 36 37 37 37 36 641 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 338 37 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 339 37 37 36 37 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 641 36 37 37 37 37 36 37 37 From b5b5abac273d50d2b886c5e119ec16e3f55609ca Mon Sep 17 00:00:00 2001 From: gornekich Date: Tue, 20 Dec 2022 18:49:29 +0400 Subject: [PATCH 08/29] pulse_reader: build as static library Signed-off-by: g3gg0.de --- firmware/targets/f7/api_symbols.csv | 9 +++++++++ firmware/targets/f7/target.json | 1 + lib/SConscript | 2 ++ lib/pulse_reader/SConscript | 19 +++++++++++++++++++ 4 files changed, 31 insertions(+) create mode 100644 lib/pulse_reader/SConscript diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 63fb64a00..7b80eb8aa 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -173,6 +173,7 @@ Header,+,lib/one_wire/maxim_crc.h,, Header,+,lib/one_wire/one_wire_host.h,, Header,+,lib/one_wire/one_wire_slave.h,, Header,+,lib/print/wrappers.h,, +Header,+,lib/pulse_reader/pulse_reader.h,, Header,+,lib/subghz/blocks/const.h,, Header,+,lib/subghz/blocks/decoder.h,, Header,+,lib/subghz/blocks/encoder.h,, @@ -2166,6 +2167,14 @@ Function,+,protocol_dict_render_brief_data,void,"ProtocolDict*, FuriString*, siz Function,+,protocol_dict_render_data,void,"ProtocolDict*, FuriString*, size_t" Function,+,protocol_dict_set_data,void,"ProtocolDict*, size_t, const uint8_t*, size_t" Function,-,pselect,int,"int, fd_set*, fd_set*, fd_set*, const timespec*, const sigset_t*" +Function,+,pulse_reader_alloc,PulseReader*,"const GpioPin*, uint32_t" +Function,+,pulse_reader_free,void,PulseReader* +Function,+,pulse_reader_receive,uint32_t,"PulseReader*, int" +Function,+,pulse_reader_samples,uint32_t,PulseReader* +Function,+,pulse_reader_set_bittime,void,"PulseReader*, uint32_t" +Function,+,pulse_reader_set_timebase,void,"PulseReader*, PulseReaderUnit" +Function,+,pulse_reader_start,void,PulseReader* +Function,+,pulse_reader_stop,void,PulseReader* Function,-,putc,int,"int, FILE*" Function,-,putc_unlocked,int,"int, FILE*" Function,-,putchar,int,int diff --git a/firmware/targets/f7/target.json b/firmware/targets/f7/target.json index 14bb1cd0c..0d2367b3a 100644 --- a/firmware/targets/f7/target.json +++ b/firmware/targets/f7/target.json @@ -28,6 +28,7 @@ "flipperformat", "toolbox", "nfc", + "pulse_reader", "microtar", "usb_stm32", "st25rfal002", diff --git a/lib/SConscript b/lib/SConscript index 51f6f7544..23c74b334 100644 --- a/lib/SConscript +++ b/lib/SConscript @@ -15,6 +15,7 @@ env.Append( Dir("u8g2"), Dir("update_util"), Dir("print"), + Dir("pulse_reader"), ], ) @@ -94,6 +95,7 @@ libs = env.BuildModules( "mbedtls", "subghz", "nfc", + "pulse_reader", "appframe", "misc", "lfrfid", diff --git a/lib/pulse_reader/SConscript b/lib/pulse_reader/SConscript new file mode 100644 index 000000000..416ad4a27 --- /dev/null +++ b/lib/pulse_reader/SConscript @@ -0,0 +1,19 @@ +Import("env") + +env.Append( + CPPPATH=[ + "#/lib/pulse_reader", + ], + SDK_HEADERS=[ + File("pulse_reader.h"), + ], +) + +libenv = env.Clone(FW_LIB_NAME="pulse_reader") +libenv.ApplyLibFlags() + +sources = libenv.GlobRecursive("*.c*") + +lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) +libenv.Install("${LIB_DIST_DIR}", lib) +Return("lib") \ No newline at end of file From 396ebe1ad80c8bbb7728b14fa6d95b43b6103f06 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Wed, 21 Dec 2022 01:01:16 +0100 Subject: [PATCH 09/29] fix starting level detection in pulse_reader --- lib/pulse_reader/pulse_reader.c | 2 +- lib/pulse_reader/pulse_reader.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pulse_reader/pulse_reader.c b/lib/pulse_reader/pulse_reader.c index 20f993844..64fde7fc9 100644 --- a/lib/pulse_reader/pulse_reader.c +++ b/lib/pulse_reader/pulse_reader.c @@ -137,9 +137,9 @@ void pulse_reader_start(PulseReader* signal) { /* capture current timer */ signal->pos = 0; - signal->start_level = furi_hal_gpio_read(signal->gpio); signal->timer_value = TIM2->CNT; signal->gpio_mask = signal->gpio->pin; + signal->gpio_value = signal->gpio->port->IDR & signal->gpio_mask; /* now set up DMA with these settings */ LL_DMA_Init(DMA1, signal->dma_channel, &signal->dma_config_timer); diff --git a/lib/pulse_reader/pulse_reader.h b/lib/pulse_reader/pulse_reader.h index 564bf1063..b4e6bca57 100644 --- a/lib/pulse_reader/pulse_reader.h +++ b/lib/pulse_reader/pulse_reader.h @@ -29,7 +29,6 @@ typedef enum { } PulseReaderUnit; typedef struct { - bool start_level; uint32_t* timer_buffer; uint32_t* gpio_buffer; uint32_t size; @@ -65,7 +64,8 @@ void pulse_reader_free(PulseReader* signal); /** Start signal capturing * - * Initializes DMA1, TIM2 and DMAMUX_REQ_GEN_0 to automatically capture timer values + * Initializes DMA1, TIM2 and DMAMUX_REQ_GEN_0 to automatically capture timer values. + * Ensure that interrupts are always enabled, as the used EXTI line is handled as one. * * @param[in] signal previously allocated PulseReader object. */ From e56cd5165afb38ee36373d0d205bf954ad4605b0 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Wed, 21 Dec 2022 01:06:22 +0100 Subject: [PATCH 10/29] added unit test for pulse_reader --- applications/debug/unit_tests/nfc/nfc_test.c | 150 +++++++++++++++++++ 1 file changed, 150 insertions(+) diff --git a/applications/debug/unit_tests/nfc/nfc_test.c b/applications/debug/unit_tests/nfc/nfc_test.c index 54bdd5909..3b3a44431 100644 --- a/applications/debug/unit_tests/nfc/nfc_test.c +++ b/applications/debug/unit_tests/nfc/nfc_test.c @@ -1,10 +1,12 @@ #include #include +#include #include #include #include #include #include +#include #include #include @@ -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); From 9cf16bdfe4a9ccf81b6fa6ca282bbf81443853ba Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Wed, 21 Dec 2022 01:15:22 +0100 Subject: [PATCH 11/29] change pulse reader test timings to 1, 10 and 100 ms --- applications/debug/unit_tests/nfc/nfc_test.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/applications/debug/unit_tests/nfc/nfc_test.c b/applications/debug/unit_tests/nfc/nfc_test.c index 3b3a44431..98b996212 100644 --- a/applications/debug/unit_tests/nfc/nfc_test.c +++ b/applications/debug/unit_tests/nfc/nfc_test.c @@ -318,9 +318,9 @@ static bool nfc_test_pulse_reader_toggle( } 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(500, 500, 50, 10), "1 ms signal failed\r\n"); + mu_assert(nfc_test_pulse_reader_toggle(5000, 5000, 10, 10), "10 ms signal failed\r\n"); + mu_assert(nfc_test_pulse_reader_toggle(50000, 50000, 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"); From 146061e631fa6d446db1a76c9f8141b5d96ffca6 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Wed, 21 Dec 2022 01:26:16 +0100 Subject: [PATCH 12/29] fine tuned timings for pulse_reader test --- applications/debug/unit_tests/nfc/nfc_test.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/applications/debug/unit_tests/nfc/nfc_test.c b/applications/debug/unit_tests/nfc/nfc_test.c index 98b996212..87622f3d3 100644 --- a/applications/debug/unit_tests/nfc/nfc_test.c +++ b/applications/debug/unit_tests/nfc/nfc_test.c @@ -255,7 +255,9 @@ static bool nfc_test_pulse_reader_toggle( } prev_cnt = cur_cnt; } - /* quickly halt the counter to keep a static signal */ + /* quickly halt the counter to keep a static signal. we might get some delay here due to scheduling, + causing the timer to continue racing. there is currently no workaround, as disabling interrupts will + defunct the pulse_reader */ LL_TIM_DisableCounter(TIM1); do { @@ -318,12 +320,10 @@ static bool nfc_test_pulse_reader_toggle( } MU_TEST(nfc_pulse_reader_test) { - mu_assert(nfc_test_pulse_reader_toggle(500, 500, 50, 10), "1 ms signal failed\r\n"); mu_assert(nfc_test_pulse_reader_toggle(5000, 5000, 10, 10), "10 ms signal failed\r\n"); mu_assert(nfc_test_pulse_reader_toggle(50000, 50000, 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"); + nfc_test_pulse_reader_toggle(3333, 6667, 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"); } From a34f2d290382bc6dac61859a2eb92efa1fb160c5 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Wed, 21 Dec 2022 15:38:03 +0100 Subject: [PATCH 13/29] pulse_reader_stop now deinits GPIO as recommended by @gornekich --- lib/pulse_reader/pulse_reader.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pulse_reader/pulse_reader.c b/lib/pulse_reader/pulse_reader.c index 64fde7fc9..8b35f6d18 100644 --- a/lib/pulse_reader/pulse_reader.c +++ b/lib/pulse_reader/pulse_reader.c @@ -105,6 +105,7 @@ void pulse_reader_stop(PulseReader* signal) { LL_DMA_DisableChannel(DMA1, signal->dma_channel + 1); LL_DMAMUX_DisableRequestGen(NULL, LL_DMAMUX_REQ_GEN_0); LL_TIM_DisableCounter(TIM2); + furi_hal_gpio_init_simple(signal->gpio, GpioModeAnalog); } void pulse_reader_start(PulseReader* signal) { From f56835cb655fa15e7895fb84f65f4f45f20d9d03 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Wed, 21 Dec 2022 17:36:51 +0100 Subject: [PATCH 14/29] ran format_py --- lib/pulse_reader/SConscript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pulse_reader/SConscript b/lib/pulse_reader/SConscript index 416ad4a27..5a72b953f 100644 --- a/lib/pulse_reader/SConscript +++ b/lib/pulse_reader/SConscript @@ -16,4 +16,4 @@ sources = libenv.GlobRecursive("*.c*") lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) libenv.Install("${LIB_DIST_DIR}", lib) -Return("lib") \ No newline at end of file +Return("lib") From 4d09a50fbb4d444b1327cc35a12196cd1fe0644e Mon Sep 17 00:00:00 2001 From: gornekich Date: Wed, 21 Dec 2022 23:17:06 +0400 Subject: [PATCH 15/29] pulse_reader: remove from API, allow to link with faps Signed-off-by: g3gg0.de --- firmware/targets/f7/api_symbols.csv | 16 ++++++++-------- lib/pulse_reader/SConscript | 8 ++++++++ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 7b80eb8aa..bfd87214c 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -2167,14 +2167,14 @@ Function,+,protocol_dict_render_brief_data,void,"ProtocolDict*, FuriString*, siz Function,+,protocol_dict_render_data,void,"ProtocolDict*, FuriString*, size_t" Function,+,protocol_dict_set_data,void,"ProtocolDict*, size_t, const uint8_t*, size_t" Function,-,pselect,int,"int, fd_set*, fd_set*, fd_set*, const timespec*, const sigset_t*" -Function,+,pulse_reader_alloc,PulseReader*,"const GpioPin*, uint32_t" -Function,+,pulse_reader_free,void,PulseReader* -Function,+,pulse_reader_receive,uint32_t,"PulseReader*, int" -Function,+,pulse_reader_samples,uint32_t,PulseReader* -Function,+,pulse_reader_set_bittime,void,"PulseReader*, uint32_t" -Function,+,pulse_reader_set_timebase,void,"PulseReader*, PulseReaderUnit" -Function,+,pulse_reader_start,void,PulseReader* -Function,+,pulse_reader_stop,void,PulseReader* +Function,-,pulse_reader_alloc,PulseReader*,"const GpioPin*, uint32_t" +Function,-,pulse_reader_free,void,PulseReader* +Function,-,pulse_reader_receive,uint32_t,"PulseReader*, int" +Function,-,pulse_reader_samples,uint32_t,PulseReader* +Function,-,pulse_reader_set_bittime,void,"PulseReader*, uint32_t" +Function,-,pulse_reader_set_timebase,void,"PulseReader*, PulseReaderUnit" +Function,-,pulse_reader_start,void,PulseReader* +Function,-,pulse_reader_stop,void,PulseReader* Function,-,putc,int,"int, FILE*" Function,-,putc_unlocked,int,"int, FILE*" Function,-,putchar,int,int diff --git a/lib/pulse_reader/SConscript b/lib/pulse_reader/SConscript index 5a72b953f..f00851a20 100644 --- a/lib/pulse_reader/SConscript +++ b/lib/pulse_reader/SConscript @@ -12,6 +12,14 @@ env.Append( libenv = env.Clone(FW_LIB_NAME="pulse_reader") libenv.ApplyLibFlags() +libenv.AppendUnique( + CCFLAGS=[ + # Required for lib to be linkable with .faps + "-mword-relocations", + "-mlong-calls", + ], +) + sources = libenv.GlobRecursive("*.c*") lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) From 67b02b7785cebc7f39ceac4705d21088b854cd49 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Wed, 21 Dec 2022 21:54:33 +0100 Subject: [PATCH 16/29] remove unit test for pulse_reader again --- applications/debug/unit_tests/nfc/nfc_test.c | 150 ------------------- 1 file changed, 150 deletions(-) diff --git a/applications/debug/unit_tests/nfc/nfc_test.c b/applications/debug/unit_tests/nfc/nfc_test.c index 87622f3d3..54bdd5909 100644 --- a/applications/debug/unit_tests/nfc/nfc_test.c +++ b/applications/debug/unit_tests/nfc/nfc_test.c @@ -1,12 +1,10 @@ #include #include -#include #include #include #include #include #include -#include #include #include @@ -181,153 +179,6 @@ 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. we might get some delay here due to scheduling, - causing the timer to continue racing. there is currently no workaround, as disabling interrupts will - defunct the pulse_reader */ - 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(5000, 5000, 10, 10), "10 ms signal failed\r\n"); - mu_assert(nfc_test_pulse_reader_toggle(50000, 50000, 5, 50), "100 ms signal failed\r\n"); - mu_assert( - nfc_test_pulse_reader_toggle(3333, 6667, 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; @@ -662,7 +513,6 @@ 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); From f08c8d6a1dee43199f6a78047d323258f1561696 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Wed, 28 Dec 2022 14:40:29 +0100 Subject: [PATCH 17/29] pulse_reader: add call to set GPIO pull direction --- firmware/targets/f7/api_symbols.csv | 1 + lib/pulse_reader/pulse_reader.c | 7 ++++++- lib/pulse_reader/pulse_reader.h | 11 +++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index bfd87214c..2edbdb16d 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -2172,6 +2172,7 @@ Function,-,pulse_reader_free,void,PulseReader* Function,-,pulse_reader_receive,uint32_t,"PulseReader*, int" Function,-,pulse_reader_samples,uint32_t,PulseReader* Function,-,pulse_reader_set_bittime,void,"PulseReader*, uint32_t" +Function,-,pulse_reader_set_pull,void,"PulseReader*, GpioPull" Function,-,pulse_reader_set_timebase,void,"PulseReader*, PulseReaderUnit" Function,-,pulse_reader_start,void,PulseReader* Function,-,pulse_reader_stop,void,PulseReader* diff --git a/lib/pulse_reader/pulse_reader.c b/lib/pulse_reader/pulse_reader.c index 8b35f6d18..3d9508f9a 100644 --- a/lib/pulse_reader/pulse_reader.c +++ b/lib/pulse_reader/pulse_reader.c @@ -31,6 +31,7 @@ PulseReader* pulse_reader_alloc(const GpioPin* gpio, uint32_t size) { signal->gpio_buffer = malloc(size * sizeof(uint32_t)); signal->dma_channel = LL_DMA_CHANNEL_4; signal->gpio = gpio; + signal->pull = GpioPullNo; signal->size = size; signal->timer_value = 0; signal->pos = 0; @@ -88,6 +89,10 @@ void pulse_reader_set_bittime(PulseReader* signal, uint32_t bit_time) { signal->bit_time = bit_time; } +void pulse_reader_set_pull(PulseReader* signal, GpioPull pull) { + signal->pull = pull; +} + void pulse_reader_free(PulseReader* signal) { free(signal->timer_buffer); free(signal->gpio_buffer); @@ -134,7 +139,7 @@ void pulse_reader_start(PulseReader* signal) { /* we need the EXTI to be configured as interrupt generating line, but no ISR registered */ furi_hal_gpio_init_ex( - signal->gpio, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedVeryHigh, GpioAltFnUnused); + signal->gpio, GpioModeInterruptRiseFall, signal->pull, GpioSpeedVeryHigh, GpioAltFnUnused); /* capture current timer */ signal->pos = 0; diff --git a/lib/pulse_reader/pulse_reader.h b/lib/pulse_reader/pulse_reader.h index b4e6bca57..be0ad7012 100644 --- a/lib/pulse_reader/pulse_reader.h +++ b/lib/pulse_reader/pulse_reader.h @@ -41,6 +41,7 @@ typedef struct { uint32_t bit_time; uint32_t dma_channel; const GpioPin* gpio; + GpioPull pull; LL_DMA_InitTypeDef dma_config_timer; LL_DMA_InitTypeDef dma_config_gpio; } PulseReader; @@ -125,6 +126,16 @@ void pulse_reader_set_timebase(PulseReader* signal, PulseReaderUnit unit); */ void pulse_reader_set_bittime(PulseReader* signal, uint32_t bit_time); +/** Set GPIO pull direction + * + * Some GPIOs need pulldown, others don't. By default the + * pull direction is GpioPullNo. + * + * @param[in] signal previously allocated PulseReader object. + * @param[in] pull GPIO pull direction + */ +void pulse_reader_set_pull(PulseReader* signal, GpioPull pull); + #ifdef __cplusplus } #endif From c18cf71646ed1fa3e167efc470dae568dc96c2f9 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Wed, 28 Dec 2022 22:43:39 +0100 Subject: [PATCH 18/29] make structures private, add C implementation of digital_signal_update_dma() --- lib/digital_signal/digital_signal.c | 179 ++++++++++++++++++---------- lib/digital_signal/digital_signal.h | 29 ++--- lib/pulse_reader/pulse_reader.c | 25 +++- lib/pulse_reader/pulse_reader.h | 23 +--- 4 files changed, 147 insertions(+), 109 deletions(-) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 9ba5cc901..12b543273 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -4,6 +4,30 @@ #include #include +#include +#include + +struct DigitalSequence { + uint8_t signals_size; + bool bake; + uint32_t sequence_used; + uint32_t sequence_size; + DigitalSignal** signals; + bool* signals_prolonged; + uint8_t* sequence; + const GpioPin* gpio; + uint32_t send_time; +}; + +struct DigitalSignalInternals { + 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) @@ -17,28 +41,29 @@ DigitalSignal* digital_signal_alloc(uint32_t 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->reload_reg_entries = 0; - signal->reload_reg_remainder = 0; - signal->dma_config_gpio.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; - signal->dma_config_gpio.Mode = LL_DMA_MODE_CIRCULAR; - signal->dma_config_gpio.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; - signal->dma_config_gpio.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; - signal->dma_config_gpio.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; - signal->dma_config_gpio.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; - signal->dma_config_gpio.NbData = 2; - signal->dma_config_gpio.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; - signal->dma_config_gpio.Priority = LL_DMA_PRIORITY_VERYHIGH; + signal->internals = malloc(sizeof(DigitalSignalInternals)); + signal->internals->reload_reg_entries = 0; + signal->internals->reload_reg_remainder = 0; + signal->internals->dma_config_gpio.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + signal->internals->dma_config_gpio.Mode = LL_DMA_MODE_CIRCULAR; + signal->internals->dma_config_gpio.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + signal->internals->dma_config_gpio.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + signal->internals->dma_config_gpio.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + signal->internals->dma_config_gpio.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + signal->internals->dma_config_gpio.NbData = 2; + signal->internals->dma_config_gpio.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + signal->internals->dma_config_gpio.Priority = LL_DMA_PRIORITY_VERYHIGH; - signal->dma_config_timer.PeriphOrM2MSrcAddress = (uint32_t) & (TIM2->ARR); - signal->dma_config_timer.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; - signal->dma_config_timer.Mode = LL_DMA_MODE_NORMAL; - signal->dma_config_timer.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; - signal->dma_config_timer.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; - signal->dma_config_timer.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; - signal->dma_config_timer.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; - signal->dma_config_timer.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; - signal->dma_config_timer.Priority = LL_DMA_PRIORITY_HIGH; + signal->internals->dma_config_timer.PeriphOrM2MSrcAddress = (uint32_t) & (TIM2->ARR); + signal->internals->dma_config_timer.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + signal->internals->dma_config_timer.Mode = LL_DMA_MODE_NORMAL; + signal->internals->dma_config_timer.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + signal->internals->dma_config_timer.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + signal->internals->dma_config_timer.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + signal->internals->dma_config_timer.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + signal->internals->dma_config_timer.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + signal->internals->dma_config_timer.Priority = LL_DMA_PRIORITY_HIGH; return signal; } @@ -48,6 +73,7 @@ void digital_signal_free(DigitalSignal* signal) { free(signal->edge_timings); free(signal->reload_reg_buff); + free(signal->internals); free(signal); } @@ -133,29 +159,30 @@ void digital_signal_prepare_arr(DigitalSignal* signal) { furi_assert(signal); /* set up signal polarities */ - if(signal->gpio) { - uint32_t bit_set = signal->gpio->pin; - uint32_t bit_reset = signal->gpio->pin << 16; + if(signal->internals->gpio) { + uint32_t bit_set = signal->internals->gpio->pin; + uint32_t bit_reset = signal->internals->gpio->pin << 16; if(signal->start_level) { - signal->gpio_buff[0] = bit_set; - signal->gpio_buff[1] = bit_reset; + signal->internals->gpio_buff[0] = bit_set; + signal->internals->gpio_buff[1] = bit_reset; } else { - signal->gpio_buff[0] = bit_reset; - signal->gpio_buff[1] = bit_set; + signal->internals->gpio_buff[0] = bit_reset; + signal->internals->gpio_buff[1] = bit_set; } } /* set up edge timings */ - signal->reload_reg_entries = 0; + signal->internals->reload_reg_entries = 0; for(size_t pos = 0; pos < signal->edge_cnt; pos++) { - uint32_t pulse_duration = signal->edge_timings[pos] + signal->reload_reg_remainder; + uint32_t pulse_duration = + signal->edge_timings[pos] + signal->internals->reload_reg_remainder; uint32_t pulse_ticks = (pulse_duration + T_TIM_DIV2) / T_TIM; - signal->reload_reg_remainder = pulse_duration - (pulse_ticks * T_TIM); + signal->internals->reload_reg_remainder = pulse_duration - (pulse_ticks * T_TIM); if(pulse_ticks > 1) { - signal->reload_reg_buff[signal->reload_reg_entries++] = pulse_ticks - 1; + signal->reload_reg_buff[signal->internals->reload_reg_entries++] = pulse_ticks - 1; } } } @@ -175,18 +202,20 @@ static void digital_signal_stop_timer() { static bool digital_signal_setup_dma(DigitalSignal* signal) { furi_assert(signal); - if(!signal->reload_reg_entries) { + if(!signal->internals->reload_reg_entries) { return false; } - signal->dma_config_gpio.MemoryOrM2MDstAddress = (uint32_t)signal->gpio_buff; - signal->dma_config_gpio.PeriphOrM2MSrcAddress = (uint32_t) & (signal->gpio->port->BSRR); - signal->dma_config_timer.MemoryOrM2MDstAddress = (uint32_t)signal->reload_reg_buff; - signal->dma_config_timer.NbData = signal->reload_reg_entries; + signal->internals->dma_config_gpio.MemoryOrM2MDstAddress = + (uint32_t)signal->internals->gpio_buff; + signal->internals->dma_config_gpio.PeriphOrM2MSrcAddress = + (uint32_t) & (signal->internals->gpio->port->BSRR); + signal->internals->dma_config_timer.MemoryOrM2MDstAddress = (uint32_t)signal->reload_reg_buff; + signal->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, &signal->dma_config_gpio); - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &signal->dma_config_timer); + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &signal->internals->dma_config_gpio); + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &signal->internals->dma_config_timer); /* enable both DMA channels */ LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); @@ -220,8 +249,9 @@ void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) { } /* Configure gpio as output */ - signal->gpio = gpio; - furi_hal_gpio_init(signal->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + signal->internals->gpio = gpio; + furi_hal_gpio_init( + signal->internals->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); /* single signal, add a temporary, terminating edge at the end */ signal->edge_timings[signal->edge_cnt++] = 10; @@ -282,8 +312,8 @@ void digital_sequence_set_signal( furi_assert(signal_index < sequence->signals_size); sequence->signals[signal_index] = signal; - signal->gpio = sequence->gpio; - signal->reload_reg_remainder = 0; + signal->internals->gpio = sequence->gpio; + signal->internals->reload_reg_remainder = 0; digital_signal_prepare_arr(signal); } @@ -304,54 +334,71 @@ void digital_sequence_add(DigitalSequence* sequence, uint8_t signal_index) { sequence->sequence[sequence->sequence_used++] = signal_index; } -void digital_signal_update_dma(DigitalSignal* signal) { +#if defined(DIGITAL_SIGNAL_PORTABLE_CODE) + +static void digital_signal_update_dma(DigitalSignal* signal) { + LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_1, (uint32_t)signal->internals->gpio_buff); + LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_2, (uint32_t)signal->reload_reg_buff); + LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, 2); + LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, signal->internals->reload_reg_entries); + + LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); +} + +#else + +static void digital_signal_update_dma(DigitalSignal* signal) { volatile uint32_t dma1_data[] = { /* R6 */ (uint32_t) & (DMA1_Channel1->CCR), /* R7 */ DMA1_Channel1->CCR & ~DMA_CCR_EN, /* R8 */ 2, - /* R9 */ (uint32_t) & (signal->gpio->port->BSRR), - /* R10 */ (uint32_t)signal->gpio_buff, + /* R9 */ (uint32_t) & (signal->internals->gpio->port->BSRR), + /* R10 */ (uint32_t)signal->internals->gpio_buff, /* R11 */ DMA1_Channel1->CCR | DMA_CCR_EN}; volatile uint32_t dma2_data[] = { /* R0 */ (uint32_t) & (DMA1_Channel2->CCR), /* R1 */ DMA1_Channel2->CCR & ~DMA_CCR_EN, - /* R2 */ (uint32_t)signal->reload_reg_entries, + /* R2 */ (uint32_t)signal->internals->reload_reg_entries, /* R3 */ (uint32_t) & (TIM2->ARR), /* R4 */ (uint32_t)signal->reload_reg_buff, /* R5 */ DMA1_Channel2->CCR | DMA_CCR_EN}; /* hurry when setting up next transfer */ - asm volatile("\t" - "MOV r6, %[data1]\n\t" - "MOV r7, %[data2]\n\t" + asm volatile( + "\t" + "MOV r6, %[data1]\n\t" + "MOV r7, %[data2]\n\t" - "PUSH {r0-r12}\n\t" + "PUSH {r0-r12}\n\t" - "LDM r7, {r0-r5}\n\t" - "LDM r6, {r6-r11}\n\t" + "LDM r7, {r0-r5}\n\t" /* prepare registers with values to write into DMA config */ + "LDM r6, {r6-r11}\n\t" - "loop:\n\t" - "LDR r12, [r0, #4]\n\t" - "CMP r12, #0\n\t" - "BNE loop\n\t" + "loop:\n\t" + "LDR r12, [r0, #4]\n\t" /* read DMA_CNDTRx to get remaining transfers */ + "CMP r12, #0\n\t" + "BNE loop\n\t" - "STM r6, {r7-r10}\n\t" /* disable channel and set up new parameters */ - "STR r11, [r6, #0]\n\t" /* enable channel again */ - "STM r0, {r1-r4}\n\t" /* disable channel and set up new parameters */ - "STR r5, [r0, #0]\n\t" /* enable channel again */ + "STM r6, {r7-r10}\n\t" /* disable channel and set up new parameters */ + "STR r11, [r6, #0]\n\t" /* enable channel again */ + "STM r0, {r1-r4}\n\t" /* disable channel and set up new parameters */ + "STR r5, [r0, #0]\n\t" /* enable channel again */ - "POP {r0-r12}\n\t" + "POP {r0-r12}\n\t" - : /* no outputs*/ - : /* inputs */ - [data1] "r"(dma1_data), [data2] "r"(dma2_data) - : "r6", "r7"); + : /* no outputs*/ + : /* inputs */ + [data1] "r"(dma1_data), [data2] "r"(dma2_data) + : "r6", "r7"); LL_DMA_ClearFlag_TC1(DMA1); LL_DMA_ClearFlag_TC2(DMA1); } +#endif + static bool digital_sequence_send_signal(DigitalSequence* sequence, DigitalSignal* signal) { furi_assert(sequence); furi_assert(signal); @@ -443,7 +490,7 @@ bool digital_sequence_send(DigitalSequence* sequence) { } /* update the total remainder */ - remainder += sig->reload_reg_remainder; + remainder += sig->internals->reload_reg_remainder; /* do we need to update the prolongation? */ if(needs_prolongation != sequence->signals_prolonged[signal_index]) { diff --git a/lib/digital_signal/digital_signal.h b/lib/digital_signal/digital_signal.h index 9a91c8b45..2cb107486 100644 --- a/lib/digital_signal/digital_signal.h +++ b/lib/digital_signal/digital_signal.h @@ -5,8 +5,6 @@ #include #include -#include -#include #ifdef __cplusplus extern "C" { @@ -18,31 +16,20 @@ extern "C" { #define DIGITAL_SIGNAL_NS(x) (x * 100UL) #define DIGITAL_SIGNAL_PS(x) (x / 10UL) -typedef struct { +/* using an anonymous type for the internals */ +typedef struct DigitalSignalInternals DigitalSignalInternals; + +/* 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; - 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; + uint32_t* reload_reg_buff; /* internal, but used by unit tests */ + DigitalSignalInternals* internals; } DigitalSignal; -typedef struct { - uint8_t signals_size; - bool bake; - uint32_t sequence_used; - uint32_t sequence_size; - DigitalSignal** signals; - bool* signals_prolonged; - uint8_t* sequence; - const GpioPin* gpio; - uint32_t send_time; -} DigitalSequence; +typedef struct DigitalSequence DigitalSequence; DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt); diff --git a/lib/pulse_reader/pulse_reader.c b/lib/pulse_reader/pulse_reader.c index 3d9508f9a..84ce2ff23 100644 --- a/lib/pulse_reader/pulse_reader.c +++ b/lib/pulse_reader/pulse_reader.c @@ -1,9 +1,32 @@ +#include "pulse_reader.h" + #include #include #include #include -#include "pulse_reader.h" +#include +#include +#include +#include + +struct PulseReader { + uint32_t* timer_buffer; + uint32_t* gpio_buffer; + uint32_t size; + uint32_t pos; + uint32_t timer_value; + uint32_t gpio_value; + uint32_t gpio_mask; + uint32_t unit_multiplier; + uint32_t unit_divider; + uint32_t bit_time; + uint32_t dma_channel; + const GpioPin* gpio; + GpioPull pull; + LL_DMA_InitTypeDef dma_config_timer; + LL_DMA_InitTypeDef dma_config_gpio; +}; #define GPIO_PIN_MAP(pin, prefix) \ (((pin) == (LL_GPIO_PIN_0)) ? prefix##0 : \ diff --git a/lib/pulse_reader/pulse_reader.h b/lib/pulse_reader/pulse_reader.h index be0ad7012..08056a0ac 100644 --- a/lib/pulse_reader/pulse_reader.h +++ b/lib/pulse_reader/pulse_reader.h @@ -3,10 +3,6 @@ #include #include #include -#include -#include -#include -#include #include @@ -28,23 +24,8 @@ typedef enum { PulseReaderUnitMicrosecond, } PulseReaderUnit; -typedef struct { - uint32_t* timer_buffer; - uint32_t* gpio_buffer; - uint32_t size; - uint32_t pos; - uint32_t timer_value; - uint32_t gpio_value; - uint32_t gpio_mask; - uint32_t unit_multiplier; - uint32_t unit_divider; - uint32_t bit_time; - uint32_t dma_channel; - const GpioPin* gpio; - GpioPull pull; - LL_DMA_InitTypeDef dma_config_timer; - LL_DMA_InitTypeDef dma_config_gpio; -} PulseReader; +/* using an anonymous type */ +typedef struct PulseReader PulseReader; /** Allocate a PulseReader object * From bc80ddd0a7c13a81c190b84c2b3815ed21969439 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Thu, 29 Dec 2022 22:36:30 +0100 Subject: [PATCH 19/29] digital_signal/pulse_reader: allow parameters for free to be NULL --- lib/digital_signal/digital_signal.c | 8 ++++++++ lib/pulse_reader/pulse_reader.c | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 12b543273..ae87a09e4 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -71,6 +71,10 @@ DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt) { void digital_signal_free(DigitalSignal* signal) { furi_assert(signal); + if(!signal) { + return; + } + free(signal->edge_timings); free(signal->reload_reg_buff); free(signal->internals); @@ -298,6 +302,10 @@ DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio) { void digital_sequence_free(DigitalSequence* sequence) { furi_assert(sequence); + if(!sequence) { + return; + } + free(sequence->signals); free(sequence->sequence); free(sequence); diff --git a/lib/pulse_reader/pulse_reader.c b/lib/pulse_reader/pulse_reader.c index 84ce2ff23..c8d0e3ff7 100644 --- a/lib/pulse_reader/pulse_reader.c +++ b/lib/pulse_reader/pulse_reader.c @@ -117,6 +117,12 @@ void pulse_reader_set_pull(PulseReader* signal, GpioPull pull) { } void pulse_reader_free(PulseReader* signal) { + furi_assert(signal); + + if(!signal) { + return; + } + free(signal->timer_buffer); free(signal->gpio_buffer); free(signal); From cd082c9d90911c3d65f9b98f41eba25445cb2420 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Fri, 30 Dec 2022 10:50:55 +0100 Subject: [PATCH 20/29] digital_signal: show unoptimized and optimized code for digital_signal_update_dma() next to each other --- lib/digital_signal/digital_signal.c | 62 ++++++++++++++++++----------- 1 file changed, 38 insertions(+), 24 deletions(-) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index ae87a09e4..1be81f135 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -342,36 +342,42 @@ void digital_sequence_add(DigitalSequence* sequence, uint8_t signal_index) { sequence->sequence[sequence->sequence_used++] = signal_index; } -#if defined(DIGITAL_SIGNAL_PORTABLE_CODE) +bool digital_signal_optimization = true; -static void digital_signal_update_dma(DigitalSignal* signal) { - LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_1, (uint32_t)signal->internals->gpio_buff); +static void digital_signal_update_dma_c(DigitalSignal* signal) { + /* if transfer was already active, wait till DMA is done and the last timer ticks are running */ + while(LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2)) { + } + + LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_2, (uint32_t)signal->reload_reg_buff); - LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, 2); LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, signal->internals->reload_reg_entries); - - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_1, (uint32_t)signal->internals->gpio_buff); + LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, 2); + LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); + + LL_DMA_ClearFlag_TC1(DMA1); + LL_DMA_ClearFlag_TC2(DMA1); } -#else - -static void digital_signal_update_dma(DigitalSignal* signal) { +static void digital_signal_update_dma_asm(DigitalSignal* signal) { volatile uint32_t dma1_data[] = { - /* R6 */ (uint32_t) & (DMA1_Channel1->CCR), - /* R7 */ DMA1_Channel1->CCR & ~DMA_CCR_EN, - /* R8 */ 2, - /* R9 */ (uint32_t) & (signal->internals->gpio->port->BSRR), - /* R10 */ (uint32_t)signal->internals->gpio_buff, - /* R11 */ DMA1_Channel1->CCR | DMA_CCR_EN}; + /* R6 */ (uint32_t) & (DMA1_Channel1->CCR), /* base address of DMA channel 1 */ + /* R7 */ DMA1_Channel1->CCR & ~DMA_CCR_EN, /* CCR value to write first */ + /* R8 */ 2, /* CNDTR to write */ + /* R9 */ (uint32_t) & (signal->internals->gpio->port->BSRR), /* CPAR to write */ + /* R10 */ (uint32_t)signal->internals->gpio_buff, /* CMAR to write */ + /* R11 */ DMA1_Channel1->CCR | DMA_CCR_EN}; /* and CCR again to write after finished */ volatile uint32_t dma2_data[] = { - /* R0 */ (uint32_t) & (DMA1_Channel2->CCR), - /* R1 */ DMA1_Channel2->CCR & ~DMA_CCR_EN, - /* R2 */ (uint32_t)signal->internals->reload_reg_entries, - /* R3 */ (uint32_t) & (TIM2->ARR), - /* R4 */ (uint32_t)signal->reload_reg_buff, - /* R5 */ DMA1_Channel2->CCR | DMA_CCR_EN}; + /* R0 */ (uint32_t) & (DMA1_Channel2->CCR), /* base address of DMA channel 2 */ + /* R1 */ DMA1_Channel2->CCR & ~DMA_CCR_EN, /* CCR value to write first */ + /* R2 */ (uint32_t)signal->internals->reload_reg_entries, /* CNDTR to write */ + /* R3 */ (uint32_t) & (TIM2->ARR), /* CPAR to write */ + /* R4 */ (uint32_t)signal->reload_reg_buff, /* CMAR to write */ + /* R5 */ DMA1_Channel2->CCR | DMA_CCR_EN}; /* and CCR again to write after finished */ /* hurry when setting up next transfer */ asm volatile( @@ -389,10 +395,12 @@ static void digital_signal_update_dma(DigitalSignal* signal) { "CMP r12, #0\n\t" "BNE loop\n\t" + /* no transfers left, the DMA has finished. now quickly re-enable with new settings + the next 4 instructions are the critical part */ "STM r6, {r7-r10}\n\t" /* disable channel and set up new parameters */ - "STR r11, [r6, #0]\n\t" /* enable channel again */ + "STR r11, [r6, #0]\n\t" /* enable channel again by writing CCR */ "STM r0, {r1-r4}\n\t" /* disable channel and set up new parameters */ - "STR r5, [r0, #0]\n\t" /* enable channel again */ + "STR r5, [r0, #0]\n\t" /* enable channel again by writing CCR */ "POP {r0-r12}\n\t" @@ -405,7 +413,13 @@ static void digital_signal_update_dma(DigitalSignal* signal) { LL_DMA_ClearFlag_TC2(DMA1); } -#endif +void digital_signal_update_dma(DigitalSignal* signal) { + if(digital_signal_optimization) { + digital_signal_update_dma_asm(signal); + } else { + digital_signal_update_dma_c(signal); + } +} static bool digital_sequence_send_signal(DigitalSequence* sequence, DigitalSignal* signal) { furi_assert(sequence); From 064a34e681235587135ba083c47e046eb1823061 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Fri, 30 Dec 2022 12:21:43 +0100 Subject: [PATCH 21/29] pulse_reader: further optimize assembly code --- lib/digital_signal/digital_signal.c | 37 +++++++++++++---------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 1be81f135..073a69eb8 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -363,7 +363,17 @@ static void digital_signal_update_dma_c(DigitalSignal* signal) { } static void digital_signal_update_dma_asm(DigitalSignal* signal) { - volatile uint32_t dma1_data[] = { + /* this is an "already-prepared" buffer of all DMA channel configs to write */ + const volatile uint32_t dma_data[] = { + /* DMA channel 2 data */ + /* R0 */ (uint32_t) & (DMA1_Channel2->CCR), /* base address of DMA channel 2 */ + /* R1 */ DMA1_Channel2->CCR & ~DMA_CCR_EN, /* CCR value to write first */ + /* R2 */ (uint32_t)signal->internals->reload_reg_entries, /* CNDTR to write */ + /* R3 */ (uint32_t) & (TIM2->ARR), /* CPAR to write */ + /* R4 */ (uint32_t)signal->reload_reg_buff, /* CMAR to write */ + /* R5 */ DMA1_Channel2->CCR | DMA_CCR_EN, /* and CCR again to write after finished */ + + /* DMA channel 1 data */ /* R6 */ (uint32_t) & (DMA1_Channel1->CCR), /* base address of DMA channel 1 */ /* R7 */ DMA1_Channel1->CCR & ~DMA_CCR_EN, /* CCR value to write first */ /* R8 */ 2, /* CNDTR to write */ @@ -371,31 +381,19 @@ static void digital_signal_update_dma_asm(DigitalSignal* signal) { /* R10 */ (uint32_t)signal->internals->gpio_buff, /* CMAR to write */ /* R11 */ DMA1_Channel1->CCR | DMA_CCR_EN}; /* and CCR again to write after finished */ - volatile uint32_t dma2_data[] = { - /* R0 */ (uint32_t) & (DMA1_Channel2->CCR), /* base address of DMA channel 2 */ - /* R1 */ DMA1_Channel2->CCR & ~DMA_CCR_EN, /* CCR value to write first */ - /* R2 */ (uint32_t)signal->internals->reload_reg_entries, /* CNDTR to write */ - /* R3 */ (uint32_t) & (TIM2->ARR), /* CPAR to write */ - /* R4 */ (uint32_t)signal->reload_reg_buff, /* CMAR to write */ - /* R5 */ DMA1_Channel2->CCR | DMA_CCR_EN}; /* and CCR again to write after finished */ - - /* hurry when setting up next transfer */ + /* now wait for the DMA finishing and instantly reconfigure it with as few instructions as possible */ asm volatile( "\t" - "MOV r6, %[data1]\n\t" - "MOV r7, %[data2]\n\t" - "PUSH {r0-r12}\n\t" - "LDM r7, {r0-r5}\n\t" /* prepare registers with values to write into DMA config */ - "LDM r6, {r6-r11}\n\t" + "LDM %[data], {r0-r11}\n\t" /* prepare registers with values to write into DMA config */ - "loop:\n\t" + "wait_for_dma_finished:\n\t" "LDR r12, [r0, #4]\n\t" /* read DMA_CNDTRx to get remaining transfers */ "CMP r12, #0\n\t" - "BNE loop\n\t" + "BNE wait_for_dma_finished\n\t" - /* no transfers left, the DMA has finished. now quickly re-enable with new settings + /* no transfers left, the DMA has finished. now quickly re-enable with new settings. the next 4 instructions are the critical part */ "STM r6, {r7-r10}\n\t" /* disable channel and set up new parameters */ "STR r11, [r6, #0]\n\t" /* enable channel again by writing CCR */ @@ -406,8 +404,7 @@ static void digital_signal_update_dma_asm(DigitalSignal* signal) { : /* no outputs*/ : /* inputs */ - [data1] "r"(dma1_data), [data2] "r"(dma2_data) - : "r6", "r7"); + [data] "r"(dma_data)); LL_DMA_ClearFlag_TC1(DMA1); LL_DMA_ClearFlag_TC2(DMA1); From 17f7f0e637e2aa3714c8a491700bcb81ab2cab16 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Fri, 30 Dec 2022 16:18:55 +0100 Subject: [PATCH 22/29] digital_signal: reduce code complexity of digital_signal_update_dma() by only reconfiguring DMA2 --- lib/digital_signal/digital_signal.c | 53 +++++++++-------------------- 1 file changed, 17 insertions(+), 36 deletions(-) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 073a69eb8..4772b654f 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -350,64 +350,45 @@ static void digital_signal_update_dma_c(DigitalSignal* signal) { } LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); - LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_2, (uint32_t)signal->reload_reg_buff); LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, signal->internals->reload_reg_entries); + LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_2, (uint32_t)signal->reload_reg_buff); LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_1, (uint32_t)signal->internals->gpio_buff); - LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, 2); - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); - - LL_DMA_ClearFlag_TC1(DMA1); - LL_DMA_ClearFlag_TC2(DMA1); } static void digital_signal_update_dma_asm(DigitalSignal* signal) { - /* this is an "already-prepared" buffer of all DMA channel configs to write */ + /* this is an "already-prepared" buffer of DMA channel 2 config to write */ const volatile uint32_t dma_data[] = { - /* DMA channel 2 data */ + /* base addresses of DMA channel register */ /* R0 */ (uint32_t) & (DMA1_Channel2->CCR), /* base address of DMA channel 2 */ - /* R1 */ DMA1_Channel2->CCR & ~DMA_CCR_EN, /* CCR value to write first */ - /* R2 */ (uint32_t)signal->internals->reload_reg_entries, /* CNDTR to write */ - /* R3 */ (uint32_t) & (TIM2->ARR), /* CPAR to write */ - /* R4 */ (uint32_t)signal->reload_reg_buff, /* CMAR to write */ - /* R5 */ DMA1_Channel2->CCR | DMA_CCR_EN, /* and CCR again to write after finished */ - - /* DMA channel 1 data */ - /* R6 */ (uint32_t) & (DMA1_Channel1->CCR), /* base address of DMA channel 1 */ - /* R7 */ DMA1_Channel1->CCR & ~DMA_CCR_EN, /* CCR value to write first */ - /* R8 */ 2, /* CNDTR to write */ - /* R9 */ (uint32_t) & (signal->internals->gpio->port->BSRR), /* CPAR to write */ - /* R10 */ (uint32_t)signal->internals->gpio_buff, /* CMAR to write */ - /* R11 */ DMA1_Channel1->CCR | DMA_CCR_EN}; /* and CCR again to write after finished */ + /* R1 */ DMA1_Channel2->CCR | DMA_CCR_EN, /* CCR to write after finished */ + /* R2 */ DMA1_Channel2->CCR & ~DMA_CCR_EN, /* CCR value to write first */ + /* R3 */ (uint32_t)signal->internals->reload_reg_entries, /* CNDTR to write */ + /* R4 */ (uint32_t) & (TIM2->ARR), /* CPAR to write */ + /* R5 */ (uint32_t)signal->reload_reg_buff, /* CMAR to write */ + }; /* now wait for the DMA finishing and instantly reconfigure it with as few instructions as possible */ asm volatile( "\t" - "PUSH {r0-r12}\n\t" + "PUSH {r0-r6}\n\t" - "LDM %[data], {r0-r11}\n\t" /* prepare registers with values to write into DMA config */ + "LDM %[data], {r0-r5}\n\t" /* prepare registers with values to write into DMA config */ "wait_for_dma_finished:\n\t" - "LDR r12, [r0, #4]\n\t" /* read DMA_CNDTRx to get remaining transfers */ - "CMP r12, #0\n\t" + "LDR r6, [r0, #4]\n\t" /* read DMA_CNDTRx of DMA1 chan 2 to get remaining transfers */ + "CMP r6, #0\n\t" "BNE wait_for_dma_finished\n\t" /* no transfers left, the DMA has finished. now quickly re-enable with new settings. - the next 4 instructions are the critical part */ - "STM r6, {r7-r10}\n\t" /* disable channel and set up new parameters */ - "STR r11, [r6, #0]\n\t" /* enable channel again by writing CCR */ - "STM r0, {r1-r4}\n\t" /* disable channel and set up new parameters */ - "STR r5, [r0, #0]\n\t" /* enable channel again by writing CCR */ + these next 2 instructions are the critical part */ + "STM r0, {r2-r5}\n\t" /* disable channel and set up new parameters */ + "STR r1, [r0, #0]\n\t" /* enable channel again by writing CCR */ - "POP {r0-r12}\n\t" + "POP {r0-r6}\n\t" : /* no outputs*/ : /* inputs */ [data] "r"(dma_data)); - - LL_DMA_ClearFlag_TC1(DMA1); - LL_DMA_ClearFlag_TC2(DMA1); } void digital_signal_update_dma(DigitalSignal* signal) { From 4c3a5e9c125e61bc05f44903f0bbcf704675fa1a Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Fri, 30 Dec 2022 16:34:52 +0100 Subject: [PATCH 23/29] digital_signal: remove assembly code, limiting the performance but increasing portability --- lib/digital_signal/digital_signal.c | 54 ++++------------------------- 1 file changed, 6 insertions(+), 48 deletions(-) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 4772b654f..f420652f4 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -342,63 +342,21 @@ void digital_sequence_add(DigitalSequence* sequence, uint8_t signal_index) { sequence->sequence[sequence->sequence_used++] = signal_index; } -bool digital_signal_optimization = true; +static void digital_signal_update_dma(DigitalSignal* signal) { + /* keep them prepared in registers so there is less delay when writing */ + register volatile uint16_t len = signal->internals->reload_reg_entries; + register volatile uint32_t addr = (uint32_t)signal->reload_reg_buff; -static void digital_signal_update_dma_c(DigitalSignal* signal) { /* if transfer was already active, wait till DMA is done and the last timer ticks are running */ while(LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2)) { } LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); - LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, signal->internals->reload_reg_entries); - LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_2, (uint32_t)signal->reload_reg_buff); + LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, len); + LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_2, addr); LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); } -static void digital_signal_update_dma_asm(DigitalSignal* signal) { - /* this is an "already-prepared" buffer of DMA channel 2 config to write */ - const volatile uint32_t dma_data[] = { - /* base addresses of DMA channel register */ - /* R0 */ (uint32_t) & (DMA1_Channel2->CCR), /* base address of DMA channel 2 */ - /* R1 */ DMA1_Channel2->CCR | DMA_CCR_EN, /* CCR to write after finished */ - /* R2 */ DMA1_Channel2->CCR & ~DMA_CCR_EN, /* CCR value to write first */ - /* R3 */ (uint32_t)signal->internals->reload_reg_entries, /* CNDTR to write */ - /* R4 */ (uint32_t) & (TIM2->ARR), /* CPAR to write */ - /* R5 */ (uint32_t)signal->reload_reg_buff, /* CMAR to write */ - }; - - /* now wait for the DMA finishing and instantly reconfigure it with as few instructions as possible */ - asm volatile( - "\t" - "PUSH {r0-r6}\n\t" - - "LDM %[data], {r0-r5}\n\t" /* prepare registers with values to write into DMA config */ - - "wait_for_dma_finished:\n\t" - "LDR r6, [r0, #4]\n\t" /* read DMA_CNDTRx of DMA1 chan 2 to get remaining transfers */ - "CMP r6, #0\n\t" - "BNE wait_for_dma_finished\n\t" - - /* no transfers left, the DMA has finished. now quickly re-enable with new settings. - these next 2 instructions are the critical part */ - "STM r0, {r2-r5}\n\t" /* disable channel and set up new parameters */ - "STR r1, [r0, #0]\n\t" /* enable channel again by writing CCR */ - - "POP {r0-r6}\n\t" - - : /* no outputs*/ - : /* inputs */ - [data] "r"(dma_data)); -} - -void digital_signal_update_dma(DigitalSignal* signal) { - if(digital_signal_optimization) { - digital_signal_update_dma_asm(signal); - } else { - digital_signal_update_dma_c(signal); - } -} - static bool digital_sequence_send_signal(DigitalSequence* sequence, DigitalSignal* signal) { furi_assert(sequence); furi_assert(signal); From 7e42c9c3f0df43d683ed2002f7b0bb6b1d29df0c Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Tue, 3 Jan 2023 02:10:30 +0100 Subject: [PATCH 24/29] added recovery if the timer already expired --- lib/digital_signal/digital_signal.c | 50 +++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index f420652f4..e07235ece 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -1,6 +1,7 @@ #include "digital_signal.h" #include +#include #include #include @@ -17,6 +18,7 @@ struct DigitalSequence { uint8_t* sequence; const GpioPin* gpio; uint32_t send_time; + bool send_time_active; }; struct DigitalSignalInternals { @@ -31,7 +33,7 @@ struct DigitalSignalInternals { #define TAG "DigitalSignal" #define F_TIM (64000000.0) -#define T_TIM 1562 /* 15.625 ns *100 */ +#define T_TIM 1562 /* 15.625 ns *100 */ #define T_TIM_DIV2 781 /* 15.625 ns / 2 *100 */ DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt) { @@ -285,9 +287,12 @@ void digital_sequence_alloc_sequence(DigitalSequence* sequence, uint32_t size) { 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; @@ -327,7 +332,10 @@ void digital_sequence_set_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) { @@ -344,23 +352,40 @@ void digital_sequence_add(DigitalSequence* sequence, uint8_t signal_index) { static void digital_signal_update_dma(DigitalSignal* signal) { /* keep them prepared in registers so there is less delay when writing */ + register bool restart_needed = false; register volatile uint16_t len = signal->internals->reload_reg_entries; register volatile uint32_t addr = (uint32_t)signal->reload_reg_buff; - /* if transfer was already active, wait till DMA is done and the last timer ticks are running */ - while(LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2)) { + /* first make sure it will still count down, else we will risk waiting infinitely */ + const uint32_t wait_ms = 10; + const uint32_t wait_ticks = wait_ms * 1000 * furi_hal_cortex_instructions_per_microsecond(); + uint16_t prev_remain = LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2); + uint32_t prev_timer = DWT->CYCCNT; + + while(prev_remain == LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2)) { + if(DWT->CYCCNT - prev_timer > wait_ticks) { + restart_needed = true; + break; + } + } + + if(!restart_needed) { + /* if transfer was already active, wait till DMA is done and the last timer ticks are running */ + while(LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2)) { + } } LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, len); LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_2, addr); LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); + + if(restart_needed) { + LL_TIM_GenerateEvent_UPDATE(TIM2); + } } static bool digital_sequence_send_signal(DigitalSequence* sequence, DigitalSignal* signal) { - furi_assert(sequence); - furi_assert(signal); - /* the first iteration has to set up the whole machinery */ if(!LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_1)) { if(!digital_signal_setup_dma(signal)) { @@ -370,13 +395,9 @@ static bool digital_sequence_send_signal(DigitalSequence* sequence, DigitalSigna digital_signal_setup_timer(); /* if the send time is specified, wait till the core timer passed beyond that time */ - if(sequence->send_time != 0) { - while(true) { - uint32_t delta = sequence->send_time - DWT->CYCCNT; - /* yeah, it's making use of underflows... */ - if(delta > 0x80000000) { - break; - } + if(sequence->send_time_active) { + sequence->send_time_active = false; + while(sequence->send_time - DWT->CYCCNT < 0x80000000) { } } digital_signal_start_timer(); @@ -389,6 +410,8 @@ static bool digital_sequence_send_signal(DigitalSequence* sequence, DigitalSigna } DigitalSignal* digital_sequence_bake(DigitalSequence* sequence) { + furi_assert(sequence); + uint32_t edges = 0; for(uint32_t pos = 0; pos < sequence->sequence_used; pos++) { @@ -468,6 +491,7 @@ bool digital_sequence_send(DigitalSequence* sequence) { } FURI_CRITICAL_EXIT(); + /* wait until last dma transaction was finished */ while(LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2)) { } From 4f076485a31027f72f1b9affb0c3d370d5bac391 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Mon, 16 Jan 2023 10:49:01 +0100 Subject: [PATCH 25/29] digital_signal: fix memory leak --- lib/digital_signal/digital_signal.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index e07235ece..91f5b2173 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -292,7 +292,7 @@ void digital_sequence_alloc_sequence(DigitalSequence* sequence, uint32_t size) { DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio) { furi_assert(gpio); - + DigitalSequence* sequence = malloc(sizeof(DigitalSequence)); sequence->gpio = gpio; @@ -313,6 +313,7 @@ void digital_sequence_free(DigitalSequence* sequence) { free(sequence->signals); free(sequence->sequence); + free(sequence->signals_prolonged); free(sequence); } @@ -333,7 +334,7 @@ void digital_sequence_set_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; } @@ -365,7 +366,7 @@ static void digital_signal_update_dma(DigitalSignal* signal) { while(prev_remain == LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2)) { if(DWT->CYCCNT - prev_timer > wait_ticks) { restart_needed = true; - break; + break; } } @@ -411,7 +412,7 @@ static bool digital_sequence_send_signal(DigitalSequence* sequence, DigitalSigna DigitalSignal* digital_sequence_bake(DigitalSequence* sequence) { furi_assert(sequence); - + uint32_t edges = 0; for(uint32_t pos = 0; pos < sequence->sequence_used; pos++) { From e2b77fcfb2918b58645491b0da3006d6199a385d Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Mon, 16 Jan 2023 18:09:35 +0100 Subject: [PATCH 26/29] digital_signal: keep lock until all DMA transfers have finished --- lib/digital_signal/digital_signal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 91f5b2173..474e840a0 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -490,7 +490,6 @@ bool digital_sequence_send(DigitalSequence* sequence) { break; } } - FURI_CRITICAL_EXIT(); /* wait until last dma transaction was finished */ while(LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2)) { @@ -498,6 +497,7 @@ bool digital_sequence_send(DigitalSequence* sequence) { digital_signal_stop_timer(); digital_signal_stop_dma(); + FURI_CRITICAL_EXIT(); /* undo previously prolonged edges */ for(uint32_t pos = 0; pos < sequence->signals_size; pos++) { From 826e4d1eda4de0d8f224ce4052db30eb8da98b4e Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Wed, 1 Feb 2023 23:25:23 +0100 Subject: [PATCH 27/29] DigitalSequence: fix issues with concatenation of same levels and spurious bit flips --- lib/digital_signal/digital_signal.c | 282 +++++++++++++++++++--------- 1 file changed, 195 insertions(+), 87 deletions(-) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 474e840a0..4ceca2ead 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -8,17 +8,26 @@ #include #include + +struct ReloadBuffers { + uint32_t** buffers; /* pointers to the shadow buffers, either one or two. NULL if none */ + uint32_t count; /* number of allocated buffers, 0, 1 or 2 */ + uint32_t size; /* maximum entry count of a single buffer */ + uint32_t current; /* current buffer index, the other one is most likely being used */ + uint32_t entries; /* entries in the current buffer */ +}; + struct DigitalSequence { uint8_t signals_size; bool bake; uint32_t sequence_used; uint32_t sequence_size; DigitalSignal** signals; - bool* signals_prolonged; uint8_t* sequence; const GpioPin* gpio; uint32_t send_time; bool send_time_active; + struct ReloadBuffers* reload; }; struct DigitalSignalInternals { @@ -28,6 +37,7 @@ struct DigitalSignalInternals { const GpioPin* gpio; LL_DMA_InitTypeDef dma_config_gpio; LL_DMA_InitTypeDef dma_config_timer; + struct ReloadBuffers* reload; }; #define TAG "DigitalSignal" @@ -45,27 +55,30 @@ DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt) { signal->reload_reg_buff = malloc(signal->edges_max_cnt * sizeof(uint32_t)); signal->internals = malloc(sizeof(DigitalSignalInternals)); - signal->internals->reload_reg_entries = 0; - signal->internals->reload_reg_remainder = 0; - signal->internals->dma_config_gpio.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; - signal->internals->dma_config_gpio.Mode = LL_DMA_MODE_CIRCULAR; - signal->internals->dma_config_gpio.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; - signal->internals->dma_config_gpio.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; - signal->internals->dma_config_gpio.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; - signal->internals->dma_config_gpio.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; - signal->internals->dma_config_gpio.NbData = 2; - signal->internals->dma_config_gpio.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; - signal->internals->dma_config_gpio.Priority = LL_DMA_PRIORITY_VERYHIGH; + DigitalSignalInternals* internals = signal->internals; + internals->reload = NULL; + internals->reload_reg_entries = 0; + internals->reload_reg_remainder = 0; - signal->internals->dma_config_timer.PeriphOrM2MSrcAddress = (uint32_t) & (TIM2->ARR); - signal->internals->dma_config_timer.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; - signal->internals->dma_config_timer.Mode = LL_DMA_MODE_NORMAL; - signal->internals->dma_config_timer.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; - signal->internals->dma_config_timer.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; - signal->internals->dma_config_timer.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; - signal->internals->dma_config_timer.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; - signal->internals->dma_config_timer.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; - signal->internals->dma_config_timer.Priority = LL_DMA_PRIORITY_HIGH; + 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; return signal; } @@ -79,6 +92,12 @@ void digital_signal_free(DigitalSignal* signal) { free(signal->edge_timings); free(signal->reload_reg_buff); + if(signal->internals->reload) { + if(signal->internals->reload->buffers) { + free(signal->internals->reload->buffers); + } + free(signal->internals->reload); + } free(signal->internals); free(signal); } @@ -164,33 +183,55 @@ uint32_t digital_signal_get_edge(DigitalSignal* signal, uint32_t edge_num) { void digital_signal_prepare_arr(DigitalSignal* signal) { furi_assert(signal); + DigitalSignalInternals* internals = signal->internals; + /* set up signal polarities */ - if(signal->internals->gpio) { - uint32_t bit_set = signal->internals->gpio->pin; - uint32_t bit_reset = signal->internals->gpio->pin << 16; + if(internals->gpio) { + uint32_t bit_set = internals->gpio->pin; + uint32_t bit_reset = internals->gpio->pin << 16; + +#ifdef DEBUG_OUTPUT + bit_set |= gpio_ext_pb3.pin; + bit_reset |= gpio_ext_pb3.pin << 16; +#endif if(signal->start_level) { - signal->internals->gpio_buff[0] = bit_set; - signal->internals->gpio_buff[1] = bit_reset; + internals->gpio_buff[0] = bit_set; + internals->gpio_buff[1] = bit_reset; } else { - signal->internals->gpio_buff[0] = bit_reset; - signal->internals->gpio_buff[1] = bit_set; + internals->gpio_buff[0] = bit_reset; + internals->gpio_buff[1] = bit_set; } } /* set up edge timings */ - signal->internals->reload_reg_entries = 0; + internals->reload_reg_entries = 0; for(size_t pos = 0; pos < signal->edge_cnt; pos++) { - uint32_t pulse_duration = - signal->edge_timings[pos] + signal->internals->reload_reg_remainder; + uint32_t pulse_duration = signal->edge_timings[pos] + internals->reload_reg_remainder; uint32_t pulse_ticks = (pulse_duration + T_TIM_DIV2) / T_TIM; - signal->internals->reload_reg_remainder = pulse_duration - (pulse_ticks * T_TIM); + internals->reload_reg_remainder = pulse_duration - (pulse_ticks * T_TIM); if(pulse_ticks > 1) { - signal->reload_reg_buff[signal->internals->reload_reg_entries++] = pulse_ticks - 1; + signal->reload_reg_buff[internals->reload_reg_entries++] = pulse_ticks - 1; } } + + /* in case there are no shadow buffers defined, allocate and use the precalced data */ + if(!internals->reload || !internals->reload->count) { + if(internals->reload) { + free(internals->reload); + } + internals->reload = malloc(sizeof(struct ReloadBuffers)); + internals->reload->count = 1; + internals->reload->size = signal->edges_max_cnt; + internals->reload->buffers = malloc(sizeof(uint32_t*)); + internals->reload->buffers[0] = malloc(internals->reload->size * sizeof(uint32_t)); + memcpy( + internals->reload->buffers[0], + signal->reload_reg_buff, + internals->reload_reg_entries * sizeof(uint32_t)); + } } static void digital_signal_stop_dma() { @@ -202,32 +243,8 @@ static void digital_signal_stop_dma() { static void digital_signal_stop_timer() { LL_TIM_DisableCounter(TIM2); - LL_TIM_SetCounter(TIM2, 0); -} - -static bool digital_signal_setup_dma(DigitalSignal* signal) { - furi_assert(signal); - - if(!signal->internals->reload_reg_entries) { - return false; - } - - signal->internals->dma_config_gpio.MemoryOrM2MDstAddress = - (uint32_t)signal->internals->gpio_buff; - signal->internals->dma_config_gpio.PeriphOrM2MSrcAddress = - (uint32_t) & (signal->internals->gpio->port->BSRR); - signal->internals->dma_config_timer.MemoryOrM2MDstAddress = (uint32_t)signal->reload_reg_buff; - signal->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, &signal->internals->dma_config_gpio); - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &signal->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; + LL_TIM_DisableUpdateEvent(TIM2); + LL_TIM_DisableDMAReq_UPDATE(TIM2); } static void digital_signal_setup_timer() { @@ -236,15 +253,45 @@ static void digital_signal_setup_timer() { 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, 10); + LL_TIM_SetAutoReload(TIM2, 0xFFFFFFFF); LL_TIM_SetCounter(TIM2, 0); - LL_TIM_EnableUpdateEvent(TIM2); - LL_TIM_EnableDMAReq_UPDATE(TIM2); } static void digital_signal_start_timer() { - LL_TIM_GenerateEvent_UPDATE(TIM2); 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; + + uint32_t buffer_entries = internals->reload->entries; + if(!buffer_entries || !internals->reload || !internals->reload->buffers) { + 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)internals->reload->buffers[internals->reload->current]; + internals->dma_config_timer.NbData = buffer_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); + + /* buffer is used now by DMA, skip to next */ + internals->reload->current = (internals->reload->current + 1) % internals->reload->count; + + return true; } void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) { @@ -279,7 +326,6 @@ void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) { void digital_sequence_alloc_signals(DigitalSequence* sequence, uint32_t size) { sequence->signals_size = size; sequence->signals = malloc(sequence->signals_size * sizeof(DigitalSignal*)); - sequence->signals_prolonged = malloc(sequence->signals_size * sizeof(bool)); } void digital_sequence_alloc_sequence(DigitalSequence* sequence, uint32_t size) { @@ -298,6 +344,13 @@ DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio) { sequence->gpio = gpio; sequence->bake = false; + sequence->reload = malloc(sizeof(struct ReloadBuffers)); + sequence->reload->count = 2; + sequence->reload->size = 512; + sequence->reload->buffers = malloc(sizeof(uint32_t*)); + sequence->reload->buffers[0] = malloc(sequence->reload->size * sizeof(uint32_t)); + sequence->reload->buffers[1] = malloc(sequence->reload->size * sizeof(uint32_t)); + digital_sequence_alloc_signals(sequence, 32); digital_sequence_alloc_sequence(sequence, size); @@ -311,9 +364,17 @@ void digital_sequence_free(DigitalSequence* sequence) { return; } + /* de-assign the shared reload buffer */ + for(int pos = 0; pos < sequence->signals_size; pos++) { + if(sequence->signals[pos]) { + sequence->signals[pos]->internals->reload = NULL; + } + } + free(sequence->signals); free(sequence->sequence); - free(sequence->signals_prolonged); + free(sequence->reload->buffers); + free(sequence->reload); free(sequence); } @@ -325,10 +386,38 @@ void digital_sequence_set_signal( furi_assert(signal); furi_assert(signal_index < sequence->signals_size); + /* if there is already a signal, unassign the shared reload buffer */ + if(sequence->signals[signal_index]) { + sequence->signals[signal_index]->internals->reload = NULL; + } + sequence->signals[signal_index] = signal; signal->internals->gpio = sequence->gpio; signal->internals->reload_reg_remainder = 0; + /* free the original reload buffer */ + if(signal->internals->reload) { + if(signal->internals->reload->buffers) { + for(uint32_t pos = 0; pos < signal->internals->reload->count; pos++) { + free(signal->internals->reload->buffers[pos]); + } + free(signal->internals->reload->buffers); + } + free(signal->internals->reload); + } + + /* assign the sequence's shared reload buffer */ + signal->internals->reload = sequence->reload; + + /* ensure it is big enough and reallocate if not */ + if(sequence->reload->size < signal->edges_max_cnt) { + free(sequence->reload->buffers); + + sequence->reload->size = signal->edges_max_cnt; + sequence->reload->buffers[0] = malloc(sequence->reload->size * sizeof(uint32_t)); + sequence->reload->buffers[1] = malloc(sequence->reload->size * sizeof(uint32_t)); + } + digital_signal_prepare_arr(signal); } @@ -352,10 +441,11 @@ void digital_sequence_add(DigitalSequence* sequence, uint8_t signal_index) { } static void digital_signal_update_dma(DigitalSignal* signal) { + struct ReloadBuffers* reload = signal->internals->reload; /* keep them prepared in registers so there is less delay when writing */ register bool restart_needed = false; - register volatile uint16_t len = signal->internals->reload_reg_entries; - register volatile uint32_t addr = (uint32_t)signal->reload_reg_buff; + register volatile uint16_t len = reload->entries; + register volatile uint32_t addr = (uint32_t)reload->buffers[reload->current]; /* first make sure it will still count down, else we will risk waiting infinitely */ const uint32_t wait_ms = 10; @@ -374,6 +464,8 @@ static void digital_signal_update_dma(DigitalSignal* signal) { /* if transfer was already active, wait till DMA is done and the last timer ticks are running */ while(LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2)) { } + } else { + FURI_LOG_D(TAG, "digital_sequence_send_signal: DMA hung, restart needed"); } LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); @@ -384,6 +476,8 @@ static void digital_signal_update_dma(DigitalSignal* signal) { if(restart_needed) { LL_TIM_GenerateEvent_UPDATE(TIM2); } + + reload->current = (reload->current + 1) % reload->count; } static bool digital_sequence_send_signal(DigitalSequence* sequence, DigitalSignal* signal) { @@ -437,7 +531,12 @@ DigitalSignal* digital_sequence_bake(DigitalSequence* sequence) { bool digital_sequence_send(DigitalSequence* sequence) { furi_assert(sequence); + struct ReloadBuffers* reload = sequence->reload; + furi_hal_gpio_init(sequence->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); +#ifdef DEBUG_OUTPUT + furi_hal_gpio_init(&gpio_ext_pb3, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); +#endif if(sequence->bake) { DigitalSignal* sig = digital_sequence_bake(sequence); @@ -450,9 +549,16 @@ bool digital_sequence_send(DigitalSequence* sequence) { int32_t remainder = 0; FURI_CRITICAL_ENTER(); + bool traded_first = false; + for(uint32_t pos = 0; pos < sequence->sequence_used; pos++) { uint8_t signal_index = sequence->sequence[pos]; DigitalSignal* sig = sequence->signals[signal_index]; + DigitalSignal* sig_next = NULL; + + if(pos + 1 < sequence->sequence_used) { + sig_next = sequence->signals[sequence->sequence[pos + 1]]; + } if(!sig) { FURI_LOG_D( @@ -463,27 +569,39 @@ bool digital_sequence_send(DigitalSequence* sequence) { break; } - /* when we are too late more than half a tick, make the first edge temporarily longer */ - bool needs_prolongation = false; + /* if the first edge is handled by prolonging the last pulse of the previous signal, skip it here */ + reload->entries = sig->edge_cnt - (traded_first ? 1 : 0); + memcpy( + reload->buffers[reload->current], + &sig->reload_reg_buff[traded_first ? 1 : 0], + reload->entries * sizeof(uint32_t)); + traded_first = false; + + /* when we are too late more than half a tick, make the first edge temporarily longer */ if(remainder >= T_TIM_DIV2) { remainder -= T_TIM; - needs_prolongation = true; + reload->buffers[reload->current][0] += 1; } /* update the total remainder */ remainder += sig->internals->reload_reg_remainder; - /* do we need to update the prolongation? */ - if(needs_prolongation != sequence->signals_prolonged[signal_index]) { - if(needs_prolongation) { - sig->edge_timings[0]++; - } else { - sig->edge_timings[0]--; + /* when a signal ends with the same level as the next signal begins, let the fist signal generate the whole pulse */ + if(sig_next) { + /* 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); + + /* take from the next, add it to the first */ + if(end_level == sig_next->start_level) { + /* add the traded prolongation to the last pulse */ + reload->buffers[reload->current][reload->entries - 1] += + sig_next->reload_reg_buff[0]; + traded_first = true; } - sequence->signals_prolonged[signal_index] = needs_prolongation; } + /* transmit */ bool success = digital_sequence_send_signal(sequence, sig); if(!success) { @@ -499,16 +617,6 @@ bool digital_sequence_send(DigitalSequence* sequence) { digital_signal_stop_dma(); FURI_CRITICAL_EXIT(); - /* undo previously prolonged edges */ - for(uint32_t pos = 0; pos < sequence->signals_size; pos++) { - DigitalSignal* sig = sequence->signals[pos]; - - if(sig && sequence->signals_prolonged[pos]) { - sig->edge_timings[0]--; - sequence->signals_prolonged[pos] = false; - } - } - return true; } From 3cb1b1f6c17fe9da185a73584166c28c7eb6693c Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Sat, 4 Feb 2023 01:39:56 +0100 Subject: [PATCH 28/29] DigitalSignal: use cyclic DMA buffer for sequences --- lib/digital_signal/digital_signal.c | 435 +++++++++++++++------------- lib/digital_signal/digital_signal.h | 1 + 2 files changed, 234 insertions(+), 202 deletions(-) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 4ceca2ead..59278277c 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -8,13 +8,15 @@ #include #include +/* must be on bank B */ +#define DEBUG_OUTPUT gpio_ext_pb3 -struct ReloadBuffers { - uint32_t** buffers; /* pointers to the shadow buffers, either one or two. NULL if none */ - uint32_t count; /* number of allocated buffers, 0, 1 or 2 */ - uint32_t size; /* maximum entry count of a single buffer */ - uint32_t current; /* current buffer index, the other one is most likely being used */ - uint32_t entries; /* entries in the current buffer */ +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 { @@ -27,17 +29,20 @@ struct DigitalSequence { const GpioPin* gpio; uint32_t send_time; bool send_time_active; - struct ReloadBuffers* reload; + 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; - struct ReloadBuffers* reload; }; #define TAG "DigitalSignal" @@ -56,9 +61,8 @@ DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt) { signal->internals = malloc(sizeof(DigitalSignalInternals)); DigitalSignalInternals* internals = signal->internals; - internals->reload = NULL; - internals->reload_reg_entries = 0; - internals->reload_reg_remainder = 0; + + internals->factor = 1024 * 1024; internals->dma_config_gpio.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; internals->dma_config_gpio.Mode = LL_DMA_MODE_CIRCULAR; @@ -92,12 +96,6 @@ void digital_signal_free(DigitalSignal* signal) { free(signal->edge_timings); free(signal->reload_reg_buff); - if(signal->internals->reload) { - if(signal->internals->reload->buffers) { - free(signal->internals->reload->buffers); - } - free(signal->internals->reload); - } free(signal->internals); free(signal); } @@ -191,8 +189,8 @@ void digital_signal_prepare_arr(DigitalSignal* signal) { uint32_t bit_reset = internals->gpio->pin << 16; #ifdef DEBUG_OUTPUT - bit_set |= gpio_ext_pb3.pin; - bit_reset |= gpio_ext_pb3.pin << 16; + bit_set |= DEBUG_OUTPUT.pin; + bit_reset |= DEBUG_OUTPUT.pin << 16; #endif if(signal->start_level) { @@ -208,7 +206,17 @@ void digital_signal_prepare_arr(DigitalSignal* signal) { 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; + uint32_t edge_scaled = (internals->factor * signal->edge_timings[pos]) / (1024 * 1024); + uint32_t pulse_duration = edge_scaled + 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); @@ -216,22 +224,6 @@ void digital_signal_prepare_arr(DigitalSignal* signal) { signal->reload_reg_buff[internals->reload_reg_entries++] = pulse_ticks - 1; } } - - /* in case there are no shadow buffers defined, allocate and use the precalced data */ - if(!internals->reload || !internals->reload->count) { - if(internals->reload) { - free(internals->reload); - } - internals->reload = malloc(sizeof(struct ReloadBuffers)); - internals->reload->count = 1; - internals->reload->size = signal->edges_max_cnt; - internals->reload->buffers = malloc(sizeof(uint32_t*)); - internals->reload->buffers[0] = malloc(internals->reload->size * sizeof(uint32_t)); - memcpy( - internals->reload->buffers[0], - signal->reload_reg_buff, - internals->reload_reg_entries * sizeof(uint32_t)); - } } static void digital_signal_stop_dma() { @@ -268,17 +260,15 @@ static bool digital_signal_setup_dma(DigitalSignal* signal) { furi_assert(signal); DigitalSignalInternals* internals = signal->internals; - uint32_t buffer_entries = internals->reload->entries; - if(!buffer_entries || !internals->reload || !internals->reload->buffers) { + 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)internals->reload->buffers[internals->reload->current]; - internals->dma_config_timer.NbData = buffer_entries; + 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); @@ -288,9 +278,6 @@ static bool digital_signal_setup_dma(DigitalSignal* signal) { LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); - /* buffer is used now by DMA, skip to next */ - internals->reload->current = (internals->reload->current + 1) % internals->reload->count; - return true; } @@ -323,12 +310,12 @@ void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) { signal->edge_cnt--; } -void digital_sequence_alloc_signals(DigitalSequence* sequence, uint32_t size) { +static void digital_sequence_alloc_signals(DigitalSequence* sequence, uint32_t size) { sequence->signals_size = size; sequence->signals = malloc(sequence->signals_size * sizeof(DigitalSignal*)); } -void digital_sequence_alloc_sequence(DigitalSequence* sequence, uint32_t size) { +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); @@ -344,12 +331,31 @@ DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio) { sequence->gpio = gpio; sequence->bake = false; - sequence->reload = malloc(sizeof(struct ReloadBuffers)); - sequence->reload->count = 2; - sequence->reload->size = 512; - sequence->reload->buffers = malloc(sizeof(uint32_t*)); - sequence->reload->buffers[0] = malloc(sequence->reload->size * sizeof(uint32_t)); - sequence->reload->buffers[1] = malloc(sequence->reload->size * sizeof(uint32_t)); + sequence->dma_buffer = malloc(sizeof(struct ReloadBuffer)); + sequence->dma_buffer->size = 32; + 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, 32); digital_sequence_alloc_sequence(sequence, size); @@ -364,17 +370,10 @@ void digital_sequence_free(DigitalSequence* sequence) { return; } - /* de-assign the shared reload buffer */ - for(int pos = 0; pos < sequence->signals_size; pos++) { - if(sequence->signals[pos]) { - sequence->signals[pos]->internals->reload = NULL; - } - } - free(sequence->signals); free(sequence->sequence); - free(sequence->reload->buffers); - free(sequence->reload); + free(sequence->dma_buffer->buffer); + free(sequence->dma_buffer); free(sequence); } @@ -386,38 +385,10 @@ void digital_sequence_set_signal( furi_assert(signal); furi_assert(signal_index < sequence->signals_size); - /* if there is already a signal, unassign the shared reload buffer */ - if(sequence->signals[signal_index]) { - sequence->signals[signal_index]->internals->reload = NULL; - } - sequence->signals[signal_index] = signal; signal->internals->gpio = sequence->gpio; signal->internals->reload_reg_remainder = 0; - /* free the original reload buffer */ - if(signal->internals->reload) { - if(signal->internals->reload->buffers) { - for(uint32_t pos = 0; pos < signal->internals->reload->count; pos++) { - free(signal->internals->reload->buffers[pos]); - } - free(signal->internals->reload->buffers); - } - free(signal->internals->reload); - } - - /* assign the sequence's shared reload buffer */ - signal->internals->reload = sequence->reload; - - /* ensure it is big enough and reallocate if not */ - if(sequence->reload->size < signal->edges_max_cnt) { - free(sequence->reload->buffers); - - sequence->reload->size = signal->edges_max_cnt; - sequence->reload->buffers[0] = malloc(sequence->reload->size * sizeof(uint32_t)); - sequence->reload->buffers[1] = malloc(sequence->reload->size * sizeof(uint32_t)); - } - digital_signal_prepare_arr(signal); } @@ -440,71 +411,26 @@ void digital_sequence_add(DigitalSequence* sequence, uint8_t signal_index) { sequence->sequence[sequence->sequence_used++] = signal_index; } -static void digital_signal_update_dma(DigitalSignal* signal) { - struct ReloadBuffers* reload = signal->internals->reload; - /* keep them prepared in registers so there is less delay when writing */ - register bool restart_needed = false; - register volatile uint16_t len = reload->entries; - register volatile uint32_t addr = (uint32_t)reload->buffers[reload->current]; +static bool digital_sequence_setup_dma(DigitalSequence* sequence) { + furi_assert(sequence); - /* first make sure it will still count down, else we will risk waiting infinitely */ - const uint32_t wait_ms = 10; - const uint32_t wait_ticks = wait_ms * 1000 * furi_hal_cortex_instructions_per_microsecond(); - uint16_t prev_remain = LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2); - uint32_t prev_timer = DWT->CYCCNT; + digital_signal_stop_dma(); - while(prev_remain == LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2)) { - if(DWT->CYCCNT - prev_timer > wait_ticks) { - restart_needed = true; - break; - } - } + sequence->dma_config_gpio.MemoryOrM2MDstAddress = (uint32_t)sequence->gpio_buff; + sequence->dma_config_gpio.PeriphOrM2MSrcAddress = (uint32_t) & (sequence->gpio->port->BSRR); - if(!restart_needed) { - /* if transfer was already active, wait till DMA is done and the last timer ticks are running */ - while(LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2)) { - } - } else { - FURI_LOG_D(TAG, "digital_sequence_send_signal: DMA hung, restart needed"); - } + /* 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); - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); - LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, len); - LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_2, addr); + /* enable both DMA channels */ + LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); - if(restart_needed) { - LL_TIM_GenerateEvent_UPDATE(TIM2); - } - - reload->current = (reload->current + 1) % reload->count; -} - -static bool digital_sequence_send_signal(DigitalSequence* sequence, DigitalSignal* signal) { - /* the first iteration has to set up the whole machinery */ - if(!LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_1)) { - if(!digital_signal_setup_dma(signal)) { - FURI_LOG_D(TAG, "digital_sequence_send_signal: Signal has no entries, aborting"); - return false; - } - 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(); - } else { - /* configure next polarities and timings */ - digital_signal_update_dma(signal); - } - return true; } -DigitalSignal* digital_sequence_bake(DigitalSequence* sequence) { +static DigitalSignal* digital_sequence_bake(DigitalSequence* sequence) { furi_assert(sequence); uint32_t edges = 0; @@ -528,14 +454,94 @@ DigitalSignal* digital_sequence_bake(DigitalSequence* sequence) { return ret; } +static void digital_sequence_update_pos(DigitalSequence* sequence) { + struct ReloadBuffer* dma_buffer = sequence->dma_buffer; + + dma_buffer->read_pos = dma_buffer->size - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2); +} + +static const uint32_t wait_ms = 10; +static const uint32_t wait_ticks = wait_ms * 1000 * 64; + +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; + uint32_t end_pos = (dma_buffer->write_pos + 1) % dma_buffer->size; + do { + uint32_t last_pos = dma_buffer->read_pos; + + digital_sequence_update_pos(sequence); + + /* we are finished, when the DMA transferred the 0xFFFFFFFF-timer which is the current write_pos */ + if(dma_buffer->read_pos == end_pos) { + break; + } + + if(last_pos != dma_buffer->read_pos) { + prev_timer = DWT->CYCCNT; + } + if(DWT->CYCCNT - prev_timer > wait_ticks) { + FURI_LOG_D( + TAG, + "[SEQ] hung %lu ms in finish (ARR 0x%08lx, read %lu, write %lu)", + 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; + uint32_t end_pos = (dma_buffer->write_pos + 1) % dma_buffer->size; + do { + uint32_t last_pos = dma_buffer->read_pos; + digital_sequence_update_pos(sequence); + + if(dma_buffer->read_pos != end_pos) { + break; + } + + if(last_pos != dma_buffer->read_pos) { + prev_timer = DWT->CYCCNT; + } + if(DWT->CYCCNT - prev_timer > wait_ticks) { + FURI_LOG_D( + TAG, + "[SEQ] hung %lu ms in queue (ARR 0x%08lx, read %lu, write %lu)", + wait_ms, + 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 + 1) % dma_buffer->size; + dma_buffer->buffer[dma_buffer->write_pos] = 0xFFFFFFFF; +} + bool digital_sequence_send(DigitalSequence* sequence) { furi_assert(sequence); - struct ReloadBuffers* reload = sequence->reload; + struct ReloadBuffer* dma_buffer = sequence->dma_buffer; furi_hal_gpio_init(sequence->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); #ifdef DEBUG_OUTPUT - furi_hal_gpio_init(&gpio_ext_pb3, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(&DEBUG_OUTPUT, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); #endif if(sequence->bake) { @@ -547,74 +553,88 @@ bool digital_sequence_send(DigitalSequence* sequence) { } int32_t remainder = 0; - FURI_CRITICAL_ENTER(); - bool traded_first = false; - for(uint32_t pos = 0; pos < sequence->sequence_used; pos++) { - uint8_t signal_index = sequence->sequence[pos]; + FURI_CRITICAL_ENTER(); + + dma_buffer->dma_active = false; + dma_buffer->buffer[0] = 0xFFFFFFFF; + dma_buffer->read_pos = 0; + dma_buffer->write_pos = 0; + + for(uint32_t seq_pos = 0; seq_pos < sequence->sequence_used; seq_pos++) { + uint8_t signal_index = sequence->sequence[seq_pos]; DigitalSignal* sig = sequence->signals[signal_index]; - DigitalSignal* sig_next = NULL; + bool last_signal = ((seq_pos + 1) == sequence->sequence_used); - if(pos + 1 < sequence->sequence_used) { - sig_next = sequence->signals[sequence->sequence[pos + 1]]; + /* all signals are prepared and we can re-use the GPIO buffer from the fist signal */ + if(seq_pos == 0) { + sequence->gpio_buff = sig->internals->gpio_buff; } - if(!sig) { - FURI_LOG_D( - TAG, - "digital_sequence_send: Signal at index %u, used at pos %lu is NULL, aborting", - signal_index, - pos); - break; - } - - /* if the first edge is handled by prolonging the last pulse of the previous signal, skip it here */ - reload->entries = sig->edge_cnt - (traded_first ? 1 : 0); - - memcpy( - reload->buffers[reload->current], - &sig->reload_reg_buff[traded_first ? 1 : 0], - reload->entries * sizeof(uint32_t)); - traded_first = false; - - /* when we are too late more than half a tick, make the first edge temporarily longer */ - if(remainder >= T_TIM_DIV2) { - remainder -= T_TIM; - reload->buffers[reload->current][0] += 1; - } - - /* update the total remainder */ - remainder += sig->internals->reload_reg_remainder; - - /* when a signal ends with the same level as the next signal begins, let the fist signal generate the whole pulse */ - if(sig_next) { - /* 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); - - /* take from the next, add it to the first */ - if(end_level == sig_next->start_level) { - /* add the traded prolongation to the last pulse */ - reload->buffers[reload->current][reload->entries - 1] += - sig_next->reload_reg_buff[0]; - traded_first = true; + for(uint32_t pulse_pos = 0; pulse_pos < sig->internals->reload_reg_entries; pulse_pos++) { + if(traded_first) { + traded_first = false; + continue; } - } + uint32_t pulse_length = 0; + bool last_pulse = ((pulse_pos + 1) == sig->internals->reload_reg_entries); - /* transmit */ - bool success = digital_sequence_send_signal(sequence, sig); + pulse_length = sig->reload_reg_buff[pulse_pos]; - if(!success) { - break; + /* 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; + } + remainder += sig->internals->reload_reg_remainder; + + /* last pulse in that signal and have a next signal? */ + if(last_pulse) { + if((seq_pos + 1) < sequence->sequence_used) { + DigitalSignal* sig_next = sequence->signals[sequence->sequence[seq_pos + 1]]; + + /* when a signal ends with the same level as the next signal begins, let the fist 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); + + /* take from the next, add it to the current if they have the same level */ + if(end_level == sig_next->start_level) { + pulse_length += sig_next->reload_reg_buff[0]; + traded_first = true; + } + } + } + + digital_sequence_queue_pulse(sequence, pulse_length); + + /* start transmission when buffer was filled enough */ + bool start_send = sequence->dma_buffer->write_pos >= (sequence->dma_buffer->size - 4); + + /* or it was the last pulse */ + if(last_pulse && last_signal) { + start_send = true; + } + + /* start transmission */ + if(start_send && !dma_buffer->dma_active) { + 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; + } } } /* wait until last dma transaction was finished */ - while(LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2)) { - } - - digital_signal_stop_timer(); - digital_signal_stop_dma(); + digital_sequence_finish(sequence); FURI_CRITICAL_EXIT(); return true; @@ -625,3 +645,14 @@ void digital_sequence_clear(DigitalSequence* 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); + } + } +} diff --git a/lib/digital_signal/digital_signal.h b/lib/digital_signal/digital_signal.h index 2cb107486..40afb1e41 100644 --- a/lib/digital_signal/digital_signal.h +++ b/lib/digital_signal/digital_signal.h @@ -61,6 +61,7 @@ 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); #ifdef __cplusplus } From 999c356fedc7ac459f2b4f8193b04392e8c80f71 Mon Sep 17 00:00:00 2001 From: Tiernan Messmer Date: Mon, 1 May 2023 12:29:58 +1000 Subject: [PATCH 29/29] update api_symbols.csv --- firmware/targets/f7/api_symbols.csv | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 2edbdb16d..3e145d32f 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,23.0,, +Version,+,23.1,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -744,6 +744,7 @@ Function,-,digital_sequence_free,void,DigitalSequence* Function,-,digital_sequence_send,_Bool,DigitalSequence* Function,-,digital_sequence_set_sendtime,void,"DigitalSequence*, uint32_t" Function,-,digital_sequence_set_signal,void,"DigitalSequence*, uint8_t, DigitalSignal*" +Function,-,digital_sequence_timebase_correction,void,"DigitalSequence*, float" Function,-,digital_signal_add,void,"DigitalSignal*, uint32_t" Function,-,digital_signal_add_pulse,void,"DigitalSignal*, uint32_t, _Bool" Function,-,digital_signal_alloc,DigitalSignal*,uint32_t