Merge branch 'feature/rmt_translator' into 'master'

driver(rmt): Add API supports sending uint8_t type data

See merge request idf/esp-idf!1975
This commit is contained in:
Angus Gratton 2018-05-23 07:27:14 +08:00
commit c9f8470fdf
3 changed files with 208 additions and 12 deletions

View 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.
*

View 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;
}

View 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);