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.
*
*/
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.
*
#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
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;
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};
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;
} else {
}
- if(HPTaskAwoken == pdTRUE) {
- portYIELD_FROM_ISR();
- }
} else {
ESP_EARLY_LOGE(RMT_TAG, "RMT RX BUFFER ERROR\n");
}
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) {
}
}
}
+ if(HPTaskAwoken == pdTRUE) {
+ portYIELD_FROM_ISR();
+ }
}
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;
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();
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
#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
{{{ 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
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);