mirror of
https://github.com/espressif/esp-idf.git
synced 2025-10-03 02:20:57 +02:00
feat(rmt): support unaligned receive buffer with DMA
The driver will help to align the buffer to mem_alignment in DMA mode
This commit is contained in:
committed by
Chen Ji Chang
parent
f4a76612fa
commit
e321d3f85f
@@ -355,21 +355,35 @@ esp_err_t rmt_receive(rmt_channel_handle_t channel, void *buffer, size_t buffer_
|
||||
size_t mem_alignment = sizeof(rmt_symbol_word_t);
|
||||
|
||||
#if SOC_RMT_SUPPORT_DMA
|
||||
uint32_t int_mem_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA);
|
||||
if (channel->dma_chan) {
|
||||
// append the alignment requirement from the DMA
|
||||
mem_alignment = MAX(mem_alignment, rx_chan->dma_int_mem_alignment);
|
||||
// [IDF-8997]: Currently we assume the user buffer is allocated from internal RAM, PSRAM is not supported yet.
|
||||
ESP_RETURN_ON_FALSE_ISR(esp_ptr_internal(buffer), ESP_ERR_INVALID_ARG, TAG, "user buffer not in the internal RAM");
|
||||
size_t max_buf_sz_per_dma_node = ALIGN_DOWN(DMA_DESCRIPTOR_BUFFER_MAX_SIZE, mem_alignment);
|
||||
ESP_RETURN_ON_FALSE_ISR(buffer_size <= rx_chan->num_dma_nodes * max_buf_sz_per_dma_node,
|
||||
ESP_ERR_INVALID_ARG, TAG, "buffer size exceeds DMA capacity: %"PRIu32"", rx_chan->num_dma_nodes * max_buf_sz_per_dma_node);
|
||||
// append the alignment requirement from the DMA and cache line size
|
||||
mem_alignment = MAX(MAX(mem_alignment, rx_chan->dma_int_mem_alignment), int_mem_cache_line_size);
|
||||
}
|
||||
#endif // SOC_RMT_SUPPORT_DMA
|
||||
|
||||
// check buffer alignment
|
||||
uint32_t align_check_mask = mem_alignment - 1;
|
||||
ESP_RETURN_ON_FALSE_ISR(((((uintptr_t)buffer) & align_check_mask) == 0) && (((buffer_size) & align_check_mask) == 0), ESP_ERR_INVALID_ARG,
|
||||
TAG, "buffer address or size are not %"PRIu32 "bytes aligned", mem_alignment);
|
||||
// Align the buffer address to mem_alignment
|
||||
if ((((uintptr_t)buffer) & (mem_alignment - 1)) != 0) {
|
||||
uintptr_t aligned_address = ALIGN_UP((uintptr_t)buffer, mem_alignment);
|
||||
size_t offset = aligned_address - (uintptr_t)buffer;
|
||||
ESP_RETURN_ON_FALSE_ISR(buffer_size > offset, ESP_ERR_INVALID_ARG, TAG, "buffer size is not aligned and is too small, please increase the buffer size");
|
||||
ESP_EARLY_LOGD(TAG, "origin buffer %p not satisfy alignment %d, align buffer to %p", buffer, mem_alignment, aligned_address);
|
||||
buffer = (uint8_t *)aligned_address;
|
||||
buffer_size -= offset;
|
||||
}
|
||||
// Align the buffer size to mem_alignment
|
||||
buffer_size = ALIGN_DOWN(buffer_size, mem_alignment);
|
||||
ESP_RETURN_ON_FALSE_ISR(buffer_size > 0, ESP_ERR_INVALID_ARG, TAG, "buffer size is less than alignment: %"PRIu32", please increase the buffer size", mem_alignment);
|
||||
|
||||
#if SOC_RMT_SUPPORT_DMA
|
||||
if (channel->dma_chan) {
|
||||
size_t max_buf_sz_per_dma_node = ALIGN_DOWN(DMA_DESCRIPTOR_BUFFER_MAX_SIZE, mem_alignment);
|
||||
ESP_RETURN_ON_FALSE_ISR(buffer_size <= rx_chan->num_dma_nodes * max_buf_sz_per_dma_node,
|
||||
ESP_ERR_INVALID_ARG, TAG, "buffer size exceeds DMA capacity: %"PRIu32", please increase the mem_block_symbols", rx_chan->num_dma_nodes * max_buf_sz_per_dma_node);
|
||||
}
|
||||
#endif // SOC_RMT_SUPPORT_DMA
|
||||
|
||||
rmt_group_t *group = channel->group;
|
||||
rmt_hal_context_t *hal = &group->hal;
|
||||
@@ -398,7 +412,6 @@ esp_err_t rmt_receive(rmt_channel_handle_t channel, void *buffer, size_t buffer_
|
||||
#if SOC_RMT_SUPPORT_DMA
|
||||
if (channel->dma_chan) {
|
||||
// invalidate the user buffer, in case cache auto-write back happens and breaks the data just written by the DMA
|
||||
uint32_t int_mem_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA);
|
||||
if (int_mem_cache_line_size) {
|
||||
// this function will also check the alignment of the buffer and size, against the cache line size
|
||||
ESP_RETURN_ON_ERROR_ISR(esp_cache_msync(buffer, buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C), TAG, "cache sync failed");
|
||||
|
@@ -25,6 +25,8 @@
|
||||
typedef struct {
|
||||
TaskHandle_t task_to_notify;
|
||||
size_t received_symbol_num;
|
||||
rmt_symbol_word_t *received_symbols;
|
||||
bool is_first_event;
|
||||
} test_rx_user_data_t;
|
||||
|
||||
TEST_RMT_CALLBACK_ATTR
|
||||
@@ -45,8 +47,8 @@ static bool test_rmt_rx_done_callback(rmt_channel_handle_t channel, const rmt_rx
|
||||
static void test_rmt_rx_nec_carrier(size_t mem_block_symbols, bool with_dma, rmt_clock_source_t clk_src)
|
||||
{
|
||||
uint32_t const test_rx_buffer_symbols = 128;
|
||||
rmt_symbol_word_t *remote_codes = heap_caps_aligned_calloc(64, test_rx_buffer_symbols, sizeof(rmt_symbol_word_t),
|
||||
MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
|
||||
rmt_symbol_word_t *remote_codes = heap_caps_calloc(test_rx_buffer_symbols, sizeof(rmt_symbol_word_t),
|
||||
MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
|
||||
TEST_ASSERT_NOT_NULL(remote_codes);
|
||||
|
||||
rmt_rx_channel_config_t rx_channel_cfg = {
|
||||
@@ -180,9 +182,6 @@ static void test_rmt_rx_nec_carrier(size_t mem_block_symbols, bool with_dma, rmt
|
||||
|
||||
TEST_ESP_OK(rmt_tx_wait_all_done(tx_channel, -1));
|
||||
|
||||
// test rmt receive with unaligned buffer
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, rmt_receive(rx_channel, remote_codes, 13, &receive_config));
|
||||
|
||||
printf("disable tx and rx channels\r\n");
|
||||
TEST_ESP_OK(rmt_disable(tx_channel));
|
||||
TEST_ESP_OK(rmt_disable(rx_channel));
|
||||
@@ -234,8 +233,8 @@ static bool test_rmt_partial_receive_done(rmt_channel_handle_t channel, const rm
|
||||
static void test_rmt_partial_receive(size_t mem_block_symbols, int test_symbols_num, bool with_dma, rmt_clock_source_t clk_src)
|
||||
{
|
||||
uint32_t const test_rx_buffer_symbols = 128; // the user buffer is small, it can't hold all the received symbols
|
||||
rmt_symbol_word_t *receive_user_buf = heap_caps_aligned_calloc(64, test_rx_buffer_symbols, sizeof(rmt_symbol_word_t),
|
||||
MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
|
||||
rmt_symbol_word_t *receive_user_buf = heap_caps_calloc(test_rx_buffer_symbols, sizeof(rmt_symbol_word_t),
|
||||
MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
|
||||
TEST_ASSERT_NOT_NULL(receive_user_buf);
|
||||
|
||||
gpio_config_t sig_simulator_io_conf = {
|
||||
@@ -334,8 +333,8 @@ static bool test_rmt_received_done(rmt_channel_handle_t channel, const rmt_rx_do
|
||||
static void test_rmt_receive_filter(rmt_clock_source_t clk_src)
|
||||
{
|
||||
uint32_t const test_rx_buffer_symbols = 32;
|
||||
rmt_symbol_word_t *receive_user_buf = heap_caps_aligned_calloc(64, test_rx_buffer_symbols, sizeof(rmt_symbol_word_t),
|
||||
MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
|
||||
rmt_symbol_word_t *receive_user_buf = heap_caps_calloc(test_rx_buffer_symbols, sizeof(rmt_symbol_word_t),
|
||||
MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
|
||||
TEST_ASSERT_NOT_NULL(receive_user_buf);
|
||||
|
||||
rmt_rx_channel_config_t rx_channel_cfg = {
|
||||
@@ -440,3 +439,128 @@ TEST_CASE("rmt rx filter functionality", "[rmt]")
|
||||
test_rmt_receive_filter(clk_srcs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_RMT_CALLBACK_ATTR
|
||||
static bool test_rmt_rx_unaligned_buffer_done_callback(rmt_channel_handle_t channel, const rmt_rx_done_event_data_t *edata, void *user_data)
|
||||
{
|
||||
BaseType_t high_task_wakeup = pdFALSE;
|
||||
test_rx_user_data_t *test_user_data = (test_rx_user_data_t *)user_data;
|
||||
if (test_user_data->is_first_event) {
|
||||
test_user_data->received_symbols = edata->received_symbols;
|
||||
test_user_data->is_first_event = false;
|
||||
}
|
||||
test_user_data->received_symbol_num += edata->num_symbols;
|
||||
if (edata->flags.is_last) {
|
||||
vTaskNotifyGiveFromISR(test_user_data->task_to_notify, &high_task_wakeup);
|
||||
}
|
||||
return high_task_wakeup == pdTRUE;
|
||||
}
|
||||
|
||||
static void test_rmt_unaligned_receive(size_t mem_block_symbols, int test_symbols_num, bool with_dma, bool en_partial_rx, rmt_clock_source_t clk_src)
|
||||
{
|
||||
uint32_t const test_rx_buffer_symbols = 128;
|
||||
rmt_symbol_word_t *receive_user_buf = heap_caps_aligned_calloc(64, test_rx_buffer_symbols, sizeof(rmt_symbol_word_t),
|
||||
MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
|
||||
TEST_ASSERT_NOT_NULL(receive_user_buf);
|
||||
rmt_symbol_word_t *receive_user_buf_unaligned = (rmt_symbol_word_t *)((uint8_t *)receive_user_buf + 1);
|
||||
size_t receive_user_buf_unaligned_size = test_rx_buffer_symbols * sizeof(rmt_symbol_word_t) - 1;
|
||||
|
||||
// use TX channel to simulate the input signal
|
||||
rmt_tx_channel_config_t tx_channel_cfg = {
|
||||
.clk_src = clk_src,
|
||||
.resolution_hz = 1000000, // 1MHz, 1 tick = 1us
|
||||
.mem_block_symbols = SOC_RMT_MEM_WORDS_PER_CHANNEL,
|
||||
.trans_queue_depth = 4,
|
||||
.gpio_num = TEST_RMT_GPIO_NUM_A,
|
||||
};
|
||||
|
||||
rmt_channel_handle_t tx_channel = NULL;
|
||||
TEST_ESP_OK(rmt_new_tx_channel(&tx_channel_cfg, &tx_channel));
|
||||
|
||||
rmt_encoder_handle_t copy_encoder = NULL;
|
||||
rmt_copy_encoder_config_t encoder_cfg = {};
|
||||
TEST_ESP_OK(rmt_new_copy_encoder(&encoder_cfg, ©_encoder));
|
||||
rmt_transmit_config_t transmit_config = {
|
||||
.loop_count = 0,
|
||||
};
|
||||
|
||||
rmt_rx_channel_config_t rx_channel_cfg = {
|
||||
.clk_src = clk_src,
|
||||
.resolution_hz = 1000000, // 1MHz, 1 tick = 1us
|
||||
.mem_block_symbols = mem_block_symbols,
|
||||
.gpio_num = TEST_RMT_GPIO_NUM_A,
|
||||
.flags.with_dma = with_dma,
|
||||
};
|
||||
|
||||
rmt_channel_handle_t rx_channel = NULL;
|
||||
TEST_ESP_OK(rmt_new_rx_channel(&rx_channel_cfg, &rx_channel));
|
||||
|
||||
rmt_rx_event_callbacks_t cbs = {
|
||||
.on_recv_done = test_rmt_rx_unaligned_buffer_done_callback,
|
||||
};
|
||||
test_rx_user_data_t test_user_data = {
|
||||
.task_to_notify = xTaskGetCurrentTaskHandle(),
|
||||
.received_symbol_num = 0,
|
||||
.is_first_event = true,
|
||||
};
|
||||
TEST_ESP_OK(rmt_rx_register_event_callbacks(rx_channel, &cbs, &test_user_data));
|
||||
|
||||
TEST_ESP_OK(rmt_enable(tx_channel));
|
||||
TEST_ESP_OK(rmt_enable(rx_channel));
|
||||
|
||||
rmt_receive_config_t rx_config = {
|
||||
.signal_range_min_ns = 1250,
|
||||
.signal_range_max_ns = 12000000,
|
||||
.flags.en_partial_rx = en_partial_rx,
|
||||
};
|
||||
// ready to receive
|
||||
TEST_ESP_OK(rmt_receive(rx_channel, receive_user_buf_unaligned, receive_user_buf_unaligned_size, &rx_config));
|
||||
|
||||
rmt_symbol_word_t *transmit_buf = heap_caps_calloc(test_symbols_num, sizeof(rmt_symbol_word_t),
|
||||
MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
|
||||
for (int i = 0; i < test_symbols_num; i++) {
|
||||
transmit_buf[i] = (rmt_symbol_word_t) {
|
||||
.level0 = 1,
|
||||
.duration0 = 75,
|
||||
.level1 = 0,
|
||||
.duration1 = 25,
|
||||
};
|
||||
}
|
||||
TEST_ESP_OK(rmt_transmit(tx_channel, copy_encoder, transmit_buf, test_symbols_num * sizeof(rmt_symbol_word_t), &transmit_config));
|
||||
|
||||
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(2000)));
|
||||
printf("received %zu symbols\r\n", test_user_data.received_symbol_num);
|
||||
// Some chips do not support auto stop in loop mode, so the received symbol number may be slightly more than the expected
|
||||
TEST_ASSERT_INT_WITHIN(15, test_symbols_num, test_user_data.received_symbol_num);
|
||||
|
||||
// verify the received data
|
||||
for (int i = 0; i < 10; i++) {
|
||||
printf("{%d:%d},{%d:%d}\r\n", test_user_data.received_symbols[i].level0, test_user_data.received_symbols[i].duration0, test_user_data.received_symbols[i].level1, test_user_data.received_symbols[i].duration1);
|
||||
TEST_ASSERT_EQUAL(1, test_user_data.received_symbols[i].level0);
|
||||
TEST_ASSERT_INT_WITHIN(20, 75, test_user_data.received_symbols[i].duration0);
|
||||
TEST_ASSERT_EQUAL(0, test_user_data.received_symbols[i].level1);
|
||||
TEST_ASSERT_INT_WITHIN(20, 25, test_user_data.received_symbols[i].duration1);
|
||||
}
|
||||
|
||||
TEST_ESP_OK(rmt_disable(tx_channel));
|
||||
TEST_ESP_OK(rmt_disable(rx_channel));
|
||||
TEST_ESP_OK(rmt_del_channel(rx_channel));
|
||||
TEST_ESP_OK(rmt_del_channel(tx_channel));
|
||||
TEST_ESP_OK(rmt_del_encoder(copy_encoder));
|
||||
free(receive_user_buf);
|
||||
free(transmit_buf);
|
||||
}
|
||||
|
||||
TEST_CASE("rmt rx unaligned buffer", "[rmt]")
|
||||
{
|
||||
test_rmt_unaligned_receive(SOC_RMT_MEM_WORDS_PER_CHANNEL, SOC_RMT_MEM_WORDS_PER_CHANNEL, false, false, RMT_CLK_SRC_DEFAULT);
|
||||
#if SOC_RMT_SUPPORT_RX_PINGPONG
|
||||
test_rmt_unaligned_receive(SOC_RMT_MEM_WORDS_PER_CHANNEL, 10000, false, true, RMT_CLK_SRC_DEFAULT);
|
||||
#endif
|
||||
#if SOC_RMT_SUPPORT_DMA
|
||||
test_rmt_unaligned_receive(256, SOC_RMT_MEM_WORDS_PER_CHANNEL, true, false, RMT_CLK_SRC_DEFAULT);
|
||||
#endif
|
||||
#if SOC_RMT_SUPPORT_RX_PINGPONG && SOC_RMT_SUPPORT_DMA
|
||||
test_rmt_unaligned_receive(256, 10000, true, true, RMT_CLK_SRC_DEFAULT);
|
||||
#endif
|
||||
}
|
||||
|
Reference in New Issue
Block a user