diff --git a/CMakeLists.txt b/CMakeLists.txt index 12ed1ef1c1c57e2b1188be6c1eef85c7116e0f23..783584b87d575836ac56654bca5754652210660c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,7 @@ set(CORE_SRCS cores/esp32/esp32-hal-timer.c cores/esp32/esp32-hal-touch.c cores/esp32/esp32-hal-uart.c + cores/esp32/esp32-hal-rmt.c cores/esp32/Esp.cpp cores/esp32/FunctionalInterrupt.cpp cores/esp32/HardwareSerial.cpp diff --git a/cores/esp32/esp32-hal-rmt.c b/cores/esp32/esp32-hal-rmt.c new file mode 100644 index 0000000000000000000000000000000000000000..d46caf833b10c093a7a130626f9c741233508e32 --- /dev/null +++ b/cores/esp32/esp32-hal-rmt.c @@ -0,0 +1,821 @@ +// Copyright 2018 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 "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" +#include "freertos/semphr.h" + +#include "esp32-hal.h" +#include "esp8266-compat.h" +#include "soc/gpio_reg.h" +#include "soc/gpio_reg.h" + +#include "esp32-hal-rmt.h" +#include "driver/periph_ctrl.h" + +#include "soc/rmt_struct.h" +#include "esp_intr_alloc.h" + +/** + * Internal macros + */ +#define MAX_CHANNELS 8 +#define MAX_DATA_PER_CHANNEL 64 +#define MAX_DATA_PER_ITTERATION 62 +#define _ABS(a) (a>0?a:-a) +#define _LIMIT(a,b) (a>b?b:a) +#define __INT_TX_END (1) +#define __INT_RX_END (2) +#define __INT_ERROR (4) +#define __INT_THR_EVNT (1<<24) + +#define _INT_TX_END(channel) (__INT_TX_END<<(channel*3)) +#define _INT_RX_END(channel) (__INT_RX_END<<(channel*3)) +#define _INT_ERROR(channel) (__INT_ERROR<<(channel*3)) +#define _INT_THR_EVNT(channel) ((__INT_THR_EVNT)<<(channel)) + +#if CONFIG_DISABLE_HAL_LOCKS +# define UART_MUTEX_LOCK(channel) +# define UART_MUTEX_UNLOCK(channel) +#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]) +#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 */ + +/** + * Typedefs for internal stuctures, enums + */ +typedef enum { + E_NO_INTR = 0, + E_TX_INTR = 1, + E_TXTHR_INTR = 2, + E_RX_INTR = 4, +} intr_mode_t; + +typedef enum { + E_INACTIVE = 0, + E_FIRST_HALF = 1, + E_LAST_DATA = 2, + E_END_TRANS = 4, + E_SET_CONTI = 8, +} transaction_state_t; + +struct rmt_obj_s +{ + bool allocated; + EventGroupHandle_t events; + int pin; + int channel; + bool tx_not_rx; + int buffers; + int data_size; + uint32_t* data_ptr; + intr_mode_t intr_mode; + transaction_state_t tx_state; + rmt_rx_data_cb_t cb; + bool data_alloc; +}; + +/** + * Internal variables for channel descriptors + */ +static xSemaphoreHandle g_rmt_objlocks[MAX_CHANNELS] = { + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL +}; + +static rmt_obj_t g_rmt_objects[MAX_CHANNELS] = { + { false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false}, + { false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false}, + { false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false}, + { false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false}, + { false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false}, + { false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false}, + { false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false}, + { false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false}, +}; + +/** + * Internal variables for driver data + */ +static intr_handle_t intr_handle; + +static bool periph_enabled = false; + +static xSemaphoreHandle g_rmt_block_lock = NULL; + +/** + * Internal method (private) declarations + */ +static void _initPin(int pin, int channel, bool tx_not_rx); + +static bool _rmtSendOnce(rmt_obj_t* rmt, rmt_data_t* data, size_t size); + +static void IRAM_ATTR _rmt_isr(void* arg); + +static rmt_obj_t* _rmtAllocate(int pin, int from, int size); + +static void _initPin(int pin, int channel, bool tx_not_rx); + +static int IRAM_ATTR _rmt_get_mem_len(uint8_t channel); + +static void IRAM_ATTR _rmt_tx_mem_first(uint8_t ch); + +static void IRAM_ATTR _rmt_tx_mem_second(uint8_t ch); + + +/** + * Public method definitions + */ +bool rmtSetCarrier(rmt_obj_t* rmt, bool carrier_en, bool carrier_level, uint32_t low, uint32_t high) +{ + if (!rmt || low > 0xFFFF || high > 0xFFFF) { + return false; + } + size_t channel = rmt->channel; + + RMT_MUTEX_LOCK(channel); + + RMT.carrier_duty_ch[channel].low = low; + RMT.carrier_duty_ch[channel].low = high; + RMT.conf_ch[channel].conf0.carrier_en = carrier_en; + RMT.conf_ch[channel].conf0.carrier_out_lv = carrier_level; + + RMT_MUTEX_UNLOCK(channel); + + return true; + +} + +bool rmtSetFilter(rmt_obj_t* rmt, bool filter_en, uint32_t filter_level) +{ + if (!rmt || filter_level > 0xFF) { + return false; + } + size_t channel = rmt->channel; + + RMT_MUTEX_LOCK(channel); + + RMT.conf_ch[channel].conf1.rx_filter_thres = filter_level; + RMT.conf_ch[channel].conf1.rx_filter_en = filter_en; + + RMT_MUTEX_UNLOCK(channel); + + return true; + +} + +bool rmtSetRxThreshold(rmt_obj_t* rmt, uint32_t value) +{ + if (!rmt || value > 0xFFFF) { + return false; + } + size_t channel = rmt->channel; + + RMT_MUTEX_LOCK(channel); + RMT.conf_ch[channel].conf0.idle_thres = value; + RMT_MUTEX_UNLOCK(channel); + + return true; +} + + +bool rmtDeinit(rmt_obj_t *rmt) +{ + if (!rmt) { + return false; + } + + // sanity check + if (rmt != &(g_rmt_objects[rmt->channel])) { + return false; + } + + size_t from = rmt->channel; + size_t to = rmt->buffers + rmt->channel; + size_t i; + +#if !CONFIG_DISABLE_HAL_LOCKS + if(g_rmt_objlocks[from] != NULL) { + vSemaphoreDelete(g_rmt_objlocks[from]); + } +#endif + + if (g_rmt_objects[from].data_alloc) { + free(g_rmt_objects[from].data_ptr); + } + + for (i = from; i < to; i++) { + g_rmt_objects[i].allocated = false; + } + + g_rmt_objects[from].channel = 0; + g_rmt_objects[from].buffers = 0; + + return true; +} + +bool rmtWrite(rmt_obj_t* rmt, rmt_data_t* data, size_t size) +{ + if (!rmt) { + return false; + } + + int channel = rmt->channel; + int allocated_size = MAX_DATA_PER_CHANNEL * rmt->buffers; + + if (size > allocated_size) { + + int half_tx_nr = MAX_DATA_PER_ITTERATION/2; + RMT_MUTEX_LOCK(channel); + // setup interrupt handler if not yet installed for half and full tx + if (!intr_handle) { + esp_intr_alloc(ETS_RMT_INTR_SOURCE, (int)ESP_INTR_FLAG_IRAM, _rmt_isr, NULL, &intr_handle); + } + + rmt->data_size = size - MAX_DATA_PER_ITTERATION; + rmt->data_ptr = ((uint32_t*)data) + MAX_DATA_PER_ITTERATION; + rmt->intr_mode = E_TX_INTR | E_TXTHR_INTR; + rmt->tx_state = E_SET_CONTI | E_FIRST_HALF; + + // init the tx limit for interruption + RMT.tx_lim_ch[channel].limit = half_tx_nr+2; + // reset memory pointer + RMT.conf_ch[channel].conf1.apb_mem_rst = 1; + RMT.conf_ch[channel].conf1.apb_mem_rst = 0; + RMT.conf_ch[channel].conf1.mem_rd_rst = 1; + RMT.conf_ch[channel].conf1.mem_rd_rst = 0; + RMT.conf_ch[channel].conf1.mem_wr_rst = 1; + RMT.conf_ch[channel].conf1.mem_wr_rst = 0; + + // set the tx end mark + RMTMEM.chan[channel].data32[MAX_DATA_PER_ITTERATION].val = 0; + + // clear and enable both Tx completed and half tx event + RMT.int_clr.val = _INT_TX_END(channel); + RMT.int_clr.val = _INT_THR_EVNT(channel); + RMT.int_clr.val = _INT_ERROR(channel); + + RMT.int_ena.val |= _INT_TX_END(channel); + RMT.int_ena.val |= _INT_THR_EVNT(channel); + RMT.int_ena.val |= _INT_ERROR(channel); + + RMT_MUTEX_UNLOCK(channel); + + // start the transation + return _rmtSendOnce(rmt, data, MAX_DATA_PER_ITTERATION); + } else { + // use one-go mode if data fits one buffer + return _rmtSendOnce(rmt, data, size); + } +} + +bool rmtReadData(rmt_obj_t* rmt, uint32_t* data, size_t size) +{ + if (!rmt) { + return false; + } + int channel = rmt->channel; + + if (g_rmt_objects[channel].buffers < size/MAX_DATA_PER_CHANNEL) { + return false; + } + + size_t i; + volatile uint32_t* rmt_mem_ptr = &(RMTMEM.chan[channel].data32[0].val); + for (i=0; ichannel; + + RMT.int_clr.val = _INT_ERROR(channel); + RMT.int_ena.val |= _INT_ERROR(channel); + + RMT.conf_ch[channel].conf1.mem_owner = 1; + RMT.conf_ch[channel].conf1.mem_wr_rst = 1; + RMT.conf_ch[channel].conf1.rx_en = 1; + + return true; +} + +bool rmtReceiveCompleted(rmt_obj_t* rmt) +{ + if (!rmt) { + return false; + } + int channel = rmt->channel; + + if (RMT.int_raw.val&_INT_RX_END(channel)) { + // RX end flag + RMT.int_clr.val = _INT_RX_END(channel); + return true; + } else { + return false; + } +} + +bool rmtRead(rmt_obj_t* rmt, rmt_rx_data_cb_t cb) +{ + if (!rmt && !cb) { + return false; + } + int channel = rmt->channel; + + RMT_MUTEX_LOCK(channel); + rmt->intr_mode = E_RX_INTR; + rmt->tx_state = E_FIRST_HALF; + rmt->cb = cb; + // allocate internally two buffers which would alternate + if (!rmt->data_alloc) { + rmt->data_ptr = (uint32_t*)malloc(2*MAX_DATA_PER_CHANNEL*(rmt->buffers)*sizeof(uint32_t)); + rmt->data_size = MAX_DATA_PER_CHANNEL*rmt->buffers; + rmt->data_alloc = true; + } + + RMT.conf_ch[channel].conf1.mem_owner = 1; + + RMT.int_clr.val = _INT_RX_END(channel); + RMT.int_clr.val = _INT_ERROR(channel); + + RMT.int_ena.val |= _INT_RX_END(channel); + RMT.int_ena.val |= _INT_ERROR(channel); + + RMT.conf_ch[channel].conf1.mem_wr_rst = 1; + + RMT.conf_ch[channel].conf1.rx_en = 1; + RMT_MUTEX_UNLOCK(channel); + + return true; +} + +bool rmtReadAsync(rmt_obj_t* rmt, rmt_data_t* data, size_t size, void* eventFlag, bool waitForData, uint32_t timeout) +{ + if (!rmt) { + return false; + } + int channel = rmt->channel; + + if (g_rmt_objects[channel].buffers < size/MAX_DATA_PER_CHANNEL) { + return false; + } + + if (eventFlag) { + xEventGroupClearBits(eventFlag, RMT_FLAGS_ALL); + rmt->events = eventFlag; + } + + if (data && size>0) { + rmt->data_ptr = (uint32_t*)data; + rmt->data_size = size; + } + + RMT_MUTEX_LOCK(channel); + rmt->intr_mode = E_RX_INTR; + + RMT.conf_ch[channel].conf1.mem_owner = 1; + + RMT.int_clr.val = _INT_RX_END(channel); + RMT.int_clr.val = _INT_ERROR(channel); + + RMT.int_ena.val |= _INT_RX_END(channel); + RMT.int_ena.val |= _INT_ERROR(channel); + + RMT.conf_ch[channel].conf1.mem_wr_rst = 1; + + RMT.conf_ch[channel].conf1.rx_en = 1; + RMT_MUTEX_UNLOCK(channel); + + // wait for data if requested so + if (waitForData && eventFlag) { + uint32_t flags = xEventGroupWaitBits(eventFlag, RMT_FLAGS_ALL, + pdTRUE /* clear on exit */, pdFALSE /* wait for all bits */, timeout); + if (flags & RMT_FLAG_ERROR) { + return false; + } + } + + return true; +} + +float rmtSetTick(rmt_obj_t* rmt, float tick) +{ + if (!rmt) { + return false; + } + /* + divider field span from 1 (smallest), 2, 3, ... , 0xFF, 0x00 (highest) + * rmt tick from 1/80M -> 12.5ns (1x) div_cnt = 0x01 + 3.2 us (256x) div_cnt = 0x00 + * rmt tick for 1 MHz -> 1us (1x) div_cnt = 0x01 + 256us (256x) div_cnt = 0x00 + */ + int apb_div = _LIMIT(tick/12.5, 256); + int ref_div = _LIMIT(tick/1000, 256); + + float apb_tick = 12.5 * apb_div; + float ref_tick = 1000.0 * ref_div; + + size_t channel = rmt->channel; + + if (_ABS(apb_tick - tick) < _ABS(ref_tick - tick)) { + RMT.conf_ch[channel].conf0.div_cnt = apb_div & 0xFF; + RMT.conf_ch[channel].conf1.ref_always_on = 1; + return apb_tick; + } else { + RMT.conf_ch[channel].conf0.div_cnt = ref_div & 0xFF; + RMT.conf_ch[channel].conf1.ref_always_on = 0; + return ref_tick; + } +} + +rmt_obj_t* rmtInit(int pin, bool tx_not_rx, rmt_reserve_memsize_t memsize) +{ + int buffers = memsize; + rmt_obj_t* rmt; + size_t i; + size_t j; + + // 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) {} + + for (i=0; i= MAX_CHANNELS || j != buffers) { + xSemaphoreGive(g_rmt_block_lock); + return NULL; + } + rmt = _rmtAllocate(pin, i, buffers); + + xSemaphoreGive(g_rmt_block_lock); + + size_t channel = i; + +#if !CONFIG_DISABLE_HAL_LOCKS + if(g_rmt_objlocks[channel] == NULL) { + g_rmt_objlocks[channel] = xSemaphoreCreateMutex(); + if(g_rmt_objlocks[channel] == NULL) { + return NULL; + } + } +#endif + + RMT_MUTEX_LOCK(channel); + + rmt->pin = pin; + rmt->tx_not_rx = tx_not_rx; + rmt->buffers =buffers; + rmt->channel = channel; + _initPin(pin, channel, tx_not_rx); + + // Initialize the registers in default mode: + // - no carrier, filter + // - timebase tick of 1us + // - idle threshold set to 0x8000 (max pulse width + 1) + RMT.conf_ch[channel].conf0.div_cnt = 1; + RMT.conf_ch[channel].conf0.mem_size = buffers; + RMT.conf_ch[channel].conf0.carrier_en = 0; + RMT.conf_ch[channel].conf0.carrier_out_lv = 0; + RMT.conf_ch[channel].conf0.mem_pd = 0; + + RMT.conf_ch[channel].conf0.idle_thres = 0x80; + RMT.conf_ch[channel].conf1.rx_en = 0; + RMT.conf_ch[channel].conf1.tx_conti_mode = 0; + RMT.conf_ch[channel].conf1.ref_cnt_rst = 0; + RMT.conf_ch[channel].conf1.rx_filter_en = 0; + RMT.conf_ch[channel].conf1.rx_filter_thres = 0; + RMT.conf_ch[channel].conf1.idle_out_lv = 0; // signal level for idle + RMT.conf_ch[channel].conf1.idle_out_en = 1; // enable idle + RMT.conf_ch[channel].conf1.ref_always_on = 0; // base clock + RMT.apb_conf.fifo_mask = 1; + + if (tx_not_rx) { + // RMT.conf_ch[channel].conf1.rx_en = 0; + RMT.conf_ch[channel].conf1.mem_owner = 0; + RMT.conf_ch[channel].conf1.mem_rd_rst = 1; + } else { + // RMT.conf_ch[channel].conf1.rx_en = 1; + RMT.conf_ch[channel].conf1.mem_owner = 1; + RMT.conf_ch[channel].conf1.mem_wr_rst = 1; + } + + // install interrupt if at least one channel is active + if (!intr_handle) { + esp_intr_alloc(ETS_RMT_INTR_SOURCE, (int)ESP_INTR_FLAG_IRAM, _rmt_isr, NULL, &intr_handle); + } + RMT_MUTEX_UNLOCK(channel); + + return rmt; +} + +/** + * Private methods definitions + */ +bool _rmtSendOnce(rmt_obj_t* rmt, rmt_data_t* data, size_t size) +{ + if (!rmt) { + return false; + } + int channel = rmt->channel; + RMT.apb_conf.fifo_mask = 1; + if (data && size>0) { + size_t i; + volatile uint32_t* rmt_mem_ptr = &(RMTMEM.chan[channel].data32[0].val); + for (i = 0; i < size; i++) { + *rmt_mem_ptr++ = data[i].val; + } + // tx end mark + RMTMEM.chan[channel].data32[size].val = 0; + } + + RMT_MUTEX_LOCK(channel); + RMT.conf_ch[channel].conf1.mem_rd_rst = 1; + RMT.conf_ch[channel].conf1.tx_start = 1; + RMT_MUTEX_UNLOCK(channel); + + return true; +} + + +static rmt_obj_t* _rmtAllocate(int pin, int from, int size) +{ + size_t i; + // setup how many buffers shall we use + g_rmt_objects[from].buffers = size; + + for (i=0; i 0) { + size_t i; + uint32_t * data = g_rmt_objects[ch].data_ptr; + // in case of callback, provide switching between memories + if (g_rmt_objects[ch].cb) { + if (g_rmt_objects[ch].tx_state & E_FIRST_HALF) { + g_rmt_objects[ch].tx_state &= ~E_FIRST_HALF; + } else { + g_rmt_objects[ch].tx_state |= E_FIRST_HALF; + data += MAX_DATA_PER_CHANNEL*(g_rmt_objects[ch].buffers); + } + } + for (i = 0; i < g_rmt_objects[ch].data_size; i++ ) { + *data++ = RMTMEM.chan[ch].data32[i].val; + } + if (g_rmt_objects[ch].cb) { + // actually received data ptr + uint32_t * data = g_rmt_objects[ch].data_ptr; + (g_rmt_objects[ch].cb)(data, _rmt_get_mem_len(ch)); + + // restart the reception + RMT.conf_ch[ch].conf1.mem_owner = 1; + RMT.conf_ch[ch].conf1.mem_wr_rst = 1; + RMT.conf_ch[ch].conf1.rx_en = 1; + RMT.int_ena.val |= _INT_RX_END(ch); + } else { + // if not callback provide, expect only one Rx + g_rmt_objects[ch].intr_mode &= ~E_RX_INTR; + } + } + } else { + // Report error and disable Rx interrupt + log_e("Unexpected Rx interrupt!\n"); // TODO: eplace messages with log_X + RMT.int_ena.val &= ~_INT_RX_END(ch); + } + + + } + + if (intr_val & _INT_ERROR(ch)) { + digitalWrite(2, 1); + // clear the flag + RMT.int_clr.val = _INT_ERROR(ch); + RMT.int_ena.val &= ~_INT_ERROR(ch); + // report error + log_e("RMT Error %d!\n", ch); + if (g_rmt_objects[ch].events) { + xEventGroupSetBits(g_rmt_objects[ch].events, RMT_FLAG_ERROR); + } + // reset memory + RMT.conf_ch[ch].conf1.mem_rd_rst = 1; + RMT.conf_ch[ch].conf1.mem_rd_rst = 0; + RMT.conf_ch[ch].conf1.mem_wr_rst = 1; + RMT.conf_ch[ch].conf1.mem_wr_rst = 0; + } + + if (intr_val & _INT_TX_END(ch)) { + + RMT.int_clr.val = _INT_TX_END(ch); + _rmt_tx_mem_second(ch); + } + + if (intr_val & _INT_THR_EVNT(ch)) { + // clear the flag + RMT.int_clr.val = _INT_THR_EVNT(ch); + + // initial setup of continuous mode + if (g_rmt_objects[ch].tx_state & E_SET_CONTI) { + RMT.conf_ch[ch].conf1.tx_conti_mode = 1; + g_rmt_objects[ch].intr_mode &= ~E_SET_CONTI; + } + _rmt_tx_mem_first(ch); + } + } +} + +static void IRAM_ATTR _rmt_tx_mem_second(uint8_t ch) +{ + DEBUG_INTERRUPT_START(4) + uint32_t* data = g_rmt_objects[ch].data_ptr; + int half_tx_nr = MAX_DATA_PER_ITTERATION/2; + int i; + + RMT.tx_lim_ch[ch].limit = half_tx_nr+2; + RMT.int_clr.val = _INT_THR_EVNT(ch); + RMT.int_ena.val |= _INT_THR_EVNT(ch); + + g_rmt_objects[ch].tx_state |= E_FIRST_HALF; + + if (data) { + int remaining_size = g_rmt_objects[ch].data_size; + // will the remaining data occupy the entire halfbuffer + if (remaining_size > half_tx_nr) { + for (i = 0; i < half_tx_nr; i++) { + RMTMEM.chan[ch].data32[half_tx_nr+i].val = data[i]; + } + g_rmt_objects[ch].data_size -= half_tx_nr; + g_rmt_objects[ch].data_ptr += half_tx_nr; + } else { + for (i = 0; i < half_tx_nr; i++) { + if (i < remaining_size) { + RMTMEM.chan[ch].data32[half_tx_nr+i].val = data[i]; + } else { + RMTMEM.chan[ch].data32[half_tx_nr+i].val = 0x000F000F; + } + } + g_rmt_objects[ch].data_ptr = NULL; + + } + } else if ((!(g_rmt_objects[ch].tx_state & E_LAST_DATA)) && + (!(g_rmt_objects[ch].tx_state & E_END_TRANS))) { + for (i = 0; i < half_tx_nr; i++) { + RMTMEM.chan[ch].data32[half_tx_nr+i].val = 0x000F000F; + } + RMTMEM.chan[ch].data32[half_tx_nr+i].val = 0; + g_rmt_objects[ch].tx_state |= E_LAST_DATA; + RMT.conf_ch[ch].conf1.tx_conti_mode = 0; + } else { + log_d("RMT Tx finished %d!\n", ch); + RMT.conf_ch[ch].conf1.tx_conti_mode = 0; + RMT.int_ena.val &= ~_INT_TX_END(ch); + RMT.int_ena.val &= ~_INT_THR_EVNT(ch); + g_rmt_objects[ch].intr_mode = E_NO_INTR; + g_rmt_objects[ch].tx_state = E_INACTIVE; + } + DEBUG_INTERRUPT_END(4); +} + +static void IRAM_ATTR _rmt_tx_mem_first(uint8_t ch) +{ + DEBUG_INTERRUPT_START(2); + uint32_t* data = g_rmt_objects[ch].data_ptr; + int half_tx_nr = MAX_DATA_PER_ITTERATION/2; + int i; + RMT.int_ena.val &= ~_INT_THR_EVNT(ch); + RMT.tx_lim_ch[ch].limit = 0; + + if (data) { + int remaining_size = g_rmt_objects[ch].data_size; + + // will the remaining data occupy the entire halfbuffer + if (remaining_size > half_tx_nr) { + RMTMEM.chan[ch].data32[0].val = data[0] - 1; + for (i = 1; i < half_tx_nr; i++) { + RMTMEM.chan[ch].data32[i].val = data[i]; + } + g_rmt_objects[ch].tx_state &= ~E_FIRST_HALF; + // turn off the treshold interrupt + RMT.int_ena.val &= ~_INT_THR_EVNT(ch); + RMT.tx_lim_ch[ch].limit = 0; + g_rmt_objects[ch].data_size -= half_tx_nr; + g_rmt_objects[ch].data_ptr += half_tx_nr; + } else { + RMTMEM.chan[ch].data32[0].val = data[0] - 1; + for (i = 1; i < half_tx_nr; i++) { + if (i < remaining_size) { + RMTMEM.chan[ch].data32[i].val = data[i]; + } else { + RMTMEM.chan[ch].data32[i].val = 0x000F000F; + } + } + + g_rmt_objects[ch].tx_state &= ~E_FIRST_HALF; + g_rmt_objects[ch].data_ptr = NULL; + } + } else { + for (i = 0; i < half_tx_nr; i++) { + RMTMEM.chan[ch].data32[i].val = 0x000F000F; + } + RMTMEM.chan[ch].data32[i].val = 0; + + g_rmt_objects[ch].tx_state &= ~E_FIRST_HALF; + RMT.tx_lim_ch[ch].limit = 0; + g_rmt_objects[ch].tx_state |= E_LAST_DATA; + RMT.conf_ch[ch].conf1.tx_conti_mode = 0; + } + DEBUG_INTERRUPT_END(2); +} + +static int IRAM_ATTR _rmt_get_mem_len(uint8_t channel) +{ + int block_num = RMT.conf_ch[channel].conf0.mem_size; + int item_block_len = block_num * 64; + volatile rmt_item32_t* data = RMTMEM.chan[channel].data32; + int idx; + for(idx = 0; idx < item_block_len; idx++) { + if(data[idx].duration0 == 0) { + return idx; + } else if(data[idx].duration1 == 0) { + return idx + 1; + } + } + return idx; +} diff --git a/cores/esp32/esp32-hal-rmt.h b/cores/esp32/esp32-hal-rmt.h new file mode 100644 index 0000000000000000000000000000000000000000..944178e31ec3d53c8e81507f95915a5c271a0f7f --- /dev/null +++ b/cores/esp32/esp32-hal-rmt.h @@ -0,0 +1,137 @@ +// Copyright 2018 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. + +#ifndef MAIN_ESP32_HAL_RMT_H_ +#define MAIN_ESP32_HAL_RMT_H_ + +#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) + +struct rmt_obj_s; + +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_reserve_memsize_t; + +typedef struct rmt_obj_s rmt_obj_t; + +typedef void (*rmt_rx_data_cb_t)(uint32_t *data, size_t len); + +typedef struct { + union { + struct { + uint32_t duration0 :15; + uint32_t level0 :1; + uint32_t duration1 :15; + uint32_t level1 :1; + }; + uint32_t val; + }; +} rmt_data_t; + +/** +* Initialize the object +* +*/ +rmt_obj_t* rmtInit(int pin, bool tx_not_rx, rmt_reserve_memsize_t memsize); + +/** +* 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); + +/** +* Sending data in one-go mode or continual mode +* (more data being send while updating buffers in interrupts) +* +*/ +bool rmtWrite(rmt_obj_t* rmt, rmt_data_t* data, size_t size); + +/** +* 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); + +/** +* Initiates async receive with automatic buffering +* and callback with data from ISR +* +*/ +bool rmtRead(rmt_obj_t* rmt, rmt_rx_data_cb_t cb); + + +/* Additional interface */ + +/** +* Start reception +* +*/ +bool rmtBeginReceive(rmt_obj_t* rmt); + +/** +* Checks if reception completes +* +*/ +bool rmtReceiveCompleted(rmt_obj_t* rmt); + +/** +* Reads the data for particular channel +* +*/ +bool rmtReadData(rmt_obj_t* rmt, uint32_t* data, size_t size); + +/** + * Setting threshold for Rx completed + */ +bool rmtSetRxThreshold(rmt_obj_t* rmt, uint32_t value); + +/** + * Setting carrier + */ +bool rmtSetCarrier(rmt_obj_t* rmt, bool carrier_en, bool carrier_level, uint32_t low, uint32_t high); + +/** + * Setting input filter + */ +bool rmtSetFilter(rmt_obj_t* rmt, bool filter_en, uint32_t filter_level); + + +// 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 + +#ifdef __cplusplus +} +#endif + +#endif /* MAIN_ESP32_HAL_RMT_H_ */ diff --git a/cores/esp32/esp32-hal.h b/cores/esp32/esp32-hal.h index 3de30950e22f814876f81a904b48dcd526f775a3..09db887ad8dc7238752486e864b42eba7fdfd16a 100644 --- a/cores/esp32/esp32-hal.h +++ b/cores/esp32/esp32-hal.h @@ -57,6 +57,7 @@ void yield(void); #include "esp32-hal-spi.h" #include "esp32-hal-i2c.h" #include "esp32-hal-ledc.h" +#include "esp32-hal-rmt.h" #include "esp32-hal-sigmadelta.h" #include "esp32-hal-timer.h" #include "esp32-hal-bt.h" diff --git a/libraries/ESP32/examples/RMT/RMTLoopback/RMTLoopbakc.ino b/libraries/ESP32/examples/RMT/RMTLoopback/RMTLoopbakc.ino new file mode 100644 index 0000000000000000000000000000000000000000..bdffff470676953ce0d2f56b235505dcb778fa25 --- /dev/null +++ b/libraries/ESP32/examples/RMT/RMTLoopback/RMTLoopbakc.ino @@ -0,0 +1,61 @@ +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "Arduino.h" + +#include "esp32-hal.h" + +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; + +void setup() +{ + Serial.begin(115200); + + if ((rmt_send = rmtInit(18, true, RMT_MEM_64)) == NULL) + { + Serial.println("init sender failed\n"); + } + if ((rmt_recv = rmtInit(21, false, RMT_MEM_192)) == NULL) + { + Serial.println("init receiver failed\n"); + } + + float realTick = rmtSetTick(rmt_send, 100); + printf("real tick set to: %fns\n", realTick); + +} + +void loop() +{ + // Init data + int i; + for (i=0; i<255; i++) { + data[i].val = 0x80010001 + ((i%13)<<16) + 13-(i%13); + } + data[255].val = 0; + + // Start receiving + rmtReadAsync(rmt_recv, my_data, 100, events, false, 0); + + // Send in continous mode + rmtWrite(rmt_send, data, 100); + + // Wait for data + xEventGroupWaitBits(events, RMT_FLAG_RX_DONE, 1, 1, portMAX_DELAY); + + // Printout the received data plus the original values + for (i=0; i<60; i++) + { + Serial.printf("%08x=%08x ", my_data[i], data[i] ); + if (!((i+1)%4)) Serial.println("\n"); + } + Serial.println("\n"); + + delay(2000); +} diff --git a/libraries/ESP32/examples/RMT/RMTReadXJT/RMTReadXJT.ino b/libraries/ESP32/examples/RMT/RMTReadXJT/RMTReadXJT.ino new file mode 100644 index 0000000000000000000000000000000000000000..e2e059fdfc902e771502983ef2494fb4eef45eaa --- /dev/null +++ b/libraries/ESP32/examples/RMT/RMTReadXJT/RMTReadXJT.ino @@ -0,0 +1,204 @@ +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "Arduino.h" + +#include "esp32-hal.h" + +// +// Note: This example uses a FrSKY device communication +// using XJT D12 protocol +// +// ; 0 bit = 6us low/10us high +// ; 1 bit = 14us low/10us high +// ; +// ; --------+ +----------+ +----------+ +// ; | | | | | +// ; | 0 | | 1 | | +// ; | | | | | +// ; | | | | | +// ; +-------+ +-----------------+ +--------- +// ; +// ; | 6us 10us | 14us 10us | +// ; |-------|----------|-----------------|----------|-------- +// ; | 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) +// ; 0x03 - Flags 2, 0x00 (reserved) +// ; 0x04-0x06, Channels 1/9 and 2/10 +// ; 0x07-0x09, Channels 3/11 and 4/12 +// ; 0x0A-0x0C, Channels 5/13 and 6/14 +// ; 0x0D-0x0F, Channels 7/15 and 8/16 +// ; 0x10 - 0x00, always zero +// ; 0x11 - CRC-16 High +// ; 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]; +} xjt_packet_t; + +#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; +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; + } + + 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; + } + //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; + } + } + } + return true; +} + +void parseRmt(rmt_data_t* items, size_t len, uint32_t* channels){ + size_t chan = 0; + bool valid = true; + rmt_data_t* it = NULL; + + 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); + + } + } +} + +extern "C" void receive_data(uint32_t *data, size_t len) +{ + parseRmt((rmt_data_t*) data, len, channels); +} + +void setup() +{ + Serial.begin(115200); + + // Initialize the channel to capture up to 192 items + if ((rmt_recv = rmtInit(21, false, RMT_MEM_192)) == NULL) + { + Serial.println("init receiver failed\n"); + } + + // Setup 1us tick + float realTick = rmtSetTick(rmt_recv, 1000); + Serial.printf("real tick set to: %fns\n", realTick); + + // Ask to start reading + rmtRead(rmt_recv, receive_data); +} + +void loop() +{ + // printout some of the channels + Serial.printf("%04x %04x %04x %04x\n", channels[0], channels[1], channels[2], channels[3]); + delay(500); +} diff --git a/libraries/ESP32/examples/RMT/RMTWriteNeoPixel/RMTWriteNeoPixel.ino b/libraries/ESP32/examples/RMT/RMTWriteNeoPixel/RMTWriteNeoPixel.ino new file mode 100644 index 0000000000000000000000000000000000000000..8a880abdd186c6bcdb77f8d3547df74f3127afe3 --- /dev/null +++ b/libraries/ESP32/examples/RMT/RMTWriteNeoPixel/RMTWriteNeoPixel.ino @@ -0,0 +1,89 @@ +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "Arduino.h" + +#include "esp32-hal.h" + +#define NR_OF_LEDS 8*4 +#define NR_OF_ALL_BITS 24*NR_OF_LEDS + +// +// Note: This example uses Neopixel LED board, 32 LEDs chained one +// after another, each RGB LED has its 24 bit value +// for color configuration (8b for each color) +// +// Bits encoded as pulses as follows: +// +// "0": +// +-------+ +-- +// | | | +// | | | +// | | | +// ---| |--------------| +// + + + +// | 0.4us | 0.85 0us | +// +// "1": +// +-------------+ +-- +// | | | +// | | | +// | | | +// | | | +// ---+ +-------+ +// | 0.8us | 0.4us | + +rmt_data_t led_data[NR_OF_ALL_BITS]; + +rmt_obj_t* rmt_send = NULL; + +void setup() +{ + Serial.begin(115200); + + if ((rmt_send = rmtInit(18, true, RMT_MEM_64)) == NULL) + { + Serial.println("init sender failed\n"); + } + + float realTick = rmtSetTick(rmt_send, 100); + Serial.printf("real tick set to: %fns\n", realTick); + +} + +int color[] = { 0x55, 0x11, 0x77 }; // RGB value +int led_index = 0; + +void loop() +{ + // Init data with only one led ON + int led, col, bit; + int i=0; + for (led=0; led=NR_OF_LEDS) { + led_index = 0; + } + + // Send the data + rmtWrite(rmt_send, led_data, NR_OF_ALL_BITS); + + delay(100); +}