mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
rmt: add workaround to get user context in a graceful way
This commit is contained in:
parent
21bb6f2334
commit
95e712039c
@ -171,22 +171,6 @@ typedef struct {
|
|||||||
*/
|
*/
|
||||||
typedef void (*sample_to_rmt_t)(const void *src, rmt_item32_t *dest, size_t src_size, size_t wanted_num, size_t *translated_size, size_t *item_num);
|
typedef void (*sample_to_rmt_t)(const void *src, rmt_item32_t *dest, size_t src_size, size_t wanted_num, size_t *translated_size, size_t *item_num);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief IDF 4.x Workaround callback function for sample_to_rmt_t that lets user pass in context for rmt_write_sample
|
|
||||||
*
|
|
||||||
* @param src Pointer to the buffer storing the raw data that needs to be converted to rmt format.
|
|
||||||
* @param[out] dest Pointer to the buffer storing the rmt format data.
|
|
||||||
* @param src_size The raw data size.
|
|
||||||
* @param wanted_num The number of rmt format data that wanted to get.
|
|
||||||
* @param[out] translated_size The size of the raw data that has been converted to rmt format,
|
|
||||||
* it should return 0 if no data is converted in user callback.
|
|
||||||
* @param[out] item_num The number of the rmt format data that actually converted to,
|
|
||||||
* it can be less than wanted_num if there is not enough raw data, but cannot exceed wanted_num.
|
|
||||||
* it should return 0 if no data was converted.
|
|
||||||
* @param context User context pointer
|
|
||||||
*/
|
|
||||||
typedef void (*sample_with_context_to_rmt_t)(const void *src, rmt_item32_t *dest, size_t src_size, size_t wanted_num, size_t *translated_size, size_t *item_num, void *context);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set RMT clock divider, channel clock is divided from source clock.
|
* @brief Set RMT clock divider, channel clock is divided from source clock.
|
||||||
*
|
*
|
||||||
@ -779,27 +763,31 @@ esp_err_t rmt_get_ringbuf_handle(rmt_channel_t channel, RingbufHandle_t *buf_han
|
|||||||
esp_err_t rmt_translator_init(rmt_channel_t channel, sample_to_rmt_t fn);
|
esp_err_t rmt_translator_init(rmt_channel_t channel, sample_to_rmt_t fn);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Workaround for IDF 4.x
|
* @brief Set user context for the translator of specific channel
|
||||||
* TODO: Add context to sample_to_rmt_t callback signature and allow user to pass in context
|
|
||||||
* on rmt_translator_init
|
|
||||||
*
|
*
|
||||||
* @param channel RMT channel .
|
* @param channel RMT channel number
|
||||||
* @param fn Point to the data conversion function.
|
* @param context User context
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* - ESP_FAIL Init fail.
|
|
||||||
* - ESP_OK Init success.
|
|
||||||
*/
|
|
||||||
esp_err_t rmt_translator_init_with_context(rmt_channel_t channel, sample_with_context_to_rmt_t fn, void* context);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Sets the user context for the translator
|
|
||||||
* Requires rmt_translator_init_with_context to init the translator first
|
|
||||||
* @return
|
|
||||||
* - ESP_FAIL Set context fail
|
* - ESP_FAIL Set context fail
|
||||||
* - ESP_OK Set context success
|
* - ESP_OK Set context success
|
||||||
*/
|
*/
|
||||||
esp_err_t rmt_set_translator_context(rmt_channel_t channel, void* context);
|
esp_err_t rmt_translator_set_context(rmt_channel_t channel, void *context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the user context set by 'rmt_translator_set_context'
|
||||||
|
*
|
||||||
|
* @note This API must be invoked in the RMT translator callback function,
|
||||||
|
* and the first argument must be the actual parameter 'item_num' you got in that callback function.
|
||||||
|
*
|
||||||
|
* @param item_num Address of the memory which contains the number of translated items (It's from driver's internal memroy)
|
||||||
|
* @param context Returned User context
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_FAIL Get context fail
|
||||||
|
* - ESP_OK Get context success
|
||||||
|
*/
|
||||||
|
esp_err_t rmt_translator_get_context(const size_t *item_num, void **context);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Translate uint8_t type of data into rmt format and send it out.
|
* @brief Translate uint8_t type of data into rmt format and send it out.
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/lock.h>
|
#include <sys/lock.h>
|
||||||
|
#include <sys/cdefs.h>
|
||||||
#include "esp_compiler.h"
|
#include "esp_compiler.h"
|
||||||
#include "esp_intr_alloc.h"
|
#include "esp_intr_alloc.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
@ -99,8 +100,7 @@ typedef struct {
|
|||||||
int rx_item_start_idx;
|
int rx_item_start_idx;
|
||||||
#endif
|
#endif
|
||||||
sample_to_rmt_t sample_to_rmt;
|
sample_to_rmt_t sample_to_rmt;
|
||||||
sample_with_context_to_rmt_t sample_with_context_to_rmt;
|
void *tx_context;
|
||||||
void* tx_context;
|
|
||||||
size_t sample_size_remain;
|
size_t sample_size_remain;
|
||||||
const uint8_t *sample_cur;
|
const uint8_t *sample_cur;
|
||||||
} rmt_obj_t;
|
} rmt_obj_t;
|
||||||
@ -785,21 +785,12 @@ static void IRAM_ATTR rmt_driver_isr_default(void *arg)
|
|||||||
if (p_rmt->translator) {
|
if (p_rmt->translator) {
|
||||||
if (p_rmt->sample_size_remain > 0) {
|
if (p_rmt->sample_size_remain > 0) {
|
||||||
size_t translated_size = 0;
|
size_t translated_size = 0;
|
||||||
if (p_rmt->sample_with_context_to_rmt == NULL)
|
p_rmt->sample_to_rmt((void *)p_rmt->sample_cur,
|
||||||
p_rmt->sample_to_rmt((void *)p_rmt->sample_cur,
|
p_rmt->tx_buf,
|
||||||
p_rmt->tx_buf,
|
p_rmt->sample_size_remain,
|
||||||
p_rmt->sample_size_remain,
|
p_rmt->tx_sub_len,
|
||||||
p_rmt->tx_sub_len,
|
&translated_size,
|
||||||
&translated_size,
|
&p_rmt->tx_len_rem);
|
||||||
&p_rmt->tx_len_rem);
|
|
||||||
else
|
|
||||||
p_rmt->sample_with_context_to_rmt((void *)p_rmt->sample_cur,
|
|
||||||
p_rmt->tx_buf,
|
|
||||||
p_rmt->sample_size_remain,
|
|
||||||
p_rmt->tx_sub_len,
|
|
||||||
&translated_size,
|
|
||||||
&p_rmt->tx_len_rem,
|
|
||||||
p_rmt->tx_context);
|
|
||||||
p_rmt->sample_size_remain -= translated_size;
|
p_rmt->sample_size_remain -= translated_size;
|
||||||
p_rmt->sample_cur += translated_size;
|
p_rmt->sample_cur += translated_size;
|
||||||
p_rmt->tx_data = p_rmt->tx_buf;
|
p_rmt->tx_data = p_rmt->tx_buf;
|
||||||
@ -1227,7 +1218,6 @@ esp_err_t rmt_translator_init(rmt_channel_t channel, sample_to_rmt_t fn)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
p_rmt_obj[channel]->sample_to_rmt = fn;
|
p_rmt_obj[channel]->sample_to_rmt = fn;
|
||||||
p_rmt_obj[channel]->sample_with_context_to_rmt = NULL;
|
|
||||||
p_rmt_obj[channel]->tx_context = NULL;
|
p_rmt_obj[channel]->tx_context = NULL;
|
||||||
p_rmt_obj[channel]->sample_size_remain = 0;
|
p_rmt_obj[channel]->sample_size_remain = 0;
|
||||||
p_rmt_obj[channel]->sample_cur = NULL;
|
p_rmt_obj[channel]->sample_cur = NULL;
|
||||||
@ -1235,43 +1225,24 @@ esp_err_t rmt_translator_init(rmt_channel_t channel, sample_to_rmt_t fn)
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t rmt_translator_init_with_context(rmt_channel_t channel, sample_with_context_to_rmt_t fn, void* context)
|
esp_err_t rmt_translator_set_context(rmt_channel_t channel, void *context)
|
||||||
{
|
{
|
||||||
RMT_CHECK(fn != NULL, RMT_TRANSLATOR_NULL_STR, ESP_ERR_INVALID_ARG);
|
|
||||||
RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
|
RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
|
||||||
RMT_CHECK(p_rmt_obj[channel] != NULL, RMT_DRIVER_ERROR_STR, ESP_FAIL);
|
RMT_CHECK(p_rmt_obj[channel] != NULL, RMT_DRIVER_ERROR_STR, ESP_FAIL);
|
||||||
const uint32_t block_size = rmt_ll_get_mem_blocks(p_rmt_obj[channel]->hal.regs, channel) *
|
|
||||||
RMT_MEM_ITEM_NUM * sizeof(rmt_item32_t);
|
|
||||||
if (p_rmt_obj[channel]->tx_buf == NULL) {
|
|
||||||
#if !CONFIG_SPIRAM_USE_MALLOC
|
|
||||||
p_rmt_obj[channel]->tx_buf = (rmt_item32_t *)malloc(block_size);
|
|
||||||
#else
|
|
||||||
if (p_rmt_obj[channel]->intr_alloc_flags & ESP_INTR_FLAG_IRAM) {
|
|
||||||
p_rmt_obj[channel]->tx_buf = (rmt_item32_t *)malloc(block_size);
|
|
||||||
} else {
|
|
||||||
p_rmt_obj[channel]->tx_buf = (rmt_item32_t *)heap_caps_calloc(1, block_size, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (p_rmt_obj[channel]->tx_buf == NULL) {
|
|
||||||
ESP_LOGE(RMT_TAG, "RMT translator buffer create fail");
|
|
||||||
return ESP_FAIL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p_rmt_obj[channel]->sample_to_rmt = NULL;
|
|
||||||
p_rmt_obj[channel]->sample_with_context_to_rmt = fn;
|
|
||||||
p_rmt_obj[channel]->tx_context = context;
|
p_rmt_obj[channel]->tx_context = context;
|
||||||
p_rmt_obj[channel]->sample_size_remain = 0;
|
|
||||||
p_rmt_obj[channel]->sample_cur = NULL;
|
|
||||||
ESP_LOGD(RMT_TAG, "RMT translator init done");
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t rmt_set_translator_context(rmt_channel_t channel, void* context)
|
esp_err_t rmt_translator_get_context(const size_t *item_num, void **context)
|
||||||
{
|
{
|
||||||
RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
|
RMT_CHECK(item_num && context, "invalid arguments", ESP_ERR_INVALID_ARG);
|
||||||
RMT_CHECK(p_rmt_obj[channel] != NULL, RMT_DRIVER_ERROR_STR, ESP_FAIL);
|
|
||||||
|
// the address of tx_len_rem is directlly passed to the callback,
|
||||||
|
// so it's possible to get the object address from that
|
||||||
|
rmt_obj_t *obj = __containerof(item_num, rmt_obj_t, tx_len_rem);
|
||||||
|
*context = obj->tx_context;
|
||||||
|
|
||||||
p_rmt_obj[channel]->tx_context = context;
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1279,7 +1250,7 @@ esp_err_t rmt_write_sample(rmt_channel_t channel, const uint8_t *src, size_t src
|
|||||||
{
|
{
|
||||||
RMT_CHECK(RMT_IS_TX_CHANNEL(channel), RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
|
RMT_CHECK(RMT_IS_TX_CHANNEL(channel), RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
|
||||||
RMT_CHECK(p_rmt_obj[channel] != NULL, RMT_DRIVER_ERROR_STR, ESP_FAIL);
|
RMT_CHECK(p_rmt_obj[channel] != NULL, RMT_DRIVER_ERROR_STR, ESP_FAIL);
|
||||||
RMT_CHECK(p_rmt_obj[channel]->sample_to_rmt != NULL || p_rmt_obj[channel]->sample_with_context_to_rmt != NULL, RMT_TRANSLATOR_UNINIT_STR, ESP_FAIL);
|
RMT_CHECK(p_rmt_obj[channel]->sample_to_rmt != NULL, RMT_TRANSLATOR_UNINIT_STR, ESP_FAIL);
|
||||||
#if CONFIG_SPIRAM_USE_MALLOC
|
#if CONFIG_SPIRAM_USE_MALLOC
|
||||||
if (p_rmt_obj[channel]->intr_alloc_flags & ESP_INTR_FLAG_IRAM) {
|
if (p_rmt_obj[channel]->intr_alloc_flags & ESP_INTR_FLAG_IRAM) {
|
||||||
if (!esp_ptr_internal(src)) {
|
if (!esp_ptr_internal(src)) {
|
||||||
@ -1288,20 +1259,16 @@ esp_err_t rmt_write_sample(rmt_channel_t channel, const uint8_t *src, size_t src
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
size_t item_num = 0;
|
|
||||||
size_t translated_size = 0;
|
size_t translated_size = 0;
|
||||||
rmt_obj_t *p_rmt = p_rmt_obj[channel];
|
rmt_obj_t *p_rmt = p_rmt_obj[channel];
|
||||||
const uint32_t item_block_len = rmt_ll_tx_get_mem_blocks(rmt_contex.hal.regs, channel) * RMT_MEM_ITEM_NUM;
|
const uint32_t item_block_len = rmt_ll_tx_get_mem_blocks(rmt_contex.hal.regs, channel) * RMT_MEM_ITEM_NUM;
|
||||||
const uint32_t item_sub_len = item_block_len / 2;
|
const uint32_t item_sub_len = item_block_len / 2;
|
||||||
xSemaphoreTake(p_rmt->tx_sem, portMAX_DELAY);
|
xSemaphoreTake(p_rmt->tx_sem, portMAX_DELAY);
|
||||||
if (p_rmt->sample_with_context_to_rmt == NULL)
|
p_rmt->sample_to_rmt((void *)src, p_rmt->tx_buf, src_size, item_block_len, &translated_size, &p_rmt->tx_len_rem);
|
||||||
p_rmt->sample_to_rmt((void *)src, p_rmt->tx_buf, src_size, item_block_len, &translated_size, &item_num);
|
|
||||||
else
|
|
||||||
p_rmt->sample_with_context_to_rmt((void*)src, p_rmt->tx_buf, src_size, item_block_len, &translated_size, &item_num, p_rmt->tx_context);
|
|
||||||
p_rmt->sample_size_remain = src_size - translated_size;
|
p_rmt->sample_size_remain = src_size - translated_size;
|
||||||
p_rmt->sample_cur = src + translated_size;
|
p_rmt->sample_cur = src + translated_size;
|
||||||
rmt_fill_memory(channel, p_rmt->tx_buf, item_num, 0);
|
rmt_fill_memory(channel, p_rmt->tx_buf, p_rmt->tx_len_rem, 0);
|
||||||
if (item_num == item_block_len) {
|
if (p_rmt->tx_len_rem == item_block_len) {
|
||||||
rmt_set_tx_thr_intr_en(channel, 1, item_sub_len);
|
rmt_set_tx_thr_intr_en(channel, 1, item_sub_len);
|
||||||
p_rmt->tx_data = p_rmt->tx_buf;
|
p_rmt->tx_data = p_rmt->tx_buf;
|
||||||
p_rmt->tx_offset = 0;
|
p_rmt->tx_offset = 0;
|
||||||
@ -1309,7 +1276,7 @@ esp_err_t rmt_write_sample(rmt_channel_t channel, const uint8_t *src, size_t src
|
|||||||
p_rmt->translator = true;
|
p_rmt->translator = true;
|
||||||
} else {
|
} else {
|
||||||
rmt_item32_t stop_data = {0};
|
rmt_item32_t stop_data = {0};
|
||||||
rmt_ll_write_memory(rmt_contex.hal.mem, channel, &stop_data, 1, item_num);
|
rmt_ll_write_memory(rmt_contex.hal.mem, channel, &stop_data, 1, p_rmt->tx_len_rem);
|
||||||
p_rmt->tx_len_rem = 0;
|
p_rmt->tx_len_rem = 0;
|
||||||
p_rmt->sample_cur = NULL;
|
p_rmt->sample_cur = NULL;
|
||||||
p_rmt->translator = false;
|
p_rmt->translator = false;
|
||||||
|
@ -218,6 +218,52 @@ TEST_CASE("RMT install/uninstall test", "[rmt]")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_rmt_translator(const void *src, rmt_item32_t *dest, size_t src_size,
|
||||||
|
size_t wanted_num, size_t *translated_size, size_t *item_num)
|
||||||
|
{
|
||||||
|
const rmt_item32_t bit0 = {{{ 10, 1, 20, 0 }}}; //Logical 0
|
||||||
|
const rmt_item32_t bit1 = {{{ 20, 1, 10, 0 }}}; //Logical 1
|
||||||
|
size_t size = 0;
|
||||||
|
size_t num = 0;
|
||||||
|
uint8_t *psrc = (uint8_t *)src;
|
||||||
|
rmt_item32_t *pdest = dest;
|
||||||
|
while (size < src_size && num < wanted_num) {
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
// MSB first
|
||||||
|
if (*psrc & (1 << (7 - i))) {
|
||||||
|
pdest->val = bit1.val;
|
||||||
|
} else {
|
||||||
|
pdest->val = bit0.val;
|
||||||
|
}
|
||||||
|
num++;
|
||||||
|
pdest++;
|
||||||
|
}
|
||||||
|
size++;
|
||||||
|
psrc++;
|
||||||
|
}
|
||||||
|
*translated_size = size;
|
||||||
|
*item_num = num;
|
||||||
|
int *user_data = NULL;
|
||||||
|
rmt_translator_get_context(item_num, (void **)&user_data);
|
||||||
|
esp_rom_printf("user data=%d\r\n", *user_data);
|
||||||
|
*user_data = 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("RMT translator with user context", "[rmt]")
|
||||||
|
{
|
||||||
|
rmt_config_t tx_cfg = RMT_DEFAULT_CONFIG_TX(RMT_DATA_IO, 0);
|
||||||
|
TEST_ESP_OK(rmt_config(&tx_cfg));
|
||||||
|
TEST_ESP_OK(rmt_driver_install(tx_cfg.channel, 0, 0));
|
||||||
|
rmt_translator_init(tx_cfg.channel, test_rmt_translator);
|
||||||
|
int user_data = 999;
|
||||||
|
rmt_translator_set_context(tx_cfg.channel, &user_data);
|
||||||
|
uint8_t test_buf[] = {1, 2, 3, 4, 5, 6};
|
||||||
|
rmt_write_sample(tx_cfg.channel, test_buf, sizeof(test_buf), true);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(100));
|
||||||
|
TEST_ASSERT_EQUAL(100, user_data);
|
||||||
|
TEST_ESP_OK(rmt_driver_uninstall(tx_cfg.channel));
|
||||||
|
}
|
||||||
|
|
||||||
static void do_nec_tx_rx(uint32_t flags)
|
static void do_nec_tx_rx(uint32_t flags)
|
||||||
{
|
{
|
||||||
RingbufHandle_t rb = NULL;
|
RingbufHandle_t rb = NULL;
|
||||||
|
Loading…
Reference in New Issue
Block a user