Initial version of rmt driver (#1525)

* rmt driver initial version

* supporting conti mode plus interrupts

* using conitnous mode for sending more data

* working continous mode

* rmt driver cleanup after conti mode

* initial version of rmt driver

* adding a simple example

* adding channel and block locks

* modified of rmt interface for simpler/easier usage

* adding header sentinels, split interface to common and additional settings

* Fixes per code review + support for rx callback mode

* renamed internal structures and enums, fixed formatting

* cmake support for rmt

* refactored tx-conti interrupts to function to make it more readable

* added Tx and Rx examples

* added license headers

* minor updates per review

* used struct access, renamed defines, corrected diagram
This commit is contained in:
david-cermak 2018-09-17 23:19:27 +02:00 committed by Me No Dev
parent ea61563c69
commit 4e96bffe0e
7 changed files with 1314 additions and 0 deletions

View File

@ -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

821
cores/esp32/esp32-hal-rmt.c Normal file
View File

@ -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; i<size; i++) {
data[i] = *rmt_mem_ptr++;
}
return true;
}
bool rmtBeginReceive(rmt_obj_t* rmt)
{
if (!rmt) {
return false;
}
int channel = rmt->channel;
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; i++) {
for (j=0; j<buffers && i+j < MAX_CHANNELS; j++) {
// if the space is ocupied break and continue on other channel
if (g_rmt_objects[i+j].allocated) {
i += j; // continue searching from latter channel
break;
}
}
if (j == buffers) {
// found a space in channel descriptors
break;
}
}
if (i == MAX_CHANNELS || i+j >= 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<size; i++) {
// mark the block of channels as used
g_rmt_objects[i+from].allocated = true;
}
return &(g_rmt_objects[from]);
}
static void _initPin(int pin, int channel, bool tx_not_rx)
{
if (!periph_enabled) {
periph_enabled = true;
periph_module_enable( PERIPH_RMT_MODULE );
}
if (tx_not_rx) {
pinMode(pin, OUTPUT);
pinMatrixOutAttach(pin, RMT_SIG_OUT0_IDX + channel, 0, 0);
} else {
pinMode(pin, INPUT);
pinMatrixInAttach(pin, RMT_SIG_IN0_IDX + channel, 0);
}
}
static void IRAM_ATTR _rmt_isr(void* arg)
{
int intr_val = RMT.int_st.val;
size_t ch;
for (ch = 0; ch < MAX_CHANNELS; ch++) {
if (intr_val & _INT_RX_END(ch)) {
// clear the flag
RMT.int_clr.val = _INT_RX_END(ch);
RMT.int_ena.val &= ~_INT_RX_END(ch);
if ((g_rmt_objects[ch].intr_mode) & E_RX_INTR) {
if (g_rmt_objects[ch].events) {
xEventGroupSetBits(g_rmt_objects[ch].events, RMT_FLAG_RX_DONE);
}
if (g_rmt_objects[ch].data_ptr && g_rmt_objects[ch].data_size > 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;
}

137
cores/esp32/esp32-hal-rmt.h Normal file
View File

@ -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_ */

View File

@ -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"

View File

@ -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);
}

View File

@ -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; i<len; i++){
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)
{
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);
}

View File

@ -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++) {
for (col=0; col<3; col++ ) {
for (bit=0; bit<8; bit++){
if ( (color[col] & (1<<(8-bit))) && (led == led_index) ) {
led_data[i].level0 = 1;
led_data[i].duration0 = 8;
led_data[i].level1 = 0;
led_data[i].duration1 = 4;
} else {
led_data[i].level0 = 1;
led_data[i].duration0 = 4;
led_data[i].level1 = 0;
led_data[i].duration1 = 8;
}
i++;
}
}
}
// make the led travel in the pannel
if ((++led_index)>=NR_OF_LEDS) {
led_index = 0;
}
// Send the data
rmtWrite(rmt_send, led_data, NR_OF_ALL_BITS);
delay(100);
}