IRDA: Use DMA for async TX (#608)

This commit is contained in:
Albert Kharisov
2021-08-06 00:11:35 +03:00
committed by GitHub
parent 9c38efd4ef
commit ba399abb5d
9 changed files with 657 additions and 162 deletions

View File

@@ -46,7 +46,7 @@ IrdaDecoderHandler* irda_alloc_decoder(void);
/**
* Provide to decoder next timing.
*
* \param[in] handler - handler to IRDA decoders. Should be aquired with \c irda_alloc_decoder().
* \param[in] handler - handler to IRDA decoders. Should be acquired with \c irda_alloc_decoder().
* \param[in] level - high(true) or low(false) level of input signal to analyze.
* it should alternate every call, otherwise it is an error case,
* and decoder resets its state and start decoding from the start.
@@ -58,14 +58,14 @@ const IrdaMessage* irda_decode(IrdaDecoderHandler* handler, bool level, uint32_t
/**
* Deinitialize decoder and free allocated memory.
*
* \param[in] handler - handler to IRDA decoders. Should be aquired with \c irda_alloc_decoder().
* \param[in] handler - handler to IRDA decoders. Should be acquired with \c irda_alloc_decoder().
*/
void irda_free_decoder(IrdaDecoderHandler* handler);
/**
* Reset IRDA decoder.
*
* \param[in] handler - handler to IRDA decoders. Should be aquired with \c irda_alloc_decoder().
* \param[in] handler - handler to IRDA decoders. Should be acquired with \c irda_alloc_decoder().
*/
void irda_reset_decoder(IrdaDecoderHandler* handler);
@@ -119,7 +119,7 @@ IrdaEncoderHandler* irda_alloc_encoder(void);
/**
* Free encoder handler previously allocated with \c irda_alloc_encoder().
*
* \param[in] handler - handler to IRDA encoder. Should be aquired with \c irda_alloc_encoder().
* \param[in] handler - handler to IRDA encoder. Should be acquired with \c irda_alloc_encoder().
*/
void irda_free_encoder(IrdaEncoderHandler* handler);
@@ -132,7 +132,7 @@ void irda_free_encoder(IrdaEncoderHandler* handler);
* 4) when \c irda_encode() returns IrdaStatusDone, it means new message is fully encoded.
* 5) to encode additional timings, just continue calling \c irda_encode().
*
* \param[in] handler - handler to IRDA encoder. Should be aquired with \c irda_alloc_encoder().
* \param[in] handler - handler to IRDA encoder. Should be acquired with \c irda_alloc_encoder().
* \param[out] duration - encoded timing.
* \param[out] level - encoded level.
*
@@ -145,7 +145,7 @@ IrdaStatus irda_encode(IrdaEncoderHandler* handler, uint32_t* duration, bool* le
* IrdaStatusDone in \c irda_encode(), encoder will encode repeat messages
* till the end of time.
*
* \param[in] handler - handler to IRDA encoder. Should be aquired with \c irda_alloc_encoder().
* \param[in] handler - handler to IRDA encoder. Should be acquired with \c irda_alloc_encoder().
* \param[in] message - message to encode.
*/
void irda_reset_encoder(IrdaEncoderHandler* handler, const IrdaMessage* message);

View File

@@ -6,72 +6,96 @@
#include <api-hal-irda.h>
#include <api-hal-delay.h>
#define IRDA_SET_TX_COMMON(d, l) irda_set_tx((d), (l), IRDA_COMMON_DUTY_CYCLE, IRDA_COMMON_CARRIER_FREQUENCY)
static uint32_t irda_tx_number_of_transmissions = 0;
static uint32_t irda_tx_raw_timings_index = 0;
static uint32_t irda_tx_raw_timings_number = 0;
static uint32_t irda_tx_raw_start_from_mark = 0;
static bool irda_tx_raw_add_silence = false;
static void irda_set_tx(uint32_t duration, bool level, float duty_cycle, float frequency) {
if (level) {
api_hal_irda_pwm_set(duty_cycle, frequency);
delay_us(duration);
ApiHalIrdaTxGetDataState irda_get_raw_data_callback (void* context, uint32_t* duration, bool* level) {
furi_assert(duration);
furi_assert(level);
furi_assert(context);
ApiHalIrdaTxGetDataState state = ApiHalIrdaTxGetDataStateOk;
const uint32_t* timings = context;
if (irda_tx_raw_add_silence && (irda_tx_raw_timings_index == 0)) {
irda_tx_raw_add_silence = false;
*level = false;
*duration = 180000; // 180 ms delay between raw packets
} else {
api_hal_irda_pwm_stop();
delay_us(duration);
*level = irda_tx_raw_start_from_mark ^ (irda_tx_raw_timings_index % 2);
*duration = timings[irda_tx_raw_timings_index++];
}
if (irda_tx_raw_timings_number == irda_tx_raw_timings_index) {
state = ApiHalIrdaTxGetDataStateLastDone;
}
return state;
}
void irda_send_raw_ext(const uint32_t timings[], uint32_t timings_cnt, bool start_from_mark, float duty_cycle, float frequency) {
__disable_irq();
for (uint32_t i = 0; i < timings_cnt; ++i) {
irda_set_tx(timings[i], (i % 2) ^ start_from_mark, duty_cycle, frequency);
}
IRDA_SET_TX_COMMON(0, false);
__enable_irq();
void irda_send_raw_ext(const uint32_t timings[], uint32_t timings_cnt, bool start_from_mark, uint32_t frequency, float duty_cycle) {
furi_assert(timings);
furi_assert(timings_cnt > 1);
irda_tx_raw_start_from_mark = start_from_mark;
irda_tx_raw_timings_index = 0;
irda_tx_raw_timings_number = timings_cnt;
irda_tx_raw_add_silence = start_from_mark;
api_hal_irda_async_tx_set_data_isr_callback(irda_get_raw_data_callback, (void*) timings);
api_hal_irda_async_tx_start(frequency, duty_cycle);
api_hal_irda_async_tx_wait_termination();
furi_assert(!api_hal_irda_is_busy());
}
void irda_send_raw(const uint32_t timings[], uint32_t timings_cnt, bool start_from_mark) {
__disable_irq();
for (uint32_t i = 0; i < timings_cnt; ++i) {
IRDA_SET_TX_COMMON(timings[i], (i % 2) ^ start_from_mark);
irda_send_raw_ext(timings, timings_cnt, start_from_mark, IRDA_COMMON_CARRIER_FREQUENCY, IRDA_COMMON_DUTY_CYCLE);
}
ApiHalIrdaTxGetDataState irda_get_data_callback (void* context, uint32_t* duration, bool* level) {
ApiHalIrdaTxGetDataState state = ApiHalIrdaTxGetDataStateError;
IrdaEncoderHandler* handler = context;
IrdaStatus status = IrdaStatusError;
if (irda_tx_number_of_transmissions > 0) {
status = irda_encode(handler, duration, level);
}
IRDA_SET_TX_COMMON(0, false);
__enable_irq();
if (status == IrdaStatusError) {
state = ApiHalIrdaTxGetDataStateError;
} else if (status == IrdaStatusOk) {
state = ApiHalIrdaTxGetDataStateOk;
} else if (status == IrdaStatusDone) {
state = ApiHalIrdaTxGetDataStateDone;
if (--irda_tx_number_of_transmissions == 0) {
state = ApiHalIrdaTxGetDataStateLastDone;
}
} else {
furi_assert(0);
state = ApiHalIrdaTxGetDataStateError;
}
return state;
}
void irda_send(const IrdaMessage* message, int times) {
furi_assert(message);
furi_assert(times);
furi_assert(irda_is_protocol_valid(message->protocol));
IrdaStatus status;
uint32_t duration = 0;
bool level = false;
IrdaEncoderHandler* handler = irda_alloc_encoder();
irda_reset_encoder(handler, message);
irda_tx_number_of_transmissions = times;
/* Hotfix: first timings is space timing, so make delay instead of locking
* whole system for that long. Replace when async timing lib will be ready.
* This timing doesn't have to be precise.
*/
status = irda_encode(handler, &duration, &level);
furi_assert(status != IrdaStatusError);
furi_assert(level == false);
delay_us(duration);
__disable_irq();
while (times) {
status = irda_encode(handler, &duration, &level);
if (status != IrdaStatusError) {
IRDA_SET_TX_COMMON(duration, level);
} else {
furi_assert(0);
break;
}
if (status == IrdaStatusDone)
--times;
}
IRDA_SET_TX_COMMON(0, false);
__enable_irq();
api_hal_irda_async_tx_set_data_isr_callback(irda_get_data_callback, handler);
api_hal_irda_async_tx_start(IRDA_COMMON_CARRIER_FREQUENCY, IRDA_COMMON_DUTY_CYCLE);
api_hal_irda_async_tx_wait_termination();
irda_free_encoder(handler);
furi_assert(!api_hal_irda_is_busy());
}

View File

@@ -1,5 +1,6 @@
#include <api-hal-irda.h>
#include <irda.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
@@ -33,7 +34,7 @@ void irda_send_raw(const uint32_t timings[], uint32_t timings_cnt, bool start_fr
* \param[in] duty_cycle - duty cycle to generate on PWM
* \param[in] frequency - frequency to generate on PWM
*/
void irda_send_raw_ext(const uint32_t timings[], uint32_t timings_cnt, bool start_from_mark, float duty_cycle, float frequency);
void irda_send_raw_ext(const uint32_t timings[], uint32_t timings_cnt, bool start_from_mark, uint32_t frequency, float duty_cycle);
#ifdef __cplusplus
}

View File

@@ -190,19 +190,19 @@ void irda_worker_start(IrdaWorker* instance) {
furi_thread_start(instance->thread);
instance->worker_handle = furi_thread_get_thread_id(instance->thread);
api_hal_irda_rx_irq_init();
api_hal_irda_rx_timeout_irq_init(IRDA_WORKER_RX_TIMEOUT);
api_hal_irda_rx_irq_set_callback(irda_worker_rx_callback, instance);
api_hal_irda_rx_timeout_irq_set_callback(irda_worker_rx_timeout_callback, instance);
api_hal_irda_async_rx_start();
api_hal_irda_async_rx_set_timeout(IRDA_WORKER_RX_TIMEOUT);
api_hal_irda_async_rx_set_capture_isr_callback(irda_worker_rx_callback, instance);
api_hal_irda_async_rx_set_timeout_isr_callback(irda_worker_rx_timeout_callback, instance);
}
void irda_worker_stop(IrdaWorker* instance) {
furi_assert(instance);
furi_assert(instance->worker_handle);
api_hal_irda_rx_timeout_irq_set_callback(NULL, NULL);
api_hal_irda_rx_irq_set_callback(NULL, NULL);
api_hal_irda_rx_irq_deinit();
api_hal_irda_async_rx_set_timeout_isr_callback(NULL, NULL);
api_hal_irda_async_rx_set_capture_isr_callback(NULL, NULL);
api_hal_irda_async_rx_stop();
xTaskNotify(instance->worker_handle, IRDA_WORKER_EXIT, eSetBits);