]> granicus.if.org Git - esp-idf/commitdiff
add RMT driver and example
authorWangjialin <wangjialin@espressif.com>
Thu, 10 Nov 2016 03:23:40 +0000 (11:23 +0800)
committerIvan Grokhotkov <ivan@espressif.com>
Mon, 21 Nov 2016 16:57:19 +0000 (00:57 +0800)
components/driver/include/driver/periph_ctrl.h
components/driver/include/driver/rmt.h [new file with mode: 0644]
components/driver/periph_ctrl.c
components/driver/rmt.c [new file with mode: 0644]
components/esp32/include/esp_intr.h
components/esp32/include/soc/rmt_reg.h
components/esp32/include/soc/rmt_struct.h
examples/11_rmt_nec_tx_rx/Makefile [new file with mode: 0644]
examples/11_rmt_nec_tx_rx/main/component.mk [new file with mode: 0644]
examples/11_rmt_nec_tx_rx/main/infrared_nec.c [new file with mode: 0644]
examples/11_rmt_nec_tx_rx/main/infrared_nec_main.c [new file with mode: 0644]

index 3faa347b54a9a48c2de2c2fc28dea8681cb30798..d7a98284b7045060c90f62cbb64f9da6f6cc3c10 100644 (file)
@@ -39,6 +39,7 @@ typedef enum {
     PERIPH_PWM3_MODULE,
     PERIPH_UHCI0_MODULE,
     PERIPH_UHCI1_MODULE,
+    PERIPH_RMT_MODULE,
 } periph_module_t;
 
 /**
diff --git a/components/driver/include/driver/rmt.h b/components/driver/include/driver/rmt.h
new file mode 100644 (file)
index 0000000..d5d8d19
--- /dev/null
@@ -0,0 +1,785 @@
+// 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_ */
index 3a671abad4cda8c5f00b97f9e2a9bc73efa3964e..00987768af33fd961fa3c03905e36a06f1f28ee3 100644 (file)
@@ -25,6 +25,10 @@ void periph_module_enable(periph_module_t periph)
 {
     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);
diff --git a/components/driver/rmt.c b/components/driver/rmt.c
new file mode 100644 (file)
index 0000000..21bc869
--- /dev/null
@@ -0,0 +1,716 @@
+// 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;
+}
+
index e138133f64f34fb46cf3d96ea4f00448c6deb1a4..579eb6353d87ed6a497f6d8a8b035b7aeac1ce5f 100644 (file)
@@ -82,126 +82,6 @@ extern "C" {
 #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
index a95aa6c0848c610108bb911de35294d9be5988d6..07ba24ac70cf7a991f60b1a2ed419effddbcbcea 100644 (file)
 #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
index 511fefa2671c340be0c51c4f0f9201e347bc34c7..335cb81940b62d7cdfcda7e3d82b734cfa1f44b9 100644 (file)
@@ -226,18 +226,35 @@ typedef volatile struct {
 } 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;
diff --git a/examples/11_rmt_nec_tx_rx/Makefile b/examples/11_rmt_nec_tx_rx/Makefile
new file mode 100644 (file)
index 0000000..df418c6
--- /dev/null
@@ -0,0 +1,9 @@
+#
+# 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
+
diff --git a/examples/11_rmt_nec_tx_rx/main/component.mk b/examples/11_rmt_nec_tx_rx/main/component.mk
new file mode 100644 (file)
index 0000000..b4fa727
--- /dev/null
@@ -0,0 +1,4 @@
+#
+# 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.)
diff --git a/examples/11_rmt_nec_tx_rx/main/infrared_nec.c b/examples/11_rmt_nec_tx_rx/main/infrared_nec.c
new file mode 100644 (file)
index 0000000..dbd11fb
--- /dev/null
@@ -0,0 +1,359 @@
+/* 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);
+}
diff --git a/examples/11_rmt_nec_tx_rx/main/infrared_nec_main.c b/examples/11_rmt_nec_tx_rx/main/infrared_nec_main.c
new file mode 100644 (file)
index 0000000..c680f25
--- /dev/null
@@ -0,0 +1,23 @@
+/* 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);
+}