From 310264048e542284c6afa4e3b7ed215afb2288a1 Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Sun, 26 Mar 2023 01:07:22 -0300 Subject: [PATCH 01/14] RMT IDF5.1 refactoring --- cores/esp32/esp32-hal-rgb-led.c | 20 +- cores/esp32/esp32-hal-rmt.c | 1124 +++++++++-------- cores/esp32/esp32-hal-rmt.h | 175 ++- .../examples/RMT/RMTCallback/RMTCallback.ino | 19 +- .../examples/RMT/RMTLoopback/RMTLoopback.ino | 28 +- .../examples/RMT/RMTReadXJT/RMTReadXJT.ino | 15 +- .../RMT/RMTWriteNeoPixel/RMTWriteNeoPixel.ino | 9 +- 7 files changed, 724 insertions(+), 666 deletions(-) diff --git a/cores/esp32/esp32-hal-rgb-led.c b/cores/esp32/esp32-hal-rgb-led.c index 61558b86ee6..6e0f74307b6 100644 --- a/cores/esp32/esp32-hal-rgb-led.c +++ b/cores/esp32/esp32-hal-rgb-led.c @@ -3,24 +3,18 @@ void neopixelWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val){ rmt_data_t led_data[24]; - static rmt_obj_t* rmt_send = NULL; - static bool initialized = false; - uint8_t _pin = pin; #ifdef RGB_BUILTIN if(pin == RGB_BUILTIN){ - _pin = RGB_BUILTIN-SOC_GPIO_PIN_COUNT; + pin = RGB_BUILTIN-SOC_GPIO_PIN_COUNT; } #endif - if(!initialized){ - if((rmt_send = rmtInit(_pin, RMT_TX_MODE, RMT_MEM_64)) == NULL){ - log_e("RGB LED driver initialization failed!"); - rmt_send = NULL; - return; - } - rmtSetTick(rmt_send, 100); - initialized = true; + // 10MHz RMT Frequency -> tick = 100ns + // force initialization to make sure it has the right RMT Frequency + if(!rmtInit(pin, RMT_TX_MODE, RMT_MEM_64, 10000000)){ + log_e("RGB LED driver initialization failed!"); + return; } int color[] = {green_val, red_val, blue_val}; // Color coding is in order GREEN, RED, BLUE @@ -43,5 +37,5 @@ void neopixelWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue i++; } } - rmtWrite(rmt_send, led_data, 24); + rmtWrite(pin, led_data, 24); } diff --git a/cores/esp32/esp32-hal-rmt.c b/cores/esp32/esp32-hal-rmt.c index 309cd13b327..08091fafccb 100644 --- a/cores/esp32/esp32-hal-rmt.c +++ b/cores/esp32/esp32-hal-rmt.c @@ -1,4 +1,4 @@ -// Copyright 2018 Espressif Systems (Shanghai) PTE LTD +// Copyright 2023 Espressif Systems (Shanghai) PTE LTD // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -13,622 +13,700 @@ // limitations under the License. #include "esp32-hal.h" -#include "driver/rmt.h" +#include "driver/gpio.h" +#include "driver/rmt_tx.h" +#include "driver/rmt_rx.h" -/** - * Internal macros - */ - -#define MAX_CHANNELS (SOC_RMT_GROUPS * SOC_RMT_CHANNELS_PER_GROUP) +#include "esp32-hal-rmt.h" +#include "esp32-hal-periman.h" -#define RMT_TX_CH_START (0) -#define RMT_TX_CH_END (SOC_RMT_TX_CANDIDATES_PER_GROUP - 1) -#define RMT_RX_CH_START (SOC_RMT_CHANNELS_PER_GROUP - SOC_RMT_TX_CANDIDATES_PER_GROUP) -#define RMT_RX_CH_END (SOC_RMT_CHANNELS_PER_GROUP - 1) -#define _LIMIT(a,b) (a>b?b:a) +/** + Internal macros +*/ #if CONFIG_DISABLE_HAL_LOCKS -# define RMT_MUTEX_LOCK(channel) -# define RMT_MUTEX_UNLOCK(channel) +# define RMT_MUTEX_LOCK(busptr) +# define RMT_MUTEX_UNLOCK(busptr) #else -# define RMT_MUTEX_LOCK(channel) do {} while (xSemaphoreTake(g_rmt_objlocks[channel], portMAX_DELAY) != pdPASS) -# define RMT_MUTEX_UNLOCK(channel) xSemaphoreGive(g_rmt_objlocks[channel]) +# define RMT_MUTEX_LOCK(busptr) do {} while (xSemaphoreTake(busptr->g_rmt_objlocks, portMAX_DELAY) != pdPASS) +# define RMT_MUTEX_UNLOCK(busptr) xSemaphoreGive(busptr->g_rmt_objlocks) #endif /* CONFIG_DISABLE_HAL_LOCKS */ -//#define _RMT_INTERNAL_DEBUG -#ifdef _RMT_INTERNAL_DEBUG -# define DEBUG_INTERRUPT_START(pin) digitalWrite(pin, 1); -# define DEBUG_INTERRUPT_END(pin) digitalWrite(pin, 0); -#else -# define DEBUG_INTERRUPT_START(pin) -# define DEBUG_INTERRUPT_END(pin) -#endif /* _RMT_INTERNAL_DEBUG */ - -#define RMT_DEFAULT_ARD_CONFIG_TX(gpio, channel_id, buffers) \ - { \ - .rmt_mode = RMT_MODE_TX, \ - .channel = channel_id, \ - .gpio_num = gpio, \ - .clk_div = 1, \ - .mem_block_num = buffers, \ - .flags = 0, \ - .tx_config = { \ - .carrier_level = RMT_CARRIER_LEVEL_HIGH, \ - .idle_level = RMT_IDLE_LEVEL_LOW, \ - .carrier_duty_percent = 50, \ - .carrier_en = false, \ - .loop_en = false, \ - .idle_output_en = true, \ - } \ - } -#define RMT_DEFAULT_ARD_CONFIG_RX(gpio, channel_id, buffers) \ - { \ - .rmt_mode = RMT_MODE_RX, \ - .channel = channel_id, \ - .gpio_num = gpio, \ - .clk_div = 1, \ - .mem_block_num = buffers, \ - .flags = 0, \ - .rx_config = { \ - .idle_threshold = 0x80, \ - .filter_ticks_thresh = 100, \ - .filter_en = false, \ - } \ - } +/** + Typedefs for internal stuctures, enums +*/ +// structure used as interface for RMT Reading Operations +struct rmt_rx_obj_s { + EventGroupHandle_t rmt_rx_events; // reading event user handle + rmt_rx_data_cb_t rmt_rx_cb; // Rx user function callback + void * rmt_rx_cb_arg; // Rx user function argument + TaskHandle_t rmt_rxTaskHandle; // Rx task created to wait for received RMT data + QueueHandle_t rmt_rx_queue; // Rx callback data queue + bool rmt_rx_completed; // rmtReceiveCompleted() rmtReadAsync() async reading + bool rmt_async_rx_enabled; // rmtEnd() rmtBeginReceive() rmtRead() rmtReadAsync() control + rmt_data_t* rmt_data_rx_ptr; // Rx Task - data user pointer + size_t rmt_data_rx_size; // Rx Task - data length +}; +typedef struct rmt_rx_obj_s *rmt_rx_obj_handle_t; +struct rmt_obj_s { + // general rmt information + rmt_channel_handle_t rmt_channel_h; // NULL value means channel not alocated + rmt_encoder_handle_t rmt_copy_encoder_h; // RMT simple copy encoder handle -/** - * Typedefs for internal stuctures, enums - */ -struct rmt_obj_s -{ - bool allocated; - EventGroupHandle_t events; - uint32_t channel; - uint32_t buffers; - uint32_t data_size; - uint32_t* data_ptr; - rmt_rx_data_cb_t cb; - void * arg; - TaskHandle_t rxTaskHandle; - bool rx_completed; - bool tx_not_rx; -}; + // rmt RX (reading) information + rmt_rx_obj_handle_t rmt_ch_rx_obj_h; // RMT Reading Processing necessary data -/** - * Internal variables for channel descriptors - */ -static xSemaphoreHandle g_rmt_objlocks[MAX_CHANNELS] = { - NULL, NULL, NULL, NULL, -#if MAX_CHANNELS > 4 - NULL, NULL, NULL, NULL -#endif -}; + uint8_t num_buffers; // Number of allocated buffers needed for reading + uint32_t signal_range_min_ns; // RX Filter data - Low Pass pulse width + uint32_t signal_range_max_ns; // RX idle time that defines end of reading -static rmt_obj_t g_rmt_objects[MAX_CHANNELS] = { - { false, NULL, 0, 0, 0, NULL, NULL, NULL, NULL, true, true}, - { false, NULL, 0, 0, 0, NULL, NULL, NULL, NULL, true, true}, - { false, NULL, 0, 0, 0, NULL, NULL, NULL, NULL, true, true}, - { false, NULL, 0, 0, 0, NULL, NULL, NULL, NULL, true, true}, -#if MAX_CHANNELS > 4 - { false, NULL, 0, 0, 0, NULL, NULL, NULL, NULL, true, true}, - { false, NULL, 0, 0, 0, NULL, NULL, NULL, NULL, true, true}, - { false, NULL, 0, 0, 0, NULL, NULL, NULL, NULL, true, true}, - { false, NULL, 0, 0, 0, NULL, NULL, NULL, NULL, true, true}, -#endif + xSemaphoreHandle g_rmt_objlocks; // Channel Semaphore Lock }; +typedef struct rmt_obj_s *rmt_bus_handle_t; /** - * Internal variables for driver data - */ + Internal variables used in RMT API +*/ static xSemaphoreHandle g_rmt_block_lock = NULL; /** - * Internal method (private) declarations - */ + Internal method (private) declarations +*/ -static rmt_obj_t* _rmtAllocate(int pin, int from, int size) +// allocate the bus reading strucuture whenever necessary +static bool _rmtStartReading(rmt_bus_handle_t bus, uint8_t gpio_num, const char* labelFunc) { - int i; - // setup how many buffers shall we use - g_rmt_objects[from].buffers = size; - - for (i=0; irmt_ch_rx_obj_h == NULL) { + // allocate the rmt bus object + bus->rmt_ch_rx_obj_h = (rmt_rx_obj_handle_t)heap_caps_calloc(1, sizeof(struct rmt_rx_obj_s), MALLOC_CAP_DEFAULT); + if (bus == NULL) { + log_e("==>%s():GPIO %d - Bus RX Struct Memory allocation fault.", labelFunc, gpio_num); + return false; + } + // when the RMT channel is created, it is enabled, thus we just set the flag here + bus->rmt_ch_rx_obj_h->rmt_async_rx_enabled = true; + } + return true; } -void _rmtDumpStatus(rmt_obj_t* rmt) +static bool _rmt_rx_done_callback(rmt_channel_handle_t channel, const rmt_rx_done_event_data_t *data, void *args) { - bool loop_en; - uint8_t div_cnt; - uint8_t memNum; - bool lowPowerMode; - rmt_mem_owner_t owner; - uint16_t idleThreshold; - uint32_t status; - rmt_source_clk_t srcClk; - rmt_channel_t channel = rmt->channel; - -RMT_MUTEX_LOCK(channel); - rmt_get_tx_loop_mode(channel, &loop_en); - rmt_get_clk_div(channel, &div_cnt); - rmt_get_mem_block_num(channel, &memNum); - rmt_get_mem_pd(channel, &lowPowerMode); - rmt_get_memory_owner(channel, &owner); - rmt_get_rx_idle_thresh(channel, &idleThreshold); - rmt_get_status(channel, &status); - rmt_get_source_clk(channel, &srcClk); - - log_d("Status for RMT channel %d", channel); - log_d("- Loop enabled: %d", loop_en); - log_d("- Clock divisor: %d", div_cnt); - log_d("- Number of memory blocks: %d", memNum); - log_d("- Low power mode: %d", lowPowerMode); - log_d("- Memory owner: %s", owner==RMT_MEM_OWNER_TX?"TX":"RX"); - log_d("- Idle threshold: %d", idleThreshold); - log_d("- Status: %d", status); - log_d("- Source clock: %s", srcClk==RMT_BASECLK_APB?"APB (80MHz)":"1MHz"); -RMT_MUTEX_UNLOCK(channel); + BaseType_t high_task_wakeup = pdFALSE; + QueueHandle_t rmt_rx_queue = (QueueHandle_t)args; + // send the received RMT symbols to the _rmtRxTask of that channel + xQueueSendFromISR(rmt_rx_queue, data, &high_task_wakeup); + return high_task_wakeup == pdTRUE; } static void _rmtRxTask(void *args) { - rmt_obj_t *rmt = (rmt_obj_t *) args; - RingbufHandle_t rb = NULL; - size_t rmt_len = 0; - rmt_item32_t *data = NULL; - - if (!rmt) { - log_e(" -- Inavalid Argument"); - goto err; - } - - int channel = rmt->channel; - rmt_get_ringbuf_handle(channel, &rb); - if (!rb) { - log_e(" -- Failed to get RMT ringbuffer handle"); - goto err; - } - - for(;;) { - data = (rmt_item32_t *) xRingbufferReceive(rb, &rmt_len, portMAX_DELAY); - if (data) { - log_d(" -- Got %d bytes on RX Ringbuffer - CH %d", rmt_len, rmt->channel); - rmt->rx_completed = true; // used in rmtReceiveCompleted() - // callback - if (rmt->cb) { - (rmt->cb)((uint32_t *)data, rmt_len / sizeof(rmt_item32_t), rmt->arg); - } else { - // stop RX -- will force a correct call with a callback pointer / new rmtReadData() / rmtReadAsync() - rmt_rx_stop(channel); - } - // Async Read -- copy data to caller - if (rmt->data_ptr && rmt->data_size) { - uint32_t data_size = rmt->data_size; - uint32_t read_len = rmt_len / sizeof(rmt_item32_t); - if (read_len < rmt->data_size) data_size = read_len; - rmt_item32_t *p = (rmt_item32_t *)rmt->data_ptr; - for (uint32_t i = 0; i < data_size; i++) { - p[i] = data[i]; - } - } - // set events - if (rmt->events) { - xEventGroupSetBits(rmt->events, RMT_FLAG_RX_DONE); - } - vRingbufferReturnItem(rb, (void *) data); - } // xRingbufferReceive - } // for(;;) + rmt_bus_handle_t bus = (rmt_bus_handle_t) args; + + // sanity check - it should never happen + assert(bus && "_rmtRxTask bus NULL pointer."); + + bus->rmt_ch_rx_obj_h->rmt_rx_queue = xQueueCreate(1, sizeof(rmt_rx_done_event_data_t)); + if (bus->rmt_ch_rx_obj_h->rmt_rx_queue == NULL) { + log_e("Error creating RX Queue."); + goto err; + } + + rmt_rx_event_callbacks_t cbs = { .on_recv_done = _rmt_rx_done_callback }; + if (ESP_OK != rmt_rx_register_event_callbacks(bus->rmt_channel_h, &cbs, bus->rmt_ch_rx_obj_h->rmt_rx_queue)) { + log_e("Error registering RX Callback."); + vQueueDelete(bus->rmt_ch_rx_obj_h->rmt_rx_queue); + goto err; + } + + for (;;) { + // request reading RMT Channel Data + rmt_receive_config_t receive_config; + receive_config.signal_range_min_ns = bus->signal_range_min_ns; + receive_config.signal_range_max_ns = bus->signal_range_max_ns; + // buffer to save the received RMT symbols from the callback, in STACK for easier memory deallocation + size_t num_rmt_symbols = bus->num_buffers * SOC_RMT_MEM_WORDS_PER_CHANNEL; // max is 8*64*4 = 2KB + rmt_data_t rmt_symbols[num_rmt_symbols]; // number of symbols requested by user + // must make sure that the RX Channel is enabled! + while (!bus->rmt_ch_rx_obj_h->rmt_async_rx_enabled) { + delay(1); // let other lower priority tasks to run, such as Arduino Task... + } + rmt_receive(bus->rmt_channel_h, rmt_symbols, num_rmt_symbols * sizeof(rmt_data_t), &receive_config); + + // wait for ever for RX done signal + rmt_rx_done_event_data_t rx_data; + if (xQueueReceive(bus->rmt_ch_rx_obj_h->rmt_rx_queue, &rx_data, portMAX_DELAY) == pdPASS) { + log_d("RMT has read %d bytes.", rx_data.num_symbols * sizeof(rmt_data_t)); + // process the user callback + if (bus->rmt_ch_rx_obj_h->rmt_rx_cb) { + (bus->rmt_ch_rx_obj_h->rmt_rx_cb)((uint32_t *)rx_data.received_symbols, rx_data.num_symbols, bus->rmt_ch_rx_obj_h->rmt_rx_cb_arg); + } + // Async Read -- copy data to caller + uint32_t data_size = bus->rmt_ch_rx_obj_h->rmt_data_rx_size; + rmt_data_t *p = (rmt_data_t *)bus->rmt_ch_rx_obj_h->rmt_data_rx_ptr; + if (p != NULL && data_size > 0) { + if (rx_data.num_symbols < data_size) data_size = rx_data.num_symbols; + for (uint32_t i = 0; i < data_size; i++) { + p[i].val = rx_data.received_symbols[i].val; + } + } + // set RX event group + if (bus->rmt_ch_rx_obj_h->rmt_rx_events) { + xEventGroupSetBits(bus->rmt_ch_rx_obj_h->rmt_rx_events, RMT_FLAG_RX_DONE); + } + // used in rmtReceiveCompleted() + bus->rmt_ch_rx_obj_h->rmt_rx_completed = true; + } // xQueueReceive + } // for(;;) err: - vTaskDelete(NULL); + vTaskDelete(NULL); } - -static bool _rmtCreateRxTask(rmt_obj_t* rmt) +static bool _rmtCreateRxTask(rmt_bus_handle_t bus) { - if (!rmt) { - return false; - } - if (rmt->rxTaskHandle) { // Task already created - return false; - } - - xTaskCreate(_rmtRxTask, "rmt_rx_task", 4096, rmt, 20, &rmt->rxTaskHandle); - - if(rmt->rxTaskHandle == NULL){ - log_e("RMT RX Task create failed"); - return false; - } + if (bus == NULL) { + return false; + } + if (bus->rmt_ch_rx_obj_h->rmt_rxTaskHandle != NULL) { // Task already created return true; -} + } -// Helper function to test if an RMT channel is correctly assigned to TX or RX, issuing an error message if necessary -// Also test RMT pointer for NULL and returns false in case it is NULL -// return true when it is correctly assigned, false otherwise -static bool _rmtCheckTXnotRX(rmt_obj_t* rmt, bool tx_not_rx) -{ - if (!rmt) { // also returns false on NULL - return false; - } + xTaskCreate(_rmtRxTask, "rmt_rx_task", 1024 * 6, bus, configMAX_PRIORITIES - 1, &bus->rmt_ch_rx_obj_h->rmt_rxTaskHandle); - if (rmt->tx_not_rx == tx_not_rx) { // matches expected RX/TX channel - return true; - } - - if (tx_not_rx) { // expected TX channel - log_e("Can't write on a RX RMT Channel"); - } else{ // expected RX channel - log_e("Can't read on a TX RMT Channel"); - } - return false; // missmatched + if (bus->rmt_ch_rx_obj_h->rmt_rxTaskHandle == NULL) { + log_e("RMT RX Task create failed"); + return false; + } + return true; } -/** - * Public method definitions - */ - -bool rmtSetCarrier(rmt_obj_t* rmt, bool carrier_en, bool carrier_level, uint32_t low, uint32_t high) +// This function must be called only after checking the pin and its bus with _rmtGetBus() +static bool _rmtCheckDirection(uint8_t gpio_num, rmt_ch_dir_t rmt_dir, const char* labelFunc) { - if (!_rmtCheckTXnotRX(rmt, RMT_TX_MODE) || low > 0xFFFF || high > 0xFFFF) { - return false; - } - size_t channel = rmt->channel; + // gets bus RMT direction from the Peripheral Manager information + rmt_ch_dir_t bus_rmt_dir = perimanGetPinBusType(gpio_num) == ESP32_BUS_TYPE_RMT_TX ? RMT_TX_MODE : RMT_RX_MODE; - RMT_MUTEX_LOCK(channel); - rmt_set_tx_carrier(channel, carrier_en, high, low, carrier_level); - RMT_MUTEX_UNLOCK(channel); + if (bus_rmt_dir == rmt_dir) { // matches expected RX/TX channel return true; + } + + // print error message + if (rmt_dir == RMT_RX_MODE) { + log_w("==>%s():Channel set as TX instead of RX.", labelFunc); + } else { + log_w("==>%s():Channel set as RX instead of TX.", labelFunc); + } + return false; // mismatched } -bool rmtSetFilter(rmt_obj_t* rmt, bool filter_en, uint32_t filter_level) +static rmt_bus_handle_t _rmtGetBus(int pin, const char* labelFunc) { - if (!_rmtCheckTXnotRX(rmt, RMT_RX_MODE) || filter_level > 0xFF) { - return false; - } - size_t channel = rmt->channel; - - RMT_MUTEX_LOCK(channel); - rmt_set_rx_filter(channel, filter_en, filter_level); - RMT_MUTEX_UNLOCK(channel); - return true; + // Is pin RX or TX? Let's find it out + peripheral_bus_type_t rmt_bus_type = perimanGetPinBusType(pin); + if (rmt_bus_type != ESP32_BUS_TYPE_RMT_TX && rmt_bus_type != ESP32_BUS_TYPE_RMT_RX) { + log_e("==>%s():GPIO %u is not attached to an RMT channel.", labelFunc, pin); + return NULL; + } + + return (rmt_bus_handle_t)perimanGetPinBus(pin, rmt_bus_type); } -bool rmtSetRxThreshold(rmt_obj_t* rmt, uint32_t value) +// Peripheral Manager detach callback +static bool _rmtDetachBus(void *busptr) { - if (!_rmtCheckTXnotRX(rmt, RMT_RX_MODE) || value > 0xFFFF) { - return false; - } - size_t channel = rmt->channel; - - RMT_MUTEX_LOCK(channel); - rmt_set_rx_idle_thresh(channel, value); - RMT_MUTEX_UNLOCK(channel); - return true; + // sanity check - it should never happen + assert(busptr && "_rmtDetachBus bus NULL pointer."); + + bool retCode = true; + rmt_bus_handle_t bus = (rmt_bus_handle_t) busptr; + + // delete RX data + if (bus->rmt_ch_rx_obj_h != NULL) { + // deleted RX channel tasks + if (bus->rmt_ch_rx_obj_h->rmt_rxTaskHandle != NULL) { + vTaskDelete(bus->rmt_ch_rx_obj_h->rmt_rxTaskHandle); + } + // delete RX Queue + if (bus->rmt_ch_rx_obj_h->rmt_rx_queue != NULL) { + vQueueDelete(bus->rmt_ch_rx_obj_h->rmt_rx_queue); + } + free(bus->rmt_ch_rx_obj_h); + } + // deallocate the channel encoder + if (bus->rmt_copy_encoder_h != NULL) { + if (ESP_OK != rmt_del_encoder(bus->rmt_copy_encoder_h)) { + log_w("RMT Encoder Deletion has failed."); + retCode = false; + } + } + // disable and deallocate RMT channal + if (bus->rmt_channel_h != NULL) { + // force stopping rmt TX/RX processing + rmt_disable(bus->rmt_channel_h); + if (ESP_OK != rmt_del_channel(bus->rmt_channel_h)) { + log_w("RMT Channel Deletion has failed."); + retCode = false; + } + } +#if !CONFIG_DISABLE_HAL_LOCKS + // deallocate locker + if (bus->g_rmt_objlocks != NULL) { + vSemaphoreDelete(bus->g_rmt_objlocks); + } +#endif + // free the allocated bus data structure + free(bus); + return retCode; } +/** + Public method definitions +*/ -bool rmtDeinit(rmt_obj_t *rmt) +// RX demodulation carrier or TX modulatin carrier +bool rmtSetCarrier(int pin, bool carrier_en, bool carrier_level, uint32_t frequency_Hz, float duty_percent) { - if (!rmt) { - return false; - } - - // sanity check - if (rmt != &(g_rmt_objects[rmt->channel])) { - return false; - } - - RMT_MUTEX_LOCK(rmt->channel); - // force stopping rmt processing - if (rmt->tx_not_rx) { - rmt_tx_stop(rmt->channel); - } else { - rmt_rx_stop(rmt->channel); - if(rmt->rxTaskHandle){ - vTaskDelete(rmt->rxTaskHandle); - rmt->rxTaskHandle = NULL; - } - } - - rmt_driver_uninstall(rmt->channel); - - size_t from = rmt->channel; - size_t to = rmt->buffers + rmt->channel; - size_t i; - - for (i = from; i < to; i++) { - g_rmt_objects[i].allocated = false; - } - - g_rmt_objects[from].channel = 0; - g_rmt_objects[from].buffers = 0; - RMT_MUTEX_UNLOCK(rmt->channel); - -#if !CONFIG_DISABLE_HAL_LOCKS - if(g_rmt_objlocks[from] != NULL) { - vSemaphoreDelete(g_rmt_objlocks[from]); - g_rmt_objlocks[from] = NULL; - } -#endif - - return true; + rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__); + if (bus == NULL) { + return false; + } + + if (duty_percent > 1) { + log_w("GPIO %d - RMT Carrier must be a float percentage from 0 to 1. Setting to 50%.", pin); + duty_percent = 0.5; + } + rmt_carrier_config_t carrier_cfg = {0}; + carrier_cfg.duty_cycle = duty_percent; // duty cycle + carrier_cfg.frequency_hz = carrier_en ? frequency_Hz : 0; // carrier frequency in Hz + carrier_cfg.flags.polarity_active_low = carrier_level; // carrier modulation polarity level + + bool retCode = true; + RMT_MUTEX_LOCK(bus); + // modulate carrier to TX channel + if (ESP_OK != rmt_apply_carrier(bus->rmt_channel_h, &carrier_cfg)) { + log_w("GPIO %d - Error applying RMT carrier.", pin); + retCode = false; + } + RMT_MUTEX_UNLOCK(bus); + + return retCode; } -bool rmtLoop(rmt_obj_t* rmt, rmt_data_t* data, size_t size) +//In receive mode, channel will ignore input pulse when the pulse width is smaller than +bool rmtSetFilter(int pin, bool filter_en, uint8_t filter_pulse_ns) { - if (!_rmtCheckTXnotRX(rmt, RMT_TX_MODE)) { - return false; - } - int channel = rmt->channel; - RMT_MUTEX_LOCK(channel); - rmt_tx_stop(channel); - rmt_set_tx_loop_mode(channel, true); - rmt_write_items(channel, (const rmt_item32_t *)data, size, false); - RMT_MUTEX_UNLOCK(channel); - return true; + rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__); + if (bus == NULL) { + return false; + } + + if (!_rmtCheckDirection(pin, RMT_RX_MODE, __FUNCTION__)) { + return false; + } + + RMT_MUTEX_LOCK(bus); + bus->signal_range_min_ns = filter_en ? filter_pulse_ns : 0; // set as zero to disable it + RMT_MUTEX_UNLOCK(bus); + return true; } -bool rmtWrite(rmt_obj_t* rmt, rmt_data_t* data, size_t size) +//In receive mode, when no edge is detected on the input signal for +//longer than idle_thres channel clock cycles, the receive process is finished. +bool rmtSetRxThreshold(int pin, uint16_t value) { - if (!_rmtCheckTXnotRX(rmt, RMT_TX_MODE)) { - return false; - } - int channel = rmt->channel; - RMT_MUTEX_LOCK(channel); - rmt_tx_stop(channel); - rmt_set_tx_loop_mode(channel, false); - rmt_write_items(channel, (const rmt_item32_t *)data, size, false); - RMT_MUTEX_UNLOCK(channel); - return true; + rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__); + if (bus == NULL) { + return false; + } + + if (!_rmtCheckDirection(pin, RMT_RX_MODE, __FUNCTION__)) { + return false; + } + + RMT_MUTEX_LOCK(bus); + bus->signal_range_max_ns = value; // set as zero to disable it + RMT_MUTEX_UNLOCK(bus); + return true; } -bool rmtWriteBlocking(rmt_obj_t* rmt, rmt_data_t* data, size_t size) +bool rmtDeinit(int pin) { - if (!_rmtCheckTXnotRX(rmt, RMT_TX_MODE)) { - return false; - } - int channel = rmt->channel; - RMT_MUTEX_LOCK(channel); - rmt_tx_stop(channel); - rmt_set_tx_loop_mode(channel, false); - rmt_write_items(channel, (const rmt_item32_t *)data, size, true); - RMT_MUTEX_UNLOCK(channel); - return true; + if (_rmtGetBus(pin, __FUNCTION__) != NULL) { + // release all allocated data + return perimanSetPinBus(pin, ESP32_BUS_TYPE_INIT, NULL); + } + log_e("GPIO %d - No RMT channel associated.", pin); + return false; } -bool rmtReadData(rmt_obj_t* rmt, uint32_t* data, size_t size) +bool rmtLoop(int pin, rmt_data_t* data, size_t size) { - if (!_rmtCheckTXnotRX(rmt, RMT_RX_MODE)) { - return false; - } - rmtReadAsync(rmt, (rmt_data_t*) data, size, NULL, false, 0); - return true; + rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__); + if (bus == NULL) { + return false; + } + if (!_rmtCheckDirection(pin, RMT_TX_MODE, __FUNCTION__)) { + return false; + } + + rmt_transmit_config_t transmit_cfg = { -1, 0}; // enable infinite loop mode + bool retCode = true; + RMT_MUTEX_LOCK(bus); + + if (ESP_OK != rmt_transmit(bus->rmt_channel_h, bus->rmt_copy_encoder_h, + (const void *) data, size * sizeof(rmt_data_t), &transmit_cfg)) { + retCode = false; + log_w("GPIO %d - RMT Loop Transmission failed.", pin); + } + RMT_MUTEX_UNLOCK(bus); + return retCode; } - -bool rmtBeginReceive(rmt_obj_t* rmt) +bool rmtWrite(int pin, rmt_data_t* data, size_t size) { - if (!_rmtCheckTXnotRX(rmt, RMT_RX_MODE)) { - return false; - } - int channel = rmt->channel; - - RMT_MUTEX_LOCK(channel); - rmt_set_memory_owner(channel, RMT_MEM_OWNER_RX); - rmt_rx_start(channel, true); - rmt->rx_completed = false; - RMT_MUTEX_UNLOCK(channel); - return true; + rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__); + if (bus == NULL) { + return false; + } + if (!_rmtCheckDirection(pin, RMT_TX_MODE, __FUNCTION__)) { + return false; + } + + rmt_transmit_config_t transmit_cfg = {0, 0}; // disable loop mode + bool retCode = true; + RMT_MUTEX_LOCK(bus); + + if (ESP_OK != rmt_transmit(bus->rmt_channel_h, bus->rmt_copy_encoder_h, + (const void *) data, size * sizeof(rmt_data_t), &transmit_cfg)) { + retCode = false; + log_w("GPIO %d - RMT single Transmission failed.", pin); + } + RMT_MUTEX_UNLOCK(bus); + return retCode; } -bool rmtReceiveCompleted(rmt_obj_t* rmt) +bool rmtWriteBlocking(int pin, rmt_data_t* data, size_t size) { - if (!rmt) { - return false; - } - - return rmt->rx_completed; + rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__); + if (bus == NULL) { + return false; + } + if (!_rmtCheckDirection(pin, RMT_TX_MODE, __FUNCTION__)) { + return false; + } + + rmt_transmit_config_t transmit_cfg = {0, 0}; // disable loop mode + bool retCode = true; + RMT_MUTEX_LOCK(bus); + + if (ESP_OK != rmt_transmit(bus->rmt_channel_h, bus->rmt_copy_encoder_h, + (const void *) data, size * sizeof(rmt_data_t), &transmit_cfg)) { + retCode = false; + log_w("GPIO %d - RMT Blocking Transmission failed.", pin); + } + if (ESP_OK != rmt_tx_wait_all_done(bus->rmt_channel_h, -1)) { + retCode = false; + log_w("GPIO %d - RMT Blocking TX Setup failed.", pin); + } + RMT_MUTEX_UNLOCK(bus); + return retCode; } -bool rmtRead(rmt_obj_t* rmt, rmt_rx_data_cb_t cb, void * arg) +// Sets a user buffer and length for reading RMT signal. +// rmtReceiveCompleted() shall be used to check if there is data read fromthe RMT channel +bool rmtReadData(int pin, uint32_t* data, size_t size) { - if (!_rmtCheckTXnotRX(rmt, RMT_RX_MODE)) { - return false; - } - int channel = rmt->channel; - - rmt->arg = arg; - rmt->cb = cb; + rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__); + if (bus == NULL) { + return false; + } - RMT_MUTEX_LOCK(channel); - // cb as NULL is a way to cancel the callback process - if (cb == NULL) { - rmt_rx_stop(channel); - return true; - } - // Start a read process but now with a call back function - rmt_set_memory_owner(channel, RMT_MEM_OWNER_RX); - rmt_rx_start(channel, true); - rmt->rx_completed = false; - _rmtCreateRxTask(rmt); - RMT_MUTEX_UNLOCK(channel); - return true; + rmtReadAsync(pin, (rmt_data_t*) data, size, NULL, false, 0); + return true; } -bool rmtEnd(rmt_obj_t* rmt) +bool rmtBeginReceive(int pin) { - if (!rmt) { - return false; - } - int channel = rmt->channel; - - RMT_MUTEX_LOCK(channel); - if (rmt->tx_not_rx) { - rmt_tx_stop(channel); - } else { - rmt_rx_stop(channel); - rmt->rx_completed = true; - } - RMT_MUTEX_UNLOCK(channel); - return true; + rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__); + if (bus == NULL) { + return false; + } + if (!_rmtCheckDirection(pin, RMT_RX_MODE, __FUNCTION__)) { + return false; + } + if (!_rmtStartReading(bus, pin, __FUNCTION__)) { + return false; + } + + RMT_MUTEX_LOCK(bus); + bus->rmt_ch_rx_obj_h->rmt_async_rx_enabled = true; + bus->rmt_ch_rx_obj_h->rmt_rx_completed = false; + RMT_MUTEX_UNLOCK(bus); + return true; } -bool rmtReadAsync(rmt_obj_t* rmt, rmt_data_t* data, size_t size, void* eventFlag, bool waitForData, uint32_t timeout) +bool rmtReceiveCompleted(int pin) { - if (!_rmtCheckTXnotRX(rmt, RMT_RX_MODE)) { - return false; - } - int channel = rmt->channel; + rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__); + if (bus == NULL) { + return false; + } + if (!_rmtCheckDirection(pin, RMT_RX_MODE, __FUNCTION__)) { + return false; + } + if (!_rmtStartReading(bus, pin, __FUNCTION__)) { + return false; + } + + RMT_MUTEX_LOCK(bus); + bool retCode = bus->rmt_ch_rx_obj_h->rmt_rx_completed; + RMT_MUTEX_UNLOCK(bus); + + return retCode; +} - // No limit on size with IDF ;-) - //if (g_rmt_objects[channel].buffers < size/SOC_RMT_MEM_WORDS_PER_CHANNEL) { - // return false; - //} +bool rmtRead(int pin, rmt_rx_data_cb_t cb, void * arg) +{ + rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__); + if (bus == NULL) { + return false; + } + if (!_rmtCheckDirection(pin, RMT_RX_MODE, __FUNCTION__)) { + return false; + } + if (!_rmtStartReading(bus, pin, __FUNCTION__)) { + return false; + } + + // Start a read process but now with a call back function + bus->rmt_ch_rx_obj_h->rmt_rx_cb_arg = arg; + bus->rmt_ch_rx_obj_h->rmt_rx_cb = cb; + + // cb as NULL is a way to cancel the callback process + if (cb == NULL) { + rmtEnd(pin); // disable reading + } else { + rmtBeginReceive(pin); // enable reading + } + + RMT_MUTEX_LOCK(bus); + _rmtCreateRxTask(bus); + RMT_MUTEX_UNLOCK(bus); + return true; +} - RMT_MUTEX_LOCK(channel); - if (eventFlag) { - xEventGroupClearBits(eventFlag, RMT_FLAGS_ALL); - } - // if NULL, no problems - rmtReadAsync works as a plain rmtReadData() - rmt->events = eventFlag; - - // if NULL, no problems - task will take care of it - rmt->data_ptr = (uint32_t*)data; - rmt->data_size = size; - - // Start a read process - rmt_set_memory_owner(channel, RMT_MEM_OWNER_RX); - rmt_rx_start(channel, true); - rmt->rx_completed = false; - _rmtCreateRxTask(rmt); - RMT_MUTEX_UNLOCK(channel); - - // wait for data if requested so - if (waitForData && eventFlag) { - xEventGroupWaitBits(eventFlag, RMT_FLAGS_ALL, - pdTRUE /* clear on exit */, pdFALSE /* wait for all bits */, timeout); - } - return true; +bool rmtEnd(int pin) +{ + rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__); + if (bus == NULL) { + return false; + } + if (!_rmtCheckDirection(pin, RMT_RX_MODE, __FUNCTION__)) { + return false; + } + if (!_rmtStartReading(bus, pin, __FUNCTION__)) { + return false; + } + + RMT_MUTEX_LOCK(bus); + bus->rmt_ch_rx_obj_h->rmt_async_rx_enabled = false; + RMT_MUTEX_UNLOCK(bus); + return true; } -float rmtSetTick(rmt_obj_t* rmt, float tick) +bool rmtReadAsync(int pin, rmt_data_t* data, size_t size, void* eventFlag, bool waitForData, uint32_t timeout) { - if (!rmt) { - return false; - } - size_t channel = rmt->channel; - - RMT_MUTEX_LOCK(channel); - // RMT_BASECLK_REF (1MHz) is not supported in IDF upon Programmming Guide - // Only APB works - rmt_set_source_clk(channel, RMT_BASECLK_APB); - int apb_div = _LIMIT(tick/12.5f, 256); - float apb_tick = 12.5f * apb_div; - - rmt_set_clk_div(channel, apb_div & 0xFF); - RMT_MUTEX_UNLOCK(channel); - return apb_tick; + rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__); + if (bus == NULL) { + return false; + } + if (!_rmtCheckDirection(pin, RMT_RX_MODE, __FUNCTION__)) { + return false; + } + if (!_rmtStartReading(bus, pin, __FUNCTION__)) { + return false; + } + + rmtEnd(pin); // disable reading + if (eventFlag) { + xEventGroupClearBits(eventFlag, RMT_FLAG_RX_DONE); + } + // if NULL, no problems - rmtReadAsync works as a plain rmtReadData() + bus->rmt_ch_rx_obj_h->rmt_rx_events = eventFlag; + + // if NULL, no problems - task will take care of it + bus->rmt_ch_rx_obj_h->rmt_data_rx_ptr = data; + bus->rmt_ch_rx_obj_h->rmt_data_rx_size = size; + bus->rmt_ch_rx_obj_h->rmt_rx_completed = false; + + // Start a read task + RMT_MUTEX_LOCK(bus); + _rmtCreateRxTask(bus); + RMT_MUTEX_UNLOCK(bus); + + // wait for data if requested so + rmtBeginReceive(pin); // enable reading + if (waitForData && eventFlag) { + xEventGroupWaitBits(eventFlag, RMT_FLAG_RX_DONE, + pdTRUE /* clear on exit */, pdFALSE /* wait for all bits */, timeout); + } + return true; } -rmt_obj_t* rmtInit(int pin, bool tx_not_rx, rmt_reserve_memsize_t memsize) +bool rmtInit(int pin, rmt_ch_dir_t channel_direction, rmt_reserve_memsize_t mem_size, uint32_t frequency_Hz) { - uint32_t buffers = memsize; - rmt_obj_t* rmt = NULL; - uint32_t i = 0; - uint32_t j = 0; - - // create common block mutex for protecting allocs from multiple threads - if (!g_rmt_block_lock) { - g_rmt_block_lock = xSemaphoreCreateMutex(); - } - // lock - while (xSemaphoreTake(g_rmt_block_lock, portMAX_DELAY) != pdPASS) {} - - // Some SoC may have fixed channel numbers for TX and RX - example: ESP32C3 - uint8_t ch_start, ch_end; - if (tx_not_rx) { - ch_start = RMT_TX_CH_START; - ch_end = RMT_TX_CH_END; - } else { - ch_start = RMT_RX_CH_START; - ch_end = RMT_RX_CH_END; - } - for (i=ch_start; i<=ch_end; i++) { - for (j=0; j MAX_CHANNELS || j != buffers) { - xSemaphoreGive(g_rmt_block_lock); - log_e("rmInit Failed - not enough channels"); - return NULL; + // check is pin is valid and in the right direction + if ((channel_direction == RMT_TX_MODE && !GPIO_IS_VALID_OUTPUT_GPIO(pin)) || (!GPIO_IS_VALID_GPIO(pin))) { + log_e("GPIO %d is not valid or can't be used for output in TX mode.", pin); + return false; + } + + // set Peripheral Manager deInit Callback + perimanSetBusDeinit(ESP32_BUS_TYPE_RMT_TX, _rmtDetachBus); + perimanSetBusDeinit(ESP32_BUS_TYPE_RMT_RX, _rmtDetachBus); + + // validate the RMT ticks by the requested frequency +#if SOC_RMT_SUPPORT_APB + // Based on 80Mhz using a divider of 8 bits (calculated as 1..256) :: ESP32 and ESP32S2 + if (frequency_Hz > 80000000 || frequency_Hz < 312500) { + log_e("GPIO %d - Bad RMT frequency resolution. Must be between 312.5KHz to 80MHz.", pin); + return false; + } +#elif SOC_RMT_SUPPORT_XTAL + // Based on 40Mhz using a divider of 8 bits (calculated as 1..256) :: ESP32C3 and ESP32S3 + if (frequency_Hz > 40000000 || frequency_Hz < 156250) { + log_e("GPIO %d - Bad RMT frequency resolution. Must be between 156.25KHz to 40MHz.", pin); + return false; + } +#endif + + // create common block mutex for protecting allocs from multiple threads + if (!g_rmt_block_lock) { + g_rmt_block_lock = xSemaphoreCreateMutex(); + if (g_rmt_block_lock == NULL) { + log_e("GPIO %d - Failed creating RMT Mutex.", pin); + return false; } - - // A suitable channel has been found, it has to block its resources in our internal data strucuture - size_t channel = i; - rmt = _rmtAllocate(pin, i, buffers); + } + // lock it + while (xSemaphoreTake(g_rmt_block_lock, portMAX_DELAY) != pdPASS) {} + // Try to dettach any previous bus or just keep it as not attached + if (perimanGetPinBusType(pin) != ESP32_BUS_TYPE_INIT && !perimanSetPinBus(pin, ESP32_BUS_TYPE_INIT, NULL)) { + log_w("GPIO %d - Can't detach previous peripheral.", pin); xSemaphoreGive(g_rmt_block_lock); + return false; + } - rmt->buffers = buffers; - rmt->channel = channel; - rmt->arg = NULL; - rmt->cb = NULL; - rmt->data_ptr = NULL; - rmt->data_size = 0; - rmt->rx_completed = false; - rmt->events = NULL; - rmt->tx_not_rx = tx_not_rx; + // allocate the rmt bus object + rmt_bus_handle_t bus = (rmt_bus_handle_t)heap_caps_calloc(1, sizeof(struct rmt_obj_s), MALLOC_CAP_DEFAULT); + if (bus == NULL) { + log_e("GPIO %d - Bus Memory allocation fault.", pin); + xSemaphoreGive(g_rmt_block_lock); + return false; + } + + // common RX/TX channel configuration - common fields + bus->num_buffers = mem_size; + bus->rmt_ch_rx_obj_h = NULL; + // pulses with width smaller than min_ns will be ignored (as a glitch) + bus->signal_range_min_ns = 1000000000 / (frequency_Hz * 2); // 1/2 pulse width + // RMT stops reading if the input stays idle for longer than max_ns + bus->signal_range_max_ns = (1000000000 / frequency_Hz) * 10; // 10 pulses width + + // channel particular configuration + if (channel_direction == RMT_TX_MODE) { + // TX Channel + rmt_tx_channel_config_t tx_cfg; + tx_cfg.gpio_num = pin; +#if SOC_RMT_SUPPORT_APB + tx_cfg.clk_src = RMT_CLK_SRC_APB; +#elif SOC_RMT_SUPPORT_XTAL + tx_cfg.clk_src = RMT_CLK_SRC_XTAL; +#endif + tx_cfg.resolution_hz = frequency_Hz; + tx_cfg.mem_block_symbols = SOC_RMT_MEM_WORDS_PER_CHANNEL * mem_size; + tx_cfg.trans_queue_depth = 10; // maximum allowed + tx_cfg.flags.invert_out = 0; + tx_cfg.flags.with_dma = 0; + tx_cfg.flags.io_loop_back = 0; + tx_cfg.flags.io_od_mode = 0; + + if (rmt_new_tx_channel(&tx_cfg, &bus->rmt_channel_h) != ESP_OK) { + log_e("GPIO %d - RMT TX Initialization error.", pin); + // release the RMT object + _rmtDetachBus((void *)bus); + xSemaphoreGive(g_rmt_block_lock); + return false; + } + } else { + // RX Channel + rmt_rx_channel_config_t rx_cfg; + rx_cfg.gpio_num = pin; +#if SOC_RMT_SUPPORT_APB + rx_cfg.clk_src = RMT_CLK_SRC_APB; +#elif SOC_RMT_SUPPORT_XTAL + rx_cfg.clk_src = RMT_CLK_SRC_XTAL; +#endif + rx_cfg.resolution_hz = frequency_Hz; + rx_cfg.mem_block_symbols = SOC_RMT_MEM_WORDS_PER_CHANNEL * mem_size; + rx_cfg.flags.invert_in = 0; + rx_cfg.flags.with_dma = 0; + rx_cfg.flags.io_loop_back = 0; + // try to allocate the RMT Channel + if (ESP_OK != rmt_new_rx_channel(&rx_cfg, &bus->rmt_channel_h)) { + log_e("GPIO %d RMT - RX Initialization error.", pin); + // release the RMT object + _rmtDetachBus((void *)bus); + xSemaphoreGive(g_rmt_block_lock); + return false; + } + } + + // allocate memory for the RMT Copy encoder + rmt_copy_encoder_config_t copy_encoder_config = {}; + if (rmt_new_copy_encoder(©_encoder_config, &bus->rmt_copy_encoder_h) != ESP_OK) { + log_e("GPIO %d - RMT Encoder Memory Allocation error.", pin); + // release the RMT object + _rmtDetachBus((void *)bus); + xSemaphoreGive(g_rmt_block_lock); + return false; + } + // create each channel Mutex for multi thread operations #if !CONFIG_DISABLE_HAL_LOCKS - if(g_rmt_objlocks[channel] == NULL) { - g_rmt_objlocks[channel] = xSemaphoreCreateMutex(); - if(g_rmt_objlocks[channel] == NULL) { - return NULL; - } - } + bus->g_rmt_objlocks = xSemaphoreCreateMutex(); + if (bus->g_rmt_objlocks == NULL) { + log_e("GPIO %d - Failed creating RMT Channel Mutex.", pin); + // release the RMT object + _rmtDetachBus((void *)bus); + xSemaphoreGive(g_rmt_block_lock); + return false; + } #endif - RMT_MUTEX_LOCK(channel); - esp_err_t esp_err_code = ESP_OK; - - if (tx_not_rx) { - rmt_config_t config = RMT_DEFAULT_ARD_CONFIG_TX(pin, channel, buffers); - esp_err_code = rmt_config(&config); - if (esp_err_code == ESP_OK) - esp_err_code = rmt_driver_install(channel, 0, 0); - log_d(" -- %s RMT - CH %d - %d RAM Blocks - pin %d", tx_not_rx?"TX":"RX", channel, buffers, pin); - } else { - rmt_config_t config = RMT_DEFAULT_ARD_CONFIG_RX(pin, channel, buffers); - esp_err_code = rmt_config(&config); - if (esp_err_code == ESP_OK) - esp_err_code = rmt_driver_install(channel, 1024, 0); - if (esp_err_code == ESP_OK) - esp_err_code = rmt_set_memory_owner(channel, RMT_MEM_OWNER_RX); - log_d(" -- %s RMT - CH %d - %d RAM Blocks - pin %d", tx_not_rx?"TX":"RX", channel, buffers, pin); - } - - RMT_MUTEX_UNLOCK(channel); - - if (esp_err_code == ESP_OK) { - return rmt; - } else { - log_e("RMT failed to initilize."); - return NULL; - } + rmt_enable(bus->rmt_channel_h); // starts the channel + + // Finally, allocate Peripheral Manager RMT bus and associate it to its GPIO + peripheral_bus_type_t pinBusType = + channel_direction == RMT_TX_MODE ? ESP32_BUS_TYPE_RMT_TX : ESP32_BUS_TYPE_RMT_RX; + if (!perimanSetPinBus(pin, pinBusType, (void *) bus)) { + log_e("Can't allocate the GPIO %d in the Peripheral Manager.", pin); + // release the RMT object + _rmtDetachBus((void *)bus); + xSemaphoreGive(g_rmt_block_lock); + return false; + } + + // release the mutex + xSemaphoreGive(g_rmt_block_lock); + return true; } diff --git a/cores/esp32/esp32-hal-rmt.h b/cores/esp32/esp32-hal-rmt.h index 201fe594009..0710017beff 100644 --- a/cores/esp32/esp32-hal-rmt.h +++ b/cores/esp32/esp32-hal-rmt.h @@ -1,4 +1,4 @@ -// Copyright 2018 Espressif Systems (Shanghai) PTE LTD +// Copyright 2023 Espressif Systems (Shanghai) PTE LTD // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,155 +15,144 @@ #ifndef MAIN_ESP32_HAL_RMT_H_ #define MAIN_ESP32_HAL_RMT_H_ +// Rx Done Event +#define RMT_FLAG_RX_DONE (1) + + #ifdef __cplusplus extern "C" { #endif -// notification flags -#define RMT_FLAG_TX_DONE (1) -#define RMT_FLAG_RX_DONE (2) -#define RMT_FLAG_ERROR (4) -#define RMT_FLAGS_ALL (RMT_FLAG_TX_DONE | RMT_FLAG_RX_DONE | RMT_FLAG_ERROR) - -#define RMT_TX_MODE true -#define RMT_RX_MODE false - -struct rmt_obj_s; +typedef enum { + RMT_RX_MODE = 0, // false + RMT_TX_MODE = 1, // true +} rmt_ch_dir_t; typedef enum { - RMT_MEM_64 = 1, - RMT_MEM_128 = 2, - RMT_MEM_192 = 3, - RMT_MEM_256 = 4, - RMT_MEM_320 = 5, - RMT_MEM_384 = 6, - RMT_MEM_448 = 7, - RMT_MEM_512 = 8, + RMT_MEM_64 = 1, + RMT_MEM_128 = 2, + RMT_MEM_192 = 3, + RMT_MEM_256 = 4, + RMT_MEM_320 = 5, + RMT_MEM_384 = 6, + RMT_MEM_448 = 7, + RMT_MEM_512 = 8, } rmt_reserve_memsize_t; -typedef struct rmt_obj_s rmt_obj_t; typedef void (*rmt_rx_data_cb_t)(uint32_t *data, size_t len, void *arg); -typedef struct { - union { - struct { - uint32_t duration0 :15; - uint32_t level0 :1; - uint32_t duration1 :15; - uint32_t level1 :1; - }; - uint32_t val; - }; +typedef union { + struct { + uint32_t duration0 : 15; + uint32_t level0 : 1; + uint32_t duration1 : 15; + uint32_t level1 : 1; + }; + uint32_t val; } rmt_data_t; /** -* Prints object information -* + Initialize the object + + New Parameters in Arduino Core 3: RMT tick is set in the rmtInit() function by the + frequency of the RMT channel. Example: 100ns tick => 10MHz, thus frequency will be 10,000,000 Hz */ -void _rmtDumpStatus(rmt_obj_t* rmt); +bool rmtInit(int pin, rmt_ch_dir_t channel_direction, rmt_reserve_memsize_t memsize, uint32_t frequency_Hz); /** -* Initialize the object -* -*/ -rmt_obj_t* rmtInit(int pin, bool tx_not_rx, rmt_reserve_memsize_t memsize); + BREAKING CHANGE in Arduino Core 3: rmtSetTick() was removed, in favor od rmtInit() -/** -* Sets the clock/divider of timebase the nearest tick to the supplied value in nanoseconds -* return the real actual tick value in ns + Sets the clock/divider of timebase the nearest tick to the supplied value in nanoseconds + return the real actual tick value in ns */ -float rmtSetTick(rmt_obj_t* rmt, float tick); +//float rmtSetTick(int pin, float tick); /** -* Sending data in one-go mode or continual mode -* (more data being send while updating buffers in interrupts) -* Non-Blocking mode - returns right after executing + Sending data in one-go mode or continual mode + (more data being send while updating buffers in interrupts) + Non-Blocking mode - returns right after executing */ -bool rmtWrite(rmt_obj_t* rmt, rmt_data_t* data, size_t size); +bool rmtWrite(int pin, rmt_data_t* data, size_t size_byte); /** -* Sending data in one-go mode or continual mode -* (more data being send while updating buffers in interrupts) -* Blocking mode - only returns when data has been sent + Sending data in one-go mode or continual mode + (more data being send while updating buffers in interrupts) + Blocking mode - only returns when data has been sent */ -bool rmtWriteBlocking(rmt_obj_t* rmt, rmt_data_t* data, size_t size); +bool rmtWriteBlocking(int pin, rmt_data_t* data, size_t size_byte); /** -* Loop data up to the reserved memsize continuously -* + Loop data up to the reserved memsize continuously + */ -bool rmtLoop(rmt_obj_t* rmt, rmt_data_t* data, size_t size); +bool rmtLoop(int pin, rmt_data_t* data, size_t size_byte); /** -* Initiates async receive, event flag indicates data received -* + Initiates async receive, event flag indicates data received + */ -bool rmtReadAsync(rmt_obj_t* rmt, rmt_data_t* data, size_t size, void* eventFlag, bool waitForData, uint32_t timeout); +bool rmtReadAsync(int pin, rmt_data_t* data, size_t size, void* eventFlag, bool waitForData, uint32_t timeout); /** -* Initiates async receive with automatic buffering -* and callback with data from ISR -* + Initiates async receive with automatic buffering + and callback with data from ISR + */ -bool rmtRead(rmt_obj_t* rmt, rmt_rx_data_cb_t cb, void * arg); +bool rmtRead(int pin, rmt_rx_data_cb_t cb, void * arg); /*** - * Ends async receive started with rmtRead(); but does not - * rmtDeInit(). - */ -bool rmtEnd(rmt_obj_t* rmt); + Ends async receive started with rmtRead(); but does not + rmtDeInit(). +*/ +bool rmtEnd(int pin); /* Additional interface */ /** -* Start reception -* + Start reception + */ -bool rmtBeginReceive(rmt_obj_t* rmt); +bool rmtBeginReceive(int pin); /** -* Checks if reception completes -* + Checks if reception completes + */ -bool rmtReceiveCompleted(rmt_obj_t* rmt); +bool rmtReceiveCompleted(int pin); /** -* Reads the data for particular channel -* + Reads the data for particular channel + */ -bool rmtReadData(rmt_obj_t* rmt, uint32_t* data, size_t size); +bool rmtReadData(int pin, uint32_t* data, size_t size); /** - * Setting threshold for Rx completed - */ -bool rmtSetRxThreshold(rmt_obj_t* rmt, uint32_t value); + Setting threshold for Rx completed +*/ +bool rmtSetRxThreshold(int pin, uint16_t value); /** - * Setting carrier - */ -bool rmtSetCarrier(rmt_obj_t* rmt, bool carrier_en, bool carrier_level, uint32_t low, uint32_t high); - + Parameters changed in Arduino Core 3: low and high (ticks) are now expressed in Carrier Freq in Hz and + duty cycle in percentage float 0.0 to 1.0 - example: 38.5KHz 33% High => 38500, 0.33 + Setting carrier + +*/ +//bool rmtSetCarrier(int pin, bool carrier_en, bool carrier_level, uint32_t low, uint32_t high); +bool rmtSetCarrier(int pin, bool carrier_en, bool carrier_level, uint32_t frequency_Hz, float duty_percent); /** - * Setting input filter - */ -bool rmtSetFilter(rmt_obj_t* rmt, bool filter_en, uint32_t filter_level); + Setting input filter +*/ +bool rmtSetFilter(int pin, bool filter_en, uint8_t filter_level); /** - * Deinitialize the driver - */ -bool rmtDeinit(rmt_obj_t *rmt); - -// TODO: -// * uninstall interrupt when all channels are deinit -// * send only-conti mode with circular-buffer -// * put sanity checks to some macro or inlines -// * doxy comments -// * error reporting + Deinitialize the driver +*/ +bool rmtDeinit(int pin); #ifdef __cplusplus } #endif -#endif /* MAIN_ESP32_HAL_RMT_H_ */ +#endif /* MAIN_ESP32_HAL_RMT_H_ */ \ No newline at end of file diff --git a/libraries/ESP32/examples/RMT/RMTCallback/RMTCallback.ino b/libraries/ESP32/examples/RMT/RMTCallback/RMTCallback.ino index 61e6b55abcc..6537ecb763c 100644 --- a/libraries/ESP32/examples/RMT/RMTCallback/RMTCallback.ino +++ b/libraries/ESP32/examples/RMT/RMTCallback/RMTCallback.ino @@ -5,22 +5,21 @@ extern "C" void receive_trampoline(uint32_t *data, size_t len, void * arg); class MyProcessor { private: - rmt_obj_t* rmt_recv = NULL; - float realNanoTick; + int8_t gpio = -1; uint32_t buff; // rolling buffer of most recent 32 bits. int at = 0; public: - MyProcessor(uint8_t pin, float nanoTicks) { - if ((rmt_recv = rmtInit(pin, RMT_RX_MODE, RMT_MEM_192)) == NULL) + MyProcessor(uint8_t pin, uint32_t rmtFreqHz) { + if (!rmtInit(pin, RMT_RX_MODE, RMT_MEM_64, rmtFreqHz)) { Serial.println("init receiver failed\n"); + return; } - - realNanoTick = rmtSetTick(rmt_recv, nanoTicks); + gpio = pin; }; void begin() { - rmtRead(rmt_recv, receive_trampoline, this); + rmtRead(gpio, receive_trampoline, this); }; void process(rmt_data_t *data, size_t len) { @@ -48,9 +47,9 @@ void receive_trampoline(uint32_t *data, size_t len, void * arg) } // Attach 3 processors to GPIO 4, 5 and 10 with different tick/speeds. -MyProcessor mp1 = MyProcessor(4, 1000); -MyProcessor mp2 = MyProcessor(5, 1000); -MyProcessor mp3 = MyProcessor(10, 500); +MyProcessor mp1 = MyProcessor(4, 1000000); +MyProcessor mp2 = MyProcessor(5, 1000000); +MyProcessor mp3 = MyProcessor(10, 2000000); void setup() { diff --git a/libraries/ESP32/examples/RMT/RMTLoopback/RMTLoopback.ino b/libraries/ESP32/examples/RMT/RMTLoopback/RMTLoopback.ino index 851ced04131..188acfcb8a7 100644 --- a/libraries/ESP32/examples/RMT/RMTLoopback/RMTLoopback.ino +++ b/libraries/ESP32/examples/RMT/RMTLoopback/RMTLoopback.ino @@ -19,29 +19,32 @@ rmt_data_t my_data[256]; rmt_data_t data[256]; -rmt_obj_t* rmt_send = NULL; -rmt_obj_t* rmt_recv = NULL; - static EventGroupHandle_t events; +#define RMT_FREQ 10000000 +#define RMT_NUM_EXCHANGED_DATA 30 + void setup() { Serial.begin(115200); events = xEventGroupCreate(); - if ((rmt_send = rmtInit(RMT_TX_PIN, RMT_TX_MODE, RMT_MEM_64)) == NULL) + if (!rmtInit(RMT_TX_PIN, RMT_TX_MODE, RMT_MEM_64, RMT_FREQ)) { Serial.println("init sender failed\n"); } - if ((rmt_recv = rmtInit(RMT_RX_PIN, RMT_RX_MODE, RMT_MEM_RX)) == NULL) + if (!rmtInit(RMT_RX_PIN, RMT_RX_MODE, RMT_MEM_RX, RMT_FREQ)) { Serial.println("init receiver failed\n"); } - float realTick = rmtSetTick(rmt_send, 100); - printf("real tick set to: %fns\n", realTick); - // both will keep same tick - realTick = rmtSetTick(rmt_recv, 100); + // End of transmission shall be detected when line is idle for 2us + rmtSetRxThreshold(RMT_RX_PIN, 2000); + // Disable Glitch filter + rmtSetFilter(RMT_RX_PIN, false, 0); + + Serial.println("real tick set to: 100ns"); + Serial.printf("\nPlease connect GPIO %d to GPIO %d, now.\n", RMT_TX_PIN, RMT_RX_PIN); } void loop() @@ -50,14 +53,15 @@ void loop() int i; for (i=0; i<255; i++) { data[i].val = 0x80010001 + ((i%13)<<16) + 13-(i%13); + my_data[i].val = 0; } data[255].val = 0; // Start receiving - rmtReadAsync(rmt_recv, my_data, 100, events, false, 0); + rmtReadAsync(RMT_RX_PIN, my_data, RMT_NUM_EXCHANGED_DATA, events, false, 0); // Send in continous mode - rmtWrite(rmt_send, data, 100); + rmtWrite(RMT_TX_PIN, data, RMT_NUM_EXCHANGED_DATA); // Wait for data xEventGroupWaitBits(events, RMT_FLAG_RX_DONE, 1, 1, portMAX_DELAY); @@ -66,7 +70,7 @@ void loop() for (i=0; i<60; i++) { Serial.printf("%08lx=%08lx ", my_data[i].val, data[i].val ); - if (!((i+1)%4)) Serial.println("\n"); + if (!((i+1)%4)) Serial.println(""); } Serial.println("\n"); diff --git a/libraries/ESP32/examples/RMT/RMTReadXJT/RMTReadXJT.ino b/libraries/ESP32/examples/RMT/RMTReadXJT/RMTReadXJT.ino index 1dcd8542da1..df42873cc25 100644 --- a/libraries/ESP32/examples/RMT/RMTReadXJT/RMTReadXJT.ino +++ b/libraries/ESP32/examples/RMT/RMTReadXJT/RMTReadXJT.ino @@ -62,8 +62,6 @@ typedef union { #define XJT_VALID(i) (i->level0 && !i->level1 && i->duration0 >= 8 && i->duration0 <= 11) -rmt_obj_t* rmt_recv = NULL; - static uint32_t *s_channels; static uint32_t channels[16]; static uint8_t xjt_flags = 0x0; @@ -177,22 +175,21 @@ extern "C" void receive_data(uint32_t *data, size_t len, void * arg) parseRmt((rmt_data_t*) data, len, channels); } +#define RMT_GPIO 21 + void setup() { Serial.begin(115200); - // Initialize the channel to capture up to 192 items - if ((rmt_recv = rmtInit(21, RMT_RX_MODE, RMT_MEM_192)) == NULL) + // Initialize the channel to capture up to 192 items - 1us tick + if (!rmtInit(RMT_GPIO, RMT_RX_MODE, RMT_MEM_192, 1000000)) { Serial.println("init receiver failed\n"); } - - // Setup 1us tick - float realTick = rmtSetTick(rmt_recv, 1000); - Serial.printf("real tick set to: %fns\n", realTick); + Serial.println("real tick set to: 1us"); // Ask to start reading - rmtRead(rmt_recv, receive_data, NULL); + rmtRead(RMT_GPIO, receive_data, NULL); } void loop() diff --git a/libraries/ESP32/examples/RMT/RMTWriteNeoPixel/RMTWriteNeoPixel.ino b/libraries/ESP32/examples/RMT/RMTWriteNeoPixel/RMTWriteNeoPixel.ino index 11f659f8895..113a3a7e8a0 100644 --- a/libraries/ESP32/examples/RMT/RMTWriteNeoPixel/RMTWriteNeoPixel.ino +++ b/libraries/ESP32/examples/RMT/RMTWriteNeoPixel/RMTWriteNeoPixel.ino @@ -46,19 +46,16 @@ rmt_data_t led_data[NR_OF_ALL_BITS]; -rmt_obj_t* rmt_send = NULL; - void setup() { Serial.begin(115200); - if ((rmt_send = rmtInit(BUILTIN_RGBLED_PIN, RMT_TX_MODE, RMT_MEM_64)) == NULL) + if (!rmtInit(BUILTIN_RGBLED_PIN, RMT_TX_MODE, RMT_MEM_64, 10000000)) { Serial.println("init sender failed\n"); } - float realTick = rmtSetTick(rmt_send, 100); - Serial.printf("real tick set to: %fns\n", realTick); + Serial.println("real tick set to: 100ns"); } @@ -94,7 +91,7 @@ void loop() } // Send the data - rmtWrite(rmt_send, led_data, NR_OF_ALL_BITS); + rmtWrite(BUILTIN_RGBLED_PIN, led_data, NR_OF_ALL_BITS); delay(100); } From 32792f8ff71d17e68365b75e4df00e9a6ce79644 Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Mon, 27 Mar 2023 09:26:42 -0300 Subject: [PATCH 02/14] Fixes initial value setting --- cores/esp32/esp32-hal-rmt.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cores/esp32/esp32-hal-rmt.c b/cores/esp32/esp32-hal-rmt.c index 08091fafccb..ec774cb2e8f 100644 --- a/cores/esp32/esp32-hal-rmt.c +++ b/cores/esp32/esp32-hal-rmt.c @@ -357,7 +357,8 @@ bool rmtLoop(int pin, rmt_data_t* data, size_t size) return false; } - rmt_transmit_config_t transmit_cfg = { -1, 0}; // enable infinite loop mode + rmt_transmit_config_t transmit_cfg = {0}; + transmit_cfg.loop_count = 1; // enable infinite loop mode bool retCode = true; RMT_MUTEX_LOCK(bus); @@ -380,7 +381,7 @@ bool rmtWrite(int pin, rmt_data_t* data, size_t size) return false; } - rmt_transmit_config_t transmit_cfg = {0, 0}; // disable loop mode + rmt_transmit_config_t transmit_cfg = {0}; // disable loop mode bool retCode = true; RMT_MUTEX_LOCK(bus); @@ -403,7 +404,7 @@ bool rmtWriteBlocking(int pin, rmt_data_t* data, size_t size) return false; } - rmt_transmit_config_t transmit_cfg = {0, 0}; // disable loop mode + rmt_transmit_config_t transmit_cfg = {0}; // disable loop mode bool retCode = true; RMT_MUTEX_LOCK(bus); From 8e835fddad48ff89f1fc574d92c629b392666367 Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Sun, 16 Apr 2023 22:57:35 -0300 Subject: [PATCH 03/14] removed rmtRead() with user callback --- cores/esp32/esp32-hal-rmt.c | 36 ------------------------------------ cores/esp32/esp32-hal-rmt.h | 11 ----------- 2 files changed, 47 deletions(-) diff --git a/cores/esp32/esp32-hal-rmt.c b/cores/esp32/esp32-hal-rmt.c index ec774cb2e8f..4de7a4699a0 100644 --- a/cores/esp32/esp32-hal-rmt.c +++ b/cores/esp32/esp32-hal-rmt.c @@ -40,8 +40,6 @@ // structure used as interface for RMT Reading Operations struct rmt_rx_obj_s { EventGroupHandle_t rmt_rx_events; // reading event user handle - rmt_rx_data_cb_t rmt_rx_cb; // Rx user function callback - void * rmt_rx_cb_arg; // Rx user function argument TaskHandle_t rmt_rxTaskHandle; // Rx task created to wait for received RMT data QueueHandle_t rmt_rx_queue; // Rx callback data queue bool rmt_rx_completed; // rmtReceiveCompleted() rmtReadAsync() async reading @@ -144,10 +142,6 @@ static void _rmtRxTask(void *args) { rmt_rx_done_event_data_t rx_data; if (xQueueReceive(bus->rmt_ch_rx_obj_h->rmt_rx_queue, &rx_data, portMAX_DELAY) == pdPASS) { log_d("RMT has read %d bytes.", rx_data.num_symbols * sizeof(rmt_data_t)); - // process the user callback - if (bus->rmt_ch_rx_obj_h->rmt_rx_cb) { - (bus->rmt_ch_rx_obj_h->rmt_rx_cb)((uint32_t *)rx_data.received_symbols, rx_data.num_symbols, bus->rmt_ch_rx_obj_h->rmt_rx_cb_arg); - } // Async Read -- copy data to caller uint32_t data_size = bus->rmt_ch_rx_obj_h->rmt_data_rx_size; rmt_data_t *p = (rmt_data_t *)bus->rmt_ch_rx_obj_h->rmt_data_rx_ptr; @@ -474,36 +468,6 @@ bool rmtReceiveCompleted(int pin) return retCode; } -bool rmtRead(int pin, rmt_rx_data_cb_t cb, void * arg) -{ - rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__); - if (bus == NULL) { - return false; - } - if (!_rmtCheckDirection(pin, RMT_RX_MODE, __FUNCTION__)) { - return false; - } - if (!_rmtStartReading(bus, pin, __FUNCTION__)) { - return false; - } - - // Start a read process but now with a call back function - bus->rmt_ch_rx_obj_h->rmt_rx_cb_arg = arg; - bus->rmt_ch_rx_obj_h->rmt_rx_cb = cb; - - // cb as NULL is a way to cancel the callback process - if (cb == NULL) { - rmtEnd(pin); // disable reading - } else { - rmtBeginReceive(pin); // enable reading - } - - RMT_MUTEX_LOCK(bus); - _rmtCreateRxTask(bus); - RMT_MUTEX_UNLOCK(bus); - return true; -} - bool rmtEnd(int pin) { rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__); diff --git a/cores/esp32/esp32-hal-rmt.h b/cores/esp32/esp32-hal-rmt.h index 0710017beff..61346a224a9 100644 --- a/cores/esp32/esp32-hal-rmt.h +++ b/cores/esp32/esp32-hal-rmt.h @@ -18,7 +18,6 @@ // Rx Done Event #define RMT_FLAG_RX_DONE (1) - #ifdef __cplusplus extern "C" { #endif @@ -39,9 +38,6 @@ typedef enum { RMT_MEM_512 = 8, } rmt_reserve_memsize_t; - -typedef void (*rmt_rx_data_cb_t)(uint32_t *data, size_t len, void *arg); - typedef union { struct { uint32_t duration0 : 15; @@ -95,13 +91,6 @@ bool rmtLoop(int pin, rmt_data_t* data, size_t size_byte); */ bool rmtReadAsync(int pin, rmt_data_t* data, size_t size, void* eventFlag, bool waitForData, uint32_t timeout); -/** - Initiates async receive with automatic buffering - and callback with data from ISR - -*/ -bool rmtRead(int pin, rmt_rx_data_cb_t cb, void * arg); - /*** Ends async receive started with rmtRead(); but does not rmtDeInit(). From bbde8e251bf96368a2c4bd337cb4a36fa45ffed1 Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Sun, 16 Apr 2023 23:11:52 -0300 Subject: [PATCH 04/14] simplify/remove Read data structure --- cores/esp32/esp32-hal-rmt.c | 121 ++++++++++++------------------------ 1 file changed, 41 insertions(+), 80 deletions(-) diff --git a/cores/esp32/esp32-hal-rmt.c b/cores/esp32/esp32-hal-rmt.c index 4de7a4699a0..346a1df22d7 100644 --- a/cores/esp32/esp32-hal-rmt.c +++ b/cores/esp32/esp32-hal-rmt.c @@ -37,32 +37,24 @@ /** Typedefs for internal stuctures, enums */ -// structure used as interface for RMT Reading Operations -struct rmt_rx_obj_s { - EventGroupHandle_t rmt_rx_events; // reading event user handle - TaskHandle_t rmt_rxTaskHandle; // Rx task created to wait for received RMT data - QueueHandle_t rmt_rx_queue; // Rx callback data queue - bool rmt_rx_completed; // rmtReceiveCompleted() rmtReadAsync() async reading - bool rmt_async_rx_enabled; // rmtEnd() rmtBeginReceive() rmtRead() rmtReadAsync() control - rmt_data_t* rmt_data_rx_ptr; // Rx Task - data user pointer - size_t rmt_data_rx_size; // Rx Task - data length -}; -typedef struct rmt_rx_obj_s *rmt_rx_obj_handle_t; - - struct rmt_obj_s { // general rmt information rmt_channel_handle_t rmt_channel_h; // NULL value means channel not alocated rmt_encoder_handle_t rmt_copy_encoder_h; // RMT simple copy encoder handle - // rmt RX (reading) information - rmt_rx_obj_handle_t rmt_ch_rx_obj_h; // RMT Reading Processing necessary data - uint8_t num_buffers; // Number of allocated buffers needed for reading uint32_t signal_range_min_ns; // RX Filter data - Low Pass pulse width uint32_t signal_range_max_ns; // RX idle time that defines end of reading + EventGroupHandle_t rmt_rx_events; // reading event user handle + TaskHandle_t rmt_rxTaskHandle; // Rx task created to wait for received RMT data + QueueHandle_t rmt_rx_queue; // Rx callback data queue + bool rmt_rx_completed; // rmtReceiveCompleted() rmtReadAsync() async reading + bool rmt_async_rx_enabled; // rmtEnd() rmtBeginReceive() rmtRead() rmtReadAsync() control + rmt_data_t* rmt_data_rx_ptr; // Rx Task - data user pointer + size_t rmt_data_rx_size; // Rx Task - data length + xSemaphoreHandle g_rmt_objlocks; // Channel Semaphore Lock }; typedef struct rmt_obj_s *rmt_bus_handle_t; @@ -76,26 +68,6 @@ static xSemaphoreHandle g_rmt_block_lock = NULL; Internal method (private) declarations */ -// allocate the bus reading strucuture whenever necessary -static bool _rmtStartReading(rmt_bus_handle_t bus, uint8_t gpio_num, const char* labelFunc) -{ - // sanity check - it should never happen - assert(bus && "_rmtStartReading bus NULL pointer."); - assert(labelFunc && "_rmtStartReading Error Message NULL pointer."); - - if (bus->rmt_ch_rx_obj_h == NULL) { - // allocate the rmt bus object - bus->rmt_ch_rx_obj_h = (rmt_rx_obj_handle_t)heap_caps_calloc(1, sizeof(struct rmt_rx_obj_s), MALLOC_CAP_DEFAULT); - if (bus == NULL) { - log_e("==>%s():GPIO %d - Bus RX Struct Memory allocation fault.", labelFunc, gpio_num); - return false; - } - // when the RMT channel is created, it is enabled, thus we just set the flag here - bus->rmt_ch_rx_obj_h->rmt_async_rx_enabled = true; - } - return true; -} - static bool _rmt_rx_done_callback(rmt_channel_handle_t channel, const rmt_rx_done_event_data_t *data, void *args) { BaseType_t high_task_wakeup = pdFALSE; @@ -111,16 +83,16 @@ static void _rmtRxTask(void *args) { // sanity check - it should never happen assert(bus && "_rmtRxTask bus NULL pointer."); - bus->rmt_ch_rx_obj_h->rmt_rx_queue = xQueueCreate(1, sizeof(rmt_rx_done_event_data_t)); - if (bus->rmt_ch_rx_obj_h->rmt_rx_queue == NULL) { + bus->rmt_rx_queue = xQueueCreate(1, sizeof(rmt_rx_done_event_data_t)); + if (bus->rmt_rx_queue == NULL) { log_e("Error creating RX Queue."); goto err; } rmt_rx_event_callbacks_t cbs = { .on_recv_done = _rmt_rx_done_callback }; - if (ESP_OK != rmt_rx_register_event_callbacks(bus->rmt_channel_h, &cbs, bus->rmt_ch_rx_obj_h->rmt_rx_queue)) { + if (ESP_OK != rmt_rx_register_event_callbacks(bus->rmt_channel_h, &cbs, bus->rmt_rx_queue)) { log_e("Error registering RX Callback."); - vQueueDelete(bus->rmt_ch_rx_obj_h->rmt_rx_queue); + vQueueDelete(bus->rmt_rx_queue); goto err; } @@ -133,18 +105,18 @@ static void _rmtRxTask(void *args) { size_t num_rmt_symbols = bus->num_buffers * SOC_RMT_MEM_WORDS_PER_CHANNEL; // max is 8*64*4 = 2KB rmt_data_t rmt_symbols[num_rmt_symbols]; // number of symbols requested by user // must make sure that the RX Channel is enabled! - while (!bus->rmt_ch_rx_obj_h->rmt_async_rx_enabled) { + while (!bus->rmt_async_rx_enabled) { delay(1); // let other lower priority tasks to run, such as Arduino Task... } rmt_receive(bus->rmt_channel_h, rmt_symbols, num_rmt_symbols * sizeof(rmt_data_t), &receive_config); // wait for ever for RX done signal rmt_rx_done_event_data_t rx_data; - if (xQueueReceive(bus->rmt_ch_rx_obj_h->rmt_rx_queue, &rx_data, portMAX_DELAY) == pdPASS) { + if (xQueueReceive(bus->rmt_rx_queue, &rx_data, portMAX_DELAY) == pdPASS) { log_d("RMT has read %d bytes.", rx_data.num_symbols * sizeof(rmt_data_t)); // Async Read -- copy data to caller - uint32_t data_size = bus->rmt_ch_rx_obj_h->rmt_data_rx_size; - rmt_data_t *p = (rmt_data_t *)bus->rmt_ch_rx_obj_h->rmt_data_rx_ptr; + uint32_t data_size = bus->rmt_data_rx_size; + rmt_data_t *p = (rmt_data_t *)bus->rmt_data_rx_ptr; if (p != NULL && data_size > 0) { if (rx_data.num_symbols < data_size) data_size = rx_data.num_symbols; for (uint32_t i = 0; i < data_size; i++) { @@ -152,11 +124,11 @@ static void _rmtRxTask(void *args) { } } // set RX event group - if (bus->rmt_ch_rx_obj_h->rmt_rx_events) { - xEventGroupSetBits(bus->rmt_ch_rx_obj_h->rmt_rx_events, RMT_FLAG_RX_DONE); + if (bus->rmt_rx_events) { + xEventGroupSetBits(bus->rmt_rx_events, RMT_FLAG_RX_DONE); } // used in rmtReceiveCompleted() - bus->rmt_ch_rx_obj_h->rmt_rx_completed = true; + bus->rmt_rx_completed = true; } // xQueueReceive } // for(;;) @@ -169,13 +141,13 @@ static bool _rmtCreateRxTask(rmt_bus_handle_t bus) if (bus == NULL) { return false; } - if (bus->rmt_ch_rx_obj_h->rmt_rxTaskHandle != NULL) { // Task already created + if (bus->rmt_rxTaskHandle != NULL) { // Task already created return true; } - xTaskCreate(_rmtRxTask, "rmt_rx_task", 1024 * 6, bus, configMAX_PRIORITIES - 1, &bus->rmt_ch_rx_obj_h->rmt_rxTaskHandle); + xTaskCreate(_rmtRxTask, "rmt_rx_task", 1024 * 6, bus, configMAX_PRIORITIES - 1, &bus->rmt_rxTaskHandle); - if (bus->rmt_ch_rx_obj_h->rmt_rxTaskHandle == NULL) { + if (bus->rmt_rxTaskHandle == NULL) { log_e("RMT RX Task create failed"); return false; } @@ -222,18 +194,15 @@ static bool _rmtDetachBus(void *busptr) bool retCode = true; rmt_bus_handle_t bus = (rmt_bus_handle_t) busptr; - // delete RX data - if (bus->rmt_ch_rx_obj_h != NULL) { - // deleted RX channel tasks - if (bus->rmt_ch_rx_obj_h->rmt_rxTaskHandle != NULL) { - vTaskDelete(bus->rmt_ch_rx_obj_h->rmt_rxTaskHandle); - } - // delete RX Queue - if (bus->rmt_ch_rx_obj_h->rmt_rx_queue != NULL) { - vQueueDelete(bus->rmt_ch_rx_obj_h->rmt_rx_queue); - } - free(bus->rmt_ch_rx_obj_h); + // deleted RX channel tasks + if (bus->rmt_rxTaskHandle != NULL) { + vTaskDelete(bus->rmt_rxTaskHandle); } + // delete RX Queue + if (bus->rmt_rx_queue != NULL) { + vQueueDelete(bus->rmt_rx_queue); + } + // deallocate the channel encoder if (bus->rmt_copy_encoder_h != NULL) { if (ESP_OK != rmt_del_encoder(bus->rmt_copy_encoder_h)) { @@ -437,13 +406,10 @@ bool rmtBeginReceive(int pin) if (!_rmtCheckDirection(pin, RMT_RX_MODE, __FUNCTION__)) { return false; } - if (!_rmtStartReading(bus, pin, __FUNCTION__)) { - return false; - } RMT_MUTEX_LOCK(bus); - bus->rmt_ch_rx_obj_h->rmt_async_rx_enabled = true; - bus->rmt_ch_rx_obj_h->rmt_rx_completed = false; + bus->rmt_async_rx_enabled = true; + bus->rmt_rx_completed = false; RMT_MUTEX_UNLOCK(bus); return true; } @@ -457,12 +423,14 @@ bool rmtReceiveCompleted(int pin) if (!_rmtCheckDirection(pin, RMT_RX_MODE, __FUNCTION__)) { return false; } - if (!_rmtStartReading(bus, pin, __FUNCTION__)) { + + // is read enabled? + if(!bus->rmt_async_rx_enabled) { return false; } RMT_MUTEX_LOCK(bus); - bool retCode = bus->rmt_ch_rx_obj_h->rmt_rx_completed; + bool retCode = bus->rmt_rx_completed; RMT_MUTEX_UNLOCK(bus); return retCode; @@ -477,12 +445,9 @@ bool rmtEnd(int pin) if (!_rmtCheckDirection(pin, RMT_RX_MODE, __FUNCTION__)) { return false; } - if (!_rmtStartReading(bus, pin, __FUNCTION__)) { - return false; - } RMT_MUTEX_LOCK(bus); - bus->rmt_ch_rx_obj_h->rmt_async_rx_enabled = false; + bus->rmt_async_rx_enabled = false; RMT_MUTEX_UNLOCK(bus); return true; } @@ -496,21 +461,18 @@ bool rmtReadAsync(int pin, rmt_data_t* data, size_t size, void* eventFlag, bool if (!_rmtCheckDirection(pin, RMT_RX_MODE, __FUNCTION__)) { return false; } - if (!_rmtStartReading(bus, pin, __FUNCTION__)) { - return false; - } rmtEnd(pin); // disable reading if (eventFlag) { xEventGroupClearBits(eventFlag, RMT_FLAG_RX_DONE); } // if NULL, no problems - rmtReadAsync works as a plain rmtReadData() - bus->rmt_ch_rx_obj_h->rmt_rx_events = eventFlag; + bus->rmt_rx_events = eventFlag; // if NULL, no problems - task will take care of it - bus->rmt_ch_rx_obj_h->rmt_data_rx_ptr = data; - bus->rmt_ch_rx_obj_h->rmt_data_rx_size = size; - bus->rmt_ch_rx_obj_h->rmt_rx_completed = false; + bus->rmt_data_rx_ptr = data; + bus->rmt_data_rx_size = size; + bus->rmt_rx_completed = false; // Start a read task RMT_MUTEX_LOCK(bus); @@ -581,7 +543,6 @@ bool rmtInit(int pin, rmt_ch_dir_t channel_direction, rmt_reserve_memsize_t mem_ // common RX/TX channel configuration - common fields bus->num_buffers = mem_size; - bus->rmt_ch_rx_obj_h = NULL; // pulses with width smaller than min_ns will be ignored (as a glitch) bus->signal_range_min_ns = 1000000000 / (frequency_Hz * 2); // 1/2 pulse width // RMT stops reading if the input stays idle for longer than max_ns From 45cb314ca21c7ee8af98a8ecf888ef10081084ce Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Mon, 17 Apr 2023 02:44:28 -0300 Subject: [PATCH 05/14] Deep API simplification --- cores/esp32/esp32-hal-rgb-led.c | 25 ++- cores/esp32/esp32-hal-rmt.c | 267 +++++--------------------------- cores/esp32/esp32-hal-rmt.h | 83 +++------- 3 files changed, 84 insertions(+), 291 deletions(-) diff --git a/cores/esp32/esp32-hal-rgb-led.c b/cores/esp32/esp32-hal-rgb-led.c index 6e0f74307b6..1871b36c14a 100644 --- a/cores/esp32/esp32-hal-rgb-led.c +++ b/cores/esp32/esp32-hal-rgb-led.c @@ -4,19 +4,28 @@ void neopixelWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val){ rmt_data_t led_data[24]; + bool shallAlwaysInitRMT = true; // in case it is used for any other GPIO #ifdef RGB_BUILTIN + static bool initialized = false; if(pin == RGB_BUILTIN){ - pin = RGB_BUILTIN-SOC_GPIO_PIN_COUNT; + pin = RGB_BUILTIN - SOC_GPIO_PIN_COUNT; + if(!initialized) { + initialized = true; + } else { + // RGB_BUILTIN has been already initialized before, save this time now + shallAlwaysInitRMT = false; + } } #endif - // 10MHz RMT Frequency -> tick = 100ns - // force initialization to make sure it has the right RMT Frequency - if(!rmtInit(pin, RMT_TX_MODE, RMT_MEM_64, 10000000)){ - log_e("RGB LED driver initialization failed!"); - return; + if (shallAlwaysInitRMT) { + // 10MHz RMT Frequency -> tick = 100ns + // force initialization to make sure it has the right RMT Frequency + if (!rmtInit(pin, RMT_TX_MODE, RMT_MEM_NUM_BLOCKS_1, 10000000)){ + log_e("RGB LED driver initialization failed!"); + return; + } } - int color[] = {green_val, red_val, blue_val}; // Color coding is in order GREEN, RED, BLUE int i = 0; for(int col=0; col<3; col++ ){ @@ -37,5 +46,5 @@ void neopixelWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue i++; } } - rmtWrite(pin, led_data, 24); + rmtWrite(pin, led_data, 24, false, false); } diff --git a/cores/esp32/esp32-hal-rmt.c b/cores/esp32/esp32-hal-rmt.c index 346a1df22d7..af749b0f91d 100644 --- a/cores/esp32/esp32-hal-rmt.c +++ b/cores/esp32/esp32-hal-rmt.c @@ -43,17 +43,11 @@ struct rmt_obj_s { rmt_channel_handle_t rmt_channel_h; // NULL value means channel not alocated rmt_encoder_handle_t rmt_copy_encoder_h; // RMT simple copy encoder handle - uint8_t num_buffers; // Number of allocated buffers needed for reading uint32_t signal_range_min_ns; // RX Filter data - Low Pass pulse width uint32_t signal_range_max_ns; // RX idle time that defines end of reading EventGroupHandle_t rmt_rx_events; // reading event user handle - TaskHandle_t rmt_rxTaskHandle; // Rx task created to wait for received RMT data - QueueHandle_t rmt_rx_queue; // Rx callback data queue - bool rmt_rx_completed; // rmtReceiveCompleted() rmtReadAsync() async reading - bool rmt_async_rx_enabled; // rmtEnd() rmtBeginReceive() rmtRead() rmtReadAsync() control - rmt_data_t* rmt_data_rx_ptr; // Rx Task - data user pointer - size_t rmt_data_rx_size; // Rx Task - data length + size_t *num_symbols_read; // number of RMT symbol read by IDF RMT RX Done xSemaphoreHandle g_rmt_objlocks; // Channel Semaphore Lock }; @@ -71,87 +65,15 @@ static xSemaphoreHandle g_rmt_block_lock = NULL; static bool _rmt_rx_done_callback(rmt_channel_handle_t channel, const rmt_rx_done_event_data_t *data, void *args) { BaseType_t high_task_wakeup = pdFALSE; - QueueHandle_t rmt_rx_queue = (QueueHandle_t)args; - // send the received RMT symbols to the _rmtRxTask of that channel - xQueueSendFromISR(rmt_rx_queue, data, &high_task_wakeup); - return high_task_wakeup == pdTRUE; -} - -static void _rmtRxTask(void *args) { rmt_bus_handle_t bus = (rmt_bus_handle_t) args; - - // sanity check - it should never happen - assert(bus && "_rmtRxTask bus NULL pointer."); - - bus->rmt_rx_queue = xQueueCreate(1, sizeof(rmt_rx_done_event_data_t)); - if (bus->rmt_rx_queue == NULL) { - log_e("Error creating RX Queue."); - goto err; - } - - rmt_rx_event_callbacks_t cbs = { .on_recv_done = _rmt_rx_done_callback }; - if (ESP_OK != rmt_rx_register_event_callbacks(bus->rmt_channel_h, &cbs, bus->rmt_rx_queue)) { - log_e("Error registering RX Callback."); - vQueueDelete(bus->rmt_rx_queue); - goto err; - } - - for (;;) { - // request reading RMT Channel Data - rmt_receive_config_t receive_config; - receive_config.signal_range_min_ns = bus->signal_range_min_ns; - receive_config.signal_range_max_ns = bus->signal_range_max_ns; - // buffer to save the received RMT symbols from the callback, in STACK for easier memory deallocation - size_t num_rmt_symbols = bus->num_buffers * SOC_RMT_MEM_WORDS_PER_CHANNEL; // max is 8*64*4 = 2KB - rmt_data_t rmt_symbols[num_rmt_symbols]; // number of symbols requested by user - // must make sure that the RX Channel is enabled! - while (!bus->rmt_async_rx_enabled) { - delay(1); // let other lower priority tasks to run, such as Arduino Task... - } - rmt_receive(bus->rmt_channel_h, rmt_symbols, num_rmt_symbols * sizeof(rmt_data_t), &receive_config); - - // wait for ever for RX done signal - rmt_rx_done_event_data_t rx_data; - if (xQueueReceive(bus->rmt_rx_queue, &rx_data, portMAX_DELAY) == pdPASS) { - log_d("RMT has read %d bytes.", rx_data.num_symbols * sizeof(rmt_data_t)); - // Async Read -- copy data to caller - uint32_t data_size = bus->rmt_data_rx_size; - rmt_data_t *p = (rmt_data_t *)bus->rmt_data_rx_ptr; - if (p != NULL && data_size > 0) { - if (rx_data.num_symbols < data_size) data_size = rx_data.num_symbols; - for (uint32_t i = 0; i < data_size; i++) { - p[i].val = rx_data.received_symbols[i].val; - } - } - // set RX event group - if (bus->rmt_rx_events) { - xEventGroupSetBits(bus->rmt_rx_events, RMT_FLAG_RX_DONE); - } - // used in rmtReceiveCompleted() - bus->rmt_rx_completed = true; - } // xQueueReceive - } // for(;;) - -err: - vTaskDelete(NULL); -} - -static bool _rmtCreateRxTask(rmt_bus_handle_t bus) -{ - if (bus == NULL) { - return false; - } - if (bus->rmt_rxTaskHandle != NULL) { // Task already created - return true; + *bus->num_symbols_read = data->num_symbols; + // set RX event group + if (bus->rmt_rx_events != NULL) { + // signal the received RMT symbols of that channel + xEventGroupSetBitsFromISR(bus->rmt_rx_events, RMT_FLAG_RX_DONE, &high_task_wakeup); } - - xTaskCreate(_rmtRxTask, "rmt_rx_task", 1024 * 6, bus, configMAX_PRIORITIES - 1, &bus->rmt_rxTaskHandle); - - if (bus->rmt_rxTaskHandle == NULL) { - log_e("RMT RX Task create failed"); - return false; - } - return true; + // A "need to yield" is returned in order to execute portYIELD_FROM_ISR() in the main IDF RX ISR + return high_task_wakeup == pdTRUE; } // This function must be called only after checking the pin and its bus with _rmtGetBus() @@ -194,15 +116,6 @@ static bool _rmtDetachBus(void *busptr) bool retCode = true; rmt_bus_handle_t bus = (rmt_bus_handle_t) busptr; - // deleted RX channel tasks - if (bus->rmt_rxTaskHandle != NULL) { - vTaskDelete(bus->rmt_rxTaskHandle); - } - // delete RX Queue - if (bus->rmt_rx_queue != NULL) { - vQueueDelete(bus->rmt_rx_queue); - } - // deallocate the channel encoder if (bus->rmt_copy_encoder_h != NULL) { if (ESP_OK != rmt_del_encoder(bus->rmt_copy_encoder_h)) { @@ -310,7 +223,7 @@ bool rmtDeinit(int pin) return false; } -bool rmtLoop(int pin, rmt_data_t* data, size_t size) +bool rmtWrite(int pin, rmt_data_t* data, size_t num_rmt_symbols, bool blocking, bool loop) { rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__); if (bus == NULL) { @@ -319,64 +232,25 @@ bool rmtLoop(int pin, rmt_data_t* data, size_t size) if (!_rmtCheckDirection(pin, RMT_TX_MODE, __FUNCTION__)) { return false; } - - rmt_transmit_config_t transmit_cfg = {0}; - transmit_cfg.loop_count = 1; // enable infinite loop mode - bool retCode = true; - RMT_MUTEX_LOCK(bus); - - if (ESP_OK != rmt_transmit(bus->rmt_channel_h, bus->rmt_copy_encoder_h, - (const void *) data, size * sizeof(rmt_data_t), &transmit_cfg)) { - retCode = false; - log_w("GPIO %d - RMT Loop Transmission failed.", pin); - } - RMT_MUTEX_UNLOCK(bus); - return retCode; -} - -bool rmtWrite(int pin, rmt_data_t* data, size_t size) -{ - rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__); - if (bus == NULL) { - return false; - } - if (!_rmtCheckDirection(pin, RMT_TX_MODE, __FUNCTION__)) { + if (data == NULL || num_rmt_symbols == 0) { + log_w("GPIO %d - RMT Write Data NULL pointer or size is zero.", pin); return false; } + rmt_transmit_config_t transmit_cfg = {0}; // disable loop mode - bool retCode = true; - RMT_MUTEX_LOCK(bus); - - if (ESP_OK != rmt_transmit(bus->rmt_channel_h, bus->rmt_copy_encoder_h, - (const void *) data, size * sizeof(rmt_data_t), &transmit_cfg)) { - retCode = false; - log_w("GPIO %d - RMT single Transmission failed.", pin); + if (loop) { + transmit_cfg.loop_count = 1; // enable infinite loop mode } - RMT_MUTEX_UNLOCK(bus); - return retCode; -} - -bool rmtWriteBlocking(int pin, rmt_data_t* data, size_t size) -{ - rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__); - if (bus == NULL) { - return false; - } - if (!_rmtCheckDirection(pin, RMT_TX_MODE, __FUNCTION__)) { - return false; - } - - rmt_transmit_config_t transmit_cfg = {0}; // disable loop mode bool retCode = true; RMT_MUTEX_LOCK(bus); if (ESP_OK != rmt_transmit(bus->rmt_channel_h, bus->rmt_copy_encoder_h, - (const void *) data, size * sizeof(rmt_data_t), &transmit_cfg)) { + (const void *) data, num_rmt_symbols * sizeof(rmt_data_t), &transmit_cfg)) { retCode = false; - log_w("GPIO %d - RMT Blocking Transmission failed.", pin); + log_w("GPIO %d - RMT Transmission failed.", pin); } - if (ESP_OK != rmt_tx_wait_all_done(bus->rmt_channel_h, -1)) { + if (blocking && ESP_OK != rmt_tx_wait_all_done(bus->rmt_channel_h, -1)) { retCode = false; log_w("GPIO %d - RMT Blocking TX Setup failed.", pin); } @@ -384,20 +258,7 @@ bool rmtWriteBlocking(int pin, rmt_data_t* data, size_t size) return retCode; } -// Sets a user buffer and length for reading RMT signal. -// rmtReceiveCompleted() shall be used to check if there is data read fromthe RMT channel -bool rmtReadData(int pin, uint32_t* data, size_t size) -{ - rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__); - if (bus == NULL) { - return false; - } - - rmtReadAsync(pin, (rmt_data_t*) data, size, NULL, false, 0); - return true; -} - -bool rmtBeginReceive(int pin) +bool rmtReadAsync(int pin, rmt_data_t* data, size_t *num_rmt_symbols, void* eventFlag, bool waitForData, uint32_t timeout_ms) { rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__); if (bus == NULL) { @@ -406,85 +267,35 @@ bool rmtBeginReceive(int pin) if (!_rmtCheckDirection(pin, RMT_RX_MODE, __FUNCTION__)) { return false; } - - RMT_MUTEX_LOCK(bus); - bus->rmt_async_rx_enabled = true; - bus->rmt_rx_completed = false; - RMT_MUTEX_UNLOCK(bus); - return true; -} - -bool rmtReceiveCompleted(int pin) -{ - rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__); - if (bus == NULL) { - return false; - } - if (!_rmtCheckDirection(pin, RMT_RX_MODE, __FUNCTION__)) { - return false; - } - - // is read enabled? - if(!bus->rmt_async_rx_enabled) { - return false; - } - - RMT_MUTEX_LOCK(bus); - bool retCode = bus->rmt_rx_completed; - RMT_MUTEX_UNLOCK(bus); - - return retCode; -} - -bool rmtEnd(int pin) -{ - rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__); - if (bus == NULL) { - return false; - } - if (!_rmtCheckDirection(pin, RMT_RX_MODE, __FUNCTION__)) { + if (data == NULL || num_rmt_symbols == NULL) { + log_w("GPIO %d - RMT Read Data and/or Size NULL pointer.", pin); return false; } - + + // Start a read task RMT_MUTEX_LOCK(bus); - bus->rmt_async_rx_enabled = false; - RMT_MUTEX_UNLOCK(bus); - return true; -} -bool rmtReadAsync(int pin, rmt_data_t* data, size_t size, void* eventFlag, bool waitForData, uint32_t timeout) -{ - rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__); - if (bus == NULL) { - return false; - } - if (!_rmtCheckDirection(pin, RMT_RX_MODE, __FUNCTION__)) { - return false; - } - - rmtEnd(pin); // disable reading + // request reading RMT Channel Data + rmt_receive_config_t receive_config; + receive_config.signal_range_min_ns = bus->signal_range_min_ns; + receive_config.signal_range_max_ns = bus->signal_range_max_ns; if (eventFlag) { xEventGroupClearBits(eventFlag, RMT_FLAG_RX_DONE); } - // if NULL, no problems - rmtReadAsync works as a plain rmtReadData() - bus->rmt_rx_events = eventFlag; - // if NULL, no problems - task will take care of it - bus->rmt_data_rx_ptr = data; - bus->rmt_data_rx_size = size; - bus->rmt_rx_completed = false; + bus->rmt_rx_events = eventFlag; + bus->num_symbols_read = num_rmt_symbols; - // Start a read task - RMT_MUTEX_LOCK(bus); - _rmtCreateRxTask(bus); - RMT_MUTEX_UNLOCK(bus); + rmt_receive(bus->rmt_channel_h, data, *num_rmt_symbols * sizeof(rmt_data_t), &receive_config); // wait for data if requested so - rmtBeginReceive(pin); // enable reading if (waitForData && eventFlag) { xEventGroupWaitBits(eventFlag, RMT_FLAG_RX_DONE, - pdTRUE /* clear on exit */, pdFALSE /* wait for all bits */, timeout); + pdTRUE /* clear on exit */, pdFALSE /* wait for all bits */, timeout_ms); } + + RMT_MUTEX_UNLOCK(bus); + return true; } @@ -541,8 +352,6 @@ bool rmtInit(int pin, rmt_ch_dir_t channel_direction, rmt_reserve_memsize_t mem_ return false; } - // common RX/TX channel configuration - common fields - bus->num_buffers = mem_size; // pulses with width smaller than min_ns will be ignored (as a glitch) bus->signal_range_min_ns = 1000000000 / (frequency_Hz * 2); // 1/2 pulse width // RMT stops reading if the input stays idle for longer than max_ns @@ -595,6 +404,16 @@ bool rmtInit(int pin, rmt_ch_dir_t channel_direction, rmt_reserve_memsize_t mem_ xSemaphoreGive(g_rmt_block_lock); return false; } + + // set RX Callback + rmt_rx_event_callbacks_t cbs = { .on_recv_done = _rmt_rx_done_callback }; + if (ESP_OK != rmt_rx_register_event_callbacks(bus->rmt_channel_h, &cbs, bus)) { + log_e("GPIO %d RMT - Error registering RX Callback.", pin); + // release the RMT object + _rmtDetachBus((void *)bus); + xSemaphoreGive(g_rmt_block_lock); + return false; + } } // allocate memory for the RMT Copy encoder diff --git a/cores/esp32/esp32-hal-rmt.h b/cores/esp32/esp32-hal-rmt.h index 61346a224a9..81e753849b2 100644 --- a/cores/esp32/esp32-hal-rmt.h +++ b/cores/esp32/esp32-hal-rmt.h @@ -27,17 +27,30 @@ typedef enum { RMT_TX_MODE = 1, // true } rmt_ch_dir_t; +// Reading and Writing shall use as rmt_symbols_size this unit +// ESP32 has 8 MEM BLOCKS in total shared with Reading and/or Writing +// ESP32-S2 has 4 MEM BLOCKS in total shared with Reading and/or Writing +// ESP32-S3 has 4 MEM BLOCKS for Reading and another 4 MEM BLOCKS for Writing +// ESP32-C3 has 2 MEM BLOCKS for Reading and another 2 MEM BLOCKS for Writing +#define RMT_SYMBOLS_PER_CHANNEL_BLOCK SOC_RMT_MEM_WORDS_PER_CHANNEL + typedef enum { - RMT_MEM_64 = 1, - RMT_MEM_128 = 2, - RMT_MEM_192 = 3, - RMT_MEM_256 = 4, - RMT_MEM_320 = 5, - RMT_MEM_384 = 6, - RMT_MEM_448 = 7, - RMT_MEM_512 = 8, + RMT_MEM_NUM_BLOCKS_1 = 1, + RMT_MEM_NUM_BLOCKS_2 = 2, +#if SOC_RMT_TX_CANDIDATES_PER_GROUP > 2 + RMT_MEM_NUM_BLOCKS_3 = 3, + RMT_MEM_NUM_BLOCKS_4 = 4, +#if SOC_RMT_TX_CANDIDATES_PER_GROUP > 4 + RMT_MEM_NUM_BLOCKS_5 = 5, + RMT_MEM_NUM_BLOCKS_6 = 6, + RMT_MEM_NUM_BLOCKS_7 = 7, + RMT_MEM_NUM_BLOCKS_8 = 8, +#endif +#endif } rmt_reserve_memsize_t; +// Each RMT Symbols has 4 bytes +// Total number of bytes per RMT_MEM_BLOCK is RMT_SYMBOLS_PER_CHANNEL_BLOCK * 4 bytes typedef union { struct { uint32_t duration0 : 15; @@ -57,65 +70,17 @@ typedef union { */ bool rmtInit(int pin, rmt_ch_dir_t channel_direction, rmt_reserve_memsize_t memsize, uint32_t frequency_Hz); -/** - BREAKING CHANGE in Arduino Core 3: rmtSetTick() was removed, in favor od rmtInit() - - Sets the clock/divider of timebase the nearest tick to the supplied value in nanoseconds - return the real actual tick value in ns -*/ -//float rmtSetTick(int pin, float tick); - /** Sending data in one-go mode or continual mode - (more data being send while updating buffers in interrupts) - Non-Blocking mode - returns right after executing -*/ -bool rmtWrite(int pin, rmt_data_t* data, size_t size_byte); - -/** - Sending data in one-go mode or continual mode - (more data being send while updating buffers in interrupts) - Blocking mode - only returns when data has been sent -*/ -bool rmtWriteBlocking(int pin, rmt_data_t* data, size_t size_byte); - -/** - Loop data up to the reserved memsize continuously - + Blocking and non-blocking mode */ -bool rmtLoop(int pin, rmt_data_t* data, size_t size_byte); +bool rmtWrite(int pin, rmt_data_t* data, size_t num_rmt_symbols, bool blocking, bool loop); /** Initiates async receive, event flag indicates data received */ -bool rmtReadAsync(int pin, rmt_data_t* data, size_t size, void* eventFlag, bool waitForData, uint32_t timeout); - -/*** - Ends async receive started with rmtRead(); but does not - rmtDeInit(). -*/ -bool rmtEnd(int pin); - -/* Additional interface */ - -/** - Start reception - -*/ -bool rmtBeginReceive(int pin); - -/** - Checks if reception completes - -*/ -bool rmtReceiveCompleted(int pin); - -/** - Reads the data for particular channel - -*/ -bool rmtReadData(int pin, uint32_t* data, size_t size); +bool rmtReadAsync(int pin, rmt_data_t* data, size_t *num_rmt_symbols, void* eventFlag, bool waitForData, uint32_t timeout_ms); /** Setting threshold for Rx completed From 5ac095b372dfb286c2120cc7a5b71daf361a10cf Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Mon, 17 Apr 2023 04:20:00 -0300 Subject: [PATCH 06/14] fixes the examples --- .../examples/RMT/RMTCallback/RMTCallback.ino | 47 ++- .../examples/RMT/RMTLoopback/RMTLoopback.ino | 16 +- .../examples/RMT/RMTReadXJT/RMTReadXJT.ino | 269 +++++++++--------- .../RMT/RMTWriteNeoPixel/RMTWriteNeoPixel.ino | 6 +- 4 files changed, 182 insertions(+), 156 deletions(-) diff --git a/libraries/ESP32/examples/RMT/RMTCallback/RMTCallback.ino b/libraries/ESP32/examples/RMT/RMTCallback/RMTCallback.ino index 6537ecb763c..ded57f8c675 100644 --- a/libraries/ESP32/examples/RMT/RMTCallback/RMTCallback.ino +++ b/libraries/ESP32/examples/RMT/RMTCallback/RMTCallback.ino @@ -8,44 +8,61 @@ class MyProcessor { int8_t gpio = -1; uint32_t buff; // rolling buffer of most recent 32 bits. int at = 0; + EventGroupHandle_t events = NULL; + size_t rx_num_symbols = RMT_MEM_NUM_BLOCKS_1 * RMT_SYMBOLS_PER_CHANNEL_BLOCK; + rmt_data_t rx_symbols[RMT_MEM_NUM_BLOCKS_1 * RMT_SYMBOLS_PER_CHANNEL_BLOCK]; public: MyProcessor(uint8_t pin, uint32_t rmtFreqHz) { - if (!rmtInit(pin, RMT_RX_MODE, RMT_MEM_64, rmtFreqHz)) + if (!rmtInit(pin, RMT_RX_MODE, RMT_MEM_NUM_BLOCKS_1, rmtFreqHz)) { Serial.println("init receiver failed\n"); return; } gpio = pin; - }; + events = xEventGroupCreate(); + } + void begin() { - rmtRead(gpio, receive_trampoline, this); - }; + // Creating RMT RX Callback Task + xTaskCreate(readTask, "MyProcessor", 2048, this, 4, NULL); + } + + static void readTask(void *args) { + MyProcessor *me = (MyProcessor *) args; + + while(1) { + // blocks until RMT has read data + rmtReadAsync(me->gpio, me->rx_symbols, &me->rx_num_symbols, me->events, true, portMAX_DELAY); + // process the data like a callback whenever there is data available + process(me->rx_symbols, me->rx_num_symbols, me); + } + + vTaskDelete(NULL); + } - void process(rmt_data_t *data, size_t len) { + static void process(rmt_data_t *data, size_t len, void *args) { + MyProcessor *me = (MyProcessor *) args; + uint32_t *buff = &me->buff; + for (int i = 0; len; len--) { if (data[i].duration0 == 0) break; - buff = (buff << 1) | (data[i].level0 ? 1 : 0); + *buff = (*buff << 1) | (data[i].level0 ? 1 : 0); i++; if (data[i].duration1 == 0) break; - buff = (buff << 1) | (data[i].level1 ? 1 : 0); + *buff = (*buff << 1) | (data[i].level1 ? 1 : 0); i++; }; - }; + } + uint32_t val() { return buff; } }; -void receive_trampoline(uint32_t *data, size_t len, void * arg) -{ - MyProcessor * p = (MyProcessor *)arg; - p->process((rmt_data_t*) data, len); -} - // Attach 3 processors to GPIO 4, 5 and 10 with different tick/speeds. MyProcessor mp1 = MyProcessor(4, 1000000); MyProcessor mp2 = MyProcessor(5, 1000000); @@ -63,4 +80,4 @@ void loop() { Serial.printf("GPIO 4: %08lx 5: %08lx 10: %08lx\n", mp1.val(), mp2.val(), mp3.val()); delay(500); -} \ No newline at end of file +} diff --git a/libraries/ESP32/examples/RMT/RMTLoopback/RMTLoopback.ino b/libraries/ESP32/examples/RMT/RMTLoopback/RMTLoopback.ino index 188acfcb8a7..c9ddf94db35 100644 --- a/libraries/ESP32/examples/RMT/RMTLoopback/RMTLoopback.ino +++ b/libraries/ESP32/examples/RMT/RMTLoopback/RMTLoopback.ino @@ -9,11 +9,11 @@ // ESP32 C3 has only 2 channels for RX and 2 for TX, thus MAX RMT_MEM is 128 #define RMT_TX_PIN 4 #define RMT_RX_PIN 5 -#define RMT_MEM_RX RMT_MEM_128 +#define RMT_MEM_RX RMT_MEM_NUM_BLOCKS_2 #else #define RMT_TX_PIN 18 #define RMT_RX_PIN 21 -#define RMT_MEM_RX RMT_MEM_192 +#define RMT_MEM_RX RMT_MEM_NUM_BLOCKS_3 #endif rmt_data_t my_data[256]; @@ -29,7 +29,7 @@ void setup() Serial.begin(115200); events = xEventGroupCreate(); - if (!rmtInit(RMT_TX_PIN, RMT_TX_MODE, RMT_MEM_64, RMT_FREQ)) + if (!rmtInit(RMT_TX_PIN, RMT_TX_MODE, RMT_MEM_NUM_BLOCKS_1, RMT_FREQ)) { Serial.println("init sender failed\n"); } @@ -58,14 +58,16 @@ void loop() data[255].val = 0; // Start receiving - rmtReadAsync(RMT_RX_PIN, my_data, RMT_NUM_EXCHANGED_DATA, events, false, 0); + size_t rx_num_symbols = RMT_NUM_EXCHANGED_DATA; + rmtReadAsync(RMT_RX_PIN, my_data, &rx_num_symbols, events, false, 0); - // Send in continous mode - rmtWrite(RMT_TX_PIN, data, RMT_NUM_EXCHANGED_DATA); + // Send in continous mode by calling it in a loop + rmtWrite(RMT_TX_PIN, data, RMT_NUM_EXCHANGED_DATA, false, false); // Wait for data xEventGroupWaitBits(events, RMT_FLAG_RX_DONE, 1, 1, portMAX_DELAY); - + Serial.printf("Got %d RMT symbols\n", rx_num_symbols); + // Printout the received data plus the original values for (i=0; i<60; i++) { diff --git a/libraries/ESP32/examples/RMT/RMTReadXJT/RMTReadXJT.ino b/libraries/ESP32/examples/RMT/RMTReadXJT/RMTReadXJT.ino index df42873cc25..82931461c48 100644 --- a/libraries/ESP32/examples/RMT/RMTReadXJT/RMTReadXJT.ino +++ b/libraries/ESP32/examples/RMT/RMTReadXJT/RMTReadXJT.ino @@ -6,8 +6,8 @@ #include "esp32-hal.h" // -// Note: This example uses a FrSKY device communication -// using XJT D12 protocol +// Note: This example uses a FrSKY device communication +// using XJT D12 protocol // // ; 0 bit = 6us low/10us high // ; 1 bit = 14us low/10us high @@ -24,7 +24,7 @@ // ; | 16us | 24us | // Typedef of received frame -// +// // ; 0x00 - Sync, 0x7E (sync header ID) // ; 0x01 - Rx ID, 0x?? (receiver ID number, 0x00-0x??) // ; 0x02 - Flags 1, 0x?? (used for failsafe and binding) @@ -38,26 +38,26 @@ // ; 0x12 - CRC-16 Low // ; 0x13 - Tail, 0x7E (tail ID) typedef union { - struct { - uint8_t head;//0x7E - uint8_t rxid;//Receiver Number - uint8_t flags;//Range:0x20, Bind:0x01 - uint8_t reserved0;//0x00 - union { - struct { - uint8_t ch0_l; - uint8_t ch0_h:4; - uint8_t ch1_l:4; - uint8_t ch1_h; - }; - uint8_t bytes[3]; - } channels[4]; - uint8_t reserved1;//0x00 - uint8_t crc_h; - uint8_t crc_l; - uint8_t tail;//0x7E - }; - uint8_t buffer[20]; + struct { + uint8_t head;//0x7E + uint8_t rxid;//Receiver Number + uint8_t flags;//Range:0x20, Bind:0x01 + uint8_t reserved0;//0x00 + union { + struct { + uint8_t ch0_l; + uint8_t ch0_h: 4; + uint8_t ch1_l: 4; + uint8_t ch1_h; + }; + uint8_t bytes[3]; + } channels[4]; + uint8_t reserved1;//0x00 + uint8_t crc_h; + uint8_t crc_l; + uint8_t tail;//0x7E + }; + uint8_t buffer[20]; } xjt_packet_t; #define XJT_VALID(i) (i->level0 && !i->level1 && i->duration0 >= 8 && i->duration0 <= 11) @@ -67,134 +67,141 @@ static uint32_t channels[16]; static uint8_t xjt_flags = 0x0; static uint8_t xjt_rxid = 0x0; -static bool xjtReceiveBit(size_t index, bool bit){ - static xjt_packet_t xjt; - static uint8_t xjt_bit_index = 8; - static uint8_t xht_byte_index = 0; - static uint8_t xht_ones = 0; - - if(!index){ - xjt_bit_index = 8; - xht_byte_index = 0; - xht_ones = 0; +static bool xjtReceiveBit(size_t index, bool bit) { + static xjt_packet_t xjt; + static uint8_t xjt_bit_index = 8; + static uint8_t xht_byte_index = 0; + static uint8_t xht_ones = 0; + + if (!index) { + xjt_bit_index = 8; + xht_byte_index = 0; + xht_ones = 0; + } + + if (xht_byte_index > 19) { + //fail! + return false; + } + if (bit) { + xht_ones++; + if (xht_ones > 5 && xht_byte_index && xht_byte_index < 19) { + //fail! + return false; } - - if(xht_byte_index > 19){ + //add bit + xjt.buffer[xht_byte_index] |= (1 << --xjt_bit_index); + } else if (xht_ones == 5 && xht_byte_index && xht_byte_index < 19) { + xht_ones = 0; + //skip bit + return true; + } else { + xht_ones = 0; + //add bit + xjt.buffer[xht_byte_index] &= ~(1 << --xjt_bit_index); + } + if ((!xjt_bit_index) || (xjt_bit_index == 1 && xht_byte_index == 19) ) { + xjt_bit_index = 8; + if (!xht_byte_index && xjt.buffer[0] != 0x7E) { + //fail! + return false; + } + xht_byte_index++; + if (xht_byte_index == 20) { + //done + if (xjt.buffer[19] != 0x7E) { //fail! return false; - } - if(bit){ - xht_ones++; - if(xht_ones > 5 && xht_byte_index && xht_byte_index < 19){ - //fail! - return false; + } + //check crc? + + xjt_flags = xjt.flags; + xjt_rxid = xjt.rxid; + for (int i = 0; i < 4; i++) { + uint16_t ch0 = xjt.channels[i].ch0_l | ((uint16_t)(xjt.channels[i].ch0_h & 0x7) << 8); + ch0 = ((ch0 * 2) + 2452) / 3; + uint16_t ch1 = xjt.channels[i].ch1_l | ((uint16_t)(xjt.channels[i].ch1_h & 0x7F) << 4); + ch1 = ((ch1 * 2) + 2452) / 3; + uint8_t c0n = i * 2; + if (xjt.channels[i].ch0_h & 0x8) { + c0n += 8; } - //add bit - xjt.buffer[xht_byte_index] |= (1 << --xjt_bit_index); - } else if(xht_ones == 5 && xht_byte_index && xht_byte_index < 19){ - xht_ones = 0; - //skip bit - return true; - } else { - xht_ones = 0; - //add bit - xjt.buffer[xht_byte_index] &= ~(1 << --xjt_bit_index); - } - if ((!xjt_bit_index) || (xjt_bit_index==1 && xht_byte_index==19) ) { - xjt_bit_index = 8; - if(!xht_byte_index && xjt.buffer[0] != 0x7E){ - //fail! - return false; - } - xht_byte_index++; - if(xht_byte_index == 20){ - //done - if(xjt.buffer[19] != 0x7E){ - //fail! - return false; - } - //check crc? - - xjt_flags = xjt.flags; - xjt_rxid = xjt.rxid; - for(int i=0; i<4; i++){ - uint16_t ch0 = xjt.channels[i].ch0_l | ((uint16_t)(xjt.channels[i].ch0_h & 0x7) << 8); - ch0 = ((ch0 * 2) + 2452) / 3; - uint16_t ch1 = xjt.channels[i].ch1_l | ((uint16_t)(xjt.channels[i].ch1_h & 0x7F) << 4); - ch1 = ((ch1 * 2) + 2452) / 3; - uint8_t c0n = i*2; - if(xjt.channels[i].ch0_h & 0x8){ - c0n += 8; - } - uint8_t c1n = i*2+1; - if(xjt.channels[i].ch1_h & 0x80){ - c1n += 8; - } - s_channels[c0n] = ch0; - s_channels[c1n] = ch1; - } + uint8_t c1n = i * 2 + 1; + if (xjt.channels[i].ch1_h & 0x80) { + c1n += 8; } + s_channels[c0n] = ch0; + s_channels[c1n] = ch1; + } } - return true; + } + return true; } -void parseRmt(rmt_data_t* items, size_t len, uint32_t* channels){ - bool valid = true; - rmt_data_t* it = NULL; +void parseRmt(rmt_data_t* items, size_t len, uint32_t* channels) { + bool valid = true; + rmt_data_t* it = NULL; - if (!channels) { - log_e("Please provide data block for storing channel info"); - return; - } - s_channels = channels; + if (!channels) { + log_e("Please provide data block for storing channel info"); + return; + } + s_channels = channels; - it = &items[0]; - for(size_t i = 0; iduration1 >= 5 && it->duration1 <= 8){ - valid = xjtReceiveBit(i, false); - } else if(it->duration1 >= 13 && it->duration1 <= 16){ - valid = xjtReceiveBit(i, true); - } else { - valid = false; - } - } else if(!it->duration1 && !it->level1 && it->duration0 >= 5 && it->duration0 <= 8) { - valid = xjtReceiveBit(i, false); - - } + if (!valid) { + break; } -} + it = &items[i]; + if (XJT_VALID(it)) { + if (it->duration1 >= 5 && it->duration1 <= 8) { + valid = xjtReceiveBit(i, false); + } else if (it->duration1 >= 13 && it->duration1 <= 16) { + valid = xjtReceiveBit(i, true); + } else { + valid = false; + } + } else if (!it->duration1 && !it->level1 && it->duration0 >= 5 && it->duration0 <= 8) { + valid = xjtReceiveBit(i, false); -extern "C" void receive_data(uint32_t *data, size_t len, void * arg) -{ - parseRmt((rmt_data_t*) data, len, channels); + } + } } #define RMT_GPIO 21 +static EventGroupHandle_t events; -void setup() +void setup() { - Serial.begin(115200); - - // Initialize the channel to capture up to 192 items - 1us tick - if (!rmtInit(RMT_GPIO, RMT_RX_MODE, RMT_MEM_192, 1000000)) - { - Serial.println("init receiver failed\n"); - } - Serial.println("real tick set to: 1us"); + Serial.begin(115200); + + // Initialize the channel to capture up to 64*2 or 48*2 items - 1us tick + if (!rmtInit(RMT_GPIO, RMT_RX_MODE, RMT_MEM_NUM_BLOCKS_2, 1000000)) + { + Serial.println("init receiver failed\n"); + } + Serial.println("real tick set to: 1us"); - // Ask to start reading - rmtRead(RMT_GPIO, receive_data, NULL); + events = xEventGroupCreate(); } -void loop() +void loop() { - // printout some of the channels - Serial.printf("%04lx %04lx %04lx %04lx\n", channels[0], channels[1], channels[2], channels[3]); - delay(500); + static rmt_data_t data[RMT_MEM_NUM_BLOCKS_2 * RMT_SYMBOLS_PER_CHANNEL_BLOCK]; + static size_t data_symbols = RMT_MEM_NUM_BLOCKS_2 * RMT_SYMBOLS_PER_CHANNEL_BLOCK; + + // Ask to start a blocking read with timeout of 500ms + rmtReadAsync(RMT_GPIO, data, &data_symbols, events, true, pdMS_TO_TICKS(500)); + + // If read something, process the data + EventBits_t read_bit = xEventGroupGetBits(events); + if (read_bit & RMT_FLAG_RX_DONE) { + Serial.printf("Got %d RMT Symbols. Parsing data...\n", data_symbols); + parseRmt(data, data_symbols, channels); + } + + // printout some of the channels every 500ms + Serial.printf("%04lx %04lx %04lx %04lx\n", channels[0], channels[1], channels[2], channels[3]); } diff --git a/libraries/ESP32/examples/RMT/RMTWriteNeoPixel/RMTWriteNeoPixel.ino b/libraries/ESP32/examples/RMT/RMTWriteNeoPixel/RMTWriteNeoPixel.ino index 113a3a7e8a0..a8950069acd 100644 --- a/libraries/ESP32/examples/RMT/RMTWriteNeoPixel/RMTWriteNeoPixel.ino +++ b/libraries/ESP32/examples/RMT/RMTWriteNeoPixel/RMTWriteNeoPixel.ino @@ -9,7 +9,7 @@ #if CONFIG_IDF_TARGET_ESP32S2 #define BUILTIN_RGBLED_PIN 18 #elif CONFIG_IDF_TARGET_ESP32S3 -#define BUILTIN_RGBLED_PIN 48 +#define BUILTIN_RGBLED_PIN 48 // 48 or 38 #elif CONFIG_IDF_TARGET_ESP32C3 #define BUILTIN_RGBLED_PIN 8 #else @@ -50,7 +50,7 @@ void setup() { Serial.begin(115200); - if (!rmtInit(BUILTIN_RGBLED_PIN, RMT_TX_MODE, RMT_MEM_64, 10000000)) + if (!rmtInit(BUILTIN_RGBLED_PIN, RMT_TX_MODE, RMT_MEM_NUM_BLOCKS_1, 10000000)) { Serial.println("init sender failed\n"); } @@ -91,7 +91,7 @@ void loop() } // Send the data - rmtWrite(BUILTIN_RGBLED_PIN, led_data, NR_OF_ALL_BITS); + rmtWrite(BUILTIN_RGBLED_PIN, led_data, NR_OF_ALL_BITS, false, false); delay(100); } From 087c92d4560a2ff06f4b09f3dad2e13afbb83d5f Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Mon, 17 Apr 2023 04:24:37 -0300 Subject: [PATCH 07/14] fix rmt.h --- cores/esp32/esp32-hal-rmt.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cores/esp32/esp32-hal-rmt.h b/cores/esp32/esp32-hal-rmt.h index 81e753849b2..1149151f902 100644 --- a/cores/esp32/esp32-hal-rmt.h +++ b/cores/esp32/esp32-hal-rmt.h @@ -93,8 +93,8 @@ bool rmtSetRxThreshold(int pin, uint16_t value); Setting carrier */ -//bool rmtSetCarrier(int pin, bool carrier_en, bool carrier_level, uint32_t low, uint32_t high); bool rmtSetCarrier(int pin, bool carrier_en, bool carrier_level, uint32_t frequency_Hz, float duty_percent); + /** Setting input filter */ From c6fab7aedec4ec021baf43602dec1b7e43e9f0da Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Mon, 17 Apr 2023 20:45:14 -0300 Subject: [PATCH 08/14] adds support to APB different frequencies --- cores/esp32/esp32-hal-rmt.c | 29 +++---- .../RMT/RMT_CPUFreq_Test/RMT_CPUFreq_Test.ino | 84 +++++++++++++++++++ 2 files changed, 99 insertions(+), 14 deletions(-) create mode 100644 libraries/ESP32/examples/RMT/RMT_CPUFreq_Test/RMT_CPUFreq_Test.ino diff --git a/cores/esp32/esp32-hal-rmt.c b/cores/esp32/esp32-hal-rmt.c index af749b0f91d..2b96e32d5fd 100644 --- a/cores/esp32/esp32-hal-rmt.c +++ b/cores/esp32/esp32-hal-rmt.c @@ -125,7 +125,8 @@ static bool _rmtDetachBus(void *busptr) } // disable and deallocate RMT channal if (bus->rmt_channel_h != NULL) { - // force stopping rmt TX/RX processing + // force stopping rmt TX/RX processing and unlock Power Management (APB Freq) + // it needs sdkconfig with CONFIG_PM_ENABLE set rmt_disable(bus->rmt_channel_h); if (ESP_OK != rmt_del_channel(bus->rmt_channel_h)) { log_w("RMT Channel Deletion has failed."); @@ -312,18 +313,18 @@ bool rmtInit(int pin, rmt_ch_dir_t channel_direction, rmt_reserve_memsize_t mem_ perimanSetBusDeinit(ESP32_BUS_TYPE_RMT_RX, _rmtDetachBus); // validate the RMT ticks by the requested frequency -#if SOC_RMT_SUPPORT_APB - // Based on 80Mhz using a divider of 8 bits (calculated as 1..256) :: ESP32 and ESP32S2 - if (frequency_Hz > 80000000 || frequency_Hz < 312500) { - log_e("GPIO %d - Bad RMT frequency resolution. Must be between 312.5KHz to 80MHz.", pin); - return false; - } -#elif SOC_RMT_SUPPORT_XTAL +#if SOC_RMT_SUPPORT_XTAL // ESP32-C3 and ESP32-S3 -- RMT works even with lower CPU Freq // Based on 40Mhz using a divider of 8 bits (calculated as 1..256) :: ESP32C3 and ESP32S3 if (frequency_Hz > 40000000 || frequency_Hz < 156250) { log_e("GPIO %d - Bad RMT frequency resolution. Must be between 156.25KHz to 40MHz.", pin); return false; } +#elif SOC_RMT_SUPPORT_APB // ESP32 and ESP32-S2 -- TO DO: CPU Freq < 80MHz will affect RMT Tick + // Based on 80Mhz using a divider of 8 bits (calculated as 1..256) :: ESP32 and ESP32S2 + if (frequency_Hz > 80000000 || frequency_Hz < 312500) { + log_e("GPIO %d - Bad RMT frequency resolution. Must be between 312.5KHz to 80MHz.", pin); + return false; + } #endif // create common block mutex for protecting allocs from multiple threads @@ -362,10 +363,10 @@ bool rmtInit(int pin, rmt_ch_dir_t channel_direction, rmt_reserve_memsize_t mem_ // TX Channel rmt_tx_channel_config_t tx_cfg; tx_cfg.gpio_num = pin; -#if SOC_RMT_SUPPORT_APB - tx_cfg.clk_src = RMT_CLK_SRC_APB; -#elif SOC_RMT_SUPPORT_XTAL +#if SOC_RMT_SUPPORT_XTAL tx_cfg.clk_src = RMT_CLK_SRC_XTAL; +#elif SOC_RMT_SUPPORT_APB + tx_cfg.clk_src = RMT_CLK_SRC_APB; #endif tx_cfg.resolution_hz = frequency_Hz; tx_cfg.mem_block_symbols = SOC_RMT_MEM_WORDS_PER_CHANNEL * mem_size; @@ -386,10 +387,10 @@ bool rmtInit(int pin, rmt_ch_dir_t channel_direction, rmt_reserve_memsize_t mem_ // RX Channel rmt_rx_channel_config_t rx_cfg; rx_cfg.gpio_num = pin; -#if SOC_RMT_SUPPORT_APB - rx_cfg.clk_src = RMT_CLK_SRC_APB; -#elif SOC_RMT_SUPPORT_XTAL +#if SOC_RMT_SUPPORT_XTAL rx_cfg.clk_src = RMT_CLK_SRC_XTAL; +#elif SOC_RMT_SUPPORT_APB + rx_cfg.clk_src = RMT_CLK_SRC_APB; #endif rx_cfg.resolution_hz = frequency_Hz; rx_cfg.mem_block_symbols = SOC_RMT_MEM_WORDS_PER_CHANNEL * mem_size; diff --git a/libraries/ESP32/examples/RMT/RMT_CPUFreq_Test/RMT_CPUFreq_Test.ino b/libraries/ESP32/examples/RMT/RMT_CPUFreq_Test/RMT_CPUFreq_Test.ino new file mode 100644 index 00000000000..75806f56463 --- /dev/null +++ b/libraries/ESP32/examples/RMT/RMT_CPUFreq_Test/RMT_CPUFreq_Test.ino @@ -0,0 +1,84 @@ +/* + RMT_CPUFreq_test + + Demonstrates usage of RGB LED driven by RMT to verufy that RMT works on any CPU/APB Frequency. + + RGBLedWrite demonstrates control of each RMT channel: + void neopixelWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val) +*/ + +//if not set by pins_arduino.h, define your own WS281x GPIO and default brightness +//#define RGB_BRIGHTNESS 64 // Change white brightness (max 255) +//#define RGB_BUILTIN 38 // S3 GPI 48 or 38 + +// different frequencies for the CPU +// ESP32C3 max freq is 160, but setting it to 240MHz will just fail and keep the last one +const int bauds = 115200; +uint8_t cpufreqs[] = {240, 160, 80, 40, 20, 10}; +uint32_t Freq = 0; +int i = 0; + +void setup() { + Freq = getCpuFrequencyMhz(); + + Serial.begin(bauds); + delay(500); + + Freq = getCpuFrequencyMhz(); + Serial.print("CPU Freq = "); + Serial.print(Freq); + Serial.println(" MHz"); + Freq = getXtalFrequencyMhz(); + Serial.print("XTAL Freq = "); + Serial.print(Freq); + Serial.println(" MHz"); + Freq = getApbFrequency(); + Serial.print("APB Freq = "); + Serial.print(Freq); + Serial.println(" Hz"); +} + +void loop() { + + Serial.print("\nchange CPU freq to "); + Serial.println(cpufreqs[i]); + delay(250); + + setCpuFrequencyMhz(cpufreqs[i]); + + Freq = getCpuFrequencyMhz(); + + Serial.end(); + Serial.begin(bauds); + + Serial.print("Sending to serial with baud rate = "); + Serial.println(bauds); + Serial.print("CPU Freq = "); + Serial.print(Freq); + Serial.println(" MHz"); + + i = (i + 1) % sizeof(cpufreqs); + + // Changes to the CPU Freq demand RMT to reset internal parameters for the Tick + // This is fixed by reinitializing the RMT peripheral + rmtInit(RGB_BUILTIN, RMT_TX_MODE, RMT_MEM_NUM_BLOCKS_1, 10000000); // 100ms Tick + +#ifdef RGB_BUILTIN + neopixelWrite(RGB_BUILTIN, RGB_BRIGHTNESS, 0, 0); // Red + Serial.println("LED Red"); + delay(1000); + neopixelWrite(RGB_BUILTIN, 0, RGB_BRIGHTNESS, 0); // Green + Serial.println("LED Green"); + delay(1000); + neopixelWrite(RGB_BUILTIN, 0, 0, RGB_BRIGHTNESS); // Blue + Serial.println("LED Blue"); + delay(1000); + neopixelWrite(RGB_BUILTIN, RGB_BRIGHTNESS, RGB_BRIGHTNESS, RGB_BRIGHTNESS); // White + Serial.println("White"); + delay(1000); + neopixelWrite(RGB_BUILTIN, 0, 0, 0); // Off / black + Serial.println("Off"); + delay(1000); + +#endif +} \ No newline at end of file From 591357fe76eb1689af03a450678c33fbc2c9e1f3 Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Mon, 17 Apr 2023 20:58:21 -0300 Subject: [PATCH 09/14] fixes CI and not defined RGB_BUILTIN --- .../ESP32/examples/RMT/RMT_CPUFreq_Test/RMT_CPUFreq_Test.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/ESP32/examples/RMT/RMT_CPUFreq_Test/RMT_CPUFreq_Test.ino b/libraries/ESP32/examples/RMT/RMT_CPUFreq_Test/RMT_CPUFreq_Test.ino index 75806f56463..a00675c7aa2 100644 --- a/libraries/ESP32/examples/RMT/RMT_CPUFreq_Test/RMT_CPUFreq_Test.ino +++ b/libraries/ESP32/examples/RMT/RMT_CPUFreq_Test/RMT_CPUFreq_Test.ino @@ -59,11 +59,11 @@ void loop() { i = (i + 1) % sizeof(cpufreqs); +#ifdef RGB_BUILTIN // Changes to the CPU Freq demand RMT to reset internal parameters for the Tick // This is fixed by reinitializing the RMT peripheral rmtInit(RGB_BUILTIN, RMT_TX_MODE, RMT_MEM_NUM_BLOCKS_1, 10000000); // 100ms Tick -#ifdef RGB_BUILTIN neopixelWrite(RGB_BUILTIN, RGB_BRIGHTNESS, 0, 0); // Red Serial.println("LED Red"); delay(1000); From 6e5583ec5f3df20b2b634ab5a298fa7e9b764ed2 Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Tue, 9 May 2023 12:42:54 -0300 Subject: [PATCH 10/14] new RMT API and examples --- cores/esp32/esp32-hal-rgb-led.c | 26 +- cores/esp32/esp32-hal-rmt.c | 313 ++++++++++++------ cores/esp32/esp32-hal-rmt.h | 119 ++++++- .../examples/RMT/RMTCallback/RMTCallback.ino | 34 +- .../examples/RMT/RMTLoopback/RMTLoopback.ino | 41 ++- .../examples/RMT/RMTReadXJT/RMTReadXJT.ino | 38 ++- .../RMT/RMTWriteNeoPixel/RMTWriteNeoPixel.ino | 30 +- .../RMT/RMT_CPUFreq_Test/RMT_CPUFreq_Test.ino | 132 ++++---- .../RMT/RMT_LED_Blink/RMT_LED_Blink.ino | 220 ++++++++++++ 9 files changed, 720 insertions(+), 233 deletions(-) create mode 100644 libraries/ESP32/examples/RMT/RMT_LED_Blink/RMT_LED_Blink.ino diff --git a/cores/esp32/esp32-hal-rgb-led.c b/cores/esp32/esp32-hal-rgb-led.c index 1871b36c14a..f27123968c6 100644 --- a/cores/esp32/esp32-hal-rgb-led.c +++ b/cores/esp32/esp32-hal-rgb-led.c @@ -3,29 +3,23 @@ void neopixelWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val){ rmt_data_t led_data[24]; + static bool initialized = false; - bool shallAlwaysInitRMT = true; // in case it is used for any other GPIO + uint8_t _pin = pin; #ifdef RGB_BUILTIN - static bool initialized = false; if(pin == RGB_BUILTIN){ - pin = RGB_BUILTIN - SOC_GPIO_PIN_COUNT; - if(!initialized) { - initialized = true; - } else { - // RGB_BUILTIN has been already initialized before, save this time now - shallAlwaysInitRMT = false; - } + _pin = RGB_BUILTIN-SOC_GPIO_PIN_COUNT; } #endif - if (shallAlwaysInitRMT) { - // 10MHz RMT Frequency -> tick = 100ns - // force initialization to make sure it has the right RMT Frequency - if (!rmtInit(pin, RMT_TX_MODE, RMT_MEM_NUM_BLOCKS_1, 10000000)){ - log_e("RGB LED driver initialization failed!"); - return; + if(!initialized){ + if (!rmtInit(_pin, RMT_TX_MODE, RMT_MEM_NUM_BLOCKS_1, 10000000)){ + log_e("RGB LED driver initialization failed!"); + return; } + initialized = true; } + int color[] = {green_val, red_val, blue_val}; // Color coding is in order GREEN, RED, BLUE int i = 0; for(int col=0; col<3; col++ ){ @@ -46,5 +40,5 @@ void neopixelWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue i++; } } - rmtWrite(pin, led_data, 24, false, false); + rmtWrite(_pin, led_data, 24, RMT_WAIT_FOR_EVER); } diff --git a/cores/esp32/esp32-hal-rmt.c b/cores/esp32/esp32-hal-rmt.c index 2b96e32d5fd..bbcd0cf453c 100644 --- a/cores/esp32/esp32-hal-rmt.c +++ b/cores/esp32/esp32-hal-rmt.c @@ -20,6 +20,12 @@ #include "esp32-hal-rmt.h" #include "esp32-hal-periman.h" +// Arduino Task Handle indicates if the Arduino Task has been started already +extern TaskHandle_t loopTaskHandle; + +// RMT Events +#define RMT_FLAG_RX_DONE (1) +#define RMT_FLAG_TX_DONE (2) /** Internal macros @@ -37,20 +43,24 @@ /** Typedefs for internal stuctures, enums */ -struct rmt_obj_s { - // general rmt information +struct rmt_obj_s { + // general RMT information rmt_channel_handle_t rmt_channel_h; // NULL value means channel not alocated rmt_encoder_handle_t rmt_copy_encoder_h; // RMT simple copy encoder handle uint32_t signal_range_min_ns; // RX Filter data - Low Pass pulse width uint32_t signal_range_max_ns; // RX idle time that defines end of reading - EventGroupHandle_t rmt_rx_events; // reading event user handle + EventGroupHandle_t rmt_events; // read/write done event RMT callback handle + bool rmt_ch_is_looping; // RMT TX Channel is in LOOPING MODE? size_t *num_symbols_read; // number of RMT symbol read by IDF RMT RX Done +#if !CONFIG_DISABLE_HAL_LOCKS xSemaphoreHandle g_rmt_objlocks; // Channel Semaphore Lock +#endif /* CONFIG_DISABLE_HAL_LOCKS */ }; + typedef struct rmt_obj_s *rmt_bus_handle_t; /** @@ -62,16 +72,26 @@ static xSemaphoreHandle g_rmt_block_lock = NULL; Internal method (private) declarations */ +// This is called from an IDF ISR code, therefore this code is part of an ISR static bool _rmt_rx_done_callback(rmt_channel_handle_t channel, const rmt_rx_done_event_data_t *data, void *args) { BaseType_t high_task_wakeup = pdFALSE; rmt_bus_handle_t bus = (rmt_bus_handle_t) args; + // sets the returning number of RMT symbols effectively read *bus->num_symbols_read = data->num_symbols; - // set RX event group - if (bus->rmt_rx_events != NULL) { - // signal the received RMT symbols of that channel - xEventGroupSetBitsFromISR(bus->rmt_rx_events, RMT_FLAG_RX_DONE, &high_task_wakeup); - } + // set RX event group and signal the received RMT symbols of that channel + xEventGroupSetBitsFromISR(bus->rmt_events, RMT_FLAG_RX_DONE, &high_task_wakeup); + // A "need to yield" is returned in order to execute portYIELD_FROM_ISR() in the main IDF RX ISR + return high_task_wakeup == pdTRUE; +} + +// This is called from an IDF ISR code, therefore this code is part of an ISR +static bool _rmt_tx_done_callback(rmt_channel_handle_t channel, const rmt_tx_done_event_data_t *data, void *args) +{ + BaseType_t high_task_wakeup = pdFALSE; + rmt_bus_handle_t bus = (rmt_bus_handle_t) args; + // set RX event group and signal the received RMT symbols of that channel + xEventGroupSetBitsFromISR(bus->rmt_events, RMT_FLAG_TX_DONE, &high_task_wakeup); // A "need to yield" is returned in order to execute portYIELD_FROM_ISR() in the main IDF RX ISR return high_task_wakeup == pdTRUE; } @@ -115,7 +135,16 @@ static bool _rmtDetachBus(void *busptr) bool retCode = true; rmt_bus_handle_t bus = (rmt_bus_handle_t) busptr; + log_v("Detaching RMT GPIO Bus"); + + // lock it + while (xSemaphoreTake(g_rmt_block_lock, portMAX_DELAY) != pdPASS) {} + // free Event Group + if (bus->rmt_events != NULL) { + vEventGroupDelete(bus->rmt_events); + bus->rmt_events = NULL; + } // deallocate the channel encoder if (bus->rmt_copy_encoder_h != NULL) { if (ESP_OK != rmt_del_encoder(bus->rmt_copy_encoder_h)) { @@ -126,7 +155,6 @@ static bool _rmtDetachBus(void *busptr) // disable and deallocate RMT channal if (bus->rmt_channel_h != NULL) { // force stopping rmt TX/RX processing and unlock Power Management (APB Freq) - // it needs sdkconfig with CONFIG_PM_ENABLE set rmt_disable(bus->rmt_channel_h); if (ESP_OK != rmt_del_channel(bus->rmt_channel_h)) { log_w("RMT Channel Deletion has failed."); @@ -141,6 +169,9 @@ static bool _rmtDetachBus(void *busptr) #endif // free the allocated bus data structure free(bus); + + // release the mutex + xSemaphoreGive(g_rmt_block_lock); return retCode; } @@ -148,7 +179,7 @@ static bool _rmtDetachBus(void *busptr) Public method definitions */ -// RX demodulation carrier or TX modulatin carrier +// RX demodulation carrier or TX modulation carrier bool rmtSetCarrier(int pin, bool carrier_en, bool carrier_level, uint32_t frequency_Hz, float duty_percent) { rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__); @@ -216,6 +247,7 @@ bool rmtSetRxThreshold(int pin, uint16_t value) bool rmtDeinit(int pin) { + log_v("Deiniting RMT GPIO %d", pin); if (_rmtGetBus(pin, __FUNCTION__) != NULL) { // release all allocated data return perimanSetPinBus(pin, ESP32_BUS_TYPE_INIT, NULL); @@ -224,7 +256,7 @@ bool rmtDeinit(int pin) return false; } -bool rmtWrite(int pin, rmt_data_t* data, size_t num_rmt_symbols, bool blocking, bool loop) +static bool _rmtWrite(int pin, rmt_data_t* data, size_t num_rmt_symbols, bool blocking, bool loop, uint32_t timeout_ms) { rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__); if (bus == NULL) { @@ -233,33 +265,69 @@ bool rmtWrite(int pin, rmt_data_t* data, size_t num_rmt_symbols, bool blocking, if (!_rmtCheckDirection(pin, RMT_TX_MODE, __FUNCTION__)) { return false; } + bool loopCancel = false; // user wants to cancel the writing loop mode if (data == NULL || num_rmt_symbols == 0) { - log_w("GPIO %d - RMT Write Data NULL pointer or size is zero.", pin); - return false; + if (!loop) { + log_w("GPIO %d - RMT Write Data NULL pointer or size is zero.", pin); + return false; + } else { + loopCancel = true; + } } - - rmt_transmit_config_t transmit_cfg = {0}; // disable loop mode - if (loop) { - transmit_cfg.loop_count = 1; // enable infinite loop mode + log_v("GPIO: %d - Request: %d RMT Symbols - %s - Timeout: %d", pin, num_rmt_symbols, blocking ? "Blocking" : "Non-Blocking", timeout_ms); + log_v("GPIO: %d - Currently in Loop Mode: [%s] | Asked to Loop: %s, LoopCancel: %s", pin, bus->rmt_ch_is_looping ? "YES" : "NO", loop ? "YES" : "NO", loopCancel ? "YES" : "NO"); + + if ((xEventGroupGetBits(bus->rmt_events) & RMT_FLAG_TX_DONE) == 0) { + log_v("GPIO %d - RMT Write still pending to be completed.", pin); + return false; } + + rmt_transmit_config_t transmit_cfg = {0}; // loop mode disabled bool retCode = true; - RMT_MUTEX_LOCK(bus); - if (ESP_OK != rmt_transmit(bus->rmt_channel_h, bus->rmt_copy_encoder_h, - (const void *) data, num_rmt_symbols * sizeof(rmt_data_t), &transmit_cfg)) { - retCode = false; - log_w("GPIO %d - RMT Transmission failed.", pin); - } - if (blocking && ESP_OK != rmt_tx_wait_all_done(bus->rmt_channel_h, -1)) { - retCode = false; - log_w("GPIO %d - RMT Blocking TX Setup failed.", pin); + RMT_MUTEX_LOCK(bus); + // wants to start in writing or looping over a previous looping --> resets the channel + if (bus->rmt_ch_is_looping == true) { + // must force stopping a previous loop transmission first + rmt_disable(bus->rmt_channel_h); + // enable it again for looping or writing + rmt_enable(bus->rmt_channel_h); + bus->rmt_ch_is_looping = false; // not looping anymore + } + if (loopCancel) { + // just resets and releases the channel, maybe, already done above, then exits + bus->rmt_ch_is_looping = false; + } else { // new writing | looping request + // looping | Writing over a previous looping state is valid + if (loop) { + transmit_cfg.loop_count = -1; // enable infinite loop mode + } else { + // looping mode never sets this flag (IDF 5.1) in the callback + xEventGroupClearBits(bus->rmt_events, RMT_FLAG_TX_DONE); + } + // transmits just once or looping data + if (ESP_OK != rmt_transmit(bus->rmt_channel_h, bus->rmt_copy_encoder_h, + (const void *) data, num_rmt_symbols * sizeof(rmt_data_t), &transmit_cfg)) { + retCode = false; + log_w("GPIO %d - RMT Transmission failed.", pin); + } else { // transmit OK + if (loop) { + bus->rmt_ch_is_looping = true; // for ever... until a channel canceling or new writing + } else { + if (blocking) { + // wait for transmission confirmation | timeout + retCode = (xEventGroupWaitBits(bus->rmt_events, RMT_FLAG_TX_DONE, pdFALSE /* do not clear on exit */, + pdFALSE /* wait for all bits */, timeout_ms) & RMT_FLAG_TX_DONE) != 0; + } + } + } } RMT_MUTEX_UNLOCK(bus); return retCode; } -bool rmtReadAsync(int pin, rmt_data_t* data, size_t *num_rmt_symbols, void* eventFlag, bool waitForData, uint32_t timeout_ms) +static bool _rmtRead(int pin, rmt_data_t* data, size_t *num_rmt_symbols, bool waitForData, uint32_t timeout_ms) { rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__); if (bus == NULL) { @@ -272,62 +340,87 @@ bool rmtReadAsync(int pin, rmt_data_t* data, size_t *num_rmt_symbols, void* even log_w("GPIO %d - RMT Read Data and/or Size NULL pointer.", pin); return false; } - - // Start a read task + log_v("GPIO: %d - Request: %d RMT Symbols - %s - Timeout: %d", pin, *num_rmt_symbols, waitForData ? "Blocking" : "Non-Blocking", timeout_ms); + bool retCode = true; RMT_MUTEX_LOCK(bus); // request reading RMT Channel Data rmt_receive_config_t receive_config; receive_config.signal_range_min_ns = bus->signal_range_min_ns; receive_config.signal_range_max_ns = bus->signal_range_max_ns; - if (eventFlag) { - xEventGroupClearBits(eventFlag, RMT_FLAG_RX_DONE); - } - bus->rmt_rx_events = eventFlag; + xEventGroupClearBits(bus->rmt_events, RMT_FLAG_RX_DONE); bus->num_symbols_read = num_rmt_symbols; rmt_receive(bus->rmt_channel_h, data, *num_rmt_symbols * sizeof(rmt_data_t), &receive_config); - - // wait for data if requested so - if (waitForData && eventFlag) { - xEventGroupWaitBits(eventFlag, RMT_FLAG_RX_DONE, - pdTRUE /* clear on exit */, pdFALSE /* wait for all bits */, timeout_ms); + // wait for data if requested + if (waitForData) { + retCode = (xEventGroupWaitBits(bus->rmt_events, RMT_FLAG_RX_DONE, pdFALSE /* do not clear on exit */, + pdFALSE /* wait for all bits */, timeout_ms) & RMT_FLAG_RX_DONE) != 0; } - + RMT_MUTEX_UNLOCK(bus); + return retCode; +} - return true; + +bool rmtWrite(int pin, rmt_data_t *data, size_t num_rmt_symbols, uint32_t timeout_ms) { + return _rmtWrite(pin, data, num_rmt_symbols, true /*blocks*/, false /*looping*/, timeout_ms); } -bool rmtInit(int pin, rmt_ch_dir_t channel_direction, rmt_reserve_memsize_t mem_size, uint32_t frequency_Hz) -{ - // check is pin is valid and in the right direction - if ((channel_direction == RMT_TX_MODE && !GPIO_IS_VALID_OUTPUT_GPIO(pin)) || (!GPIO_IS_VALID_GPIO(pin))) { - log_e("GPIO %d is not valid or can't be used for output in TX mode.", pin); +bool rmtWriteAsync(int pin, rmt_data_t *data, size_t num_rmt_symbols) { + return _rmtWrite(pin, data, num_rmt_symbols, false /*blocks*/, false /*looping*/, 0 /*N/A*/); +} + +bool rmtWriteLooping(int pin, rmt_data_t* data, size_t num_rmt_symbols) { + return _rmtWrite(pin, data, num_rmt_symbols, false /*blocks*/, true /*looping*/, 0 /*N/A*/); +} + +bool rmtTransmitCompleted(int pin) { + rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__); + if (bus == NULL) { + return false; + } + if (!_rmtCheckDirection(pin, RMT_TX_MODE, __FUNCTION__)) { return false; } - // set Peripheral Manager deInit Callback - perimanSetBusDeinit(ESP32_BUS_TYPE_RMT_TX, _rmtDetachBus); - perimanSetBusDeinit(ESP32_BUS_TYPE_RMT_RX, _rmtDetachBus); + bool retCode = true; + RMT_MUTEX_LOCK(bus); + retCode = (xEventGroupGetBits(bus->rmt_events) & RMT_FLAG_TX_DONE) != 0; + RMT_MUTEX_UNLOCK(bus); + return retCode; +} - // validate the RMT ticks by the requested frequency -#if SOC_RMT_SUPPORT_XTAL // ESP32-C3 and ESP32-S3 -- RMT works even with lower CPU Freq - // Based on 40Mhz using a divider of 8 bits (calculated as 1..256) :: ESP32C3 and ESP32S3 - if (frequency_Hz > 40000000 || frequency_Hz < 156250) { - log_e("GPIO %d - Bad RMT frequency resolution. Must be between 156.25KHz to 40MHz.", pin); +bool rmtRead(int pin, rmt_data_t* data, size_t *num_rmt_symbols, uint32_t timeout_ms) { + return _rmtRead(pin, data, num_rmt_symbols, true /* blocking */, timeout_ms); +} + +bool rmtReadAsync(int pin, rmt_data_t* data, size_t *num_rmt_symbols) { + return _rmtRead(pin, data, num_rmt_symbols, false /* non-blocking */, 0 /* N/A */); +} + +bool rmtReceiveCompleted(int pin) { + rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__); + if (bus == NULL) { return false; } -#elif SOC_RMT_SUPPORT_APB // ESP32 and ESP32-S2 -- TO DO: CPU Freq < 80MHz will affect RMT Tick - // Based on 80Mhz using a divider of 8 bits (calculated as 1..256) :: ESP32 and ESP32S2 - if (frequency_Hz > 80000000 || frequency_Hz < 312500) { - log_e("GPIO %d - Bad RMT frequency resolution. Must be between 312.5KHz to 80MHz.", pin); + if (!_rmtCheckDirection(pin, RMT_RX_MODE, __FUNCTION__)) { return false; } -#endif - // create common block mutex for protecting allocs from multiple threads + bool retCode = true; + RMT_MUTEX_LOCK(bus); + retCode = (xEventGroupGetBits(bus->rmt_events) & RMT_FLAG_RX_DONE) != 0; + RMT_MUTEX_UNLOCK(bus); + return retCode; +} + +bool rmtInit(int pin, rmt_ch_dir_t channel_direction, rmt_reserve_memsize_t mem_size, uint32_t frequency_Hz) +{ + log_v("GPIO %d - %s - MemSize[%d] - Freq=%dHz", pin, channel_direction == RMT_RX_MODE ? "RX MODE" : "TX MODE", mem_size * RMT_SYMBOLS_PER_CHANNEL_BLOCK, frequency_Hz); + + // create common block mutex for protecting allocs from multiple threads allocating RMT channels if (!g_rmt_block_lock) { g_rmt_block_lock = xSemaphoreCreateMutex(); if (g_rmt_block_lock == NULL) { @@ -335,39 +428,60 @@ bool rmtInit(int pin, rmt_ch_dir_t channel_direction, rmt_reserve_memsize_t mem_ return false; } } - // lock it - while (xSemaphoreTake(g_rmt_block_lock, portMAX_DELAY) != pdPASS) {} - // Try to dettach any previous bus or just keep it as not attached + // set Peripheral Manager deInit Callback + perimanSetBusDeinit(ESP32_BUS_TYPE_RMT_TX, _rmtDetachBus); + perimanSetBusDeinit(ESP32_BUS_TYPE_RMT_RX, _rmtDetachBus); + + // check is pin is valid and in the right direction + if ((channel_direction == RMT_TX_MODE && !GPIO_IS_VALID_OUTPUT_GPIO(pin)) || (!GPIO_IS_VALID_GPIO(pin))) { + log_e("GPIO %d is not valid or can't be used for output in TX mode.", pin); + return false; + } + + // validate the RMT ticks by the requested frequency + // Based on 80Mhz using a divider of 8 bits (calculated as 1..256) + if (frequency_Hz > 80000000 || frequency_Hz < 312500) { + log_e("GPIO %d - Bad RMT frequency resolution. Must be between 312.5KHz to 80MHz.", pin); + return false; + } + + // Try to dettach any (Tx|Rx|Whatever) previous bus or just keep it as not attached if (perimanGetPinBusType(pin) != ESP32_BUS_TYPE_INIT && !perimanSetPinBus(pin, ESP32_BUS_TYPE_INIT, NULL)) { log_w("GPIO %d - Can't detach previous peripheral.", pin); - xSemaphoreGive(g_rmt_block_lock); return false; } - // allocate the rmt bus object + // lock it + while (xSemaphoreTake(g_rmt_block_lock, portMAX_DELAY) != pdPASS) {} + + // allocate the rmt bus object and sets all fields to NULL rmt_bus_handle_t bus = (rmt_bus_handle_t)heap_caps_calloc(1, sizeof(struct rmt_obj_s), MALLOC_CAP_DEFAULT); if (bus == NULL) { log_e("GPIO %d - Bus Memory allocation fault.", pin); - xSemaphoreGive(g_rmt_block_lock); - return false; + goto Err; } // pulses with width smaller than min_ns will be ignored (as a glitch) bus->signal_range_min_ns = 1000000000 / (frequency_Hz * 2); // 1/2 pulse width // RMT stops reading if the input stays idle for longer than max_ns bus->signal_range_max_ns = (1000000000 / frequency_Hz) * 10; // 10 pulses width + // creates the event group to control read_done and write_done + bus->rmt_events = xEventGroupCreate(); + if (bus->rmt_events == NULL) { + log_e("GPIO %d - RMT Group Event allocation fault.", pin); + goto Err; + } + + // Stating with Receive|Transmit DONE as true for allowing a new request from user + xEventGroupSetBits(bus->rmt_events, RMT_FLAG_RX_DONE | RMT_FLAG_TX_DONE); // channel particular configuration if (channel_direction == RMT_TX_MODE) { // TX Channel rmt_tx_channel_config_t tx_cfg; tx_cfg.gpio_num = pin; -#if SOC_RMT_SUPPORT_XTAL - tx_cfg.clk_src = RMT_CLK_SRC_XTAL; -#elif SOC_RMT_SUPPORT_APB tx_cfg.clk_src = RMT_CLK_SRC_APB; -#endif tx_cfg.resolution_hz = frequency_Hz; tx_cfg.mem_block_symbols = SOC_RMT_MEM_WORDS_PER_CHANNEL * mem_size; tx_cfg.trans_queue_depth = 10; // maximum allowed @@ -378,20 +492,21 @@ bool rmtInit(int pin, rmt_ch_dir_t channel_direction, rmt_reserve_memsize_t mem_ if (rmt_new_tx_channel(&tx_cfg, &bus->rmt_channel_h) != ESP_OK) { log_e("GPIO %d - RMT TX Initialization error.", pin); - // release the RMT object - _rmtDetachBus((void *)bus); - xSemaphoreGive(g_rmt_block_lock); - return false; + goto Err; + } + + // set TX Callback + rmt_tx_event_callbacks_t cbs = { .on_trans_done = _rmt_tx_done_callback }; + if (ESP_OK != rmt_tx_register_event_callbacks(bus->rmt_channel_h, &cbs, bus)) { + log_e("GPIO %d RMT - Error registering TX Callback.", pin); + goto Err; } + } else { // RX Channel rmt_rx_channel_config_t rx_cfg; rx_cfg.gpio_num = pin; -#if SOC_RMT_SUPPORT_XTAL - rx_cfg.clk_src = RMT_CLK_SRC_XTAL; -#elif SOC_RMT_SUPPORT_APB rx_cfg.clk_src = RMT_CLK_SRC_APB; -#endif rx_cfg.resolution_hz = frequency_Hz; rx_cfg.mem_block_symbols = SOC_RMT_MEM_WORDS_PER_CHANNEL * mem_size; rx_cfg.flags.invert_in = 0; @@ -400,20 +515,14 @@ bool rmtInit(int pin, rmt_ch_dir_t channel_direction, rmt_reserve_memsize_t mem_ // try to allocate the RMT Channel if (ESP_OK != rmt_new_rx_channel(&rx_cfg, &bus->rmt_channel_h)) { log_e("GPIO %d RMT - RX Initialization error.", pin); - // release the RMT object - _rmtDetachBus((void *)bus); - xSemaphoreGive(g_rmt_block_lock); - return false; + goto Err; } // set RX Callback rmt_rx_event_callbacks_t cbs = { .on_recv_done = _rmt_rx_done_callback }; if (ESP_OK != rmt_rx_register_event_callbacks(bus->rmt_channel_h, &cbs, bus)) { log_e("GPIO %d RMT - Error registering RX Callback.", pin); - // release the RMT object - _rmtDetachBus((void *)bus); - xSemaphoreGive(g_rmt_block_lock); - return false; + goto Err; } } @@ -421,10 +530,7 @@ bool rmtInit(int pin, rmt_ch_dir_t channel_direction, rmt_reserve_memsize_t mem_ rmt_copy_encoder_config_t copy_encoder_config = {}; if (rmt_new_copy_encoder(©_encoder_config, &bus->rmt_copy_encoder_h) != ESP_OK) { log_e("GPIO %d - RMT Encoder Memory Allocation error.", pin); - // release the RMT object - _rmtDetachBus((void *)bus); - xSemaphoreGive(g_rmt_block_lock); - return false; + goto Err; } // create each channel Mutex for multi thread operations @@ -432,27 +538,34 @@ bool rmtInit(int pin, rmt_ch_dir_t channel_direction, rmt_reserve_memsize_t mem_ bus->g_rmt_objlocks = xSemaphoreCreateMutex(); if (bus->g_rmt_objlocks == NULL) { log_e("GPIO %d - Failed creating RMT Channel Mutex.", pin); - // release the RMT object - _rmtDetachBus((void *)bus); - xSemaphoreGive(g_rmt_block_lock); - return false; + goto Err; } #endif - rmt_enable(bus->rmt_channel_h); // starts the channel + rmt_enable(bus->rmt_channel_h); // starts/enables the channel // Finally, allocate Peripheral Manager RMT bus and associate it to its GPIO peripheral_bus_type_t pinBusType = channel_direction == RMT_TX_MODE ? ESP32_BUS_TYPE_RMT_TX : ESP32_BUS_TYPE_RMT_RX; if (!perimanSetPinBus(pin, pinBusType, (void *) bus)) { log_e("Can't allocate the GPIO %d in the Peripheral Manager.", pin); - // release the RMT object - _rmtDetachBus((void *)bus); - xSemaphoreGive(g_rmt_block_lock); - return false; + goto Err; } - + + // this delay is necessary when CPU frequency changes, but internal RMT setup is "old/wrong" + // The use case is related to the RMT_CPUFreq_Test example. The very first RMT Write + // goes in the wrong pace (frequency). The delay allows other IDF tasks to run to fix it. + if (loopTaskHandle != NULL) { + // it can only run when Arduino task has been already started. + delay(1); + } // prevent panic when rmtInit() is executed within an C++ object constructor // release the mutex xSemaphoreGive(g_rmt_block_lock); return true; + +Err: + // release LOCK and the RMT object + xSemaphoreGive(g_rmt_block_lock); + _rmtDetachBus((void *)bus); + return false; } diff --git a/cores/esp32/esp32-hal-rmt.h b/cores/esp32/esp32-hal-rmt.h index 1149151f902..8c2606da994 100644 --- a/cores/esp32/esp32-hal-rmt.h +++ b/cores/esp32/esp32-hal-rmt.h @@ -15,9 +15,6 @@ #ifndef MAIN_ESP32_HAL_RMT_H_ #define MAIN_ESP32_HAL_RMT_H_ -// Rx Done Event -#define RMT_FLAG_RX_DONE (1) - #ifdef __cplusplus extern "C" { #endif @@ -27,13 +24,6 @@ typedef enum { RMT_TX_MODE = 1, // true } rmt_ch_dir_t; -// Reading and Writing shall use as rmt_symbols_size this unit -// ESP32 has 8 MEM BLOCKS in total shared with Reading and/or Writing -// ESP32-S2 has 4 MEM BLOCKS in total shared with Reading and/or Writing -// ESP32-S3 has 4 MEM BLOCKS for Reading and another 4 MEM BLOCKS for Writing -// ESP32-C3 has 2 MEM BLOCKS for Reading and another 2 MEM BLOCKS for Writing -#define RMT_SYMBOLS_PER_CHANNEL_BLOCK SOC_RMT_MEM_WORDS_PER_CHANNEL - typedef enum { RMT_MEM_NUM_BLOCKS_1 = 1, RMT_MEM_NUM_BLOCKS_2 = 2, @@ -61,26 +51,123 @@ typedef union { uint32_t val; } rmt_data_t; +// Reading and Writing shall use as rmt_symbols_size this unit +// ESP32 has 8 MEM BLOCKS in total shared with Reading and/or Writing +// ESP32-S2 has 4 MEM BLOCKS in total shared with Reading and/or Writing +// ESP32-S3 has 4 MEM BLOCKS for Reading and another 4 MEM BLOCKS for Writing +// ESP32-C3 has 2 MEM BLOCKS for Reading and another 2 MEM BLOCKS for Writing +#define RMT_SYMBOLS_PER_CHANNEL_BLOCK SOC_RMT_MEM_WORDS_PER_CHANNEL + +// Used to tell rmtRead() to wait for ever until reading data from the RMT channel +#define RMT_WAIT_FOR_EVER ((uint32_t)portMAX_DELAY) + +// Helper macro to calculate the number of RTM symbols in a array or type +#define RMT_SYMBOLS_OF(x) (sizeof(x) / sizeof(rmt_data_t)) /** - Initialize the object + Initialize the object New Parameters in Arduino Core 3: RMT tick is set in the rmtInit() function by the frequency of the RMT channel. Example: 100ns tick => 10MHz, thus frequency will be 10,000,000 Hz + Returns true on execution success, false otherwise */ bool rmtInit(int pin, rmt_ch_dir_t channel_direction, rmt_reserve_memsize_t memsize, uint32_t frequency_Hz); /** - Sending data in one-go mode or continual mode - Blocking and non-blocking mode + Sending data in Blocking Mode. + is a 32 bits structure as defined by rmt_data_t type. + It is possible to use the macro RMT_SYMBOLS_OF(data), if data is an array of rmt_data_t + + Blocking mode - only returns after sending all data or by timeout. + If the writing operation takes longer than in milliseconds, it will end its + execution returning false. + Timeout can be set as undefined time by passing RMT_WAIT_FOR_EVER as parameter. + When the operation is timed out, rmtTransmitCompleted() will retrun false until the transmission + is finished, only when rmtTransmitCompleted() will return true. + + Returns true when there is no error in the write operation, false otherwise, including when it + exits by timeout. */ -bool rmtWrite(int pin, rmt_data_t* data, size_t num_rmt_symbols, bool blocking, bool loop); +bool rmtWrite(int pin, rmt_data_t *data, size_t num_rmt_symbols, uint32_t timeout_ms); /** - Initiates async receive, event flag indicates data received + Sending data in Async Mode. + is a 32 bits structure as defined by rmt_data_t type. + It is possible to use the macro RMT_SYMBOLS_OF(data), if data is an array of rmt_data_t + + If more than one rmtWriteAsync() is executed in sequence, the previous is canceled and a + new transmission is set up and enqueued to be executed. This is valis for ESP32C3, S2 and S3. + Note: There is an exception with ESP32 that can't stop an async transmission already started, + resulting in a return code False that indicates that the rmtWriteAsync() call has failed. + In such case, ESP32 will have to finish the previous transmission before starting a new one. + + Non-Blocking mode - returns right after execution + Returns true on execution success, false otherwise + + will return true when all data is sent +*/ +bool rmtWriteAsync(int pin, rmt_data_t *data, size_t num_rmt_symbols); + +/** + Writing data up to the reserved memsize, looping continuously + is a 32 bits structure as defined by rmt_data_t type. + It is possible to use the macro RMT_SYMBOLS_OF(data), if data is an array of rmt_data_t + + If *data or size_byte are NULL | Zero, it will disable the writing loop and stop transmission + + Non-Blocking mode - returns right after execution + Returns true on execution success, false otherwise + + will return always false while its looping, given that + the RMT transmission never ends. +*/ +bool rmtWriteLooping(int pin, rmt_data_t* data, size_t num_rmt_symbols); + +/** + Checks if transmission is completed and the rmtChannel ready for transmiting new data + Returns true when all data has been sent, false otherwise +*/ +bool rmtTransmitCompleted(int pin); + +/** + Initiates blocking receive. Read data will be stored in a user provided buffer <*data> + It will read up to RMT Symbols and the value of this variable will + change to the effective number of symbols read. + is a 32 bits structure as defined by rmt_data_t type. + + If the reading operation takes longer than in milliseconds, it will end its + execution and the function will return false. In timeout case won't change + and rmtReceiveCompleted() can be used latter to check it there is data available. + Timeout can be set as undefined time by passing RMT_WAIT_FOR_EVER as parameter + + Returns true when there is no error in the read operation, false otherwise, including when it + exits by timeout. + Returns, by value, the number of RMT Symbols read and copied to the user buffer when + the read operation has success within the defined . If the function times out, it + will read RMT data latter asynchronously, affecting <*data> and <*num_rmt_symbols>. After tiemout, + the application can check if data is already available using +*/ +bool rmtRead(int pin, rmt_data_t* data, size_t *num_rmt_symbols, uint32_t timeout_ms); +/** + Initiates async (non-blocking) receive. It will return immediately after execution. + Read data will be stored in a user provided buffer <*data> + It will read up to RMT Symbols and the value of this variable will + change to the effective number of symbols read, whenever the read is completed. + is a 32 bits structure as defined by rmt_data_t type. + + Returns true when there is no error in the read operation, false otherwise. + Returns asynchronously, by value, the number of RMT Symbols read, and also, it will copy + the RMT received data to the user buffer when the read operation happens. + The application can check if data is already available using +*/ +bool rmtReadAsync(int pin, rmt_data_t* data, size_t *num_rmt_symbols); + +/** + Checks if a data reception is completed and the rmtChannel has new data for processing + Returns true when data has been received, false otherwise */ -bool rmtReadAsync(int pin, rmt_data_t* data, size_t *num_rmt_symbols, void* eventFlag, bool waitForData, uint32_t timeout_ms); +bool rmtReceiveCompleted(int pin); /** Setting threshold for Rx completed diff --git a/libraries/ESP32/examples/RMT/RMTCallback/RMTCallback.ino b/libraries/ESP32/examples/RMT/RMTCallback/RMTCallback.ino index ded57f8c675..4a10ce9d62f 100644 --- a/libraries/ESP32/examples/RMT/RMTCallback/RMTCallback.ino +++ b/libraries/ESP32/examples/RMT/RMTCallback/RMTCallback.ino @@ -1,18 +1,38 @@ -#include "Arduino.h" -#include "esp32-hal.h" +// Copyright 2023 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @brief This example demonstrate how to use a C++ Class to read several GPIO RMT signals + * calling a data processor when data is availble in background, using taks. + * + * The output is the last RMT data read in the GPIO, just to ilustrate how it works. + * + */ + extern "C" void receive_trampoline(uint32_t *data, size_t len, void * arg); class MyProcessor { private: - int8_t gpio = -1; uint32_t buff; // rolling buffer of most recent 32 bits. int at = 0; - EventGroupHandle_t events = NULL; size_t rx_num_symbols = RMT_MEM_NUM_BLOCKS_1 * RMT_SYMBOLS_PER_CHANNEL_BLOCK; rmt_data_t rx_symbols[RMT_MEM_NUM_BLOCKS_1 * RMT_SYMBOLS_PER_CHANNEL_BLOCK]; public: + int8_t gpio = -1; + MyProcessor(uint8_t pin, uint32_t rmtFreqHz) { if (!rmtInit(pin, RMT_RX_MODE, RMT_MEM_NUM_BLOCKS_1, rmtFreqHz)) { @@ -20,7 +40,6 @@ class MyProcessor { return; } gpio = pin; - events = xEventGroupCreate(); } void begin() { @@ -33,7 +52,7 @@ class MyProcessor { while(1) { // blocks until RMT has read data - rmtReadAsync(me->gpio, me->rx_symbols, &me->rx_num_symbols, me->events, true, portMAX_DELAY); + rmtRead(me->gpio, me->rx_symbols, &me->rx_num_symbols, RMT_WAIT_FOR_EVER); // process the data like a callback whenever there is data available process(me->rx_symbols, me->rx_num_symbols, me); } @@ -78,6 +97,7 @@ void setup() void loop() { - Serial.printf("GPIO 4: %08lx 5: %08lx 10: %08lx\n", mp1.val(), mp2.val(), mp3.val()); + // The reading values will come from the 3 tasks started by setup() + Serial.printf("GPIO %d: %08lx | %d: %08lx | %d: %08lx\n", mp1.gpio, mp1.val(), mp2.gpio, mp2.val(), mp3.gpio, mp3.val()); delay(500); } diff --git a/libraries/ESP32/examples/RMT/RMTLoopback/RMTLoopback.ino b/libraries/ESP32/examples/RMT/RMTLoopback/RMTLoopback.ino index c9ddf94db35..dd90fdd329c 100644 --- a/libraries/ESP32/examples/RMT/RMTLoopback/RMTLoopback.ino +++ b/libraries/ESP32/examples/RMT/RMTLoopback/RMTLoopback.ino @@ -1,9 +1,25 @@ -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/event_groups.h" -#include "Arduino.h" +// Copyright 2023 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at -#include "esp32-hal.h" +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @brief This example demonstrates usage of RMT for testing a circuit loopback + * using 2 GPIOs, one for sending RMT data and the other for receiving the data. + * Those 2 GPIO must be connected to each other. + * + * The output is the RMT data comparing what was sent and received + * + */ #if CONFIG_IDF_TARGET_ESP32C3 // ESP32 C3 has only 2 channels for RX and 2 for TX, thus MAX RMT_MEM is 128 @@ -57,15 +73,18 @@ void loop() } data[255].val = 0; - // Start receiving + // Start an async data read size_t rx_num_symbols = RMT_NUM_EXCHANGED_DATA; - rmtReadAsync(RMT_RX_PIN, my_data, &rx_num_symbols, events, false, 0); + rmtReadAsync(RMT_RX_PIN, my_data, &rx_num_symbols); - // Send in continous mode by calling it in a loop - rmtWrite(RMT_TX_PIN, data, RMT_NUM_EXCHANGED_DATA, false, false); + // Write blocking the data to the loopback + rmtWrite(RMT_TX_PIN, data, RMT_NUM_EXCHANGED_DATA, RMT_WAIT_FOR_EVER); - // Wait for data - xEventGroupWaitBits(events, RMT_FLAG_RX_DONE, 1, 1, portMAX_DELAY); + // Wait until data is read + while (!rmtReceiveCompleted(RMT_RX_PIN)); + + // Once data is available, the number of RMT Symbols is stored in rx_num_symbols + // and the received data is copied to my_data Serial.printf("Got %d RMT symbols\n", rx_num_symbols); // Printout the received data plus the original values diff --git a/libraries/ESP32/examples/RMT/RMTReadXJT/RMTReadXJT.ino b/libraries/ESP32/examples/RMT/RMTReadXJT/RMTReadXJT.ino index 82931461c48..c252e4613f4 100644 --- a/libraries/ESP32/examples/RMT/RMTReadXJT/RMTReadXJT.ino +++ b/libraries/ESP32/examples/RMT/RMTReadXJT/RMTReadXJT.ino @@ -1,9 +1,23 @@ -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/event_groups.h" -#include "Arduino.h" +// Copyright 2023 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at -#include "esp32-hal.h" +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @brief This example demonstrates usage of RMT for receiving XJT D12 data + * + * The output is the RMT data read and processed + * + */ // // Note: This example uses a FrSKY device communication @@ -171,7 +185,6 @@ void parseRmt(rmt_data_t* items, size_t len, uint32_t* channels) { } #define RMT_GPIO 21 -static EventGroupHandle_t events; void setup() { @@ -183,8 +196,6 @@ void setup() Serial.println("init receiver failed\n"); } Serial.println("real tick set to: 1us"); - - events = xEventGroupCreate(); } void loop() @@ -192,14 +203,17 @@ void loop() static rmt_data_t data[RMT_MEM_NUM_BLOCKS_2 * RMT_SYMBOLS_PER_CHANNEL_BLOCK]; static size_t data_symbols = RMT_MEM_NUM_BLOCKS_2 * RMT_SYMBOLS_PER_CHANNEL_BLOCK; - // Ask to start a blocking read with timeout of 500ms - rmtReadAsync(RMT_GPIO, data, &data_symbols, events, true, pdMS_TO_TICKS(500)); + // Blocking read with timeout of 500ms + // If data is read, data_symbols will have the number of RMT symbols effectively read + // to check if something was read and it didn't just timeout, use rmtReceiveCompleted() + rmtRead(RMT_GPIO, data, &data_symbols, 500); // If read something, process the data - EventBits_t read_bit = xEventGroupGetBits(events); - if (read_bit & RMT_FLAG_RX_DONE) { + if (rmtReceiveCompleted(RMT_GPIO)) { Serial.printf("Got %d RMT Symbols. Parsing data...\n", data_symbols); parseRmt(data, data_symbols, channels); + } else { + Serial.println("No RMT data read..."); } // printout some of the channels every 500ms diff --git a/libraries/ESP32/examples/RMT/RMTWriteNeoPixel/RMTWriteNeoPixel.ino b/libraries/ESP32/examples/RMT/RMTWriteNeoPixel/RMTWriteNeoPixel.ino index a8950069acd..9ba32a9eeb3 100644 --- a/libraries/ESP32/examples/RMT/RMTWriteNeoPixel/RMTWriteNeoPixel.ino +++ b/libraries/ESP32/examples/RMT/RMTWriteNeoPixel/RMTWriteNeoPixel.ino @@ -1,9 +1,23 @@ -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/event_groups.h" -#include "Arduino.h" +// Copyright 2023 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. -#include "esp32-hal.h" +/** + * @brief This example demonstrates usage of RGB LED driven by RMT + * + * The output is a visual WS2812 RGB LED color moving in a 8 x 4 LED matrix + * Parameters can be changed by the user. In a single LED circuit, it will just blink. + */ // The effect seen in ESP32C3, ESP32S2 and ESP32S3 is like a Blink of RGB LED #if CONFIG_IDF_TARGET_ESP32S2 @@ -59,7 +73,7 @@ void setup() } -int color[] = { 0x55, 0x11, 0x77 }; // RGB value +int color[] = { 0x55, 0x11, 0x77 }; // Green Red Blue values int led_index = 0; void loop() @@ -90,8 +104,8 @@ void loop() led_index = 0; } - // Send the data - rmtWrite(BUILTIN_RGBLED_PIN, led_data, NR_OF_ALL_BITS, false, false); + // Send the data and wait until it is fully sent + rmtWrite(BUILTIN_RGBLED_PIN, led_data, NR_OF_ALL_BITS, RMT_WAIT_FOR_EVER); delay(100); } diff --git a/libraries/ESP32/examples/RMT/RMT_CPUFreq_Test/RMT_CPUFreq_Test.ino b/libraries/ESP32/examples/RMT/RMT_CPUFreq_Test/RMT_CPUFreq_Test.ino index a00675c7aa2..8c5909b4a45 100644 --- a/libraries/ESP32/examples/RMT/RMT_CPUFreq_Test/RMT_CPUFreq_Test.ino +++ b/libraries/ESP32/examples/RMT/RMT_CPUFreq_Test/RMT_CPUFreq_Test.ino @@ -1,84 +1,90 @@ -/* - RMT_CPUFreq_test - - Demonstrates usage of RGB LED driven by RMT to verufy that RMT works on any CPU/APB Frequency. - - RGBLedWrite demonstrates control of each RMT channel: - void neopixelWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val) -*/ +// Copyright 2023 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @brief This example demonstrates usage of RGB LED driven by RMT to verify + * that RMT works on any CPU/APB Frequency. + * + * It uses an ESP32 Arduino builtin RGB NeoLED function based on RMT: + * void neopixelWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val) + * + * The output is a visual WS2812 RGB LED color change routine using each time a + * different CPU Frequency, just to ilustrate how it works. + * + */ + + +// Default DevKit RGB LED GPIOs: +#if CONFIG_IDF_TARGET_ESP32S2 +#define MY_LED_GPIO 18 +#elif CONFIG_IDF_TARGET_ESP32S3 +#define MY_LED_GPIO 48 // 48 or 38 +#elif CONFIG_IDF_TARGET_ESP32C3 +#define MY_LED_GPIO 8 +#else +#define MY_LED_GPIO 21 // Any, ESP32 has no RGB LED - depends on circuit setup +#endif -//if not set by pins_arduino.h, define your own WS281x GPIO and default brightness -//#define RGB_BRIGHTNESS 64 // Change white brightness (max 255) -//#define RGB_BUILTIN 38 // S3 GPI 48 or 38 +// Set the correct GPIO to any necessary by changing RGB_LED_GPIO value +#define RGB_LED_GPIO MY_LED_GPIO // Any GPIO valid in the board -// different frequencies for the CPU -// ESP32C3 max freq is 160, but setting it to 240MHz will just fail and keep the last one -const int bauds = 115200; -uint8_t cpufreqs[] = {240, 160, 80, 40, 20, 10}; -uint32_t Freq = 0; -int i = 0; +// Change the RGB Brightness to any value from 0 (off) to 255 (max) +#define BRIGHTNESS 20 // Change color brightness (max 255) void setup() { - Freq = getCpuFrequencyMhz(); - - Serial.begin(bauds); + Serial.begin(115200); delay(500); + Serial.printf("\nUsing GPIO %d attached to the RGB LED.\nInitial CPU setup:\n", RGB_LED_GPIO); - Freq = getCpuFrequencyMhz(); - Serial.print("CPU Freq = "); - Serial.print(Freq); - Serial.println(" MHz"); - Freq = getXtalFrequencyMhz(); - Serial.print("XTAL Freq = "); - Serial.print(Freq); - Serial.println(" MHz"); - Freq = getApbFrequency(); - Serial.print("APB Freq = "); - Serial.print(Freq); - Serial.println(" Hz"); + Serial.printf("CPU Freq = %lu MHz\n", getCpuFrequencyMhz()); + Serial.printf("XTAL Freq = %lu MHz\n", getXtalFrequencyMhz()); + Serial.printf("APB Freq = %lu Hz\n", getApbFrequency()); } void loop() { - - Serial.print("\nchange CPU freq to "); - Serial.println(cpufreqs[i]); - delay(250); + const uint8_t cpufreqs[] = {240, 160, 80, 40, 20, 10}; + static uint8_t i = 0; setCpuFrequencyMhz(cpufreqs[i]); - - Freq = getCpuFrequencyMhz(); - - Serial.end(); - Serial.begin(bauds); - - Serial.print("Sending to serial with baud rate = "); - Serial.println(bauds); - Serial.print("CPU Freq = "); - Serial.print(Freq); - Serial.println(" MHz"); - + // moves to the next CPU freq for the next loop i = (i + 1) % sizeof(cpufreqs); -#ifdef RGB_BUILTIN - // Changes to the CPU Freq demand RMT to reset internal parameters for the Tick - // This is fixed by reinitializing the RMT peripheral - rmtInit(RGB_BUILTIN, RMT_TX_MODE, RMT_MEM_NUM_BLOCKS_1, 10000000); // 100ms Tick + // Changing the CPU Freq demands RMT to reset internals parameters setting it correctly + // This is fixed by reinitializing the RMT peripheral as done below + // 100ns RMT Tick for driving the NeoLED as in the code of esp32-hal-rgb-led.c (github) + rmtInit(RGB_LED_GPIO, RMT_TX_MODE, RMT_MEM_NUM_BLOCKS_1, 10000000); + + // resets also UART to adapt to the new CPU Freq + Serial.updateBaudRate(115200); + Serial.printf("\n--changed CPU Frequency to %lu MHz\n", getCpuFrequencyMhz()); - neopixelWrite(RGB_BUILTIN, RGB_BRIGHTNESS, 0, 0); // Red - Serial.println("LED Red"); + neopixelWrite(RGB_LED_GPIO, BRIGHTNESS, BRIGHTNESS, BRIGHTNESS); // White + Serial.println("White"); delay(1000); - neopixelWrite(RGB_BUILTIN, 0, RGB_BRIGHTNESS, 0); // Green - Serial.println("LED Green"); + neopixelWrite(RGB_LED_GPIO, 0, 0, 0); // Off + Serial.println("Off"); delay(1000); - neopixelWrite(RGB_BUILTIN, 0, 0, RGB_BRIGHTNESS); // Blue - Serial.println("LED Blue"); + neopixelWrite(RGB_LED_GPIO, BRIGHTNESS, 0, 0); // Red + Serial.println("Red"); delay(1000); - neopixelWrite(RGB_BUILTIN, RGB_BRIGHTNESS, RGB_BRIGHTNESS, RGB_BRIGHTNESS); // White - Serial.println("White"); + neopixelWrite(RGB_LED_GPIO, 0, BRIGHTNESS, 0); // Green + Serial.println("Green"); + delay(1000); + neopixelWrite(RGB_LED_GPIO, 0, 0, BRIGHTNESS); // Blue + Serial.println("Blue"); delay(1000); - neopixelWrite(RGB_BUILTIN, 0, 0, 0); // Off / black + neopixelWrite(RGB_LED_GPIO, 0, 0, 0); // Off Serial.println("Off"); delay(1000); - -#endif } \ No newline at end of file diff --git a/libraries/ESP32/examples/RMT/RMT_LED_Blink/RMT_LED_Blink.ino b/libraries/ESP32/examples/RMT/RMT_LED_Blink/RMT_LED_Blink.ino new file mode 100644 index 00000000000..16c9107e2c0 --- /dev/null +++ b/libraries/ESP32/examples/RMT/RMT_LED_Blink/RMT_LED_Blink.ino @@ -0,0 +1,220 @@ +// Copyright 2023 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @brief This example demonstrate how to use RMT to just blink a regular LED (GPIO) + * It uses all the different RMT Writing APIs to blink the LED by hardware, not being + * necessary the regular Blink code in Arduino. + * + * The output is the Blinking LED in the GPIO and a serial output describing what is + * going on along the execution. + * + * The circuit is just a LED and a resistor of 270 ohms connected to the GPIO + * GPIO ---> resistor 270 ohms ---> + LED - ---> GND + */ + +#define BLINK_GPIO 2 + +// RMT is at 400KHz with a 2.5us tick +// This RMT data sends a 0.5Hz pulse with 1s High and 1s Low signal +rmt_data_t blink_1s_rmt_data[] = { + // 400,000 x 2.5us = 1 second ON + {25000, 1, 25000, 1,}, + {25000, 1, 25000, 1,}, + {25000, 1, 25000, 1,}, + {25000, 1, 25000, 1,}, + {25000, 1, 25000, 1,}, + {25000, 1, 25000, 1,}, + {25000, 1, 25000, 1,}, + {25000, 1, 25000, 1,}, + // 400,000 x 2.5us = 1 second OFF + {25000, 0, 25000, 0,}, + {25000, 0, 25000, 0,}, + {25000, 0, 25000, 0,}, + {25000, 0, 25000, 0,}, + {25000, 0, 25000, 0,}, + {25000, 0, 25000, 0,}, + {25000, 0, 25000, 0,}, + {25000, 0, 25000, 0,}, + // Looping mode needs a Zero ending data to mark the EOF + {0, 0, 0, 0} +}; + +// RMT is at 400KHz with a 2.5us tick +// This RMT data sends a 1Hz pulse with 500ms High and 500ms Low signal +rmt_data_t blink_500ms_rmt_data[] = { + // 200,000 x 2.5us = 0.5 second ON + {25000, 1, 25000, 1,}, + {25000, 1, 25000, 1,}, + {25000, 1, 25000, 1,}, + {25000, 1, 25000, 1,}, + // 200,000 x 2.5us = 0.5 second OFF + {25000, 0, 25000, 0,}, + {25000, 0, 25000, 0,}, + {25000, 0, 25000, 0,}, + {25000, 0, 25000, 0,}, + // Looping mode needs a Zero ending data to mark the EOF + {0, 0, 0, 0} +}; + +// RMT is at 400KHz with a 2.5us tick +// This RMT data sends a 2Hz pulse with 250ms High and 250ms Low signal +rmt_data_t blink_250ms_rmt_data[] = { + // 100,000 x 2.5us = 0.25 second ON + {25000, 1, 25000, 1,}, + {25000, 1, 25000, 1,}, + // 100,000 x 2.5us = 0.25 second OFF + {25000, 0, 25000, 0,}, + {25000, 0, 25000, 0,}, + // Looping mode needs a Zero ending data to mark the EOF + {0, 0, 0, 0} +}; + +void RMT_Mixed_Write_Blink() { + Serial.println("===> rmtWriteLooping() to Blink the LED."); + Serial.println("Blinking at 1s on + 1s off :: 3 blinks"); + if (!rmtWriteLooping(BLINK_GPIO, blink_1s_rmt_data, RMT_SYMBOLS_OF(blink_1s_rmt_data))) { + Serial.println("===> rmtWriteLooping Blink 1s Error!"); + } + delay(6000); // blinking happens here, done by hardware! + + Serial.println("===> rmtWrite() (Blocking Mode) to Blink the LED."); + Serial.println("Blinking at 500ms on + 500ms off :: 4 blinks"); + for (uint8_t i = 0; i < 4; i++) { + if (!rmtWrite(BLINK_GPIO, blink_500ms_rmt_data, RMT_SYMBOLS_OF(blink_500ms_rmt_data) - 2, RMT_WAIT_FOR_EVER)) { + Serial.println("===> rmtWrite Blink 0.5s Error!"); + } + } + + Serial.println("===> rmtWriteAsync() (Non-Blocking Mode) to Blink the LED."); + Serial.println("Blinking at 250ms on + 250ms off :: 5 blinks"); + for (uint8_t i = 0; i < 5; i++) { + if (!rmtWriteAsync(BLINK_GPIO, blink_250ms_rmt_data, RMT_SYMBOLS_OF(blink_250ms_rmt_data) - 2)) { + Serial.println("===> rmtWrite Blink 0.25s Error!"); + } + // wait (blocks) until all the data is transmited + while (!rmtTransmitCompleted(BLINK_GPIO)); + } + Serial.println("Blinking OFF for 1 seconds"); + delay(1000); +} + +void RMT_Loop_Write_Blink() { + Serial.println("Using RMT Writing loop to blink an LED."); + Serial.println("Blinking at 1s on + 1s off :: 3 blinks"); + if (!rmtWriteLooping(BLINK_GPIO, blink_1s_rmt_data, RMT_SYMBOLS_OF(blink_1s_rmt_data))) { + Serial.println("===> rmtWriteLooping Blink 1s Error!"); + } + delay(6000); + Serial.println("Blinking at 500ms on + 500ms off :: 5 blinks"); + if (!rmtWriteLooping(BLINK_GPIO, blink_500ms_rmt_data, RMT_SYMBOLS_OF(blink_500ms_rmt_data))) { + Serial.println("===> rmtWriteLooping Blink 0.5s Error!"); + } + delay(5000); + Serial.println("Blinking at 250ms on + 250ms off :: 10 blinks"); + if (!rmtWriteLooping(BLINK_GPIO, blink_250ms_rmt_data, RMT_SYMBOLS_OF(blink_250ms_rmt_data))) { + Serial.println("===> rmtWriteLooping Blink 0.25s Error!"); + } + delay(5000); + Serial.println("Blinking OFF for 2 seconds"); + if (!rmtWriteLooping(BLINK_GPIO, NULL, 0)) { + Serial.println("===> rmtWriteLooping Blink OFF Error!"); + } + delay(2000); +} + +void RMT_Single_Write_Blocking_Blink() { + Serial.println("Using RMT Writing and its Completion to blink an LED."); + Serial.println("Blinking at 1s on + 1s off :: 2 blinks"); + for (uint8_t i = 0; i < 2; i++) { + if (!rmtWrite(BLINK_GPIO, blink_1s_rmt_data, RMT_SYMBOLS_OF(blink_1s_rmt_data) - 2, RMT_WAIT_FOR_EVER)) { + Serial.println("===> rmtWrite Blink 1s Error!"); + } + } + Serial.println("Blinking at 500ms on + 500ms off :: 4 blinks"); + for (uint8_t i = 0; i < 4; i++) { + if (!rmtWrite(BLINK_GPIO, blink_500ms_rmt_data, RMT_SYMBOLS_OF(blink_500ms_rmt_data) - 2, RMT_WAIT_FOR_EVER)) { + Serial.println("===> rmtWrite Blink 0.5s Error!"); + } + } + Serial.println("Blinking at 250ms on + 250ms off :: 8 blinks"); + for (uint8_t i = 0; i < 8; i++) { + if (!rmtWrite(BLINK_GPIO, blink_250ms_rmt_data, RMT_SYMBOLS_OF(blink_250ms_rmt_data) - 2, RMT_WAIT_FOR_EVER)) { + Serial.println("===> rmtWrite Blink 0.25s Error!"); + } + } + Serial.println("Blinking OFF for 3 seconds"); + delay(3000); +} + +void RMT_Write_Aync_Non_Blocking_Blink() { + Serial.println("Using RMT Async Writing and its Completion to blink an LED."); + Serial.println("Blinking at 1s on + 1s off :: 5 blinks"); + for (uint8_t i = 0; i < 5; i++) { + if (!rmtWriteAsync(BLINK_GPIO, blink_1s_rmt_data, RMT_SYMBOLS_OF(blink_1s_rmt_data) - 2)) { + Serial.println("===> rmtWrite Blink 1s Error!"); + } + // wait (blocks) until all the data is transmited + while (!rmtTransmitCompleted(BLINK_GPIO)); + } + Serial.println("Blinking at 500ms on + 500ms off :: 5 blinks"); + for (uint8_t i = 0; i < 5; i++) { + if (!rmtWriteAsync(BLINK_GPIO, blink_500ms_rmt_data, RMT_SYMBOLS_OF(blink_500ms_rmt_data) - 2)) { + Serial.println("===> rmtWrite Blink 0.5s Error!"); + } + // wait (blocks) until all the data is transmited + while (!rmtTransmitCompleted(BLINK_GPIO)); + } + Serial.println("Blinking at 250ms on + 250ms off :: 5 blinks"); + for (uint8_t i = 0; i < 5; i++) { + if (!rmtWriteAsync(BLINK_GPIO, blink_250ms_rmt_data, RMT_SYMBOLS_OF(blink_250ms_rmt_data) - 2)) { + Serial.println("===> rmtWrite Blink 0.25s Error!"); + } + // wait (blocks) until all the data is transmited + while (!rmtTransmitCompleted(BLINK_GPIO)); + } + Serial.println("Blinking OFF for 1 seconds"); + delay(1000); +} + +void setup() { + Serial.begin(115200); + Serial.println("Starting Blink testing..."); + Serial.flush(); + + // 1 RMT Block has 64 x 2 RMT_SYMBOLS (ESP32|ESP32S2) or 48 x 2 RMT_SYMBOLS (ESP32C3|ESP32S3) + if (!rmtInit(BLINK_GPIO, RMT_TX_MODE, RMT_MEM_NUM_BLOCKS_1, 400000)) { //2.5us tick + Serial.println("===> rmtInit Error!"); + } else { + Serial.println("===> rmtInit OK! Tick = 2.5us - OK for testing"); + } + Serial.println("\n======================================"); + Serial.println( "All set. Starting RMT testing Routine."); + Serial.println( "======================================\n"); + + RMT_Mixed_Write_Blink(); + Serial.println("End of Mixed Calls testing"); + delay(1000); + + Serial.println("\n==============================="); + Serial.println( "Starting a Blinking sequence..."); + Serial.println( "===============================\n"); +} + +void loop() { + RMT_Write_Aync_Non_Blocking_Blink(); + RMT_Loop_Write_Blink(); + RMT_Single_Write_Blocking_Blink(); + Serial.println("\nStarting OVER...\n"); +} \ No newline at end of file From b09e9398f3ee71c052d3de2aee5c95f05774edcf Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Tue, 9 May 2023 21:25:57 -0300 Subject: [PATCH 11/14] fixing commentaties --- cores/esp32/esp32-hal-rmt.c | 17 ++-- cores/esp32/esp32-hal-rmt.h | 90 +++++++++++-------- .../examples/RMT/RMTCallback/RMTCallback.ino | 11 +-- .../examples/RMT/RMTLoopback/RMTLoopback.ino | 19 ++-- .../examples/RMT/RMTReadXJT/RMTReadXJT.ino | 11 +-- .../RMT/RMTWriteNeoPixel/RMTWriteNeoPixel.ino | 16 +--- .../RMT/RMT_CPUFreq_Test/RMT_CPUFreq_Test.ino | 6 +- .../RMT/RMT_LED_Blink/RMT_LED_Blink.ino | 14 +-- 8 files changed, 89 insertions(+), 95 deletions(-) diff --git a/cores/esp32/esp32-hal-rmt.c b/cores/esp32/esp32-hal-rmt.c index bbcd0cf453c..20e8a547509 100644 --- a/cores/esp32/esp32-hal-rmt.c +++ b/cores/esp32/esp32-hal-rmt.c @@ -46,15 +46,15 @@ extern TaskHandle_t loopTaskHandle; struct rmt_obj_s { // general RMT information - rmt_channel_handle_t rmt_channel_h; // NULL value means channel not alocated + rmt_channel_handle_t rmt_channel_h; // IDF RMT channel handler rmt_encoder_handle_t rmt_copy_encoder_h; // RMT simple copy encoder handle uint32_t signal_range_min_ns; // RX Filter data - Low Pass pulse width uint32_t signal_range_max_ns; // RX idle time that defines end of reading EventGroupHandle_t rmt_events; // read/write done event RMT callback handle - bool rmt_ch_is_looping; // RMT TX Channel is in LOOPING MODE? - size_t *num_symbols_read; // number of RMT symbol read by IDF RMT RX Done + bool rmt_ch_is_looping; // Is this RMT TX Channel in LOOPING MODE? + size_t *num_symbols_read; // Pointer to the number of RMT symbol read by IDF RMT RX Done #if !CONFIG_DISABLE_HAL_LOCKS xSemaphoreHandle g_rmt_objlocks; // Channel Semaphore Lock @@ -77,7 +77,7 @@ static bool _rmt_rx_done_callback(rmt_channel_handle_t channel, const rmt_rx_don { BaseType_t high_task_wakeup = pdFALSE; rmt_bus_handle_t bus = (rmt_bus_handle_t) args; - // sets the returning number of RMT symbols effectively read + // sets the returning number of RMT symbols (32 bits) effectively read *bus->num_symbols_read = data->num_symbols; // set RX event group and signal the received RMT symbols of that channel xEventGroupSetBitsFromISR(bus->rmt_events, RMT_FLAG_RX_DONE, &high_task_wakeup); @@ -152,7 +152,7 @@ static bool _rmtDetachBus(void *busptr) retCode = false; } } - // disable and deallocate RMT channal + // disable and deallocate RMT channel if (bus->rmt_channel_h != NULL) { // force stopping rmt TX/RX processing and unlock Power Management (APB Freq) rmt_disable(bus->rmt_channel_h); @@ -162,7 +162,7 @@ static bool _rmtDetachBus(void *busptr) } } #if !CONFIG_DISABLE_HAL_LOCKS - // deallocate locker + // deallocate channel semaphore if (bus->g_rmt_objlocks != NULL) { vSemaphoreDelete(bus->g_rmt_objlocks); } @@ -179,7 +179,6 @@ static bool _rmtDetachBus(void *busptr) Public method definitions */ -// RX demodulation carrier or TX modulation carrier bool rmtSetCarrier(int pin, bool carrier_en, bool carrier_level, uint32_t frequency_Hz, float duty_percent) { rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__); @@ -208,7 +207,6 @@ bool rmtSetCarrier(int pin, bool carrier_en, bool carrier_level, uint32_t freque return retCode; } -//In receive mode, channel will ignore input pulse when the pulse width is smaller than bool rmtSetFilter(int pin, bool filter_en, uint8_t filter_pulse_ns) { rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__); @@ -226,8 +224,6 @@ bool rmtSetFilter(int pin, bool filter_en, uint8_t filter_pulse_ns) return true; } -//In receive mode, when no edge is detected on the input signal for -//longer than idle_thres channel clock cycles, the receive process is finished. bool rmtSetRxThreshold(int pin, uint16_t value) { rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__); @@ -302,6 +298,7 @@ static bool _rmtWrite(int pin, rmt_data_t* data, size_t num_rmt_symbols, bool bl // looping | Writing over a previous looping state is valid if (loop) { transmit_cfg.loop_count = -1; // enable infinite loop mode + // keeps RMT_FLAG_TX_DONE set - it never changes } else { // looping mode never sets this flag (IDF 5.1) in the callback xEventGroupClearBits(bus->rmt_events, RMT_FLAG_TX_DONE); diff --git a/cores/esp32/esp32-hal-rmt.h b/cores/esp32/esp32-hal-rmt.h index 8c2606da994..f8c60a67ad8 100644 --- a/cores/esp32/esp32-hal-rmt.h +++ b/cores/esp32/esp32-hal-rmt.h @@ -69,23 +69,23 @@ typedef union { New Parameters in Arduino Core 3: RMT tick is set in the rmtInit() function by the frequency of the RMT channel. Example: 100ns tick => 10MHz, thus frequency will be 10,000,000 Hz - Returns true on execution success, false otherwise + Returns on execution success, otherwise */ bool rmtInit(int pin, rmt_ch_dir_t channel_direction, rmt_reserve_memsize_t memsize, uint32_t frequency_Hz); /** Sending data in Blocking Mode. is a 32 bits structure as defined by rmt_data_t type. - It is possible to use the macro RMT_SYMBOLS_OF(data), if data is an array of rmt_data_t + It is possible to use the macro RMT_SYMBOLS_OF(data), if data is an array of . Blocking mode - only returns after sending all data or by timeout. If the writing operation takes longer than in milliseconds, it will end its - execution returning false. - Timeout can be set as undefined time by passing RMT_WAIT_FOR_EVER as parameter. - When the operation is timed out, rmtTransmitCompleted() will retrun false until the transmission - is finished, only when rmtTransmitCompleted() will return true. + execution returning . + Timeout can be set as undefined time by passing as parameter. + When the operation is timed out, rmtTransmitCompleted() will return until the transmission + is finished, when rmtTransmitCompleted() will return . - Returns true when there is no error in the write operation, false otherwise, including when it + Returns when there is no error in the write operation, otherwise, including when it exits by timeout. */ bool rmtWrite(int pin, rmt_data_t *data, size_t num_rmt_symbols, uint32_t timeout_ms); @@ -93,18 +93,16 @@ bool rmtWrite(int pin, rmt_data_t *data, size_t num_rmt_symbols, uint32_t timeou /** Sending data in Async Mode. is a 32 bits structure as defined by rmt_data_t type. - It is possible to use the macro RMT_SYMBOLS_OF(data), if data is an array of rmt_data_t + It is possible to use the macro RMT_SYMBOLS_OF(data), if is an array of - If more than one rmtWriteAsync() is executed in sequence, the previous is canceled and a - new transmission is set up and enqueued to be executed. This is valis for ESP32C3, S2 and S3. - Note: There is an exception with ESP32 that can't stop an async transmission already started, - resulting in a return code False that indicates that the rmtWriteAsync() call has failed. - In such case, ESP32 will have to finish the previous transmission before starting a new one. + If more than one rmtWriteAsync() is executed in sequence, it will wait for the first transmission + to finish, resulting in a return that indicates that the rmtWriteAsync() call has failed. + In such case, this channel will have to finish the previous transmission before starting a new one. - Non-Blocking mode - returns right after execution - Returns true on execution success, false otherwise + Non-Blocking mode - returns right after execution. + Returns on execution success, otherwise. - will return true when all data is sent + will return when all data is sent. */ bool rmtWriteAsync(int pin, rmt_data_t *data, size_t num_rmt_symbols); @@ -116,16 +114,21 @@ bool rmtWriteAsync(int pin, rmt_data_t *data, size_t num_rmt_symbols); If *data or size_byte are NULL | Zero, it will disable the writing loop and stop transmission Non-Blocking mode - returns right after execution - Returns true on execution success, false otherwise + Returns on execution success, otherwise - will return always false while its looping, given that - the RMT transmission never ends. + will return always while it is looping. */ bool rmtWriteLooping(int pin, rmt_data_t* data, size_t num_rmt_symbols); /** - Checks if transmission is completed and the rmtChannel ready for transmiting new data - Returns true when all data has been sent, false otherwise + Checks if transmission is completed and the rmtChannel ready for transmiting new data. + To be ready for a new transmission, means that the previous transmission is completed. + Returns when all data has been sent, otherwise. + The data transmition information is reset when a new rmtWrite/Async function is called. + If rmtWrite() times out or rmtWriteAsync() is called, this function will return until + all data is sent out. + rmtTranmitCompleted() will always return when rmtWriteLooping() is called, + beacuse it has no effect in such case. */ bool rmtTransmitCompleted(int pin); @@ -136,27 +139,27 @@ bool rmtTransmitCompleted(int pin); is a 32 bits structure as defined by rmt_data_t type. If the reading operation takes longer than in milliseconds, it will end its - execution and the function will return false. In timeout case won't change - and rmtReceiveCompleted() can be used latter to check it there is data available. + execution and the function will return . In a time out scenario, won't + change and rmtReceiveCompleted() can be used latter to check if there is data available. Timeout can be set as undefined time by passing RMT_WAIT_FOR_EVER as parameter - Returns true when there is no error in the read operation, false otherwise, including when it + Returns when there is no error in the read operation, otherwise, including when it exits by timeout. - Returns, by value, the number of RMT Symbols read and copied to the user buffer when - the read operation has success within the defined . If the function times out, it - will read RMT data latter asynchronously, affecting <*data> and <*num_rmt_symbols>. After tiemout, + Returns, by value, the number of RMT Symbols read in and the user buffer + when the read operation has success within the defined . If the function times out, it + will read RMT data latter asynchronously, affecting <*data> and <*num_rmt_symbols>. After timeout, the application can check if data is already available using */ bool rmtRead(int pin, rmt_data_t* data, size_t *num_rmt_symbols, uint32_t timeout_ms); /** Initiates async (non-blocking) receive. It will return immediately after execution. - Read data will be stored in a user provided buffer <*data> + Read data will be stored in a user provided buffer <*data>. It will read up to RMT Symbols and the value of this variable will change to the effective number of symbols read, whenever the read is completed. - is a 32 bits structure as defined by rmt_data_t type. + is a 32 bits structure as defined by type. - Returns true when there is no error in the read operation, false otherwise. + Returns when there is no error in the read operation, otherwise. Returns asynchronously, by value, the number of RMT Symbols read, and also, it will copy the RMT received data to the user buffer when the read operation happens. The application can check if data is already available using @@ -164,31 +167,46 @@ bool rmtRead(int pin, rmt_data_t* data, size_t *num_rmt_symbols, uint32_t timeou bool rmtReadAsync(int pin, rmt_data_t* data, size_t *num_rmt_symbols); /** - Checks if a data reception is completed and the rmtChannel has new data for processing - Returns true when data has been received, false otherwise + Checks if a data reception is completed and the rmtChannel has new data for processing. + Returns when data has been received, otherwise. + The data reception information is reset when a new rmtRead/Async function is called. */ bool rmtReceiveCompleted(int pin); /** - Setting threshold for Rx completed + Function used to set a threshold for the time used to consider that a data reception has ended. + In receive mode, when no edge is detected on the input signal for longer than idle_thres + channel clock cycles, the receiving process is finished and the Data is made available by + the rmtRead/Async functions. Note that this time (in RMT channel frequency cycles) will also + define how many low bits are read at the end of the received data. + The function returns if it is correctly executed, otherwise. */ bool rmtSetRxThreshold(int pin, uint16_t value); /** Parameters changed in Arduino Core 3: low and high (ticks) are now expressed in Carrier Freq in Hz and duty cycle in percentage float 0.0 to 1.0 - example: 38.5KHz 33% High => 38500, 0.33 - Setting carrier + Function to set a RX demodulation carrier or TX modulation carrier + is used to enable/disable the use of demodulation/modulation for RX/TX + true means that the polarity level for the (de)modulation is positive + is the carrier frequency used + is a float deom 0 to 1 (0.5 means a square wave) of the carrier frequency + The function returns if it is correctly executed, otherwise. */ bool rmtSetCarrier(int pin, bool carrier_en, bool carrier_level, uint32_t frequency_Hz, float duty_percent); /** - Setting input filter + Function used to filter input noise in the RX channel. + In receiving mode, channel will ignore any input pulse which width is smaller than + is used to enable/disable the filter. enables it, disables. + The function returns if it is correctly executed, otherwise. */ bool rmtSetFilter(int pin, bool filter_en, uint8_t filter_level); /** - Deinitialize the driver + Deinitializes the driver and releases all allocated memory + It also disables RMT for this gpio */ bool rmtDeinit(int pin); diff --git a/libraries/ESP32/examples/RMT/RMTCallback/RMTCallback.ino b/libraries/ESP32/examples/RMT/RMTCallback/RMTCallback.ino index 4a10ce9d62f..8a9a0ac27e0 100644 --- a/libraries/ESP32/examples/RMT/RMTCallback/RMTCallback.ino +++ b/libraries/ESP32/examples/RMT/RMTCallback/RMTCallback.ino @@ -21,8 +21,6 @@ */ -extern "C" void receive_trampoline(uint32_t *data, size_t len, void * arg); - class MyProcessor { private: uint32_t buff; // rolling buffer of most recent 32 bits. @@ -55,8 +53,7 @@ class MyProcessor { rmtRead(me->gpio, me->rx_symbols, &me->rx_num_symbols, RMT_WAIT_FOR_EVER); // process the data like a callback whenever there is data available process(me->rx_symbols, me->rx_num_symbols, me); - } - + } vTaskDelete(NULL); } @@ -87,16 +84,14 @@ MyProcessor mp1 = MyProcessor(4, 1000000); MyProcessor mp2 = MyProcessor(5, 1000000); MyProcessor mp3 = MyProcessor(10, 2000000); -void setup() -{ +void setup() { Serial.begin(115200); mp1.begin(); mp2.begin(); mp3.begin(); } -void loop() -{ +void loop() { // The reading values will come from the 3 tasks started by setup() Serial.printf("GPIO %d: %08lx | %d: %08lx | %d: %08lx\n", mp1.gpio, mp1.val(), mp2.gpio, mp2.val(), mp3.gpio, mp3.val()); delay(500); diff --git a/libraries/ESP32/examples/RMT/RMTLoopback/RMTLoopback.ino b/libraries/ESP32/examples/RMT/RMTLoopback/RMTLoopback.ino index dd90fdd329c..ab11d90820c 100644 --- a/libraries/ESP32/examples/RMT/RMTLoopback/RMTLoopback.ino +++ b/libraries/ESP32/examples/RMT/RMTLoopback/RMTLoopback.ino @@ -40,17 +40,14 @@ static EventGroupHandle_t events; #define RMT_FREQ 10000000 #define RMT_NUM_EXCHANGED_DATA 30 -void setup() -{ +void setup() { Serial.begin(115200); events = xEventGroupCreate(); - if (!rmtInit(RMT_TX_PIN, RMT_TX_MODE, RMT_MEM_NUM_BLOCKS_1, RMT_FREQ)) - { - Serial.println("init sender failed\n"); + if (!rmtInit(RMT_TX_PIN, RMT_TX_MODE, RMT_MEM_NUM_BLOCKS_1, RMT_FREQ)) { + Serial.println("init sender failed\n"); } - if (!rmtInit(RMT_RX_PIN, RMT_RX_MODE, RMT_MEM_RX, RMT_FREQ)) - { + if (!rmtInit(RMT_RX_PIN, RMT_RX_MODE, RMT_MEM_RX, RMT_FREQ)) { Serial.println("init receiver failed\n"); } @@ -63,8 +60,7 @@ void setup() Serial.printf("\nPlease connect GPIO %d to GPIO %d, now.\n", RMT_TX_PIN, RMT_RX_PIN); } -void loop() -{ +void loop() { // Init data int i; for (i=0; i<255; i++) { @@ -88,12 +84,11 @@ void loop() Serial.printf("Got %d RMT symbols\n", rx_num_symbols); // Printout the received data plus the original values - for (i=0; i<60; i++) - { + for (i=0; i<60; i++) { Serial.printf("%08lx=%08lx ", my_data[i].val, data[i].val ); if (!((i+1)%4)) Serial.println(""); } Serial.println("\n"); - delay(2000); + delay(500); } diff --git a/libraries/ESP32/examples/RMT/RMTReadXJT/RMTReadXJT.ino b/libraries/ESP32/examples/RMT/RMTReadXJT/RMTReadXJT.ino index c252e4613f4..78e7e0b54cf 100644 --- a/libraries/ESP32/examples/RMT/RMTReadXJT/RMTReadXJT.ino +++ b/libraries/ESP32/examples/RMT/RMTReadXJT/RMTReadXJT.ino @@ -184,22 +184,19 @@ void parseRmt(rmt_data_t* items, size_t len, uint32_t* channels) { } } +// Change the RMT reading GPIO here: #define RMT_GPIO 21 -void setup() -{ +void setup() { Serial.begin(115200); - // Initialize the channel to capture up to 64*2 or 48*2 items - 1us tick - if (!rmtInit(RMT_GPIO, RMT_RX_MODE, RMT_MEM_NUM_BLOCKS_2, 1000000)) - { + if (!rmtInit(RMT_GPIO, RMT_RX_MODE, RMT_MEM_NUM_BLOCKS_2, 1000000)) { Serial.println("init receiver failed\n"); } Serial.println("real tick set to: 1us"); } -void loop() -{ +void loop() { static rmt_data_t data[RMT_MEM_NUM_BLOCKS_2 * RMT_SYMBOLS_PER_CHANNEL_BLOCK]; static size_t data_symbols = RMT_MEM_NUM_BLOCKS_2 * RMT_SYMBOLS_PER_CHANNEL_BLOCK; diff --git a/libraries/ESP32/examples/RMT/RMTWriteNeoPixel/RMTWriteNeoPixel.ino b/libraries/ESP32/examples/RMT/RMTWriteNeoPixel/RMTWriteNeoPixel.ino index 9ba32a9eeb3..9a381def79b 100644 --- a/libraries/ESP32/examples/RMT/RMTWriteNeoPixel/RMTWriteNeoPixel.ino +++ b/libraries/ESP32/examples/RMT/RMTWriteNeoPixel/RMTWriteNeoPixel.ino @@ -60,24 +60,18 @@ rmt_data_t led_data[NR_OF_ALL_BITS]; -void setup() -{ +void setup() { Serial.begin(115200); - - if (!rmtInit(BUILTIN_RGBLED_PIN, RMT_TX_MODE, RMT_MEM_NUM_BLOCKS_1, 10000000)) - { + if (!rmtInit(BUILTIN_RGBLED_PIN, RMT_TX_MODE, RMT_MEM_NUM_BLOCKS_1, 10000000)) { Serial.println("init sender failed\n"); } - Serial.println("real tick set to: 100ns"); - } int color[] = { 0x55, 0x11, 0x77 }; // Green Red Blue values int led_index = 0; -void loop() -{ +void loop() { // Init data with only one led ON int led, col, bit; int i=0; @@ -103,9 +97,7 @@ void loop() if ((++led_index)>=NR_OF_LEDS) { led_index = 0; } - - // Send the data and wait until it is fully sent + // Send the data and wait until it is done rmtWrite(BUILTIN_RGBLED_PIN, led_data, NR_OF_ALL_BITS, RMT_WAIT_FOR_EVER); - delay(100); } diff --git a/libraries/ESP32/examples/RMT/RMT_CPUFreq_Test/RMT_CPUFreq_Test.ino b/libraries/ESP32/examples/RMT/RMT_CPUFreq_Test/RMT_CPUFreq_Test.ino index 8c5909b4a45..8150dcc7069 100644 --- a/libraries/ESP32/examples/RMT/RMT_CPUFreq_Test/RMT_CPUFreq_Test.ino +++ b/libraries/ESP32/examples/RMT/RMT_CPUFreq_Test/RMT_CPUFreq_Test.ino @@ -20,8 +20,8 @@ * void neopixelWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val) * * The output is a visual WS2812 RGB LED color change routine using each time a - * different CPU Frequency, just to ilustrate how it works. - * + * different CPU Frequency, just to ilustrate how it works. Serial output indicates + * information about the CPU Frequency while controlling the RGB LED using RMT. */ @@ -87,4 +87,4 @@ void loop() { neopixelWrite(RGB_LED_GPIO, 0, 0, 0); // Off Serial.println("Off"); delay(1000); -} \ No newline at end of file +} diff --git a/libraries/ESP32/examples/RMT/RMT_LED_Blink/RMT_LED_Blink.ino b/libraries/ESP32/examples/RMT/RMT_LED_Blink/RMT_LED_Blink.ino index 16c9107e2c0..3232fb71459 100644 --- a/libraries/ESP32/examples/RMT/RMT_LED_Blink/RMT_LED_Blink.ino +++ b/libraries/ESP32/examples/RMT/RMT_LED_Blink/RMT_LED_Blink.ino @@ -18,7 +18,7 @@ * necessary the regular Blink code in Arduino. * * The output is the Blinking LED in the GPIO and a serial output describing what is - * going on along the execution. + * going on, along the execution. * * The circuit is just a LED and a resistor of 270 ohms connected to the GPIO * GPIO ---> resistor 270 ohms ---> + LED - ---> GND @@ -103,7 +103,7 @@ void RMT_Mixed_Write_Blink() { if (!rmtWriteAsync(BLINK_GPIO, blink_250ms_rmt_data, RMT_SYMBOLS_OF(blink_250ms_rmt_data) - 2)) { Serial.println("===> rmtWrite Blink 0.25s Error!"); } - // wait (blocks) until all the data is transmited + // wait (blocks) until all the data is sent out while (!rmtTransmitCompleted(BLINK_GPIO)); } Serial.println("Blinking OFF for 1 seconds"); @@ -165,7 +165,7 @@ void RMT_Write_Aync_Non_Blocking_Blink() { if (!rmtWriteAsync(BLINK_GPIO, blink_1s_rmt_data, RMT_SYMBOLS_OF(blink_1s_rmt_data) - 2)) { Serial.println("===> rmtWrite Blink 1s Error!"); } - // wait (blocks) until all the data is transmited + // wait (blocks) until all the data is sent out while (!rmtTransmitCompleted(BLINK_GPIO)); } Serial.println("Blinking at 500ms on + 500ms off :: 5 blinks"); @@ -173,7 +173,7 @@ void RMT_Write_Aync_Non_Blocking_Blink() { if (!rmtWriteAsync(BLINK_GPIO, blink_500ms_rmt_data, RMT_SYMBOLS_OF(blink_500ms_rmt_data) - 2)) { Serial.println("===> rmtWrite Blink 0.5s Error!"); } - // wait (blocks) until all the data is transmited + // wait (blocks) until all the data is sent out while (!rmtTransmitCompleted(BLINK_GPIO)); } Serial.println("Blinking at 250ms on + 250ms off :: 5 blinks"); @@ -181,7 +181,7 @@ void RMT_Write_Aync_Non_Blocking_Blink() { if (!rmtWriteAsync(BLINK_GPIO, blink_250ms_rmt_data, RMT_SYMBOLS_OF(blink_250ms_rmt_data) - 2)) { Serial.println("===> rmtWrite Blink 0.25s Error!"); } - // wait (blocks) until all the data is transmited + // wait (blocks) until all the data is sent out while (!rmtTransmitCompleted(BLINK_GPIO)); } Serial.println("Blinking OFF for 1 seconds"); @@ -193,7 +193,7 @@ void setup() { Serial.println("Starting Blink testing..."); Serial.flush(); - // 1 RMT Block has 64 x 2 RMT_SYMBOLS (ESP32|ESP32S2) or 48 x 2 RMT_SYMBOLS (ESP32C3|ESP32S3) + // 1 RMT Block has 64 RMT_SYMBOLS (ESP32|ESP32S2) or 48 RMT_SYMBOLS (ESP32C3|ESP32S3) if (!rmtInit(BLINK_GPIO, RMT_TX_MODE, RMT_MEM_NUM_BLOCKS_1, 400000)) { //2.5us tick Serial.println("===> rmtInit Error!"); } else { @@ -217,4 +217,4 @@ void loop() { RMT_Loop_Write_Blink(); RMT_Single_Write_Blocking_Blink(); Serial.println("\nStarting OVER...\n"); -} \ No newline at end of file +} From cb0444f5775d6950ce47bc7f023a47d7220b4225 Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Tue, 9 May 2023 21:33:37 -0300 Subject: [PATCH 12/14] Update esp32-hal-rgb-led.c --- cores/esp32/esp32-hal-rgb-led.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cores/esp32/esp32-hal-rgb-led.c b/cores/esp32/esp32-hal-rgb-led.c index f27123968c6..c74c812f3bb 100644 --- a/cores/esp32/esp32-hal-rgb-led.c +++ b/cores/esp32/esp32-hal-rgb-led.c @@ -8,7 +8,7 @@ void neopixelWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue uint8_t _pin = pin; #ifdef RGB_BUILTIN if(pin == RGB_BUILTIN){ - _pin = RGB_BUILTIN-SOC_GPIO_PIN_COUNT; + _pin = RGB_BUILTIN - SOC_GPIO_PIN_COUNT; } #endif @@ -40,5 +40,5 @@ void neopixelWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue i++; } } - rmtWrite(_pin, led_data, 24, RMT_WAIT_FOR_EVER); + rmtWrite(_pin, led_data, RMT_SYMBOLS_OF(led_data), RMT_WAIT_FOR_EVER); } From 78a8ebca7b9c424aac512f01529fab6b1b172bd5 Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Thu, 11 May 2023 10:29:36 -0300 Subject: [PATCH 13/14] changes Filter API --- cores/esp32/esp32-hal-rmt.c | 6 +++--- cores/esp32/esp32-hal-rmt.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cores/esp32/esp32-hal-rmt.c b/cores/esp32/esp32-hal-rmt.c index 20e8a547509..75ce1f41ac1 100644 --- a/cores/esp32/esp32-hal-rmt.c +++ b/cores/esp32/esp32-hal-rmt.c @@ -207,7 +207,7 @@ bool rmtSetCarrier(int pin, bool carrier_en, bool carrier_level, uint32_t freque return retCode; } -bool rmtSetFilter(int pin, bool filter_en, uint8_t filter_pulse_ns) +bool rmtSetFilter(int pin, uint8_t filter_pulse_ns) { rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__); if (bus == NULL) { @@ -219,7 +219,7 @@ bool rmtSetFilter(int pin, bool filter_en, uint8_t filter_pulse_ns) } RMT_MUTEX_LOCK(bus); - bus->signal_range_min_ns = filter_en ? filter_pulse_ns : 0; // set as zero to disable it + bus->signal_range_min_ns = filter_pulse_ns; // set zero to disable it RMT_MUTEX_UNLOCK(bus); return true; } @@ -470,7 +470,7 @@ bool rmtInit(int pin, rmt_ch_dir_t channel_direction, rmt_reserve_memsize_t mem_ goto Err; } - // Stating with Receive|Transmit DONE as true for allowing a new request from user + // Starting with Receive|Transmit DONE bits set, for allowing a new request from user xEventGroupSetBits(bus->rmt_events, RMT_FLAG_RX_DONE | RMT_FLAG_TX_DONE); // channel particular configuration diff --git a/cores/esp32/esp32-hal-rmt.h b/cores/esp32/esp32-hal-rmt.h index f8c60a67ad8..a968d471377 100644 --- a/cores/esp32/esp32-hal-rmt.h +++ b/cores/esp32/esp32-hal-rmt.h @@ -199,10 +199,10 @@ bool rmtSetCarrier(int pin, bool carrier_en, bool carrier_level, uint32_t freque /** Function used to filter input noise in the RX channel. In receiving mode, channel will ignore any input pulse which width is smaller than - is used to enable/disable the filter. enables it, disables. + If is Zero, it will to disable the filter. The function returns if it is correctly executed, otherwise. */ -bool rmtSetFilter(int pin, bool filter_en, uint8_t filter_level); +bool rmtSetFilter(int pin, uint8_t filter_level); /** Deinitializes the driver and releases all allocated memory From 85df97ef979710764bef8d10248072e8cda03564 Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Thu, 11 May 2023 10:34:31 -0300 Subject: [PATCH 14/14] Fixes example with Filter API --- libraries/ESP32/examples/RMT/RMTLoopback/RMTLoopback.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/ESP32/examples/RMT/RMTLoopback/RMTLoopback.ino b/libraries/ESP32/examples/RMT/RMTLoopback/RMTLoopback.ino index ab11d90820c..b7469fc837a 100644 --- a/libraries/ESP32/examples/RMT/RMTLoopback/RMTLoopback.ino +++ b/libraries/ESP32/examples/RMT/RMTLoopback/RMTLoopback.ino @@ -54,7 +54,7 @@ void setup() { // End of transmission shall be detected when line is idle for 2us rmtSetRxThreshold(RMT_RX_PIN, 2000); // Disable Glitch filter - rmtSetFilter(RMT_RX_PIN, false, 0); + rmtSetFilter(RMT_RX_PIN, 0); Serial.println("real tick set to: 100ns"); Serial.printf("\nPlease connect GPIO %d to GPIO %d, now.\n", RMT_TX_PIN, RMT_RX_PIN);