mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
add RMT driver and example
This commit is contained in:
parent
07fc868ea6
commit
6a1dbc3f1c
@ -39,6 +39,7 @@ typedef enum {
|
||||
PERIPH_PWM3_MODULE,
|
||||
PERIPH_UHCI0_MODULE,
|
||||
PERIPH_UHCI1_MODULE,
|
||||
PERIPH_RMT_MODULE,
|
||||
} periph_module_t;
|
||||
|
||||
/**
|
||||
|
785
components/driver/include/driver/rmt.h
Normal file
785
components/driver/include/driver/rmt.h
Normal file
@ -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_ */
|
@ -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);
|
||||
|
716
components/driver/rmt.c
Normal file
716
components/driver/rmt.c
Normal file
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -2163,7 +2163,8 @@
|
||||
#define RMT_DATE_V 0xFFFFFFFF
|
||||
#define RMT_DATE_S 0
|
||||
|
||||
|
||||
/* RMT memory block address */
|
||||
#define RMT_CHANNEL_MEM(i) (DR_REG_RMT_BASE + 0x800 + 64 * 4 * (i))
|
||||
|
||||
|
||||
#endif /*_SOC_RMT_REG_H_ */
|
||||
|
@ -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;
|
||||
|
9
examples/11_rmt_nec_tx_rx/Makefile
Normal file
9
examples/11_rmt_nec_tx_rx/Makefile
Normal file
@ -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
|
||||
|
4
examples/11_rmt_nec_tx_rx/main/component.mk
Normal file
4
examples/11_rmt_nec_tx_rx/main/component.mk
Normal file
@ -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.)
|
359
examples/11_rmt_nec_tx_rx/main/infrared_nec.c
Normal file
359
examples/11_rmt_nec_tx_rx/main/infrared_nec.c
Normal file
@ -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);
|
||||
}
|
23
examples/11_rmt_nec_tx_rx/main/infrared_nec_main.c
Normal file
23
examples/11_rmt_nec_tx_rx/main/infrared_nec_main.c
Normal file
@ -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);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user