PERIPH_PWM3_MODULE,
PERIPH_UHCI0_MODULE,
PERIPH_UHCI1_MODULE,
+ PERIPH_RMT_MODULE,
} periph_module_t;
/**
--- /dev/null
+// Copyright 2015-2016 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 _DRIVER_RMT_CTRL_H_
+#define _DRIVER_RMT_CTRL_H_
+#include "esp_err.h"
+#include "soc/rmt_reg.h"
+#include "soc/dport_reg.h"
+#include "soc/rmt_struct.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/semphr.h"
+#include "freertos/xtensa_api.h"
+#include "freertos/ringbuf.h"
+#include "driver/gpio.h"
+#include "driver/periph_ctrl.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define RMT_MEM_BLOCK_BYTE_NUM (256)
+#define RMT_MEM_ITEM_NUM (RMT_MEM_BLOCK_BYTE_NUM/4)
+
+typedef enum {
+ RMT_CHANNEL_0=0, /*!< RMT Channel0 */
+ RMT_CHANNEL_1, /*!< RMT Channel1 */
+ RMT_CHANNEL_2, /*!< RMT Channel2 */
+ RMT_CHANNEL_3, /*!< RMT Channel3 */
+ RMT_CHANNEL_4, /*!< RMT Channel4 */
+ RMT_CHANNEL_5, /*!< RMT Channel5 */
+ RMT_CHANNEL_6, /*!< RMT Channel6 */
+ RMT_CHANNEL_7, /*!< RMT Channel7 */
+ RMT_CHANNEL_MAX
+} rmt_channel_t;
+
+typedef enum {
+ RMT_MEM_OWNER_TX = 0, /*!< RMT RX mode, RMT transmitter owns the memory block*/
+ RMT_MEM_OWNER_RX = 1, /*!< RMT RX mode, RMT receiver owns the memory block*/
+ RMT_MEM_OWNER_MAX,
+}rmt_mem_owner_t;
+
+typedef enum {
+ RMT_BASECLK_REF = 0, /*!< RMT source clock system reference tick, 1MHz by default(Not supported in this version) */
+ RMT_BASECLK_APB, /*!< RMT source clock is APB CLK, 80Mhz by default */
+ RMT_BASECLK_MAX,
+} rmt_source_clk_t;
+
+typedef enum {
+ RMT_DATA_MODE_FIFO = 0, /*<! RMT memory access in FIFO mode */
+ RMT_DATA_MODE_MEM = 1, /*<! RMT memory access in memory mode */
+ RMT_DATA_MODE_MAX,
+} rmt_data_mode_t;
+
+typedef enum {
+ RMT_MODE_TX=0, /*!< RMT TX mode */
+ RMT_MODE_RX, /*!< RMT RX mode */
+ RMT_MODE_MAX
+} rmt_mode_t;
+
+typedef enum {
+ RMT_IDLE_LEVEL_LOW=0, /*!< RMT TX idle level: low Level */
+ RMT_IDLE_LEVEL_HIGH, /*!< RMT TX idle level: high Level */
+ RMT_IDLE_LEVEL_MAX,
+} rmt_idle_level_t;
+
+typedef enum {
+ RMT_CARRIER_LEVEL_LOW=0, /*!< RMT carrier wave is modulated for low Level output */
+ RMT_CARRIER_LEVEL_HIGH, /*!< RMT carrier wave is modulated for high Level output */
+ RMT_CARRIER_LEVEL_MAX
+} rmt_carrier_level_t;
+
+typedef struct {
+ bool loop_en; /*!< RMT loop output mode*/
+ uint32_t carrier_freq_hz; /*!< RMT carrier frequency */
+ uint8_t carrier_duty_percent; /*!< RMT carrier duty (%) */
+ rmt_carrier_level_t carrier_level; /*!< RMT carrier level */
+ bool carrier_en; /*!< RMT carrier enable */
+ rmt_idle_level_t idle_level; /*!< RMT idle level */
+ bool idle_output_en; /*!< RMT idle level output enable*/
+}rmt_tx_config_t;
+
+typedef struct {
+ bool filter_en; /*!< RMT receiver filer enable*/
+ uint8_t filter_ticks_thresh; /*!< RMT filter tick number */
+ uint16_t idle_threshold; /*!< RMT RX idle threshold */
+}rmt_rx_config_t;
+
+typedef struct {
+ rmt_mode_t rmt_mode; /*!< RMT mode: transmitter or receiver */
+ rmt_channel_t channel; /*!< RMT channel */
+ uint8_t clk_div; /*!< RMT channel counter divider */
+ gpio_num_t gpio_num; /*!< RMT GPIO number */
+ uint8_t mem_block_num; /*!< RMT memory block number */
+ union{
+ rmt_tx_config_t tx_config; /*!< RMT TX parameter */
+ rmt_rx_config_t rx_config; /*!< RMT RX parameter */
+ };
+} rmt_config_t;
+
+/**
+ * @brief Set RMT clock divider, channel clock is divided from source clock.
+ *
+ * @param channel RMT channel (0-7)
+ *
+ * @param div_cnt RMT counter clock divider
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG Parameter error
+ * - ESP_OK Success
+ */
+esp_err_t rmt_set_clk_div(rmt_channel_t channel, uint8_t div_cnt);
+
+/**
+ * @brief Get RMT clock divider, channel clock is divided from source clock.
+ *
+ * @param channel RMT channel (0-7)
+ *
+ * @param div_cnt pointer to accept RMT counter divider
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG Parameter error
+ * - ESP_OK Success
+ */
+esp_err_t rmt_get_clk_div(rmt_channel_t channel, uint8_t* div_cnt);
+
+/**
+ * @brief Set RMT RX idle threshold value
+ *
+ * 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.
+ *
+ * @param channel RMT channel (0-7)
+ *
+ * @param thresh RMT RX idle threshold
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG Parameter error
+ * - ESP_OK Success
+ */
+esp_err_t rmt_set_rx_idle_thresh(rmt_channel_t channel, uint16_t thresh);
+
+/**
+ * @brief Get RMT idle threshold value.
+ *
+ * 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.
+ *
+ * @param channel RMT channel (0-7)
+ *
+ * @param thresh pointer to accept RMT RX idle threshold value
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG Parameter error
+ * - ESP_OK Success
+ */
+esp_err_t rmt_get_rx_idle_thresh(rmt_channel_t channel, uint16_t *thresh);
+
+/**
+ * @brief Set RMT memory block number for RMT channel
+ *
+ * This function is used to configure the amount of memory blocks allocated to channel n
+ * The 8 channels share a 512x32-bit RAM block which can be read and written
+ * by the processor cores over the APB bus, as well as read by the transmitters
+ * and written by the receivers.
+ * The RAM address range for channel n is start_addr_CHn to end_addr_CHn, which are defined by:
+ * Memory block start address is RMT_CHANNEL_MEM(n) (in soc/rmt_reg.h),
+ * that is, start_addr_chn = RMT base address + 0x800 + 64 ∗ 4 ∗ n, and
+ * end_addr_chn = RMT base address + 0x800 + 64 ∗ 4 ∗ n + 64 ∗ 4 ∗ RMT_MEM_SIZE_CHn mod 512 ∗ 4
+ * @note
+ * If memory block number of one channel is set to a value greater than 1, this channel will occupy the memory
+ * block of the next channel.
+ * Channel0 can use at most 8 blocks of memory, accordingly channel7 can only use one memory block.
+ *
+ * @param channel RMT channel (0-7)
+ *
+ * @param rmt_mem_num RMT RX memory block number, one block has 64 * 32 bits.
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG Parameter error
+ * - ESP_OK Success
+ */
+esp_err_t rmt_set_mem_block_num(rmt_channel_t channel, uint8_t rmt_mem_num);
+
+/**
+ * @brief Get RMT memory block number
+ *
+ * @param channel RMT channel (0-7)
+ *
+ * @param rmt_mem_num Pointer to accept RMT RX memory block number
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG Parameter error
+ * - ESP_OK Success
+ */
+esp_err_t rmt_get_mem_block_num(rmt_channel_t channel, uint8_t* rmt_mem_num);
+
+/**
+ * @brief Configure RMT carrier for TX signal.
+ *
+ * Set different values for carrier_high and carrier_low to set different frequency of carrier.
+ * The unit of carrier_high/low is the source clock tick, not the divided channel counter clock.
+ *
+ * @param channel RMT channel (0-7)
+ *
+ * @param carrier_en Whether to enable output carrier.
+ *
+ * @param high_level High level duration of carrier
+ *
+ * @param low_level Low level duration of carrier.
+ *
+ * @param carrier_level Configure the way carrier wave is modulated for channel0-7.
+ *
+ * 1'b1:transmit on low output level
+ *
+ * 1'b0:transmit on high output level
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG Parameter error
+ * - ESP_OK Success
+ */
+esp_err_t rmt_set_tx_carrier(rmt_channel_t channel, bool carrier_en, uint16_t high_level, uint16_t low_level, rmt_carrier_level_t carrier_level);
+
+/**
+ * @brief Set RMT memory in low power mode.
+ *
+ * Reduce power consumed by memory. 1:memory is in low power state.
+ *
+ * @param channel RMT channel (0-7)
+ *
+ * @param pd_en RMT memory low power enable.
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG Parameter error
+ * - ESP_OK Success
+ */
+esp_err_t rmt_set_mem_pd(rmt_channel_t channel, bool pd_en);
+
+/**
+ * @brief Get RMT memory low power mode.
+ *
+ * @param channel RMT channel (0-7)
+ *
+ * @param pd_en Pointer to accept RMT memory low power mode.
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG Parameter error
+ * - ESP_OK Success
+ */
+esp_err_t rmt_get_mem_pd(rmt_channel_t channel, bool* pd_en);
+
+/**
+ * @brief Set RMT start sending data from memory.
+ *
+ * @param channel RMT channel (0-7)
+ *
+ * @param tx_idx_rst Set true to reset memory index for TX.
+ * Otherwise, transmitter will continue sending from the last index in memory.
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG Parameter error
+ * - ESP_OK Success
+ */
+esp_err_t rmt_tx_start(rmt_channel_t channel, bool tx_idx_rst);
+
+/**
+ * @brief Set RMT stop sending.
+ *
+ * @param channel RMT channel (0-7)
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG Parameter error
+ * - ESP_OK Success
+ */
+esp_err_t rmt_tx_stop(rmt_channel_t channel);
+
+/**
+ * @brief Set RMT start receiving data.
+ *
+ * @param channel RMT channel (0-7)
+ *
+ * @param rx_idx_rst Set true to reset memory index for receiver.
+ * Otherwise, receiver will continue receiving data to the last index in memory.
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG Parameter error
+ * - ESP_OK Success
+ */
+esp_err_t rmt_rx_start(rmt_channel_t channel, bool rx_idx_rst);
+
+/**
+ * @brief Set RMT stop receiving data.
+ *
+ * @param channel RMT channel (0-7)
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG Parameter error
+ * - ESP_OK Success
+ */
+esp_err_t rmt_rx_stop(rmt_channel_t channel);
+
+/**
+ * @brief Reset RMT TX/RX memory index.
+ *
+ * @param channel RMT channel (0-7)
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG Parameter error
+ * - ESP_OK Success
+ */
+esp_err_t rmt_memory_rw_rst(rmt_channel_t channel);
+
+/**
+ * @brief Set RMT memory owner.
+ *
+ * @param channel RMT channel (0-7)
+ *
+ * @param owner To set when the transmitter or receiver can process the memory of channel.
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG Parameter error
+ * - ESP_OK Success
+ */
+esp_err_t rmt_set_memory_owner(rmt_channel_t channel, rmt_mem_owner_t owner);
+
+/**
+ * @brief Get RMT memory owner.
+ *
+ * @param channel RMT channel (0-7)
+ *
+ * @param owner Pointer to get memory owner.
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG Parameter error
+ * - ESP_OK Success
+ */
+esp_err_t rmt_get_memory_owner(rmt_channel_t channel, rmt_mem_owner_t* owner);
+
+/**
+ * @brief Set RMT tx loop mode.
+ *
+ * @param channel RMT channel (0-7)
+ *
+ * @param loop_en To enable RMT transmitter loop sending mode.
+ *
+ * If set true, transmitter will continue sending from the first data
+ * to the last data in channel0-7 again and again.
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG Parameter error
+ * - ESP_OK Success
+ */
+esp_err_t rmt_set_tx_loop_mode(rmt_channel_t channel, bool loop_en);
+
+/**
+ * @brief Get RMT tx loop mode.
+ *
+ * @param channel RMT channel (0-7)
+ *
+ * @param loop_en Pointer to accept RMT transmitter loop sending mode.
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG Parameter error
+ * - ESP_OK Success
+ */
+esp_err_t rmt_get_tx_loop_mode(rmt_channel_t channel, bool* loop_en);
+
+/**
+ * @brief Set RMT RX filter.
+ *
+ * In receive mode, channel0-7 will ignore input pulse when the pulse width is smaller than threshold.
+ * Counted in source clock, not divided counter clock.
+ *
+ * @param channel RMT channel (0-7)
+ *
+ * @param rx_filter_en To enable RMT receiver filter.
+ *
+ * @param thresh Threshold of pulse width for receiver.
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG Parameter error
+ * - ESP_OK Success
+ */
+esp_err_t rmt_set_rx_filter(rmt_channel_t channel, bool rx_filter_en, uint8_t thresh);
+
+/**
+ * @brief Set RMT source clock
+ *
+ * RMT module has two source clock:
+ * 1. APB clock which is 80Mhz
+ * 2. REF tick clock, which would be 1Mhz( not supported in this version).
+ *
+ * @param channel RMT channel (0-7)
+ *
+ * @param base_clk To choose source clock for RMT module.
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG Parameter error
+ * - ESP_OK Success
+ */
+esp_err_t rmt_set_source_clk(rmt_channel_t channel, rmt_source_clk_t base_clk);
+
+/**
+ * @brief Get RMT source clock
+ *
+ * RMT module has two source clock:
+ * 1. APB clock which is 80Mhz
+ * 2. REF tick clock, which would be 1Mhz( not supported in this version).
+ *
+ * @param channel RMT channel (0-7)
+ *
+ * @param src_clk Pointer to accept source clock for RMT module.
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG Parameter error
+ * - ESP_OK Success
+ */
+esp_err_t rmt_get_source_clk(rmt_channel_t channel, rmt_source_clk_t* src_clk);
+
+/**
+ * @brief Set RMT idle output level for transmitter
+ *
+ * @param channel RMT channel (0-7)
+ *
+ * @param idle_out_en To enable idle level output.
+ *
+ * @param level To set the output signal's level for channel0-7 in idle state.
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG Parameter error
+ * - ESP_OK Success
+ */
+esp_err_t rmt_set_idle_level(rmt_channel_t channel, bool idle_out_en, rmt_idle_level_t level);
+
+/**
+ * @brief Get RMT status
+ *
+ * @param channel RMT channel (0-7)
+ *
+ * @param status Pointer to accept channel status.
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG Parameter error
+ * - ESP_OK Success
+ */
+esp_err_t rmt_get_status(rmt_channel_t channel, uint32_t* status);
+
+/**
+ * @brief Set mask value to RMT interrupt enable register.
+ *
+ * @param mask Bit mask to set to the register
+ *
+ */
+void rmt_set_intr_enable_mask(uint32_t mask);
+
+/**
+ * @brief Clear mask value to RMT interrupt enable register.
+ *
+ * @param mask Bit mask to clear the register
+ *
+ */
+void rmt_clr_intr_enable_mask(uint32_t mask);
+
+/**
+ * @brief Set RMT RX interrupt enable
+ *
+ * @param channel RMT channel (0 - 7)
+ *
+ * @param en enable or disable RX interrupt.
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG Parameter error
+ * - ESP_OK Success
+ */
+esp_err_t rmt_set_rx_intr_en(rmt_channel_t channel, bool en);
+
+/**
+ * @brief Set RMT RX error interrupt enable
+ *
+ * @param channel RMT channel (0 - 7)
+ *
+ * @param en enable or disable RX err interrupt.
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG Parameter error
+ * - ESP_OK Success
+ */
+esp_err_t rmt_set_err_intr_en(rmt_channel_t channel, bool en);
+
+/**
+ * @brief Set RMT TX interrupt enable
+ *
+ * @param channel RMT channel (0 - 7)
+ *
+ * @param en enable or disable TX interrupt.
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG Parameter error
+ * - ESP_OK Success
+ */
+esp_err_t rmt_set_tx_intr_en(rmt_channel_t channel, bool en);
+
+/**
+ * @brief Set RMT TX event interrupt enable
+ *
+ * @param channel RMT channel (0 - 7)
+ *
+ * @param en enable or disable TX event interrupt.
+ *
+ * @param evt_thresh RMT event interrupt threshold value
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG Parameter error
+ * - ESP_OK Success
+ */
+esp_err_t rmt_set_evt_intr_en(rmt_channel_t channel, bool en, uint16_t evt_thresh);
+
+/**
+ * @brief Set RMT pins
+ *
+ * @param channel RMT channel (0 - 7)
+ *
+ * @param mode TX or RX mode for RMT
+ *
+ * @param gpio_num GPIO number to transmit or receive the signal.
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG Parameter error
+ * - ESP_OK Success
+ */
+esp_err_t rmt_set_pin(rmt_channel_t channel, rmt_mode_t mode, gpio_num_t gpio_num);
+
+/**
+ * @brief Configure RMT parameters
+ *
+ * @param rmt_param RMT parameter structor
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG Parameter error
+ * - ESP_OK Success
+ */
+esp_err_t rmt_config(rmt_config_t* rmt_param);
+
+/**
+ * @brief register RMT interrupt handler, the handler is an ISR.
+ *
+ * The handler will be attached to the same CPU core that this function is running on.
+ * Users should know that which CPU is running and then pick a INUM that is not used by system.
+ * We can find the information of INUM and interrupt level in soc.h.
+ * @note
+ * If you already called rmt_driver_install to use system RMT driver,
+ * please do not register ISR handler again.
+ *
+ * @param rmt_intr_num RMT interrupt number, check the info in soc.h, and please see the core-isa.h for more details
+ *
+ * @param fn Interrupt handler function.
+ *
+ * @note
+ * the handler function MUST be defined with attribution of "IRAM_ATTR".
+ *
+ * @param arg Parameter for handler function
+ *
+ * @return
+ * - ESP_OK Success
+ * - ESP_ERR_INVALID_ARG Function pointer error.
+ * - ESP_FAIL System driver installed, can not register ISR handler for RMT
+ */
+esp_err_t rmt_isr_register(uint8_t rmt_intr_num, void (* fn)(void* ), void * arg);
+
+/**
+ * @brief Fill memory data of channel with given RMT items.
+ *
+ * @param channel RMT channel (0 - 7)
+ *
+ * @param item Pointer of items.
+ *
+ * @param item_num RMT sending items number.
+ *
+ * @param mem_offset Index offset of memory.
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG Parameter error
+ * - ESP_OK Success
+ */
+esp_err_t rmt_fill_tx_items(rmt_channel_t channel, rmt_item32_t* item, uint16_t item_num, uint16_t mem_offset);
+
+/**
+ * @brief Initialize RMT driver
+ *
+ * @param channel RMT channel (0 - 7)
+ *
+ * @param rx_buf_size Size of RMT RX ringbuffer.
+ *
+ * @note
+ * If we do not need RX ringbuffer, just set rx_buf_size to 0.
+ *
+ * @note
+ * When we call rmt_driver_install function, it will register a driver ISR handler,
+ * DO NOT REGISTER ISR HANDLER AGAIN.
+ *
+ * @param rmt_intr_num RMT interrupt number.
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG Parameter error
+ * - ESP_OK Success
+ */
+esp_err_t rmt_driver_install(rmt_channel_t channel, size_t rx_buf_size, int rmt_intr_num);
+
+/**
+ * @brief Uninstall RMT driver.
+ *
+ * @param channel RMT channel (0 - 7)
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG Parameter error
+ * - ESP_OK Success
+ */
+esp_err_t rmt_driver_uninstall(rmt_channel_t channel);
+
+/**
+ * @brief RMT send waveform from rmt_item array.
+ *
+ * This API allows user to send waveform with any length.
+ *
+ * @param channel RMT channel (0 - 7)
+ *
+ * @param rmt_item head point of RMT items array.
+ *
+ * @param item_num RMT data item number.
+ *
+ * @param wait_tx_done If set 1, it will block the task and wait for sending done.
+ *
+ * If set 0, it will not wait and return immediately.
+ *
+ * @note
+ * This function will not copy data, instead, it will point to the original items,
+ * and send the waveform items.
+ * If wait_tx_done is set to true, this function will block and will not return until
+ * all items have been sent out.
+ * If wait_tx_done is set to false, this function will return immediately, and the driver
+ * interrupt will continue sending the items. We must make sure the item data will not be
+ * damaged when the driver is still sending items in driver interrupt.
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG Parameter error
+ * - ESP_OK Success
+ */
+esp_err_t rmt_write_items(rmt_channel_t channel, rmt_item32_t* rmt_item, int item_num, bool wait_tx_done);
+
+/**
+ * @brief Wait RMT TX finished.
+ *
+ * @param channel RMT channel (0 - 7)
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG Parameter error
+ * - ESP_OK Success
+ */
+esp_err_t rmt_wait_tx_done(rmt_channel_t channel);
+
+/**
+ * @brief Get ringbuffer from UART.
+ *
+ * Users can get the RMT RX ringbuffer handler, and process the RX data.
+ *
+ * @param channel RMT channel (0 - 7)
+ *
+ * @param buf_handler Pointer to buffer handler to accept RX ringbuffer handler.
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG Parameter error
+ * - ESP_OK Success
+ */
+esp_err_t rmt_get_ringbuf_handler(rmt_channel_t channel, RingbufHandle_t* buf_handler);
+
+/***************************EXAMPLE**********************************
+ *
+ * @note
+ * You can also refer to example/09_rmt_nec_tx_rx to have more information about how to use RMT module.
+ *
+ * ----------------EXAMPLE OF RMT SETTING ---------------------
+ * @code{c}
+ * //1. enable RMT
+ * //enable RMT module, or you can not set any register of it.
+ * //this will be done in rmt_config API.
+ * periph_module_enable(PERIPH_RMT_MODULE);
+ * @endcode
+ *
+ * @code{c}
+ * //2. set RMT transmitter
+ * void rmt_tx_init()
+ * {
+ * rmt_config_t rmt_tx;
+ * rmt_tx.channel = 0;
+ * rmt_tx.gpio_num = 16;
+ * rmt_tx.mem_block_num = 1;
+ * rmt_tx.clk_div = 100;
+ * rmt_tx.tx_config.loop_en = false;
+ * rmt_tx.tx_config.carrier_duty_percent = 50;
+ * rmt_tx.tx_config.carrier_freq_hz = 38000;
+ * rmt_tx.tx_config.carrier_level = 1;
+ * rmt_tx.tx_config.carrier_en = RMT_TX_CARRIER_EN;
+ * rmt_tx.tx_config.idle_level = 0;
+ * rmt_tx.tx_config.idle_output_en = true;
+ * rmt_tx.rmt_mode = 0;
+ * rmt_config(&rmt_tx);
+ *
+ * //install system RMT driver, disable rx ringbuffer for transmitter.
+ * rmt_driver_install(rmt_tx.channel, 0, RMT_INTR_NUM);
+ * }
+ *
+ * @endcode
+ * @code{c}
+ * //3. set RMT receiver
+ * void rmt_rx_init()
+ * {
+ * rmt_config_t rmt_rx;
+ * rmt_rx.channel = 1;
+ * rmt_rx.gpio_num = 19;
+ * rmt_rx.clk_div = 100;
+ * rmt_rx.mem_block_num = 1;
+ * rmt_rx.rmt_mode = RMT_MODE_RX;
+ * rmt_rx.rx_config.filter_en = true;
+ * rmt_rx.rx_config.filter_ticks_thresh = 100;
+ * rmt_rx.rx_config.idle_threshold = 0xffff;
+ * rmt_config(&rmt_rx);
+ *
+ * //install system RMT driver.
+ * rmt_driver_install(rmt_rx.channel, 1000, RMT_INTR_NUM);
+ * }
+ *
+ * ----------------EXAMPLE OF RMT INTERRUPT ------------------
+ * @code{c}
+ *
+ * rmt_isr_register(RMT_INTR_NUM, rmt_isr, NULL); //hook the ISR handler for RMT interrupt
+ * @endcode
+ * @note
+ * 0. If you have called rmt_driver_install, you don't need to set ISR handler any more.
+ * 1. the first parameter is INUM, you can pick one form interrupt level 1/2 which is not used by the system.
+ * 2. user should arrange the INUMs that used, better not to use a same INUM for different interrupt source.
+ * 3. do not pick the INUM that already occupied by the system.
+ * 4. refer to soc.h to check which INUMs that can be used.
+ *
+ * ----------------EXAMPLE OF INTERRUPT HANDLER ---------------
+ * @code{c}
+ * #include "esp_attr.h"
+ * //we should add 'IRAM_ATTR' attribution when we declare the isr function
+ * void IRAM_ATTR rmt_isr_handler(void* arg)
+ * {
+ * //read RMT interrupt status.
+ * uint32_t intr_st = RMT.int_st.val;
+ *
+ * //you will find which channels have triggered fade_end interrupt here,
+ * //then, you can post some event to RTOS queue to process the event.
+ * //later we will add a queue in the driver code.
+ *
+ * //clear RMT interrupt status.
+ * RMT.int_clr.val = intr_st;
+ * }
+ * @endcode
+ *
+ *--------------------------END OF EXAMPLE --------------------------
+ */
+
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DRIVER_RMT_CTRL_H_ */
{
portENTER_CRITICAL(&periph_spinlock);
switch(periph) {
+ case PERIPH_RMT_MODULE:
+ SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_RMT_CLK_EN);
+ CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_RMT_RST);
+ break;
case PERIPH_LEDC_MODULE:
SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_LEDC_CLK_EN);
CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_LEDC_RST);
--- /dev/null
+// Copyright 2015-2016 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 <esp_types.h>
+#include <string.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/semphr.h"
+#include "freertos/xtensa_api.h"
+#include "freertos/ringbuf.h"
+#include "esp_intr.h"
+#include "esp_log.h"
+#include "esp_err.h"
+#include "soc/gpio_sig_map.h"
+#include "soc/rmt_struct.h"
+#include "driver/periph_ctrl.h"
+#include "driver/rmt.h"
+
+#define RMT_SOUCCE_CLK_APB (APB_CLK_FREQ) /*!< RMT source clock is APB_CLK */
+#define RMT_SOURCE_CLK_REF (1 * 1000000) /*!< not used yet */
+#define RMT_SOURCE_CLK(select) ((select == RMT_BASECLK_REF) ? (RMT_SOURCE_CLK_REF) : (RMT_SOUCCE_CLK_APB)) /*! RMT source clock frequency */
+
+#define RMT_CHANNEL_ERROR_STR "RMT CHANNEL ERR"
+#define RMT_ADDR_ERROR_STR "RMT ADDRESS ERR"
+#define RMT_MEM_CNT_ERROR_STR "RMT MEM BLOCK NUM ERR"
+#define RMT_CARRIER_ERROR_STR "RMT CARRIER LEVEL ERR"
+#define RMT_MEM_OWNER_ERROR_STR "RMT MEM OWNER_ERR"
+#define RMT_BASECLK_ERROR_STR "RMT BASECLK ERR"
+#define RMT_WR_MEM_OVF_ERROR_STR "RMT WR MEM OVERFLOW"
+#define RMT_GPIO_ERROR_STR "RMT GPIO ERROR"
+#define RMT_MODE_ERROR_STR "RMT MODE ERROR"
+#define RMT_CLK_DIV_ERROR_STR "RMT CLK DIV ERR"
+#define RMT_DRIVER_ERROR_STR "RMT DRIVER ERR"
+#define RMT_DRIVER_LENGTH_ERROR_STR "RMT PARAM LEN ERROR"
+
+static const char* RMT_TAG = "RMT";
+static bool s_rmt_driver_installed = false;
+
+#define RMT_CHECK(a, str, ret) if (!(a)) { \
+ ESP_LOGE(RMT_TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
+ return (ret); \
+ }
+static portMUX_TYPE rmt_spinlock = portMUX_INITIALIZER_UNLOCKED;
+
+typedef struct {
+ int tx_offset;
+ int tx_len_rem;
+ int tx_sub_len;
+ rmt_channel_t channel;
+ rmt_item32_t* tx_data;
+ xSemaphoreHandle tx_sem;
+ RingbufHandle_t tx_buf;
+ RingbufHandle_t rx_buf;
+} rmt_obj_t;
+
+rmt_obj_t* p_rmt_obj[RMT_CHANNEL_MAX] = {0};
+
+static void rmt_set_tx_wrap_en(rmt_channel_t channel, bool en)
+{
+ portENTER_CRITICAL(&rmt_spinlock);
+ RMT.apb_conf.mem_tx_wrap_en = en;
+ portEXIT_CRITICAL(&rmt_spinlock);
+}
+
+static void rmt_set_data_mode(rmt_data_mode_t data_mode)
+{
+ portENTER_CRITICAL(&rmt_spinlock);
+ RMT.apb_conf.fifo_mask = data_mode;
+ portEXIT_CRITICAL(&rmt_spinlock);
+}
+
+esp_err_t rmt_set_clk_div(rmt_channel_t channel, uint8_t div_cnt)
+{
+ RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
+ RMT.conf_ch[channel].conf0.div_cnt = div_cnt;
+ return ESP_OK;
+}
+
+esp_err_t rmt_get_clk_div(rmt_channel_t channel, uint8_t* div_cnt)
+{
+ RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
+ RMT_CHECK(div_cnt != NULL, RMT_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG);
+ *div_cnt = RMT.conf_ch[channel].conf0.div_cnt;
+ return ESP_OK;
+}
+
+esp_err_t rmt_set_rx_idle_thresh(rmt_channel_t channel, uint16_t thresh)
+{
+ RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
+ RMT.conf_ch[channel].conf0.idle_thres = thresh;
+ return ESP_OK;
+}
+
+esp_err_t rmt_get_rx_idle_thresh(rmt_channel_t channel, uint16_t *thresh)
+{
+ RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
+ RMT_CHECK(thresh != NULL, RMT_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG);
+ *thresh = RMT.conf_ch[channel].conf0.idle_thres;
+ return ESP_OK;
+}
+
+esp_err_t rmt_set_mem_block_num(rmt_channel_t channel, uint8_t rmt_mem_num)
+{
+ RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
+ RMT_CHECK(rmt_mem_num < 16, RMT_MEM_CNT_ERROR_STR, ESP_ERR_INVALID_ARG);
+ RMT.conf_ch[channel].conf0.mem_size = rmt_mem_num;
+ return ESP_OK;
+}
+
+esp_err_t rmt_get_mem_block_num(rmt_channel_t channel, uint8_t* rmt_mem_num)
+{
+ RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
+ RMT_CHECK(rmt_mem_num != NULL, RMT_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG);
+ *rmt_mem_num = RMT.conf_ch[channel].conf0.mem_size;
+ return ESP_OK;
+}
+
+esp_err_t rmt_set_tx_carrier(rmt_channel_t channel, bool carrier_en, uint16_t high_level, uint16_t low_level,
+ rmt_carrier_level_t carrier_level)
+{
+ RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
+ RMT_CHECK(carrier_level < RMT_CARRIER_LEVEL_MAX, RMT_CARRIER_ERROR_STR, ESP_ERR_INVALID_ARG);
+ RMT.carrier_duty_ch[channel].high = high_level;
+ RMT.carrier_duty_ch[channel].low = low_level;
+ RMT.conf_ch[channel].conf0.carrier_out_lv = carrier_level;
+ RMT.conf_ch[channel].conf0.carrier_en = carrier_en;
+ return ESP_OK;
+}
+
+esp_err_t rmt_set_mem_pd(rmt_channel_t channel, bool pd_en)
+{
+ RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
+ RMT.conf_ch[channel].conf0.mem_pd = pd_en;
+ return ESP_OK;
+}
+
+esp_err_t rmt_get_mem_pd(rmt_channel_t channel, bool* pd_en)
+{
+ RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
+ *pd_en = (bool) RMT.conf_ch[channel].conf0.mem_pd;
+ return ESP_OK;
+}
+
+esp_err_t rmt_tx_start(rmt_channel_t channel, bool tx_idx_rst)
+{
+ RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
+ portENTER_CRITICAL(&rmt_spinlock);
+ if(tx_idx_rst) {
+ RMT.conf_ch[channel].conf1.mem_rd_rst = 1;
+ }
+ RMT.conf_ch[channel].conf1.mem_owner = RMT_MEM_OWNER_TX;
+ RMT.conf_ch[channel].conf1.tx_start = 1;
+ portEXIT_CRITICAL(&rmt_spinlock);
+ return ESP_OK;
+}
+
+esp_err_t rmt_tx_stop(rmt_channel_t channel)
+{
+ RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
+ portENTER_CRITICAL(&rmt_spinlock);
+ RMT.conf_ch[channel].conf1.tx_start = 0;
+ portEXIT_CRITICAL(&rmt_spinlock);
+ return ESP_OK;
+}
+
+esp_err_t rmt_rx_start(rmt_channel_t channel, bool rx_idx_rst)
+{
+ RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
+ portENTER_CRITICAL(&rmt_spinlock);
+ if(rx_idx_rst) {
+ RMT.conf_ch[channel].conf1.mem_wr_rst = 1;
+ }
+ RMT.conf_ch[channel].conf1.rx_en = 0;
+ RMT.conf_ch[channel].conf1.mem_owner = RMT_MEM_OWNER_RX;
+ RMT.conf_ch[channel].conf1.rx_en = 1;
+ portEXIT_CRITICAL(&rmt_spinlock);
+ return ESP_OK;
+}
+
+esp_err_t rmt_rx_stop(rmt_channel_t channel)
+{
+ RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
+ portENTER_CRITICAL(&rmt_spinlock);
+ RMT.conf_ch[channel].conf1.rx_en = 0;
+ portEXIT_CRITICAL(&rmt_spinlock);
+ return ESP_OK;
+}
+
+esp_err_t rmt_memory_rw_rst(rmt_channel_t channel)
+{
+ RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
+ portENTER_CRITICAL(&rmt_spinlock);
+ RMT.conf_ch[channel].conf1.mem_rd_rst = 1;
+ RMT.conf_ch[channel].conf1.mem_wr_rst = 1;
+ portEXIT_CRITICAL(&rmt_spinlock);
+ return ESP_OK;
+}
+
+esp_err_t rmt_set_memory_owner(rmt_channel_t channel, rmt_mem_owner_t owner)
+{
+ RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
+ RMT_CHECK(owner < RMT_MEM_OWNER_MAX, RMT_MEM_OWNER_ERROR_STR, ESP_ERR_INVALID_ARG);
+ portENTER_CRITICAL(&rmt_spinlock);
+ RMT.conf_ch[channel].conf1.mem_owner = owner;
+ portEXIT_CRITICAL(&rmt_spinlock);
+ return ESP_OK;
+}
+
+esp_err_t rmt_get_memory_owner(rmt_channel_t channel, rmt_mem_owner_t* owner)
+{
+ RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
+ RMT_CHECK(owner != NULL, RMT_MEM_OWNER_ERROR_STR, ESP_ERR_INVALID_ARG);
+ *owner = (rmt_mem_owner_t) RMT.conf_ch[channel].conf1.mem_owner;
+ return ESP_OK;
+}
+
+esp_err_t rmt_set_tx_loop_mode(rmt_channel_t channel, bool loop_en)
+{
+ RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
+ portENTER_CRITICAL(&rmt_spinlock);
+ RMT.conf_ch[channel].conf1.tx_conti_mode = loop_en;
+ portEXIT_CRITICAL(&rmt_spinlock);
+ return ESP_OK;
+}
+
+esp_err_t rmt_get_tx_loop_mode(rmt_channel_t channel, bool* loop_en)
+{
+ RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
+ *loop_en = (bool) RMT.conf_ch[channel].conf1.tx_conti_mode;
+ return ESP_OK;
+}
+
+esp_err_t rmt_set_rx_filter(rmt_channel_t channel, bool rx_filter_en, uint8_t thresh)
+{
+ RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
+ portENTER_CRITICAL(&rmt_spinlock);
+ RMT.conf_ch[channel].conf1.rx_filter_en = rx_filter_en;
+ RMT.conf_ch[channel].conf1.rx_filter_thres = thresh;
+ portEXIT_CRITICAL(&rmt_spinlock);
+ return ESP_OK;
+}
+
+esp_err_t rmt_set_source_clk(rmt_channel_t channel, rmt_source_clk_t base_clk)
+{
+ RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
+ RMT_CHECK(base_clk < RMT_BASECLK_MAX, RMT_BASECLK_ERROR_STR, ESP_ERR_INVALID_ARG);
+ portENTER_CRITICAL(&rmt_spinlock);
+ RMT.conf_ch[channel].conf1.ref_always_on = base_clk;
+ portEXIT_CRITICAL(&rmt_spinlock);
+ return ESP_OK;
+}
+
+esp_err_t rmt_get_source_clk(rmt_channel_t channel, rmt_source_clk_t* src_clk)
+{
+ RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
+ *src_clk = (rmt_source_clk_t) (RMT.conf_ch[channel].conf1.ref_always_on);
+ return ESP_OK;
+}
+
+esp_err_t rmt_set_idle_level(rmt_channel_t channel, bool idle_out_en, rmt_idle_level_t level)
+{
+ RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
+ RMT_CHECK(level < RMT_IDLE_LEVEL_MAX, "RMT IDLE LEVEL ERR", ESP_ERR_INVALID_ARG);
+ portENTER_CRITICAL(&rmt_spinlock);
+ RMT.conf_ch[channel].conf1.idle_out_en = idle_out_en;
+ RMT.conf_ch[channel].conf1.idle_out_lv = level;
+ portEXIT_CRITICAL(&rmt_spinlock);
+ return ESP_OK;
+}
+
+esp_err_t rmt_get_status(rmt_channel_t channel, uint32_t* status)
+{
+ RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
+ *status = RMT.status_ch[channel];
+ return ESP_OK;
+}
+
+rmt_data_mode_t rmt_get_data_mode()
+{
+ return (rmt_data_mode_t) (RMT.apb_conf.fifo_mask);
+}
+
+void rmt_set_intr_enable_mask(uint32_t mask)
+{
+ portENTER_CRITICAL(&rmt_spinlock);
+ RMT.int_ena.val |= mask;
+ portEXIT_CRITICAL(&rmt_spinlock);
+}
+
+void rmt_clr_intr_enable_mask(uint32_t mask)
+{
+ portENTER_CRITICAL(&rmt_spinlock);
+ RMT.int_ena.val &= (~mask);
+ portEXIT_CRITICAL(&rmt_spinlock);
+}
+
+esp_err_t rmt_set_rx_intr_en(rmt_channel_t channel, bool en)
+{
+ RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
+ if(en) {
+ rmt_set_intr_enable_mask(BIT(channel * 3 + 1));
+ } else {
+ rmt_clr_intr_enable_mask(BIT(channel * 3 + 1));
+ }
+ return ESP_OK;
+}
+
+esp_err_t rmt_set_err_intr_en(rmt_channel_t channel, bool en)
+{
+ RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
+ if(en) {
+ rmt_set_intr_enable_mask(BIT(channel * 3 + 2));
+ } else {
+ rmt_clr_intr_enable_mask(BIT(channel * 3 + 2));
+ }
+ return ESP_OK;
+}
+
+esp_err_t rmt_set_tx_intr_en(rmt_channel_t channel, bool en)
+{
+ RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
+ if(en) {
+ rmt_set_intr_enable_mask(BIT(channel * 3));
+ } else {
+ rmt_clr_intr_enable_mask(BIT(channel * 3));
+ }
+ return ESP_OK;
+}
+
+esp_err_t rmt_set_evt_intr_en(rmt_channel_t channel, bool en, uint16_t evt_thresh)
+{
+ RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
+ RMT_CHECK(evt_thresh < 256, "RMT EVT THRESH ERR", ESP_ERR_INVALID_ARG);
+ if(en) {
+ RMT.tx_lim_ch[channel].limit = evt_thresh;
+ rmt_set_tx_wrap_en(channel, true);
+ rmt_set_intr_enable_mask(BIT(channel + 24));
+ } else {
+ rmt_clr_intr_enable_mask(BIT(channel + 24));
+ }
+ return ESP_OK;
+}
+
+esp_err_t rmt_set_pin(rmt_channel_t channel, rmt_mode_t mode, gpio_num_t gpio_num)
+{
+ RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
+ RMT_CHECK(mode < RMT_MODE_MAX, RMT_MODE_ERROR_STR, ESP_ERR_INVALID_ARG);
+ RMT_CHECK(((GPIO_IS_VALID_GPIO(gpio_num) && (mode == RMT_MODE_RX)) || (GPIO_IS_VALID_OUTPUT_GPIO(gpio_num) && (mode == RMT_MODE_TX))),
+ RMT_GPIO_ERROR_STR, ESP_ERR_INVALID_ARG);
+
+ PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[gpio_num], 2);
+ if(mode == RMT_MODE_TX) {
+ gpio_set_direction(gpio_num, GPIO_MODE_OUTPUT);
+ gpio_matrix_out(gpio_num, RMT_SIG_OUT0_IDX + channel, 0, 0);
+ } else {
+ gpio_set_direction(gpio_num, GPIO_MODE_INPUT);
+ gpio_matrix_in(gpio_num, RMT_SIG_IN0_IDX + channel, 0);
+ }
+ return ESP_OK;
+}
+
+esp_err_t rmt_config(rmt_config_t* rmt_param)
+{
+ uint8_t mode = rmt_param->rmt_mode;
+ uint8_t channel = rmt_param->channel;
+ uint8_t gpio_num = rmt_param->gpio_num;
+ uint8_t mem_cnt = rmt_param->mem_block_num;
+ int clk_div = rmt_param->clk_div;
+ RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
+ RMT_CHECK(GPIO_IS_VALID_GPIO(gpio_num), RMT_GPIO_ERROR_STR, ESP_ERR_INVALID_ARG);
+ RMT_CHECK((mem_cnt + channel <= 8 && mem_cnt > 0), RMT_MEM_CNT_ERROR_STR, ESP_ERR_INVALID_ARG);
+ RMT_CHECK((clk_div > 0), RMT_CLK_DIV_ERROR_STR, ESP_ERR_INVALID_ARG);
+ periph_module_enable(PERIPH_RMT_MODULE);
+
+ RMT.conf_ch[channel].conf0.div_cnt = clk_div;
+ /*Visit data use memory not FIFO*/
+ rmt_set_data_mode(RMT_DATA_MODE_MEM);
+ /*Reset tx/rx memory index */
+ portENTER_CRITICAL(&rmt_spinlock);
+ RMT.conf_ch[channel].conf1.mem_rd_rst = 1;
+ RMT.conf_ch[channel].conf1.mem_wr_rst = 1;
+ portEXIT_CRITICAL(&rmt_spinlock);
+
+ if(mode == RMT_MODE_TX) {
+ uint32_t rmt_source_clk_hz = 0;
+ uint32_t carrier_freq_hz = rmt_param->tx_config.carrier_freq_hz;
+ uint16_t carrier_duty_percent = rmt_param->tx_config.carrier_duty_percent;
+ uint8_t carrier_level = rmt_param->tx_config.carrier_level;
+ uint8_t idle_level = rmt_param->tx_config.idle_level;
+
+ portENTER_CRITICAL(&rmt_spinlock);
+ RMT.conf_ch[channel].conf1.tx_conti_mode = rmt_param->tx_config.loop_en;
+ /*Memory set block number*/
+ RMT.conf_ch[channel].conf0.mem_size = mem_cnt;
+ RMT.conf_ch[channel].conf1.mem_owner = RMT_MEM_OWNER_TX;
+ /*We use APB clock in this version, which is 80Mhz, later we will release system reference clock*/
+ RMT.conf_ch[channel].conf1.ref_always_on = RMT_BASECLK_APB;
+ rmt_source_clk_hz = RMT_SOURCE_CLK(RMT_BASECLK_APB);
+ /*Set idle level */
+ RMT.conf_ch[channel].conf1.idle_out_en = rmt_param->tx_config.idle_output_en;
+ RMT.conf_ch[channel].conf1.idle_out_lv = idle_level;
+ portEXIT_CRITICAL(&rmt_spinlock);
+
+ /*Set carrier*/
+ uint32_t duty_div, duty_h, duty_l;
+ duty_div = rmt_source_clk_hz / carrier_freq_hz;
+ duty_h = duty_div * carrier_duty_percent / 100;
+ duty_l = duty_div - duty_h;
+ RMT.conf_ch[channel].conf0.carrier_out_lv = carrier_level;
+ RMT.carrier_duty_ch[channel].high = duty_h;
+ RMT.carrier_duty_ch[channel].low = duty_l;
+ RMT.conf_ch[channel].conf0.carrier_en = rmt_param->tx_config.carrier_en;
+ ESP_LOGD(RMT_TAG, "Rmt Tx Channel %u|Gpio %u|Sclk_Hz %u|Div %u|Carrier_Hz %u|Duty %u",
+ channel, gpio_num, rmt_source_clk_hz, clk_div, carrier_freq_hz, carrier_duty_percent);
+ }
+ else if(RMT_MODE_RX == mode) {
+ uint8_t filter_cnt = rmt_param->rx_config.filter_ticks_thresh;
+ uint16_t threshold = rmt_param->rx_config.idle_threshold;
+
+ portENTER_CRITICAL(&rmt_spinlock);
+ /*clock init*/
+ RMT.conf_ch[channel].conf1.ref_always_on = RMT_BASECLK_APB;
+ uint32_t rmt_source_clk_hz = RMT_SOURCE_CLK(RMT_BASECLK_APB);
+ /*memory set block number and owner*/
+ RMT.conf_ch[channel].conf0.mem_size = mem_cnt;
+ RMT.conf_ch[channel].conf1.mem_owner = RMT_MEM_OWNER_RX;
+ /*Set idle threshold*/
+ RMT.conf_ch[channel].conf0.idle_thres = threshold;
+ /* Set RX filter */
+ RMT.conf_ch[channel].conf1.rx_filter_thres = filter_cnt;
+ RMT.conf_ch[channel].conf1.rx_filter_en = rmt_param->rx_config.filter_en;
+ portEXIT_CRITICAL(&rmt_spinlock);
+
+ ESP_LOGD(RMT_TAG, "Rmt Rx Channel %u|Gpio %u|Sclk_Hz %u|Div %u|Thresold %u|Filter %u",
+ channel, gpio_num, rmt_source_clk_hz, clk_div, threshold, filter_cnt);
+ }
+ rmt_set_pin(channel, mode, gpio_num);
+ return ESP_OK;
+}
+
+static void IRAM_ATTR rmt_fill_memory(rmt_channel_t channel, rmt_item32_t* item, uint16_t item_num, uint16_t mem_offset)
+{
+ portENTER_CRITICAL(&rmt_spinlock);
+ RMT.apb_conf.fifo_mask = RMT_DATA_MODE_MEM;
+ portEXIT_CRITICAL(&rmt_spinlock);
+ int i;
+ for(i = 0; i < item_num; i++) {
+ RMTMEM.chan[channel].data32[i + mem_offset].val = item[i].val;
+ }
+}
+
+esp_err_t rmt_fill_tx_items(rmt_channel_t channel, rmt_item32_t* item, uint16_t item_num, uint16_t mem_offset)
+{
+ RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, (0));
+ RMT_CHECK((item != NULL), RMT_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG);
+ RMT_CHECK((item_num > 0), RMT_DRIVER_LENGTH_ERROR_STR, ESP_ERR_INVALID_ARG);
+
+ /*Each block has 64 x 32 bits of data*/
+ uint8_t mem_cnt = RMT.conf_ch[channel].conf0.mem_size;
+ RMT_CHECK((mem_cnt * RMT_MEM_ITEM_NUM >= item_num), RMT_WR_MEM_OVF_ERROR_STR, ESP_ERR_INVALID_ARG);
+ rmt_fill_memory(channel, item, item_num, mem_offset);
+ return ESP_OK;
+}
+
+esp_err_t rmt_isr_register(uint8_t rmt_intr_num, void (*fn)(void*), void * arg)
+{
+ RMT_CHECK((fn != NULL), RMT_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG);
+ RMT_CHECK(s_rmt_driver_installed == false, "RMT DRIVER INSTALLED, CAN NOT REG ISR HANDLER", ESP_FAIL);
+ portENTER_CRITICAL(&rmt_spinlock);
+ ESP_INTR_DISABLE(rmt_intr_num);
+ intr_matrix_set(xPortGetCoreID(), ETS_RMT_INTR_SOURCE, rmt_intr_num);
+ xt_set_interrupt_handler(rmt_intr_num, fn, arg);
+ ESP_INTR_ENABLE(rmt_intr_num);
+ portEXIT_CRITICAL(&rmt_spinlock);
+ return ESP_OK;
+}
+
+static int IRAM_ATTR rmt_get_mem_len(rmt_channel_t channel)
+{
+ int block_num = RMT.conf_ch[channel].conf0.mem_size;
+ int item_block_len = block_num * RMT_MEM_ITEM_NUM;
+ 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;
+}
+
+static void IRAM_ATTR rmt_driver_isr_default(void* arg)
+{
+ uint32_t intr_st = RMT.int_st.val;
+ uint32_t i = 0;
+ uint8_t channel;
+ portBASE_TYPE HPTaskAwoken = 0;
+ for(i = 0; i < 32; i++) {
+ if(i < 24) {
+ if(intr_st & (BIT(i))) {
+ channel = i / 3;
+ rmt_obj_t* p_rmt = p_rmt_obj[channel];
+ switch(i % 3) {
+ //TX END
+ case 0:
+ ESP_EARLY_LOGD(RMT_TAG, "RMT INTR : TX END\n");
+ xSemaphoreGiveFromISR(p_rmt->tx_sem, &HPTaskAwoken);
+ if(HPTaskAwoken == pdTRUE) {
+ portYIELD_FROM_ISR();
+ }
+ p_rmt->tx_data = NULL;
+ p_rmt->tx_len_rem = 0;
+ p_rmt->tx_offset = 0;
+ p_rmt->tx_sub_len = 0;
+ break;
+ //RX_END
+ case 1:
+ ESP_EARLY_LOGD(RMT_TAG, "RMT INTR : RX END");
+ RMT.conf_ch[channel].conf1.rx_en = 0;
+ int item_len = rmt_get_mem_len(channel);
+ //change memory owner to protect data.
+ RMT.conf_ch[channel].conf1.mem_owner = RMT_MEM_OWNER_TX;
+ if(p_rmt->rx_buf) {
+ BaseType_t res = xRingbufferSendFromISR(p_rmt->rx_buf, (void*) RMTMEM.chan[channel].data32, item_len * 4, &HPTaskAwoken);
+ if(res == pdFALSE) {
+ ESP_LOGE(RMT_TAG, "RMT RX BUFFER FULL");
+ } else {
+
+ }
+ if(HPTaskAwoken == pdTRUE) {
+ portYIELD_FROM_ISR();
+ }
+ } else {
+ ESP_EARLY_LOGE(RMT_TAG, "RMT RX BUFFER ERROR\n");
+ }
+ RMT.conf_ch[channel].conf1.mem_wr_rst = 1;
+ RMT.conf_ch[channel].conf1.mem_owner = RMT_MEM_OWNER_RX;
+ RMT.conf_ch[channel].conf1.rx_en = 1;
+ break;
+ //ERR
+ case 2:
+ ESP_EARLY_LOGE(RMT_TAG, "RMT[%d] ERR", channel);
+ ESP_EARLY_LOGE(RMT_TAG, "status: 0x%08x", RMT.status_ch[channel]);
+ RMT.int_ena.val &= (~(BIT(i)));
+ break;
+ default:
+ break;
+ }
+ RMT.int_clr.val = BIT(i);
+ }
+ } else {
+ if(intr_st & (BIT(i))) {
+ channel = i - 24;
+ rmt_obj_t* p_rmt = p_rmt_obj[channel];
+ RMT.int_clr.val = BIT(i);
+ ESP_EARLY_LOGD(RMT_TAG, "RMT CH[%d]: EVT INTR", channel);
+ if(p_rmt->tx_data == NULL) {
+ //skip
+ } else {
+ rmt_item32_t* pdata = p_rmt->tx_data;
+ int len_rem = p_rmt->tx_len_rem;
+ if(len_rem >= p_rmt->tx_sub_len) {
+ rmt_fill_memory(channel, pdata, p_rmt->tx_sub_len, p_rmt->tx_offset);
+ p_rmt->tx_data += p_rmt->tx_sub_len;
+ p_rmt->tx_len_rem -= p_rmt->tx_sub_len;
+ } else if(len_rem == 0) {
+ RMTMEM.chan[channel].data32[p_rmt->tx_offset].val = 0;
+ } else {
+ rmt_fill_memory(channel, pdata, len_rem, p_rmt->tx_offset);
+ RMTMEM.chan[channel].data32[p_rmt->tx_offset + len_rem].val = 0;
+ p_rmt->tx_data += len_rem;
+ p_rmt->tx_len_rem -= len_rem;
+ }
+ if(p_rmt->tx_offset == 0) {
+ p_rmt->tx_offset = p_rmt->tx_sub_len;
+ } else {
+ p_rmt->tx_offset = 0;
+ }
+ }
+ }
+ }
+ }
+}
+
+esp_err_t rmt_driver_uninstall(rmt_channel_t channel)
+{
+ RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
+ if(p_rmt_obj[channel] == NULL) {
+ return ESP_OK;
+ }
+ xSemaphoreTake(p_rmt_obj[channel]->tx_sem, portMAX_DELAY);
+ rmt_set_rx_intr_en(channel, 0);
+ rmt_set_err_intr_en(channel, 0);
+ rmt_set_tx_intr_en(channel, 0);
+ rmt_set_evt_intr_en(channel, 0, 0xffff);
+ if(p_rmt_obj[channel]->tx_sem) {
+ vSemaphoreDelete(p_rmt_obj[channel]->tx_sem);
+ p_rmt_obj[channel]->tx_sem = NULL;
+ }
+ if(p_rmt_obj[channel]->rx_buf) {
+ vRingbufferDelete(p_rmt_obj[channel]->rx_buf);
+ p_rmt_obj[channel]->rx_buf = NULL;
+ }
+ free(p_rmt_obj[channel]);
+ p_rmt_obj[channel] = NULL;
+ s_rmt_driver_installed = false;
+ return ESP_OK;
+}
+
+esp_err_t rmt_driver_install(rmt_channel_t channel, size_t rx_buf_size, int rmt_intr_num)
+{
+ RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
+ if(p_rmt_obj[channel] != NULL) {
+ ESP_LOGD(RMT_TAG, "RMT DRIVER ALREADY INSTALLED");
+ return ESP_FAIL;
+ }
+
+ ESP_INTR_DISABLE(rmt_intr_num);
+ p_rmt_obj[channel] = (rmt_obj_t*) malloc(sizeof(rmt_obj_t));
+
+ if(p_rmt_obj[channel] == NULL) {
+ ESP_LOGE(RMT_TAG, "RMT driver malloc error");
+ return ESP_FAIL;
+ }
+ memset(p_rmt_obj[channel], 0, sizeof(rmt_obj_t));
+
+ p_rmt_obj[channel]->tx_len_rem = 0;
+ p_rmt_obj[channel]->tx_data = NULL;
+ p_rmt_obj[channel]->channel = channel;
+ p_rmt_obj[channel]->tx_offset = 0;
+ p_rmt_obj[channel]->tx_sub_len = 0;
+
+ if(p_rmt_obj[channel]->tx_sem == NULL) {
+ p_rmt_obj[channel]->tx_sem = xSemaphoreCreateBinary();
+ xSemaphoreGive(p_rmt_obj[channel]->tx_sem);
+ }
+ if(p_rmt_obj[channel]->rx_buf == NULL && rx_buf_size > 0) {
+ p_rmt_obj[channel]->rx_buf = xRingbufferCreate(rx_buf_size, RINGBUF_TYPE_NOSPLIT);
+ rmt_set_rx_intr_en(channel, 1);
+ rmt_set_err_intr_en(channel, 1);
+ }
+ if(s_rmt_driver_installed == false) {
+ rmt_isr_register(rmt_intr_num, rmt_driver_isr_default, NULL);
+ s_rmt_driver_installed = true;
+ }
+ rmt_set_tx_intr_en(channel, 1);
+ ESP_INTR_ENABLE(rmt_intr_num);
+ return ESP_OK;
+}
+
+esp_err_t rmt_write_items(rmt_channel_t channel, rmt_item32_t* rmt_item, int item_num, bool wait_tx_done)
+{
+ RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
+ RMT_CHECK(p_rmt_obj[channel] != NULL, RMT_DRIVER_ERROR_STR, ESP_FAIL);
+ RMT_CHECK(rmt_item != NULL, RMT_ADDR_ERROR_STR, ESP_FAIL);
+ RMT_CHECK(item_num > 0, RMT_DRIVER_LENGTH_ERROR_STR, ESP_ERR_INVALID_ARG);
+ rmt_obj_t* p_rmt = p_rmt_obj[channel];
+ int block_num = RMT.conf_ch[channel].conf0.mem_size;
+ int item_block_len = block_num * RMT_MEM_ITEM_NUM;
+ int item_sub_len = block_num * RMT_MEM_ITEM_NUM / 2;
+ int len_rem = item_num;
+ xSemaphoreTake(p_rmt->tx_sem, portMAX_DELAY);
+ // fill the memory block first
+ if(item_num >= item_block_len) {
+ rmt_fill_memory(channel, rmt_item, item_block_len, 0);
+ RMT.tx_lim_ch[channel].limit = item_sub_len;
+ RMT.apb_conf.mem_tx_wrap_en = 1;
+ len_rem -= item_block_len;
+ RMT.conf_ch[channel].conf1.tx_conti_mode = 0;
+ rmt_set_evt_intr_en(channel, 1, item_sub_len);
+ p_rmt->tx_data = rmt_item + item_block_len;
+ p_rmt->tx_len_rem = len_rem;
+ p_rmt->tx_offset = 0;
+ p_rmt->tx_sub_len = item_sub_len;
+ } else {
+ rmt_fill_memory(channel, rmt_item, len_rem, 0);
+ RMTMEM.chan[channel].data32[len_rem].val = 0;
+ len_rem = 0;
+ }
+ rmt_tx_start(channel, true);
+ if(wait_tx_done) {
+ xSemaphoreTake(p_rmt->tx_sem, portMAX_DELAY);
+ xSemaphoreGive(p_rmt->tx_sem);
+ }
+ return ESP_OK;
+}
+
+esp_err_t rmt_wait_tx_done(rmt_channel_t channel)
+{
+ RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
+ RMT_CHECK(p_rmt_obj[channel] != NULL, RMT_DRIVER_ERROR_STR, ESP_FAIL);
+ xSemaphoreTake(p_rmt_obj[channel]->tx_sem, portMAX_DELAY);
+ xSemaphoreGive(p_rmt_obj[channel]->tx_sem);
+ return ESP_OK;
+}
+
+esp_err_t rmt_get_ringbuf_handler(rmt_channel_t channel, RingbufHandle_t* buf_handler)
+{
+ RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG);
+ RMT_CHECK(p_rmt_obj[channel] != NULL, RMT_DRIVER_ERROR_STR, ESP_FAIL);
+ RMT_CHECK(buf_handler != NULL, RMT_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG);
+ *buf_handler = p_rmt_obj[channel]->rx_buf;
+ return ESP_OK;
+}
+
#define ESP_INTR_DISABLE(inum) \
xt_ints_off((1<<inum))
-#define ESP_CCOMPARE_INTR_ENBALE() \
- ESP_INTR_ENABLE(ETS_CCOMPARE_INUM)
-
-#define ESP_CCOMPARE_INTR_DISBALE() \
- ESP_INTR_DISABLE(ETS_CCOMPARE_INUM)
-
-#define ESP_SPI1_INTR_ENABLE() \
- ESP_INTR_ENABLE(ETS_SPI1_INUM)
-
-#define ESP_SPI1_INTR_DISABLE() \
- ESP_INTR_DISABLE(ETS_SPI1_INUM)
-
-#define ESP_SPI2_INTR_ENABLE() \
- ESP_INTR_ENABLE(ETS_SPI2_INUM)
-
-#define ESP_PWM_INTR_ENABLE() \
- ESP_INTR_ENABLE(ETS_PWM_INUM)
-
-#define ESP_PWM_INTR_DISABLE() \
- ESP_INTR_DISABLE(ETS_PWM_INUM)
-
-#define ESP_SPI2_INTR_DISABLE() \
- ESP_INTR_DISABLE(ETS_SPI2_INUM)
-
-#define ESP_SPI3_INTR_ENABLE() \
- ESP_INTR_ENABLE(ETS_SPI3_INUM)
-
-#define ESP_SPI3_INTR_DISABLE() \
- ESP_INTR_DISABLE(ETS_SPI3_INUM)
-
-#define ESP_I2S0_INTR_ENABLE() \
- ESP_INTR_ENABLE(ETS_I2S0_INUM)
-
-#define ESP_I2S0_INTR_DISABLE() \
- ESP_INTR_DISABLE(ETS_I2S0_INUM)
-
-#define ESP_I2S1_INTR_ENABLE() \
- ESP_INTR_ENABLE(ETS_I2S1_INUM)
-
-#define ESP_I2S1_INTR_DISABLE() \
- ESP_INTR_DISABLE(ETS_I2S1_INUM)
-
-#define ESP_MPWM_INTR_ENABLE() \
- ESP_INTR_ENABLE(ETS_MPWM_INUM)
-
-#define ESP_EPWM_INTR_ENABLE() \
- ESP_INTR_ENABLE(ETS_EPWM_INUM)
-
-#define ESP_MPWM_INTR_DISABLE() \
- ESP_INTR_DISABLE(ETS_MPWM_INUM)
-
-#define ESP_EPWM_INTR_DISABLE() \
- ESP_INTR_DISABLE(ETS_EPWM_INUM)
-
-#define ESP_BB_INTR_ENABLE() \
- ESP_INTR_ENABLE(ETS_BB_INUM)
-
-#define ESP_BB_INTR_DISABLE() \
- ESP_INTR_DISABLE(ETS_BB_INUM)
-
-#define ESP_UART0_INTR_ENABLE() \
- ESP_INTR_ENABLE(ETS_UART0_INUM)
-
-#define ESP_UART0_INTR_DISABLE() \
- ESP_INTR_DISABLE(ETS_UART0_INUM)
-
-#define ESP_LEDC_INTR_ENABLE() \
- ESP_INTR_ENABLE(ETS_LEDC_INUM)
-
-#define ESP_LEDC_INTR_DISABLE() \
- ESP_INTR_DISABLE(ETS_LEDC_INUM)
-
-#define ESP_GPIO_INTR_ENABLE() \
- ESP_INTR_ENABLE(ETS_GPIO_INUM)
-
-#define ESP_GPIO_INTR_DISABLE() \
- ESP_INTR_DISABLE(ETS_GPIO_INUM)
-
-#define ESP_WDT_INTR_ENABLE() \
- ESP_INTR_ENABLE(ETS_WDT_INUM)
-
-#define ESP_WDT_INTR_DISABLE() \
- ESP_INTR_DISABLE(ETS_WDT_INUM)
-
-#define ESP_FRC1_INTR_ENABLE() \
- ESP_INTR_ENABLE(ETS_FRC_TIMER1_INUM)
-
-#define ESP_FRC1_INTR_DISABLE() \
- ESP_INTR_DISABLE(ETS_FRC_TIMER1_INUM)
-
-#define ESP_FRC2_INTR_ENABLE() \
- ESP_INTR_ENABLE(ETS_FRC_TIMER2_INUM)
-
-#define ESP_FRC2_INTR_DISABLE() \
- ESP_INTR_DISABLE(ETS_FRC_TIMER2_INUM)
-
-#define ESP_RTC_INTR_ENABLE() \
- ESP_INTR_ENABLE(ETS_RTC_INUM)
-
-#define ESP_RTC_INTR_DISABLE() \
- ESP_INTR_DISABLE(ETS_RTC_INUM)
-
-#define ESP_SLC_INTR_ENABLE() \
- ESP_INTR_ENABLE(ETS_SLC_INUM)
-
-#define ESP_SLC_INTR_DISABLE() \
- ESP_INTR_DISABLE(ETS_SLC_INUM)
-
-#define ESP_PCNT_INTR_ENABLE() \
- ESP_INTR_ENABLE(ETS_PCNT_INUM)
-
-#define ESP_PCNT_INTR_DISABLE() \
- ESP_INTR_DISABLE(ETS_PCNT_INUM)
-
-#define ESP_RMT_CTRL_ENABLE() \
- ESP_INTR_ENABLE(ETS_RMT_CTRL_INUM)
-
-#define ESP_RMT_CTRL_DIABLE() \
- ESP_INTR_DISABLE(ETS_RMT_CTRL_INUM)
-
#ifdef __cplusplus
}
#endif
#define RMT_DATE_V 0xFFFFFFFF\r
#define RMT_DATE_S 0\r
\r
-\r
+/* RMT memory block address */\r
+#define RMT_CHANNEL_MEM(i) (DR_REG_RMT_BASE + 0x800 + 64 * 4 * (i))\r
\r
\r
#endif /*_SOC_RMT_REG_H_ */\r
} rmt_dev_t;
extern rmt_dev_t RMT;
-//Allow access to RMT memory using RMTMEM.chan[0].data[8]
+typedef struct {
+ union {
+ struct {
+ uint32_t duration0 :15;
+ uint32_t level0 :1;
+ uint32_t duration1 :15;
+ uint32_t level1 :1;
+ };
+ uint32_t val;
+ };
+} rmt_item32_t;
+
+typedef struct {
+ union {
+ struct {
+ uint16_t duration :15;
+ uint16_t level :1;
+ };
+ uint16_t val;
+ };
+} rmt_item16_t;
+
+//Allow access to RMT memory using RMTMEM.chan[0].data32[8]
typedef volatile struct {
struct {
union {
- struct {
- uint32_t duration0: 15;
- uint32_t level0: 1;
- uint32_t duration1: 15;
- uint32_t level1: 1;
- };
- uint32_t val;
- } data[64];
+ rmt_item32_t data32[64];
+ rmt_item16_t data16[128];
+ };
} chan[8];
} rmt_mem_t;
extern rmt_mem_t RMTMEM;
--- /dev/null
+#
+# This is a project Makefile. It is assumed the directory this Makefile resides in is a
+# project subdirectory.
+#
+
+PROJECT_NAME := infrared_nec
+
+include $(IDF_PATH)/make/project.mk
+
--- /dev/null
+#
+# Main Makefile. This is basically the same as a component makefile.
+#
+# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
--- /dev/null
+/* NEC remote infrared RMT example
+
+ This example code is in the Public Domain (or CC0 licensed, at your option.)
+
+ Unless required by applicable law or agreed to in writing, this
+ software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ CONDITIONS OF ANY KIND, either express or implied.
+*/
+#include <stdio.h>
+#include <string.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/queue.h"
+#include "freertos/semphr.h"
+#include "esp_err.h"
+#include "esp_log.h"
+#include "driver/rmt.h"
+#include "driver/periph_ctrl.h"
+#include "soc/rmt_reg.h"
+
+static const char* NEC_TAG = "NEC";
+
+//CHOOSE SELF TEST OR NORMAL TEST
+#define RMT_RX_SELF_TEST 1
+
+/******************************************************/
+/***** SELF TEST: *****/
+/*Connect RMT_TX_GPIO_NUM with RMT_RX_GPIO_NUM */
+/*TX task will send NEC data with carrier disabled */
+/*RX task will print NEC data it receives. */
+/******************************************************/
+#if RMT_RX_SELF_TEST
+#define RMT_RX_ACTIVE_LEVEL 1 /*!< Data bit is active high for self test mode */
+#define RMT_TX_CARRIER_EN 0 /*!< Disable carrier for self test mode */
+#else
+//Test with infrared LED, we have to enable carrier for transmitter
+//When testing via IR led, the receiver waveform is usually active-low.
+#define RMT_RX_ACTIVE_LEVEL 0 /*!< If we connect with a IR receiver, the data is active low */
+#define RMT_TX_CARRIER_EN 1 /*!< Enable carrier for IR transmitter test with IR led */
+#endif
+
+#define RMT_TX_CHANNEL 1 /*!< RMT channel for transmitter */
+#define RMT_TX_GPIO_NUM 16 /*!< GPIO number for transmitter signal */
+#define RMT_RX_CHANNEL 0 /*!< RMT channel for receiver */
+#define RMT_RX_GPIO_NUM 19 /*!< GPIO number for receiver */
+#define RMT_INTR_NUM 19 /*!< RMT interrupt number, select from soc.h */
+#define RMT_CLK_DIV 100 /*!< RMT counter clock divider */
+#define RMT_TICK_10_US (80000000/RMT_CLK_DIV/100000) /*!< RMT counter value for 10 us.(Source clock is APB clock) */
+
+#define NEC_HEADER_HIGH_US 9000 /*!< NEC protocol header: positive 9ms */
+#define NEC_HEADER_LOW_US 4500 /*!< NEC protocol header: negative 4.5ms*/
+#define NEC_BIT_ONE_HIGH_US 560 /*!< NEC protocol data bit 1: positive 0.56ms */
+#define NEC_BIT_ONE_LOW_US (2250-NEC_BIT_ONE_HIGH_US) /*!< NEC protocol data bit 1: negative 1.69ms */
+#define NEC_BIT_ZERO_HIGH_US 560 /*!< NEC protocol data bit 0: positive 0.56ms */
+#define NEC_BIT_ZERO_LOW_US (1120-NEC_BIT_ZERO_HIGH_US) /*!< NEC protocol data bit 0: negative 0.56ms */
+#define NEC_BIT_END 560 /*!< NEC protocol end: positive 0.56ms */
+#define NEC_BIT_MARGIN 20 /*!< NEC parse margin time */
+
+#define NEC_ITEM_DURATION(d) ((d & 0x7fff)*10/RMT_TICK_10_US) /*!< Parse duration time from memory register value */
+#define NEC_DATA_ITEM_NUM 34 /*!< NEC code item number: header + 32bit data + end */
+#define RMT_TX_DATA_NUM 100 /*!< NEC tx test data number */
+#define rmt_item32_tIMEOUT_US 9500 /*!< RMT receiver timeout value(us) */
+
+/*
+ * @brief Build register value of waveform for NEC one data bit
+ */
+inline void nec_fill_item_level(rmt_item32_t* item, int high_us, int low_us)
+{
+ item->level0 = 1;
+ item->duration0 = (high_us) / 10 * RMT_TICK_10_US;
+ item->level1 = 0;
+ item->duration1 = (low_us) / 10 * RMT_TICK_10_US;
+}
+
+/*
+ * @brief Generate NEC header value: active 9ms + negative 4.5ms
+ */
+static void nec_fill_item_header(rmt_item32_t* item)
+{
+ nec_fill_item_level(item, NEC_HEADER_HIGH_US, NEC_HEADER_LOW_US);
+}
+
+/*
+ * @brief Generate NEC data bit 1: positive 0.56ms + negative 1.69ms
+ */
+static void nec_fill_item_bit_one(rmt_item32_t* item)
+{
+ nec_fill_item_level(item, NEC_BIT_ONE_HIGH_US, NEC_BIT_ONE_LOW_US);
+}
+
+/*
+ * @brief Generate NEC data bit 0: positive 0.56ms + negative 0.56ms
+ */
+static void nec_fill_item_bit_zero(rmt_item32_t* item)
+{
+ nec_fill_item_level(item, NEC_BIT_ZERO_HIGH_US, NEC_BIT_ZERO_LOW_US);
+}
+
+/*
+ * @brief Generate NEC end signal: positive 0.56ms
+ */
+static void nec_fill_item_end(rmt_item32_t* item)
+{
+ nec_fill_item_level(item, NEC_BIT_END, 0x7fff);
+}
+
+/*
+ * @brief Check whether duration is around target_us
+ */
+inline bool nec_check_in_range(int duration_ticks, int target_us, int margin_us)
+{
+ if(( NEC_ITEM_DURATION(duration_ticks) < (target_us + margin_us))
+ && ( NEC_ITEM_DURATION(duration_ticks) > (target_us - margin_us))) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+/*
+ * @brief Check whether this value represents an NEC header
+ */
+static bool nec_header_if(rmt_item32_t* item)
+{
+ if((item->level0 == RMT_RX_ACTIVE_LEVEL && item->level1 != RMT_RX_ACTIVE_LEVEL)
+ && nec_check_in_range(item->duration0, NEC_HEADER_HIGH_US, NEC_BIT_MARGIN)
+ && nec_check_in_range(item->duration1, NEC_HEADER_LOW_US, NEC_BIT_MARGIN)) {
+ return true;
+ }
+ return false;
+}
+
+/*
+ * @brief Check whether this value represents an NEC data bit 1
+ */
+static bool nec_bit_one_if(rmt_item32_t* item)
+{
+ if((item->level0 == RMT_RX_ACTIVE_LEVEL && item->level1 != RMT_RX_ACTIVE_LEVEL)
+ && nec_check_in_range(item->duration0, NEC_BIT_ONE_HIGH_US, NEC_BIT_MARGIN)
+ && nec_check_in_range(item->duration1, NEC_BIT_ONE_LOW_US, NEC_BIT_MARGIN)) {
+ return true;
+ }
+ return false;
+}
+
+/*
+ * @brief Check whether this value represents an NEC data bit 0
+ */
+static bool nec_bit_zero_if(rmt_item32_t* item)
+{
+ if((item->level0 == RMT_RX_ACTIVE_LEVEL && item->level1 != RMT_RX_ACTIVE_LEVEL)
+ && nec_check_in_range(item->duration0, NEC_BIT_ZERO_HIGH_US, NEC_BIT_MARGIN)
+ && nec_check_in_range(item->duration1, NEC_BIT_ZERO_LOW_US, NEC_BIT_MARGIN)) {
+ return true;
+ }
+ return false;
+}
+
+
+/*
+ * @brief Parse NEC 32 bit waveform to address and command.
+ */
+static int nec_parse_items(rmt_item32_t* item, int item_num, uint16_t* addr, uint16_t* data)
+{
+ int w_len = item_num;
+ if(w_len < NEC_DATA_ITEM_NUM) {
+ return -1;
+ }
+ int i = 0, j = 0;
+ if(!nec_header_if(item++)) {
+ return -1;
+ }
+ uint16_t addr_t = 0;
+ for(j = 0; j < 16; j++) {
+ if(nec_bit_one_if(item)) {
+ addr_t |= (1 << j);
+ } else if(nec_bit_zero_if(item)) {
+ addr_t |= (0 << j);
+ } else {
+ return -1;
+ }
+ item++;
+ i++;
+ }
+ uint16_t data_t = 0;
+ for(j = 0; j < 16; j++) {
+ if(nec_bit_one_if(item)) {
+ data_t |= (1 << j);
+ } else if(nec_bit_zero_if(item)) {
+ data_t |= (0 << j);
+ } else {
+ return -1;
+ }
+ item++;
+ i++;
+ }
+ *addr = addr_t;
+ *data = data_t;
+ return i;
+}
+
+/*
+ * @brief Build NEC 32bit waveform.
+ */
+static int nec_build_items(int channel, rmt_item32_t* item, int item_num, uint16_t addr, uint16_t cmd_data)
+{
+ int i = 0, j = 0;
+ if(item_num < NEC_DATA_ITEM_NUM) {
+ return -1;
+ }
+ nec_fill_item_header(item++);
+ i++;
+ for(j = 0; j < 16; j++) {
+ if(addr & 0x1) {
+ nec_fill_item_bit_one(item);
+ } else {
+ nec_fill_item_bit_zero(item);
+ }
+ item++;
+ i++;
+ addr >>= 1;
+ }
+ for(j = 0; j < 16; j++) {
+ if(cmd_data & 0x1) {
+ nec_fill_item_bit_one(item);
+ } else {
+ nec_fill_item_bit_zero(item);
+ }
+ item++;
+ i++;
+ cmd_data >>= 1;
+ }
+ nec_fill_item_end(item);
+ i++;
+ return i;
+}
+
+/*
+ * @brief RMT transmitter initialization
+ */
+static void rmt_tx_init()
+{
+ rmt_config_t rmt_tx;
+ rmt_tx.channel = RMT_TX_CHANNEL;
+ rmt_tx.gpio_num = RMT_TX_GPIO_NUM;
+ rmt_tx.mem_block_num = 1;
+ rmt_tx.clk_div = RMT_CLK_DIV;
+ rmt_tx.tx_config.loop_en = false;
+ rmt_tx.tx_config.carrier_duty_percent = 50;
+ rmt_tx.tx_config.carrier_freq_hz = 38000;
+ rmt_tx.tx_config.carrier_level = 1;
+ rmt_tx.tx_config.carrier_en = RMT_TX_CARRIER_EN;
+ rmt_tx.tx_config.idle_level = 0;
+ rmt_tx.tx_config.idle_output_en = true;
+ rmt_tx.rmt_mode = 0;
+ rmt_config(&rmt_tx);
+ rmt_driver_install(rmt_tx.channel, 0, RMT_INTR_NUM);
+}
+
+/*
+ * @brief RMT receiver initialization
+ */
+void rmt_rx_init()
+{
+ rmt_config_t rmt_rx;
+ rmt_rx.channel = RMT_RX_CHANNEL;
+ rmt_rx.gpio_num = RMT_RX_GPIO_NUM;
+ rmt_rx.clk_div = RMT_CLK_DIV;
+ rmt_rx.mem_block_num = 1;
+ rmt_rx.rmt_mode = RMT_MODE_RX;
+ rmt_rx.rx_config.filter_en = true;
+ rmt_rx.rx_config.filter_ticks_thresh = 100;
+ rmt_rx.rx_config.idle_threshold = rmt_item32_tIMEOUT_US / 10 * (RMT_TICK_10_US);
+ rmt_config(&rmt_rx);
+ rmt_driver_install(rmt_rx.channel, 1000, RMT_INTR_NUM);
+}
+
+/**
+ * @brief RMT receiver demo, this task will print each received NEC data.
+ *
+ */
+void rmt_nec_rx_task()
+{
+ int channel = RMT_RX_CHANNEL;
+ rmt_rx_init();
+ RingbufHandle_t rb = NULL;
+ //get RMT RX ringbuffer
+ rmt_get_ringbuf_handler(channel, &rb);
+ rmt_rx_start(channel, 1);
+ while(rb) {
+ size_t rx_size = 0;
+ //try to receive data from ringbuffer.
+ //RMT driver will push all the data it receives to its ringbuffer.
+ //We just need to parse the value and return the spaces of ringbuffer.
+ rmt_item32_t* item = (rmt_item32_t*) xRingbufferReceive(rb, &rx_size, 1000);
+ if(item) {
+ uint16_t rmt_addr;
+ uint16_t rmt_cmd;
+ int offset = 0;
+ while(1) {
+ //parse data value from ringbuffer.
+ int res = nec_parse_items(item + offset, rx_size / 4 - offset, &rmt_addr, &rmt_cmd);
+ if(res > 0) {
+ offset += res + 1;
+ ESP_LOGI(NEC_TAG, "RMT RCV --- addr: 0x%04x cmd: 0x%04x", rmt_addr, rmt_cmd);
+ } else {
+ break;
+ }
+ }
+ //after parsing the data, return spaces to ringbuffer.
+ vRingbufferReturnItem(rb, (void*) item);
+ } else {
+ break;
+ }
+ }
+ vTaskDelete(NULL);
+}
+
+/**
+ * @brief RMT transmitter demo, this task will periodically send NEC data. (100 * 32 bits each time.)
+ *
+ */
+void rmt_nec_tx_task()
+{
+ vTaskDelay(10);
+ rmt_tx_init();
+ esp_log_level_set(NEC_TAG, ESP_LOG_INFO);
+ int channel = RMT_TX_CHANNEL;
+ uint16_t cmd = 0x0;
+ uint16_t addr = 0x11;
+ int nec_tx_num = RMT_TX_DATA_NUM;
+ for(;;) {
+ ESP_LOGI(NEC_TAG, "RMT TX DATA");
+ size_t size = (sizeof(rmt_item32_t) * NEC_DATA_ITEM_NUM * nec_tx_num);
+ //each item represent a cycle of waveform.
+ rmt_item32_t* item = (rmt_item32_t*) malloc(size);
+ int item_num = NEC_DATA_ITEM_NUM * nec_tx_num;
+ memset((void*) item, 0, size);
+ int i, offset = 0;
+ while(1) {
+ //To build a series of waveforms.
+ i = nec_build_items(channel, item + offset, item_num - offset, ((~addr) << 8) | addr, cmd);
+ if(i < 0) {
+ break;
+ }
+ cmd++;
+ addr++;
+ offset += i;
+ }
+ //To send data according to the waveform items.
+ rmt_write_items(channel, item, item_num, true);
+ //Wait until sending is done.
+ rmt_wait_tx_done(channel);
+ //before we free the data, make sure sending is already done.
+ free(item);
+ vTaskDelay(2000 / portTICK_RATE_MS);
+ }
+ vTaskDelete(NULL);
+}
--- /dev/null
+/* NEC remote infrared RMT example
+
+ This example code is in the Public Domain (or CC0 licensed, at your option.)
+
+ Unless required by applicable law or agreed to in writing, this
+ software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ CONDITIONS OF ANY KIND, either express or implied.
+*/
+#include <stdio.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "esp_system.h"
+#include "nvs_flash.h"
+#include "driver/rmt.h"
+#include "driver/periph_ctrl.h"
+extern void rmt_nec_tx_task();
+extern void rmt_nec_rx_task();
+
+void app_main()
+{
+ xTaskCreate(rmt_nec_rx_task, "rmt_nec_rx_task", 2048, NULL, 10, NULL);
+ xTaskCreate(rmt_nec_tx_task, "rmt_nec_tx_task", 2048, NULL, 10, NULL);
+}