From 5bc8ae3b3a8be3e276c3318865efa9f0b5d82720 Mon Sep 17 00:00:00 2001 From: kooho <2229179028@qq.com> Date: Sat, 24 Feb 2018 13:36:49 +0800 Subject: [PATCH] driver(rmt):Add API supports sending uint8_t type data. --- components/driver/include/driver/rmt.h | 59 +++++++++ components/driver/rmt.c | 123 ++++++++++++++++-- .../peripherals/rmt_tx/main/rmt_tx_main.c | 38 +++++- 3 files changed, 208 insertions(+), 12 deletions(-) diff --git a/components/driver/include/driver/rmt.h b/components/driver/include/driver/rmt.h index a1bcbe819..7abf63ec0 100644 --- a/components/driver/include/driver/rmt.h +++ b/components/driver/include/driver/rmt.h @@ -129,6 +129,32 @@ typedef struct { void *arg; /*!< Optional argument passed to function */ } rmt_tx_end_callback_t; +/** + * @brief User callback function to convert uint8_t type data to rmt format(rmt_item32_t). + * + * This function may be called from an ISR, so, the code should be short and efficient. + * + * @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. + * + * @note + * In fact, item_num should be a multiple of translated_size, e.g. : + * When we convert each byte of uint8_t type data to rmt format data, + * the relation between item_num and translated_size should be `item_num = translated_size*8`. + */ +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 Set RMT clock divider, channel clock is divided from source clock. * @@ -714,6 +740,39 @@ esp_err_t rmt_wait_tx_done(rmt_channel_t channel, TickType_t wait_time); */ esp_err_t rmt_get_ringbuf_handle(rmt_channel_t channel, RingbufHandle_t* buf_handle); +/** + * @brief Init rmt translator and register user callback. + * The callback will convert the raw data that needs to be sent to rmt format. + * If a channel is initialized more than once, tha user callback will be replaced by the later. + * + * @param channel RMT channel (0 - 7). + * + * @param fn Point to the data conversion function. + * + * @return + * - ESP_FAIL Init fail. + * - ESP_OK Init success. + */ +esp_err_t rmt_translator_init(rmt_channel_t channel, sample_to_rmt_t fn); + +/** + * @brief Translate uint8_t type of data into rmt format and send it out. + * Requires rmt_translator_init to init the translator first. + * + * @param channel RMT channel (0 - 7). + * + * @param src Pointer to the raw data. + * + * @param src_size The size of the raw data. + * + * @param wait_tx_done Set true to wait all data send done. + * + * @return + * - ESP_FAIL Send fail + * - ESP_OK Send success + */ +esp_err_t rmt_write_sample(rmt_channel_t channel, const uint8_t *src, size_t src_size, bool wait_tx_done); + /** * @brief Registers a callback that will be called when transmission ends. * diff --git a/components/driver/rmt.c b/components/driver/rmt.c index ca0dc6a28..9460cefa7 100644 --- a/components/driver/rmt.c +++ b/components/driver/rmt.c @@ -46,6 +46,8 @@ #define RMT_DRIVER_ERROR_STR "RMT DRIVER ERR" #define RMT_DRIVER_LENGTH_ERROR_STR "RMT PARAM LEN ERROR" #define RMT_PSRAM_BUFFER_WARN_STR "Using buffer allocated from psram" +#define RMT_TRANSLATOR_NULL_STR "RMT translator is null" +#define RMT_TRANSLATOR_UNINIT_STR "RMT translator not init" static const char* RMT_TAG = "rmt"; static uint8_t s_rmt_driver_channels; // Bitmask (bits 0-7) of installed drivers' channels @@ -64,9 +66,10 @@ static portMUX_TYPE rmt_spinlock = portMUX_INITIALIZER_UNLOCKED; static _lock_t rmt_driver_isr_lock; typedef struct { - int tx_offset; - int tx_len_rem; - int tx_sub_len; + size_t tx_offset; + size_t tx_len_rem; + size_t tx_sub_len; + bool translator; bool wait_done; //Mark whether wait tx done. rmt_channel_t channel; const rmt_item32_t* tx_data; @@ -75,8 +78,11 @@ typedef struct { int intr_alloc_flags; StaticSemaphore_t tx_sem_buffer; #endif - RingbufHandle_t tx_buf; + rmt_item32_t* tx_buf; RingbufHandle_t rx_buf; + sample_to_rmt_t sample_to_rmt; + size_t sample_size_remain; + const uint8_t *sample_cur; } rmt_obj_t; rmt_obj_t* p_rmt_obj[RMT_CHANNEL_MAX] = {0}; @@ -559,9 +565,6 @@ static void IRAM_ATTR rmt_driver_isr_default(void* arg) xSemaphoreGiveFromISR(p_rmt->tx_sem, &HPTaskAwoken); RMT.conf_ch[channel].conf1.mem_rd_rst = 1; RMT.conf_ch[channel].conf1.mem_rd_rst = 0; - if(HPTaskAwoken == pdTRUE) { - portYIELD_FROM_ISR(); - } p_rmt->tx_data = NULL; p_rmt->tx_len_rem = 0; p_rmt->tx_offset = 0; @@ -583,9 +586,6 @@ static void IRAM_ATTR rmt_driver_isr_default(void* arg) } else { } - if(HPTaskAwoken == pdTRUE) { - portYIELD_FROM_ISR(); - } } else { ESP_EARLY_LOGE(RMT_TAG, "RMT RX BUFFER ERROR\n"); } @@ -613,6 +613,24 @@ static void IRAM_ATTR rmt_driver_isr_default(void* arg) if(p_rmt->tx_data == NULL) { //skip } else { + if(p_rmt->translator) { + if(p_rmt->sample_size_remain > 0) { + size_t translated_size = 0; + p_rmt->sample_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->sample_size_remain -= translated_size; + p_rmt->sample_cur += translated_size; + p_rmt->tx_data = p_rmt->tx_buf; + } else { + p_rmt->sample_cur = NULL; + p_rmt->translator = false; + } + } const rmt_item32_t* pdata = p_rmt->tx_data; int len_rem = p_rmt->tx_len_rem; if(len_rem >= p_rmt->tx_sub_len) { @@ -636,6 +654,9 @@ static void IRAM_ATTR rmt_driver_isr_default(void* arg) } } } + if(HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR(); + } } esp_err_t rmt_driver_uninstall(rmt_channel_t channel) @@ -677,6 +698,13 @@ esp_err_t rmt_driver_uninstall(rmt_channel_t channel) vRingbufferDelete(p_rmt_obj[channel]->rx_buf); p_rmt_obj[channel]->rx_buf = NULL; } + if(p_rmt_obj[channel]->tx_buf) { + free(p_rmt_obj[channel]->tx_buf); + p_rmt_obj[channel]->tx_buf = NULL; + } + if(p_rmt_obj[channel]->sample_to_rmt) { + p_rmt_obj[channel]->sample_to_rmt = NULL; + } free(p_rmt_obj[channel]); p_rmt_obj[channel] = NULL; @@ -717,7 +745,8 @@ esp_err_t rmt_driver_install(rmt_channel_t channel, size_t rx_buf_size, int intr p_rmt_obj[channel]->tx_offset = 0; p_rmt_obj[channel]->tx_sub_len = 0; p_rmt_obj[channel]->wait_done = false; - + p_rmt_obj[channel]->translator = false; + p_rmt_obj[channel]->sample_to_rmt = NULL; if(p_rmt_obj[channel]->tx_sem == NULL) { #if !CONFIG_SPIRAM_USE_MALLOC p_rmt_obj[channel]->tx_sem = xSemaphoreCreateBinary(); @@ -829,3 +858,75 @@ rmt_tx_end_callback_t rmt_register_tx_end_callback(rmt_tx_end_fn_t function, voi rmt_tx_end_callback.arg = arg; return previous; } + +esp_err_t rmt_translator_init(rmt_channel_t channel, sample_to_rmt_t fn) +{ + 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(p_rmt_obj[channel] != NULL, RMT_DRIVER_ERROR_STR, ESP_FAIL); + const uint32_t block_size = RMT.conf_ch[channel].conf0.mem_size * 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 = fn; + 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; +} + +esp_err_t rmt_write_sample(rmt_channel_t channel, const uint8_t *src, size_t src_size, bool wait_tx_done) +{ + 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]->sample_to_rmt != NULL,RMT_TRANSLATOR_UNINIT_STR, ESP_FAIL); +#if CONFIG_SPIRAM_USE_MALLOC + if( p_rmt_obj[channel]->intr_alloc_flags & ESP_INTR_FLAG_IRAM ) { + if( !esp_ptr_internal(src) ) { + ESP_LOGE(RMT_TAG, RMT_PSRAM_BUFFER_WARN_STR); + return ESP_ERR_INVALID_ARG; + } + } +#endif + size_t item_num = 0; + size_t translated_size = 0; + rmt_obj_t* p_rmt = p_rmt_obj[channel]; + const uint32_t item_block_len = RMT.conf_ch[channel].conf0.mem_size * RMT_MEM_ITEM_NUM; + const uint32_t item_sub_len = item_block_len / 2; + xSemaphoreTake(p_rmt->tx_sem, portMAX_DELAY); + p_rmt->sample_to_rmt((void *)src, p_rmt->tx_buf, src_size, item_block_len, &translated_size, &item_num); + p_rmt->sample_size_remain = src_size - translated_size; + p_rmt->sample_cur = src + translated_size; + rmt_fill_memory(channel, p_rmt->tx_buf, item_num, 0); + if (item_num == item_block_len) { + rmt_set_tx_thr_intr_en(channel, 1, item_sub_len); + p_rmt->tx_data = p_rmt->tx_buf; + p_rmt->tx_offset = 0; + p_rmt->tx_sub_len = item_sub_len; + p_rmt->translator = true; + } else { + RMTMEM.chan[channel].data32[item_num].val = 0; + p_rmt->tx_len_rem = 0; + p_rmt->sample_cur = NULL; + p_rmt->translator = false; + } + rmt_tx_start(channel, true); + p_rmt->wait_done = wait_tx_done; + if (wait_tx_done) { + xSemaphoreTake(p_rmt->tx_sem, portMAX_DELAY); + xSemaphoreGive(p_rmt->tx_sem); + } + return ESP_OK; +} \ No newline at end of file diff --git a/examples/peripherals/rmt_tx/main/rmt_tx_main.c b/examples/peripherals/rmt_tx/main/rmt_tx_main.c index d71663a58..ad6e4f6f6 100644 --- a/examples/peripherals/rmt_tx/main/rmt_tx_main.c +++ b/examples/peripherals/rmt_tx/main/rmt_tx_main.c @@ -15,6 +15,7 @@ static const char *RMT_TX_TAG = "RMT Tx"; #define RMT_TX_CHANNEL RMT_CHANNEL_0 #define RMT_TX_GPIO 18 +#define SAMPLE_CNT (10) /* * Prepare a raw table with a message in the Morse code @@ -48,6 +49,37 @@ rmt_item32_t items[] = { {{{ 0, 1, 0, 0 }}} }; +//Convert uint8_t type of data to rmt format data. +static void IRAM_ATTR u8_to_rmt(const void* src, rmt_item32_t* dest, size_t src_size, + size_t wanted_num, size_t* translated_size, size_t* item_num) +{ + if(src == NULL || dest == NULL) { + *translated_size = 0; + *item_num = 0; + return; + } + const rmt_item32_t bit0 = {{{ 32767, 1, 15000, 0 }}}; //Logical 0 + const rmt_item32_t bit1 = {{{ 32767, 1, 32767, 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++) { + if(*psrc & (0x1 << i)) { + pdest->val = bit1.val; + } else { + pdest->val = bit0.val; + } + num++; + pdest++; + } + size++; + psrc++; + } + *translated_size = size; + *item_num = num; +} /* * Initialize the RMT Tx channel @@ -77,18 +109,22 @@ static void rmt_tx_int() ESP_ERROR_CHECK(rmt_config(&config)); ESP_ERROR_CHECK(rmt_driver_install(config.channel, 0, 0)); + ESP_ERROR_CHECK(rmt_translator_init(config.channel, u8_to_rmt)); } - void app_main(void *ignore) { ESP_LOGI(RMT_TX_TAG, "Configuring transmitter"); rmt_tx_int(); int number_of_items = sizeof(items) / sizeof(items[0]); + const uint8_t sample[SAMPLE_CNT] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; while (1) { ESP_ERROR_CHECK(rmt_write_items(RMT_TX_CHANNEL, items, number_of_items, true)); ESP_LOGI(RMT_TX_TAG, "Transmission complete"); + vTaskDelay(1000 / portTICK_PERIOD_MS); + ESP_ERROR_CHECK(rmt_write_sample(RMT_TX_CHANNEL, sample, SAMPLE_CNT, true)); + ESP_LOGI(RMT_TX_TAG, "Sample transmission complete"); vTaskDelay(2000 / portTICK_PERIOD_MS); } vTaskDelete(NULL);