]> granicus.if.org Git - esp-idf/commitdiff
driver(rmt):Add API supports sending uint8_t type data.
authorkooho <2229179028@qq.com>
Sat, 24 Feb 2018 05:36:49 +0000 (13:36 +0800)
committerbot <bot@espressif.com>
Tue, 22 May 2018 01:55:44 +0000 (01:55 +0000)
components/driver/include/driver/rmt.h
components/driver/rmt.c
examples/peripherals/rmt_tx/main/rmt_tx_main.c

index a1bcbe8195247cae9ca4b5daa38a06bdc90f3916..7abf63ec08b021dd4d0960f99e70f0932aeafd4a 100644 (file)
@@ -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.
  *
index ca0dc6a28c5031bc1c714cfe47f83042716801c6..9460cefa714a3077bc7a5f17ad1390cae7f7f3b4 100644 (file)
@@ -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
index d71663a58e230ad41e7882cd0e4a94dda08d310c..ad6e4f6f651bce52b3f4dc8fa0e34b1f919f87ee 100644 (file)
@@ -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);