Skip to content

Latest commit

 

History

History
323 lines (216 loc) · 16.8 KB

File metadata and controls

323 lines (216 loc) · 16.8 KB

Remote Control (RMT)

The RMT (Remote Control) module driver can be used to send and receive infrared remote control signals. Due to flexibility of RMT module, the driver can also be used to generate or receive many other types of signals.

The signal, which consists of a series of pulses, is generated by RMT's transmitter based on a list of values. The values define the pulse duration and a binary level, see below. The transmitter can also provide a carrier and modulate it with provided pulses.

.. blockdiag::
    :scale: 100
    :caption: RMT Transmitter Overview
    :align: center

    blockdiag rmt_tx {

        node_width = 80;
        node_height = 60;
        default_group_color = lightgrey;

        a -> b -> c -> d;
        e -> f -> g -- h;
        d -> o [label=GPIO];
        h -> d [folded];

        a [style=none, width=100, label="{11,high,7,low},\n{5,high,5,low},\n..."]
        b [label="Waveform\nGenerator"]
        c [style=none, label="", background="../../../_static/rmt-waveform.png"]
        d [shape=beginpoint, label="mod"]
        e [style=none, width=60, height=40, label="Carrier\nenable"]
        f [label="Carrier\nGenerator"]
        g [style=none, label="", background="../../../_static/rmt-carrier.png"]
        h [shape=none]
        o [style=none, label="", background="../../../_static/rmt-waveform-modulated.png"]

        group {
            label = Input
            a,e;
        }
        group {
            label = "RMT Transmitter"
            b,f,c,g,d,h;
        }
        group {
            label = Output
            o;
        }
    }

The reverse operation is performed by the receiver, where a series of pulses is decoded into a list of values containing the pulse duration and binary level. A filter may be applied to remove high frequency noise from the input signal.

.. blockdiag::
    :scale: 90
    :caption: RMT Receiver Overview
    :align: center

    blockdiag rmt_rx {

        node_width = 80;
        node_height = 60;
        default_group_color = lightgrey;

        a -> b [label=GPIO];
        b -> c -> d;
        e -- f;
        f -> b [folded];

        a [style=none, label="", background="../../../_static/rmt-waveform.png"]
        b [label=Filter]
        c [label="Edge\nDetect"]
        d [style=none, width=100, label="{11,high,7,low},\n{5,high,5,low},\n..."]
        e [style=none, width=60, height=40, label="Filter\nenable"]
        f [shape=none, label=""]

        group {
            label = Input
            a,e;
        }
        group {
            label = "RMT Receiver"
            b,c;
        }
        group {
            label = Output
            d;
        }
    }

There couple of typical steps to setup and operate the RMT and they are discussed in the following sections:

  1. Configure Driver
  2. Transmit Data or Receive Data
  3. Change Operation Parameters
  4. Use Interrupts
.. only:: esp32

    The RMT has eight channels numbered from zero to seven. Each channel is able to independently transmit or receive data. They are referred to using indexes defined in structure :cpp:type:`rmt_channel_t`.

.. only:: esp32s2

    The RMT has four channels numbered from zero to three. Each channel is able to independently transmit or receive data. They are referred to using indexes defined in structure :cpp:type:`rmt_channel_t`.

.. only:: esp32c3

    The RMT has four channels numbered from zero to three. The first half (i.e. Channel 0 ~ 1) channels can only be configured for transmitting, and the other half (i.e. Channel 2 ~ 3) channels can only be configured for receiving. They are referred to using indexes defined in structure :cpp:type:`rmt_channel_t`.

.. only:: esp32s3

    The RMT has eight channels numbered from zero to seven. The first half (i.e. Channel 0 ~ 3) channels can only be configured for transmitting, and the other half (i.e. Channel 4 ~ 7) channels can only be configured for receiving. They are referred to using indexes defined in structure :cpp:type:`rmt_channel_t`.

Configure Driver

There are several parameters that define how particular channel operates. Most of these parameters are configured by setting specific members of :cpp:type:`rmt_config_t` structure. Some of the parameters are common to both transmit or receive mode, and some are mode specific. They are all discussed below.

Common Parameters

  • The channel to be configured, select one from the :cpp:type:`rmt_channel_t` enumerator.

  • The RMT operation mode - whether this channel is used to transmit or receive data, selected by setting a rmt_mode members to one of the values from :cpp:type:`rmt_mode_t`.

  • What is the pin number to transmit or receive RMT signals, selected by setting gpio_num.

  • How many memory blocks will be used by the channel, set with mem_block_num.

  • Extra miscellaneous parameters for the channel can be set in the flags.

    • When RMT_CHANNEL_FLAGS_AWARE_DFS is set, RMT channel will take REF_TICK or XTAL as source clock. The benefit is, RMT channel can continue work even when APB clock is changing. See :doc:`power_management <../system/power_management>` for more information.
    • When RMT_CHANNEL_FLAGS_INVERT_SIG is set, the driver will invert the RMT signal sending to or receiving from the channel. It just works like an external inverter connected to the GPIO of certain RMT channel.
  • A clock divider, that will determine the range of pulse length generated by the RMT transmitter or discriminated by the receiver. Selected by setting clk_div to a value within [1 .. 255] range. The RMT source clock is typically APB CLK, 80Mhz by default. But when RMT_CHANNEL_FLAGS_AWARE_DFS is set in flags, RMT source clock is changed to REF_TICK or XTAL.

Note

The period of a square wave after the clock divider is called a 'tick'. The length of the pulses generated by the RMT transmitter or discriminated by the receiver is configured in number of 'ticks'.

There are also couple of specific parameters that should be set up depending if selected channel is configured in Transmit Mode or Receive Mode:

Transmit Mode

When configuring channel in transmit mode, set tx_config and the following members of :cpp:type:`rmt_tx_config_t`:

.. list::

    * Transmit the currently configured data items in a loop - **loop_en**
    * Enable the RMT carrier signal - **carrier_en**
    * Frequency of the carrier in Hz - **carrier_freq_hz**
    * Duty cycle of the carrier signal in percent (%) - **carrier_duty_percent**
    * Level of the RMT output, when the carrier is applied - **carrier_level**
    * Enable the RMT output if idle - **idle_output_en**
    * Set the signal level on the RMT output if idle - **idle_level**
    :SOC_RMT_SUPPORT_TX_LOOP_COUNT: * Specify maximum number of transmissions in a loop - **loop_count**

Receive Mode

In receive mode, set rx_config and the following members of :cpp:type:`rmt_rx_config_t`:

.. list::

    * Enable a filter on the input of the RMT receiver - **filter_en**
    * A threshold of the filter, set in the number of ticks - **filter_ticks_thresh**. Pulses shorter than this setting will be filtered out. Note, that the range of entered tick values is [0..255].
    * A pulse length threshold that will turn the RMT receiver idle, set in number of ticks - **idle_threshold**. The receiver will ignore pulses longer than this setting.
    :SOC_RMT_SUPPORT_RX_DEMODULATION: * Enable the RMT carrier demodulation - **carrier_rm**
    :SOC_RMT_SUPPORT_RX_DEMODULATION: * Frequency of the carrier in Hz - **carrier_freq_hz**
    :SOC_RMT_SUPPORT_RX_DEMODULATION: * Duty cycle of the carrier signal in percent (%) - **carrier_duty_percent**
    :SOC_RMT_SUPPORT_RX_DEMODULATION: * Level of the RMT input, where the carrier is modulated to - **carrier_level**

Finalize Configuration

Once the :cpp:type:`rmt_config_t` structure is populated with parameters, it should be then invoked with :cpp:func:`rmt_config` to make the configuration effective.

The last configuration step is installation of the driver in memory by calling :cpp:func:`rmt_driver_install`. If :cpp:type:`rx_buf_size` parameter of this function is > 0, then a ring buffer for incoming data will be allocated. A default ISR handler will be installed, see a note in Use Interrupts.

Now, depending on how the channel is configured, we are ready to either Transmit Data or Receive Data. This is described in next two sections.

Transmit Data

Before being able to transmit some RMT pulses, we need to define the pulse pattern. The minimum pattern recognized by the RMT controller, later called an 'item', is provided in a structure :cpp:type:`rmt_item32_t`. Each item consists of two pairs of two values. The first value in a pair describes the signal duration in ticks and is 15 bits long, the second provides the signal level (high or low) and is contained in a single bit. A block of couple of items and the structure of an item is presented below.

.. packetdiag::
    :caption: Structure of RMT items (L - signal level)
    :align: center

    packetdiag rmt_items {
        colwidth = 32
        node_width = 10
        node_height = 24
        default_fontsize = 12

        0-14: Period (15)
        15: L
        16-30: Period (15)
        31: L
        32-95: ... [colheight=2]
        96-110: Period (15)
        111: L
        112-126: Period (15)
        127: L
    }

For a simple example how to define a block of items see :example:`peripherals/rmt/morse_code`.

The items are provided to the RMT controller by calling function :cpp:func:`rmt_write_items`. This function also automatically triggers start of transmission. It may be called to wait for transmission completion or exit just after transmission start. In such case you can wait for the transmission end by calling :cpp:func:`rmt_wait_tx_done`. This function does not limit the number of data items to transmit. It is using an interrupt to successively copy the new data chunks to RMT's internal memory as previously provided data are sent out.

Another way to provide data for transmission is by calling :cpp:func:`rmt_fill_tx_items`. In this case transmission is not started automatically. To control the transmission process use :cpp:func:`rmt_tx_start` and :cpp:func:`rmt_tx_stop`. The number of items to sent is restricted by the size of memory blocks allocated in the RMT controller's internal memory, see :cpp:func:`rmt_set_mem_block_num`.

Receive Data

.. only:: esp32

    .. warning::
        RMT RX channel can't receive packet whose items are larger than its memory block size. If you set the memory block number to 1, then this RX channel can't receive packet with more than 64 items. This is a hardware limitation.

.. only:: esp32

    Before starting the receiver we need some storage for incoming items. The RMT controller has 512 x 32-bits of internal RAM shared between all eight channels.

.. only:: esp32s2

    Before starting the receiver we need some storage for incoming items. The RMT controller has 256 x 32-bits of internal RAM shared between all four channels.

.. only:: esp32c3

    Before starting the receiver we need some storage for incoming items. The RMT controller has 192 x 32-bits of internal RAM shared between all four channels.

.. only:: esp32s3

    Before starting the receiver we need some storage for incoming items. The RMT controller has 384 x 32-bits of internal RAM shared between all eight channels.

In typical scenarios it is not enough as an ultimate storage for all incoming (and outgoing) items. Therefore this API supports retrieval of incoming items on the fly to save them in a ring buffer of a size defined by the user. The size is provided when calling :cpp:func:`rmt_driver_install` discussed above. To get a handle to this buffer call :cpp:func:`rmt_get_ringbuf_handle`.

With the above steps complete we can start the receiver by calling :cpp:func:`rmt_rx_start` and then move to checking what's inside the buffer. To do so, you can use common FreeRTOS functions that interact with the ring buffer. Please see an example how to do it in :example:`peripherals/rmt/ir_protocols`.

To stop the receiver, call :cpp:func:`rmt_rx_stop`.

Change Operation Parameters

Previously described function :cpp:func:`rmt_config` provides a convenient way to set several configuration parameters in one shot. This is usually done on application start. Then, when the application is running, the API provides an alternate way to update individual parameters by calling dedicated functions. Each function refers to the specific RMT channel provided as the first input parameter. Most of the functions have _get_ counterpart to read back the currently configured value.

Parameters Common to Transmit and Receive Mode

Transmit Mode Parameters

.. only:: SOC_RMT_SUPPORT_TX_LOOP_COUNT

    * Enable or disable loop count feature to automatically transmit items for N iterations, then trigger an ISR callback - :cpp:func:`rmt_set_tx_loop_count`
    * Enable automatically stopping when the number of iterations matches the set loop count. Note this is not reliable for target that doesn't support `SOC_RMT_SUPPORT_TX_LOOP_AUTOSTOP`. - :cpp:func:`rmt_enable_tx_loop_autostop`


Receive Mode Parameters

Use Interrupts

Registering of an interrupt handler for the RMT controller is done by calling :cpp:func:`rmt_isr_register`.

The RMT controller triggers interrupts on four specific events describes below. To enable interrupts on these events, the following functions are provided:

Setting or clearing an interrupt enable mask for specific channels and events may be also done by calling :cpp:func:`rmt_set_intr_enable_mask` or :cpp:func:`rmt_clr_intr_enable_mask`.

When servicing an interrupt within an ISR, the interrupt need to explicitly cleared. To do so, set specific bits described as RMT.int_clr.val.chN_event_name and defined as a volatile struct in :component_file:`soc/{IDF_TARGET_PATH_NAME}/include/soc/rmt_struct.h`, where N is the RMT channel number [0, n] and the event_name is one of four events described above.

If you do not need an ISR anymore, you can de-register it by calling a function :cpp:func:`rmt_isr_deregister`.

Warning

:cpp:func:`rmt_isr_register` is provided as a public API by mistake. It's not recommended to use it directly. Even if you have registered a customized ISR handler with that, you still don't have the context of the RMT driver object. The driver has registered a dedicated ISR handler called rmt_driver_isr_default within :cpp:func:`rmt_driver_install`.

If you want to get a notification at some specific event (for example, "transaction done"), you can register callback functions like :cpp:func:`rmt_register_tx_end_callback`.

Uninstall Driver

If the RMT driver has been installed with :cpp:func:`rmt_driver_install` for some specific period of time and then not required, the driver may be removed to free allocated resources by calling :cpp:func:`rmt_driver_uninstall`.

Application Examples

API Reference

.. include-build-file:: inc/rmt.inc
.. include-build-file:: inc/rmt_types.inc