doc: update api reference for new rmt driver

pull/8968/head
morris 2022-04-07 13:15:23 +08:00
rodzic f472551910
commit a7f6f8677a
15 zmienionych plików z 710 dodań i 301 usunięć

Wyświetl plik

@ -0,0 +1,24 @@
blockdiag rmt_encoder_chain {
orientation = portrait;
default_fontsize = 14;
node_width = 160;
node_height = 40;
span_width = 80;
span_height = 40;
new [label = "New", shape = "beginpoint"];
init [label = "Init"];
encoder_a [label = "Encoder A"];
encoder_b [label = "Encoder B"];
other_encoder [shape = "dots"];
yield [label = "Yield", shape = "endpoint"];
new -> init [folded];
init -> encoder_a [thick];
encoder_a <-> yield [folded, thick, color = "green", label = "full"];
encoder_a -> encoder_b [thick];
encoder_b <-> yield [color = "green", thick, label = "full"];
encoder_b -> other_encoder [style = "dotted"];
encoder_a, encoder_b -> init [thick, color = red, label = "reset"];
other_encoder -> init [thick, color = red, label = "finish"];
}

Wyświetl plik

@ -0,0 +1,45 @@
blockdiag rmt_rx {
node_width = 80;
node_height = 60;
default_group_color = lightgrey;
o -> p -> a;
q -> r;
r -- s;
s -> p [folded];
a -> b [label=GPIO];
b -> c -> d;
e -- f;
f -> b [folded];
o [style=none, label="", background="../../../_static/rmt-waveform-modulated.png"]
p [shape=endpoint, label="demod"]
q [label="Remove\nCarrier"]
r [style=none, label="", background="../../../_static/rmt-carrier.png"]
s [shape=none, label=""]
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="Enable\nFilter"]
f [shape=none, label=""]
group {
label = "Optional"
q,r,o,p;
}
group {
label = "Input"
a,e;
}
group {
label = "RMT Receiver"
b,c;
}
group {
label = "Output"
d;
}
}

Wyświetl plik

@ -0,0 +1,16 @@
packetdiag rmt_symbols {
colwidth = 32
node_width = 16
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
}

Wyświetl plik

@ -0,0 +1,33 @@
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="Enable\nCarrier"]
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;
}
}

BIN
docs/_static/ir_nec.png vendored 100644

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 47 KiB

BIN
docs/_static/rmt_tx_sync.png vendored 100644

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 16 KiB

Wyświetl plik

@ -67,7 +67,6 @@ INPUT = \
$(PROJECT_PATH)/components/driver/include/driver/i2c.h \
$(PROJECT_PATH)/components/driver/include/driver/i2s.h \
$(PROJECT_PATH)/components/driver/include/driver/ledc.h \
$(PROJECT_PATH)/components/driver/include/driver/rmt.h \
$(PROJECT_PATH)/components/driver/include/driver/rtc_io.h \
$(PROJECT_PATH)/components/driver/include/driver/sdio_slave.h \
$(PROJECT_PATH)/components/driver/include/driver/sdmmc_host.h \
@ -76,8 +75,8 @@ INPUT = \
$(PROJECT_PATH)/components/driver/include/driver/sigmadelta.h \
$(PROJECT_PATH)/components/driver/include/driver/spi_common.h \
$(PROJECT_PATH)/components/driver/include/driver/spi_master.h \
$(PROJECT_PATH)/components/driver/include/driver/spi_slave_hd.h \
$(PROJECT_PATH)/components/driver/include/driver/spi_slave.h \
$(PROJECT_PATH)/components/driver/include/driver/spi_slave_hd.h \
$(PROJECT_PATH)/components/driver/include/driver/touch_sensor_common.h \
$(PROJECT_PATH)/components/driver/include/driver/twai.h \
$(PROJECT_PATH)/components/driver/include/driver/uart.h \
@ -87,13 +86,13 @@ INPUT = \
$(PROJECT_PATH)/components/esp_common/include/esp_check.h \
$(PROJECT_PATH)/components/esp_common/include/esp_err.h \
$(PROJECT_PATH)/components/esp_common/include/esp_idf_version.h \
$(PROJECT_PATH)/components/esp_eth/include/esp_eth.h \
$(PROJECT_PATH)/components/esp_eth/include/esp_eth_com.h \
$(PROJECT_PATH)/components/esp_eth/include/esp_eth_mac.h \
$(PROJECT_PATH)/components/esp_eth/include/esp_eth_netif_glue.h \
$(PROJECT_PATH)/components/esp_eth/include/esp_eth_phy.h \
$(PROJECT_PATH)/components/esp_eth/include/esp_eth.h \
$(PROJECT_PATH)/components/esp_event/include/esp_event_base.h \
$(PROJECT_PATH)/components/esp_event/include/esp_event.h \
$(PROJECT_PATH)/components/esp_event/include/esp_event_base.h \
$(PROJECT_PATH)/components/esp_http_client/include/esp_http_client.h \
$(PROJECT_PATH)/components/esp_http_server/include/esp_http_server.h \
$(PROJECT_PATH)/components/esp_https_ota/include/esp_https_ota.h \
@ -112,19 +111,19 @@ INPUT = \
$(PROJECT_PATH)/components/esp_lcd/include/esp_lcd_panel_vendor.h \
$(PROJECT_PATH)/components/esp_lcd/include/esp_lcd_types.h \
$(PROJECT_PATH)/components/esp_local_ctrl/include/esp_local_ctrl.h \
$(PROJECT_PATH)/components/esp_netif/include/esp_netif.h \
$(PROJECT_PATH)/components/esp_netif/include/esp_netif_ip_addr.h \
$(PROJECT_PATH)/components/esp_netif/include/esp_netif_net_stack.h \
$(PROJECT_PATH)/components/esp_netif/include/esp_netif_types.h \
$(PROJECT_PATH)/components/esp_netif/include/esp_netif.h \
$(PROJECT_PATH)/components/esp_netif/include/esp_vfs_l2tap.h \
$(PROJECT_PATH)/components/esp_phy/include/esp_phy_init.h \
$(PROJECT_PATH)/components/esp_pm/include/$(IDF_TARGET)/pm.h \
$(PROJECT_PATH)/components/esp_pm/include/esp_pm.h \
$(PROJECT_PATH)/components/esp_ringbuf/include/freertos/ringbuf.h \
$(PROJECT_PATH)/components/esp_rom/include/esp_rom_sys.h \
$(PROJECT_PATH)/components/esp_serial_slave_link/include/esp_serial_slave_link/essl.h \
$(PROJECT_PATH)/components/esp_serial_slave_link/include/esp_serial_slave_link/essl_sdio.h \
$(PROJECT_PATH)/components/esp_serial_slave_link/include/esp_serial_slave_link/essl_spi.h \
$(PROJECT_PATH)/components/esp_serial_slave_link/include/esp_serial_slave_link/essl.h \
$(PROJECT_PATH)/components/esp_system/include/esp_expression_with_stack.h \
$(PROJECT_PATH)/components/esp_system/include/esp_freertos_hooks.h \
$(PROJECT_PATH)/components/esp_system/include/esp_int_wdt.h \
@ -134,9 +133,9 @@ INPUT = \
$(PROJECT_PATH)/components/esp_wifi/include/esp_mesh.h \
$(PROJECT_PATH)/components/esp_wifi/include/esp_now.h \
$(PROJECT_PATH)/components/esp_wifi/include/esp_smartconfig.h \
$(PROJECT_PATH)/components/esp_wifi/include/esp_wifi.h \
$(PROJECT_PATH)/components/esp_wifi/include/esp_wifi_default.h \
$(PROJECT_PATH)/components/esp_wifi/include/esp_wifi_types.h \
$(PROJECT_PATH)/components/esp_wifi/include/esp_wifi.h \
$(PROJECT_PATH)/components/esp-tls/esp_tls_errors.h \
$(PROJECT_PATH)/components/esp-tls/esp_tls.h \
$(PROJECT_PATH)/components/fatfs/diskio/diskio_impl.h \
@ -159,7 +158,6 @@ INPUT = \
$(PROJECT_PATH)/components/hal/include/hal/i2s_types.h \
$(PROJECT_PATH)/components/hal/include/hal/lcd_types.h \
$(PROJECT_PATH)/components/hal/include/hal/ledc_types.h \
$(PROJECT_PATH)/components/hal/include/hal/rmt_types.h \
$(PROJECT_PATH)/components/hal/include/hal/rtc_io_types.h \
$(PROJECT_PATH)/components/hal/include/hal/sdio_slave_types.h \
$(PROJECT_PATH)/components/hal/include/hal/sigmadelta_types.h \
@ -169,25 +167,25 @@ INPUT = \
$(PROJECT_PATH)/components/hal/include/hal/touch_sensor_types.h \
$(PROJECT_PATH)/components/hal/include/hal/twai_types.h \
$(PROJECT_PATH)/components/hal/include/hal/uart_types.h \
$(PROJECT_PATH)/components/heap/include/esp_heap_caps_init.h \
$(PROJECT_PATH)/components/heap/include/esp_heap_caps.h \
$(PROJECT_PATH)/components/heap/include/esp_heap_caps_init.h \
$(PROJECT_PATH)/components/heap/include/esp_heap_trace.h \
$(PROJECT_PATH)/components/heap/include/multi_heap.h \
$(PROJECT_PATH)/components/ieee802154/include/esp_ieee802154_types.h \
$(PROJECT_PATH)/components/ieee802154/include/esp_ieee802154.h \
$(PROJECT_PATH)/components/ieee802154/include/esp_ieee802154_types.h \
$(PROJECT_PATH)/components/log/include/esp_log.h \
$(PROJECT_PATH)/components/lwip/include/apps/esp_sntp.h \
$(PROJECT_PATH)/components/lwip/include/apps/ping/ping_sock.h \
$(PROJECT_PATH)/components/mbedtls/esp_crt_bundle/include/esp_crt_bundle.h \
$(PROJECT_PATH)/components/mdns/include/mdns.h \
$(PROJECT_PATH)/components/mqtt/esp-mqtt/include/mqtt_client.h \
$(PROJECT_PATH)/components/nvs_flash/include/nvs_flash.h \
$(PROJECT_PATH)/components/nvs_flash/include/nvs.h \
$(PROJECT_PATH)/components/nvs_flash/include/nvs_flash.h \
$(PROJECT_PATH)/components/openthread/include/esp_openthread.h \
$(PROJECT_PATH)/components/openthread/include/esp_openthread_border_router.h \
$(PROJECT_PATH)/components/openthread/include/esp_openthread_lock.h \
$(PROJECT_PATH)/components/openthread/include/esp_openthread_netif_glue.h \
$(PROJECT_PATH)/components/openthread/include/esp_openthread_types.h \
$(PROJECT_PATH)/components/openthread/include/esp_openthread.h \
$(PROJECT_PATH)/components/perfmon/include/xtensa_perfmon_access.h \
$(PROJECT_PATH)/components/perfmon/include/xtensa_perfmon_apis.h \
$(PROJECT_PATH)/components/perfmon/include/xtensa_perfmon_masks.h \
@ -204,13 +202,13 @@ INPUT = \
$(PROJECT_PATH)/components/soc/$(IDF_TARGET)/include/soc/clk_tree_defs.h \
$(PROJECT_PATH)/components/soc/$(IDF_TARGET)/include/soc/soc_caps.h \
$(PROJECT_PATH)/components/soc/$(IDF_TARGET)/include/soc/uart_channel.h \
$(PROJECT_PATH)/components/spi_flash/include/esp_flash_spi_init.h \
$(PROJECT_PATH)/components/spi_flash/include/esp_flash.h \
$(PROJECT_PATH)/components/spi_flash/include/esp_flash_spi_init.h \
$(PROJECT_PATH)/components/spi_flash/include/esp_partition.h \
$(PROJECT_PATH)/components/spi_flash/include/esp_spi_flash.h \
$(PROJECT_PATH)/components/spiffs/include/esp_spiffs.h \
$(PROJECT_PATH)/components/tinyusb/additions/include/tinyusb_types.h \
$(PROJECT_PATH)/components/tinyusb/additions/include/tinyusb.h \
$(PROJECT_PATH)/components/tinyusb/additions/include/tinyusb_types.h \
$(PROJECT_PATH)/components/tinyusb/additions/include/tusb_cdc_acm.h \
$(PROJECT_PATH)/components/tinyusb/additions/include/tusb_config.h \
$(PROJECT_PATH)/components/tinyusb/additions/include/tusb_console.h \
@ -219,10 +217,10 @@ INPUT = \
$(PROJECT_PATH)/components/ulp/ulp_common/include/ulp_common.h \
$(PROJECT_PATH)/components/ulp/ulp_fsm/include/ulp_fsm_common.h \
$(PROJECT_PATH)/components/ulp/ulp_riscv/include/ulp_riscv.h \
$(PROJECT_PATH)/components/vfs/include/esp_vfs.h \
$(PROJECT_PATH)/components/vfs/include/esp_vfs_dev.h \
$(PROJECT_PATH)/components/vfs/include/esp_vfs_eventfd.h \
$(PROJECT_PATH)/components/vfs/include/esp_vfs_semihost.h \
$(PROJECT_PATH)/components/vfs/include/esp_vfs.h \
$(PROJECT_PATH)/components/wear_levelling/include/wear_levelling.h \
$(PROJECT_PATH)/components/wifi_provisioning/include/wifi_provisioning/manager.h \
$(PROJECT_PATH)/components/wifi_provisioning/include/wifi_provisioning/scheme_ble.h \

Wyświetl plik

@ -1,15 +1,21 @@
INPUT += \
$(PROJECT_PATH)/components/soc/$(IDF_TARGET)/include/soc/dac_channel.h \
$(PROJECT_PATH)/components/driver/$(IDF_TARGET)/include/driver/dac.h \
$(PROJECT_PATH)/components/hal/include/hal/mcpwm_types.h \
$(PROJECT_PATH)/components/driver/$(IDF_TARGET)/include/driver/touch_sensor.h \
$(PROJECT_PATH)/components/driver/include/driver/mcpwm.h \
$(PROJECT_PATH)/components/hal/include/hal/pcnt_types.h \
$(PROJECT_PATH)/components/driver/include/driver/pulse_cnt.h \
$(PROJECT_PATH)/components/driver/include/driver/rmt_common.h \
$(PROJECT_PATH)/components/driver/include/driver/rmt_encoder.h \
$(PROJECT_PATH)/components/driver/include/driver/rmt_rx.h \
$(PROJECT_PATH)/components/driver/include/driver/rmt_tx.h \
$(PROJECT_PATH)/components/driver/include/driver/rmt_types.h \
$(PROJECT_PATH)/components/esp_hw_support/include/esp_himem.h \
$(PROJECT_PATH)/components/esp_system/include/esp_ipc.h \
$(PROJECT_PATH)/components/esp_system/include/esp_ipc_isr.h \
$(PROJECT_PATH)/components/hal/include/hal/mcpwm_types.h \
$(PROJECT_PATH)/components/hal/include/hal/pcnt_types.h \
$(PROJECT_PATH)/components/hal/include/hal/rmt_types.h \
$(PROJECT_PATH)/components/soc/$(IDF_TARGET)/include/soc/dac_channel.h \
$(PROJECT_PATH)/components/soc/$(IDF_TARGET)/include/soc/rtc_io_channel.h \
$(PROJECT_PATH)/components/soc/$(IDF_TARGET)/include/soc/touch_sensor_channel.h \
$(PROJECT_PATH)/components/driver/$(IDF_TARGET)/include/driver/touch_sensor.h \
$(PROJECT_PATH)/components/esp_hw_support/include/esp_himem.h \
$(PROJECT_PATH)/components/ulp/ulp_fsm/include/$(IDF_TARGET)/ulp.h \
$(PROJECT_PATH)/components/ulp/ulp_common/include/$(IDF_TARGET)/ulp_common_defs.h \
$(PROJECT_PATH)/components/esp_system/include/esp_ipc.h \
$(PROJECT_PATH)/components/esp_system/include/esp_ipc_isr.h
$(PROJECT_PATH)/components/ulp/ulp_fsm/include/$(IDF_TARGET)/ulp.h \

Wyświetl plik

@ -1,4 +1,10 @@
INPUT += \
$(PROJECT_PATH)/components/driver/include/driver/rmt_common.h \
$(PROJECT_PATH)/components/driver/include/driver/rmt_encoder.h \
$(PROJECT_PATH)/components/driver/include/driver/rmt_rx.h \
$(PROJECT_PATH)/components/driver/include/driver/rmt_tx.h \
$(PROJECT_PATH)/components/driver/include/driver/rmt_types.h \
$(PROJECT_PATH)/components/driver/include/driver/temperature_sensor.h \
$(PROJECT_PATH)/components/esp_hw_support/include/soc/esp32c3/esp_ds.h \
$(PROJECT_PATH)/components/esp_hw_support/include/soc/esp32c3/esp_hmac.h
$(PROJECT_PATH)/components/esp_hw_support/include/soc/esp32c3/esp_hmac.h \
$(PROJECT_PATH)/components/hal/include/hal/rmt_types.h \

Wyświetl plik

@ -1,4 +1,10 @@
INPUT += \
$(PROJECT_PATH)/components/driver/include/driver/rmt_common.h \
$(PROJECT_PATH)/components/driver/include/driver/rmt_encoder.h \
$(PROJECT_PATH)/components/driver/include/driver/rmt_rx.h \
$(PROJECT_PATH)/components/driver/include/driver/rmt_tx.h \
$(PROJECT_PATH)/components/driver/include/driver/rmt_types.h \
$(PROJECT_PATH)/components/driver/include/driver/temperature_sensor.h \
$(PROJECT_PATH)/components/esp_hw_support/include/soc/esp32h2/esp_ds.h \
$(PROJECT_PATH)/components/esp_hw_support/include/soc/esp32h2/esp_hmac.h
$(PROJECT_PATH)/components/esp_hw_support/include/soc/esp32h2/esp_hmac.h \
$(PROJECT_PATH)/components/hal/include/hal/rmt_types.h \

Wyświetl plik

@ -1,21 +1,27 @@
INPUT += \
$(PROJECT_PATH)/components/soc/$(IDF_TARGET)/include/soc/dac_channel.h \
$(PROJECT_PATH)/components/driver/$(IDF_TARGET)/include/driver/dac.h \
$(PROJECT_PATH)/components/hal/include/hal/pcnt_types.h \
$(PROJECT_PATH)/components/driver/include/driver/pulse_cnt.h \
$(PROJECT_PATH)/components/soc/$(IDF_TARGET)/include/soc/rtc_io_channel.h \
$(PROJECT_PATH)/components/driver/include/driver/temperature_sensor.h \
$(PROJECT_PATH)/components/soc/$(IDF_TARGET)/include/soc/touch_sensor_channel.h \
$(PROJECT_PATH)/components/driver/$(IDF_TARGET)/include/driver/touch_sensor.h \
$(PROJECT_PATH)/components/driver/include/driver/pulse_cnt.h \
$(PROJECT_PATH)/components/driver/include/driver/rmt_common.h \
$(PROJECT_PATH)/components/driver/include/driver/rmt_encoder.h \
$(PROJECT_PATH)/components/driver/include/driver/rmt_rx.h \
$(PROJECT_PATH)/components/driver/include/driver/rmt_tx.h \
$(PROJECT_PATH)/components/driver/include/driver/rmt_types.h \
$(PROJECT_PATH)/components/driver/include/driver/temperature_sensor.h \
$(PROJECT_PATH)/components/esp_hw_support/include/soc/esp32s2/esp_ds.h \
$(PROJECT_PATH)/components/esp_hw_support/include/soc/esp32s2/esp_hmac.h \
$(PROJECT_PATH)/components/ulp/ulp_fsm/include/$(IDF_TARGET)/ulp.h \
$(PROJECT_PATH)/components/ulp/ulp_common/include/$(IDF_TARGET)/ulp_common_defs.h \
$(PROJECT_PATH)/components/touch_element/include/touch_element/touch_element.h \
$(PROJECT_PATH)/components/hal/include/hal/pcnt_types.h \
$(PROJECT_PATH)/components/hal/include/hal/rmt_types.h \
$(PROJECT_PATH)/components/soc/$(IDF_TARGET)/include/soc/dac_channel.h \
$(PROJECT_PATH)/components/soc/$(IDF_TARGET)/include/soc/rtc_io_channel.h \
$(PROJECT_PATH)/components/soc/$(IDF_TARGET)/include/soc/touch_sensor_channel.h \
$(PROJECT_PATH)/components/touch_element/include/touch_element/touch_button.h \
$(PROJECT_PATH)/components/touch_element/include/touch_element/touch_slider.h \
$(PROJECT_PATH)/components/touch_element/include/touch_element/touch_element.h \
$(PROJECT_PATH)/components/touch_element/include/touch_element/touch_matrix.h \
$(PROJECT_PATH)/components/touch_element/include/touch_element/touch_slider.h \
$(PROJECT_PATH)/components/ulp/ulp_common/include/$(IDF_TARGET)/ulp_common_defs.h \
$(PROJECT_PATH)/components/ulp/ulp_fsm/include/$(IDF_TARGET)/ulp.h \
$(PROJECT_PATH)/components/usb/include/usb/usb_helpers.h \
$(PROJECT_PATH)/components/usb/include/usb/usb_host.h \
$(PROJECT_PATH)/components/usb/include/usb/usb_types_ch9.h \
$(PROJECT_PATH)/components/usb/include/usb/usb_types_stack.h
$(PROJECT_PATH)/components/usb/include/usb/usb_types_stack.h \

Wyświetl plik

@ -1,18 +1,24 @@
INPUT += \
$(PROJECT_PATH)/components/esp_hw_support/include/soc/$(IDF_TARGET)/esp_hmac.h \
$(PROJECT_PATH)/components/esp_hw_support/include/soc/$(IDF_TARGET)/esp_ds.h \
$(PROJECT_PATH)/components/hal/include/hal/mcpwm_types.h \
$(PROJECT_PATH)/components/driver/include/driver/mcpwm.h \
$(PROJECT_PATH)/components/hal/include/hal/pcnt_types.h \
$(PROJECT_PATH)/components/driver/include/driver/pulse_cnt.h \
$(PROJECT_PATH)/components/driver/include/driver/temperature_sensor.h \
$(PROJECT_PATH)/components/soc/$(IDF_TARGET)/include/soc/touch_sensor_channel.h \
$(PROJECT_PATH)/components/driver/$(IDF_TARGET)/include/driver/touch_sensor.h \
$(PROJECT_PATH)/components/ulp/ulp_fsm/include/$(IDF_TARGET)/ulp.h \
$(PROJECT_PATH)/components/driver/include/driver/mcpwm.h \
$(PROJECT_PATH)/components/driver/include/driver/pulse_cnt.h \
$(PROJECT_PATH)/components/driver/include/driver/rmt_common.h \
$(PROJECT_PATH)/components/driver/include/driver/rmt_encoder.h \
$(PROJECT_PATH)/components/driver/include/driver/rmt_rx.h \
$(PROJECT_PATH)/components/driver/include/driver/rmt_tx.h \
$(PROJECT_PATH)/components/driver/include/driver/rmt_types.h \
$(PROJECT_PATH)/components/driver/include/driver/temperature_sensor.h \
$(PROJECT_PATH)/components/esp_hw_support/include/soc/$(IDF_TARGET)/esp_ds.h \
$(PROJECT_PATH)/components/esp_hw_support/include/soc/$(IDF_TARGET)/esp_hmac.h \
$(PROJECT_PATH)/components/esp_system/include/esp_ipc.h \
$(PROJECT_PATH)/components/esp_system/include/esp_ipc_isr.h \
$(PROJECT_PATH)/components/hal/include/hal/mcpwm_types.h \
$(PROJECT_PATH)/components/hal/include/hal/pcnt_types.h \
$(PROJECT_PATH)/components/hal/include/hal/rmt_types.h \
$(PROJECT_PATH)/components/soc/$(IDF_TARGET)/include/soc/touch_sensor_channel.h \
$(PROJECT_PATH)/components/ulp/ulp_common/include/$(IDF_TARGET)/ulp_common_defs.h \
$(PROJECT_PATH)/components/ulp/ulp_fsm/include/$(IDF_TARGET)/ulp.h \
$(PROJECT_PATH)/components/usb/include/usb/usb_helpers.h \
$(PROJECT_PATH)/components/usb/include/usb/usb_host.h \
$(PROJECT_PATH)/components/usb/include/usb/usb_types_ch9.h \
$(PROJECT_PATH)/components/usb/include/usb/usb_types_stack.h \
$(PROJECT_PATH)/components/esp_system/include/esp_ipc.h \
$(PROJECT_PATH)/components/esp_system/include/esp_ipc_isr.h

Wyświetl plik

@ -1,324 +1,536 @@
Remote Control (RMT)
====================
Remote Control Transceiver (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.
Introduction
------------
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.
The RMT (Remote Control Transceiver) peripheral was designed to act as an infrared transceiver. However, due to the flexibility of its data format, the functionality of RMT can be extended to a versatile and general purpose transceiver. From the perspective of network layering, the RMT hardware contains both physical and data link layer. The physical layer defines the communication media and bit signal representation. The data link layer defines the format of an RMT frame. The minimal data unit in the frame is called **RMT symbol**, which is represented by :cpp:type:`rmt_symbol_word_t` in the driver.
.. blockdiag::
:scale: 100
{IDF_TARGET_NAME} contains multiple channels in the RMT peripheral. [1]_ Each channel can be configured as either transmitter or receiver, independently.
Typically, the RMT peripheral can be used in the following scenarios:
- Transmit or receive infrared signals, with any IR protocols, e.g. NEC
- General purpose sequence generator
- Transmit signals in a hardware controlled loop, with finite or infinite number of times
- Multi-channel simultaneous transmission
- Modulate the carrier to the output signal or demodulate the carrier from the input signal
Layout of RMT Symbols
^^^^^^^^^^^^^^^^^^^^^
The RMT hardware defines data in its own pattern -- the **RMT symbol**. Each symbol consists of two pairs of two values. The first value in a pair describes the signal duration in RMT ticks and is 15 bits long. The second provides the signal level (high or low) and is contained in a single bit, as shown below:
.. packetdiag:: /../_static/diagrams/rmt/rmt_symbols.diag
:caption: Structure of RMT symbols (L - signal level)
:align: center
RMT Transmitter Overview
^^^^^^^^^^^^^^^^^^^^^^^^
The data path and control path of an RMT TX channel is illustrated in the figure below:
.. blockdiag:: /../_static/diagrams/rmt/rmt_tx.diag
:caption: RMT Transmitter Overview
:align: center
blockdiag rmt_tx {
The driver will encode user's data into RMT data format, then the RMT transmitter can generate the waveforms according to the encoding artifacts. It is also possible to modulate a high frequency carrier signal before being routed to a GPIO pad.
node_width = 80;
node_height = 60;
default_group_color = lightgrey;
RMT Receiver Overview
^^^^^^^^^^^^^^^^^^^^^
a -> b -> c -> d;
e -> f -> g -- h;
d -> o [label=GPIO];
h -> d [folded];
The data path and control path of an RMT RX channel is illustrated in the figure below:
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
.. blockdiag:: /../_static/diagrams/rmt/rmt_rx.diag
:caption: RMT Receiver Overview
:align: center
blockdiag rmt_rx {
The RMT receiver can sample incoming signals into RMT data format, and store the data in memory. It's feasible to tell the receiver the basic characteristics of the incoming signal, so that the signal's stop condition can be recognized, and signal glitches and noise can be filtered out. The RMT peripheral also supports demodulating the high frequency carrier from the base signal.
node_width = 80;
node_height = 60;
default_group_color = lightgrey;
Functional Overview
-------------------
a -> b [label=GPIO];
b -> c -> d;
e -- f;
f -> b [folded];
Description of the RMT functionality is divided into the following sections:
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=""]
- `Resource Allocation <#resource-allocation>`__ - covers how to allocate RMT channels with properly set of configurations. It also covers how to recycle the resources when they finished working.
- `Carrier Modulation and Demodulation <#carrier-modulation-and-demodulation>`__ - describes how to modulate carrier for TX channel and demodulate carrier for RX channel.
- `Register Event Callbacks <#register-event-callbacks>`__ - covers how to hook user specific code to RMT channel specific events.
- `Enable and Disable channel <#enable-and-disable-channel>`__ - shows how to enable and disable the RMT channel.
- `Initiate TX Transaction <#initiate-tx-transaction>`__ - describes the steps to initiate a transaction for TX channel.
- `Initiate RX Transaction <#initiate-rx-transaction>`__ - describes the steps to initiate a transaction for RX channel.
- `Multiple Channels Simultaneous Transmission <#multiple-channels-simultaneous-transmission>`__ - describes how to collect multiple channels into a sync group and start transaction at the same time.
- `RMT Encoder <#rmt-encoder>`__ - focuses on how to write a customized encoder in a combination way, with the help of the primitive encoders provided by the driver.
- `Power Management <#power-management>`__ - describes how different source clock will affect power consumption.
- `IRAM Safe <#iram-safe>`__ - describes tips on how to make the RMT interrupt work better along with a disabled cache.
- `Thread Safety <#thread-safety>`__ - lists which APIs are guaranteed to be thread safe by the driver.
- `Kconfig Options <#kconfig-options>`__ - lists the supported Kconfig options that can bring different effects to the driver.
group {
label = Input
a,e;
}
group {
label = "RMT Receiver"
b,c;
}
group {
label = Output
d;
}
}
Resource Allocation
^^^^^^^^^^^^^^^^^^^
There couple of typical steps to setup and operate the RMT and they are discussed in the following sections:
Both RMT TX and RX channels are represented by :cpp:type:`rmt_channel_handle_t` in the driver. The available channels are managed in a resource pool, which will hand out a free channel on request.
1. `Configure Driver`_
2. `Transmit Data`_ or `Receive Data`_
3. `Change Operation Parameters`_
4. `Use Interrupts`_
Install RMT TX Channel
~~~~~~~~~~~~~~~~~~~~~~
.. only:: esp32
To install an RMT TX channel, there's a configuration structure that needs to be given in advance: :cpp:type:`rmt_tx_channel_config_t`:
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`.
- :cpp:member:`rmt_tx_channel_config_t::gpio_num` sets the GPIO number used by the transmitter.
- :cpp:member:`rmt_tx_channel_config_t::clk_src` selects the source clock for the RMT channel. The available clocks are listed in :cpp:type:`rmt_clock_source_t`. Note that, the selected clock will also be used by other channels, which means user should ensure this configuration is same when allocating other channels, regardless of TX or RX. For the effect on power consumption of different clock source, please refer to `Power Management <#power-management>`__ section.
- :cpp:member:`rmt_tx_channel_config_t::resolution_hz` sets the resolution of the internal tick counter. The timing parameter of RMT signal is calculated based on this **tick**.
- :cpp:member:`rmt_tx_channel_config_t::mem_block_symbols` sets the size of the dedicated memory block or DMA buffer that is used to store RMT encoding artifacts.
- :cpp:member:`rmt_tx_channel_config_t::trans_queue_depth` sets the depth of internal transaction queue, the deeper the queue, the more transactions can be prepared in the backlog.
- :cpp:member:`rmt_tx_channel_config_t::invert_out` is used to decide whether to invert the RMT signal before sending it to the GPIO pad.
- :cpp:member:`rmt_tx_channel_config_t::with_dma` is used to indicate if the channel needs a DMA backend. A channel with DMA attached can offload the CPU by a lot. However, DMA backend is not available on all ESP chips, please refer to [`TRM <{IDF_TARGET_TRM_EN_URL}#rmt>`__] before you enable this option. Or you might encounter :c:macro:`ESP_ERR_NOT_SUPPORTED` error.
- :cpp:member:`rmt_tx_channel_config_t::io_loop_back` is for debugging purposes only. It enables both the GPIO's input and output ability through the GPIO matrix peripheral. Meanwhile, if both TX and RX channels are bound to the same GPIO, then monitoring of the data transmission line can be realized.
.. only:: esp32s2
Once the :cpp:type:`rmt_tx_channel_config_t` structure is populated with mandatory parameters, users can call :cpp:func:`rmt_new_tx_channel` to allocate and initialize a TX channel. This function will return an RMT channel handle if it runs correctly. Specifically, when there are no more free channels in the RMT resource pool, this function will return :c:macro:`ESP_ERR_NOT_FOUND` error. If some feature (e.g. DMA backend) is not supported by hardware, it will return :c:macro:`ESP_ERR_NOT_SUPPORTED` error.
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`.
.. code:: c
.. only:: esp32c3
rmt_channel_handle_t tx_chan = NULL;
rmt_tx_channel_config_t tx_chan_config = {
.clk_src = RMT_CLK_SRC_DEFAULT, // select source clock
.gpio_num = 0, // GPIO number
.mem_block_symbols = 64, // memory block size, 64 * 4 = 256Bytes
.resolution_hz = 1 * 1000 * 1000, // 1MHz tick resolution, i.e. 1 tick = 1us
.trans_queue_depth = 4, // set the number of transactions that can pend in the background
.flags.invert_out = false, // don't invert output signal
.flags.with_dma = false, // don't need DMA backend
};
ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_chan_config, &tx_chan));
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`.
Install RMT RX Channel
~~~~~~~~~~~~~~~~~~~~~~
.. only:: esp32s3
To install an RMT RX channel, there's a configuration structure that needs to be given in advance: :cpp:type:`rmt_rx_channel_config_t`:
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`.
- :cpp:member:`rmt_rx_channel_config_t::gpio_num` sets the GPIO number used by the receiver.
- :cpp:member:`rmt_rx_channel_config_t::clk_src` selects the source clock for the RMT channel. The available clocks are listed in :cpp:type:`rmt_clock_source_t`. Note that, the selected clock will also be used by other channels, which means user should ensure this configuration is same when allocating other channels, regardless of TX or RX. For the effect on power consumption of different clock source, please refer to `Power Management <#power-management>`__ section.
- :cpp:member:`rmt_rx_channel_config_t::resolution_hz` sets the resolution of the internal tick counter. The timing parameter of RMT signal is calculated based on this **tick**.
- :cpp:member:`rmt_rx_channel_config_t::mem_block_symbols` sets the size of the dedicated memory block or DMA buffer that used to store RMT encoding artifacts.
- :cpp:member:`rmt_rx_channel_config_t::invert_in` is used to decide whether to invert the input signals before they going into RMT receiver. The inversion is done by GPIO matrix instead of by the RMT peripheral.
- :cpp:member:`rmt_rx_channel_config_t::with_dma` is used to indicate if the channel needs a DMA backend. A channel with DMA attached can offload the CPU by a lot. However, DMA backend is not available on all ESP chips, please refer to [`TRM <{IDF_TARGET_TRM_EN_URL}#rmt>`__] before you enable this option. Or you might encounter :c:macro:`ESP_ERR_NOT_SUPPORTED` error.
- :cpp:member:`rmt_rx_channel_config_t::io_loop_back` is for debugging purposes only. It enables both the GPIO's input and output ability through the GPIO matrix peripheral. Meanwhile, if both TX and RX channels are bound to the same GPIO, then monitoring of the data transmission line can be realized.
Configure Driver
----------------
Once the :cpp:type:`rmt_rx_channel_config_t` structure is populated with mandatory parameters, users can call :cpp:func:`rmt_new_rx_channel` to allocate and initialize a RX channel. This function will return an RMT channel handle if it runs correctly. Specifically, when there are no more free channels in the RMT resource pool, this function will return :c:macro:`ESP_ERR_NOT_FOUND` error. If some feature (e.g. DMA backend) is not supported by hardware, it will return :c:macro:`ESP_ERR_NOT_SUPPORTED` error.
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.
.. code:: c
rmt_channel_handle_t rx_chan = NULL;
rmt_rx_channel_config_t rx_chan_config = {
.clk_src = RMT_CLK_SRC_DEFAULT, // select source clock
.resolution_hz = 1 * 1000 * 1000, // 1MHz tick resolution, i.e. 1 tick = 1us
.mem_block_symbols = 64, // memory block size, 64 * 4 = 256Bytes
.gpio_num = 2, // GPIO number
.flags.invert_in = false, // don't invert input signal
.flags.with_dma = false, // don't need DMA backend
};
ESP_ERROR_CHECK(rmt_new_rx_channel(&rx_chan_config, &rx_chan));
Common Parameters
^^^^^^^^^^^^^^^^^
Uninstall RMT Channel
~~~~~~~~~~~~~~~~~~~~~
* 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**.
If a previously installed RMT channel is no longer needed, it's recommended to recycle the resources by calling :cpp:func:`rmt_del_channel`, which in return allows the underlying hardware to be usable for other purposes.
* 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.
Carrier Modulation and Demodulation
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* 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.
The RMT transmitter can generate a carrier wave and modulate it onto the base signal. Compared to the base signal, the carrier frequency is usually high. In addition, user can only set the frequency and duty cycle for the carrier. The RMT receiver can demodulate the carrier from the incoming signal. Note that, carrier modulation and demodulation is not supported on all ESP chips, please refer to [`TRM <{IDF_TARGET_TRM_EN_URL}#rmt>`__] before configuring the carrier, or you might encounter a :c:macro:`ESP_ERR_NOT_SUPPORTED` error.
Carrier related configurations lie in :cpp:type:`rmt_carrier_config_t`:
- :cpp:member:`rmt_carrier_config_t::frequency_hz` sets the carrier frequency, in Hz.
- :cpp:member:`rmt_carrier_config_t::duty_cycle` sets the carrier duty cycle.
- :cpp:member:`rmt_carrier_config_t::polarity_active_low` sets the carrier polarity, i.e. on which level the carrier is applied.
- :cpp:member:`rmt_carrier_config_t::always_on` sets whether to output the carrier even when the data transmission has finished. This configuration is only valid for TX channel.
.. 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'.
For RX channel, we shouldn't set the carrier frequency exactly to the theoretical value. It's recommended to leave a tolerance for the carrier frequency. For example, in the snippet below, we set the frequency to 25KHz, instead of the 38KHz that configured on the TX side. The reason is that reflection and refraction will occur when a signal travels through the air, leading to the a distortion on the receiver side.
There are also couple of specific parameters that should be set up depending if selected channel is configured in `Transmit Mode`_ or `Receive Mode`_:
.. code:: c
rmt_carrier_config_t tx_carrier_cfg = {
.duty_cycle = 0.33, // duty cycle 33%
.frequency_hz = 38000, // 38KHz
.flags.polarity_active_low = false, // carrier should modulated to high level
};
// modulate carrier to TX channel
ESP_ERROR_CHECK(rmt_apply_carrier(tx_chan, &tx_carrier_cfg));
Transmit Mode
^^^^^^^^^^^^^
rmt_carrier_config_t rx_carrier_cfg = {
.duty_cycle = 0.33, // duty cycle 33%
.frequency_hz = 25000, // 25KHz carrier, should be smaller than transmitter's carrier frequency
.flags.polarity_active_low = false, // the carrier is modulated to high level
};
// demodulate carrier from RX channel
ESP_ERROR_CHECK(rmt_apply_carrier(rx_chan, &rx_carrier_cfg));
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 `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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Selection of a GPIO pin number on the input or output of the RMT - :cpp:func:`rmt_set_gpio`
* Number of memory blocks allocated for the incoming or outgoing data - :cpp:func:`rmt_set_mem_pd`
* Setting of the clock divider - :cpp:func:`rmt_set_clk_div`
* Selection of the clock source, note that currently one clock source is supported, the APB clock which is 80Mhz - :cpp:func:`rmt_set_source_clk`
Transmit Mode Parameters
Register Event Callbacks
^^^^^^^^^^^^^^^^^^^^^^^^
* Enable or disable the loop back mode for the transmitter - :cpp:func:`rmt_set_tx_loop_mode`
* Binary level on the output to apply the carrier - :cpp:func:`rmt_set_tx_carrier`, selected from :cpp:type:`rmt_carrier_level_t`
* Determines the binary level on the output when transmitter is idle - :cpp:func:`rmt_set_idle_level()`, selected from :cpp:type:`rmt_idle_level_t`
When an RMT channel finishes transmitting or receiving, a specific event will be generated and notify the CPU by interrupt. If you have some function that needs to be called when those events occurred, you can hook your function to the ISR (Interrupt Service Routine) by calling :cpp:func:`rmt_tx_register_event_callbacks` and :cpp:func:`rmt_rx_register_event_callbacks` for TX and RX channel respectively. Since the registered callback functions are called in the interrupt context, user should ensure the callback function doesn't attempt to block (e.g. by making sure that only FreeRTOS APIs with ``ISR`` suffix are called from within the function). The callback function has a boolean return value, to tell the caller whether a high priority task is woke up by it.
.. only:: SOC_RMT_SUPPORT_TX_LOOP_COUNT
TX channel supported event callbacks are listed in the :cpp:type:`rmt_tx_event_callbacks_t`:
* 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_AUTO_STOP`. - :cpp:func:`rmt_enable_tx_loop_autostop`
- :cpp:member:`rmt_tx_event_callbacks_t::on_trans_done` sets a callback function for trans done event. The function prototype is declared in :cpp:type:`rmt_tx_done_callback_t`.
RX channel supported event callbacks are listed in the :cpp:type:`rmt_rx_event_callbacks_t`:
Receive Mode Parameters
- :cpp:member:`rmt_rx_event_callbacks_t::on_recv_done` sets a callback function for receive complete event. The function prototype is declared in :cpp:type:`rmt_rx_done_callback_t`.
User can save own context in :cpp:func:`rmt_tx_register_event_callbacks` and :cpp:func:`rmt_rx_register_event_callbacks` as well, via the parameter ``user_data``. The user data will be directly passed to each callback function.
In the callback function, users can fetch the event specific data that is filled by the driver in the ``edata``. Note that the ``edata`` pointer is only valid for the duration of the callback.
The TX done event data is defined in :cpp:type:`rmt_tx_done_event_data_t`:
- :cpp:member:`rmt_tx_done_event_data_t::num_symbols` tells the number of transmitted RMT symbols. This also reflects the size of encoding artifacts.
The RX complete event data is defined in :cpp:type:`rmt_rx_done_event_data_t`:
- :cpp:member:`rmt_rx_done_event_data_t::received_symbols` points to the received RMT symbols. These symbols are saved in the ``buffer`` parameter of :cpp:func:`rmt_receive` function. User shouldn't free this receive buffer before the callback returns.
- :cpp:member:`rmt_rx_done_event_data_t::num_symbols` tells the number of received RMT symbols. This value won't be bigger than ``buffer_size`` parameter of :cpp:func:`rmt_receive` function. If the ``buffer_size`` is not sufficient to accommodate all the received RMT symbols, the driver will truncate it.
Enable and Disable channel
^^^^^^^^^^^^^^^^^^^^^^^^^^
:cpp:func:`rmt_enable` must be called in advanced before transmitting or receiving RMT symbols. For transmitters, enabling a channel will enable a specific interrupt and prepare the hardware to dispatch transactions. For RX channels, enabling a channel will enable an interrupt, but the receiver is not started during this time, as it has no idea about the characteristics of the incoming signals. The receiver will be started in :cpp:func:`rmt_receive`.
:cpp:func:`rmt_disable` does the opposite work by disabling the interrupt and clearing pending status. The transmitter and receiver will be disabled as well.
.. code:: c
ESP_ERROR_CHECK(rmt_enable(tx_chan));
ESP_ERROR_CHECK(rmt_enable(rx_chan));
Initiate TX Transaction
^^^^^^^^^^^^^^^^^^^^^^^
* The filter setting - :cpp:func:`rmt_set_rx_filter`
* The receiver threshold setting - :cpp:func:`rmt_set_rx_idle_thresh`
* Whether the transmitter or receiver is entitled to access RMT's memory - :cpp:func:`rmt_set_memory_owner`, selection is from :cpp:type:`rmt_mem_owner_t`.
RMT is a special communication peripheral as it's unable to transmit raw byte streams like SPI and I2C. RMT can only send data in its own format :cpp:type:`rmt_symbol_word_t`. However, the hardware doesn't help to convert the user data into RMT symbols, this can only be done in software --- by the so-called **RMT Encoder**. The encoder is responsible for encoding user data into RMT symbols and then write to RMT memory block or DMA buffer. For how to create an RMT encoder, please refer to `RMT Encoder <#rmt-encoder>`__.
Once we got an encoder, we can initiate a TX transaction by calling :cpp:func:`rmt_transmit`. This function takes several positional parameters like channel handle, encoder handle, payload buffer. Besides that, we also need to provide a transmission specific configuration in :cpp:type:`rmt_transmit_config_t`:
Use Interrupts
--------------
Registering of an interrupt handler for the RMT controller is done be calling :cpp:func:`rmt_isr_register`.
- :cpp:member:`rmt_transmit_config_t::loop_count` sets the number of transmission loop. After the transmitter finished one round of transmission, it can restart the same transmission again if this value is not set to zero. As the loop is controlled by hardware, the RMT channel can be used to generate many periodic sequences at the cost of a very little CPU intervention. Specially, setting :cpp:member:`rmt_transmit_config_t::loop_count` to `-1` means an infinite loop transmission. In this situation, the channel won't stop until manually call of :cpp:func:`rmt_disable`. And the trans done event won't be generated as well. If :cpp:member:`rmt_transmit_config_t::loop_count` is set to a positive number, the trans done event won't be generated until target number of loop transmission have finished. Note that, the **loop transmit** feature is not supported on all ESP chips, please refer to [`TRM <{IDF_TARGET_TRM_EN_URL}#rmt>`__] before you configure this option. Or you might encounter :c:macro:`ESP_ERR_NOT_SUPPORTED` error.
- :cpp:member:`rmt_transmit_config_t::eot_level` sets the output level when the transmitter finishes working or stops working by calling :cpp:func:`rmt_disable`.
.. note::
When calling :cpp:func:`rmt_driver_install` to use the system RMT driver, a default ISR is being installed. In such a case you cannot register a generic ISR handler with :cpp:func:`rmt_isr_register`.
There's a limitation in the transmission size if the :cpp:member:`rmt_transmit_config_t::loop_count` is set to non-zero (i.e. to enable the loop feature). The encoded RMT symbols should not exceed the capacity of RMT hardware memory block size. Or you might see error message like ``encoding artifacts can't exceed hw memory block for loop transmission``. If you have to start a large transaction by loop, you can try either:
The RMT controller triggers interrupts on four specific events describes below. To enable interrupts on these events, the following functions are provided:
- Increase the :cpp:member:`rmt_tx_channel_config_t::mem_block_symbols`. This approach doesn't work if the DMA backend is also enabled.
- Customize an encoder and construct a forever loop in the encoding function. See also `RMT Encoder <#rmt-encoder>`__.
* The RMT receiver has finished receiving a signal - :cpp:func:`rmt_set_rx_intr_en`
* The RMT transmitter has finished transmitting the signal - :cpp:func:`rmt_set_tx_intr_en`
* The number of events the transmitter has sent matches a threshold value :cpp:func:`rmt_set_tx_thr_intr_en`
* Ownership to the RMT memory block has been violated - :cpp:func:`rmt_set_err_intr_en`
Internally, :cpp:func:`rmt_transmit` will construct a transaction descriptor and send to a job queue, which will be dispatched in the ISR. So it is possible that the transaction is not started yet when :cpp:func:`rmt_transmit` returns. To ensure all pending transaction to complete, user can use :cpp:func:`rmt_tx_wait_all_done`.
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.
Multiple Channels Simultaneous Transmission
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If you do not need an ISR anymore, you can deregister it by calling a function :cpp:func:`rmt_isr_deregister`.
In some real-time control applications, we don't want any time drift in between when startup multiple TX channels. For example, to make two robotic arms move simultaneously. The RMT driver can help to manage this by creating a so-called **Sync Manager**. The sync manager is represented by :cpp:type:`rmt_sync_manager_handle_t` in the driver. The procedure of RMT sync transmission is shown as follows:
.. warning::
.. figure:: /../_static/rmt_tx_sync.png
:align: center
:alt: RMT TX Sync
It's not recommended for users to register an interrupt handler in their applications. RMT driver is highly dependent on interrupt, especially when doing transaction in a ping-pong way, so the driver itself has registered a default handler called ``rmt_driver_isr_default``.
Instead, if what you want is to get a notification when transaction is done, go ahead with :cpp:func:`rmt_register_tx_end_callback`.
RMT TX Sync
Install RMT Sync Manager
~~~~~~~~~~~~~~~~~~~~~~~~
Uninstall Driver
----------------
To create a sync manager, user needs to tell which channels are going to be managed in the :cpp:type:`rmt_sync_manager_config_t`:
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`.
- :cpp:member:`rmt_sync_manager_config_t::tx_channel_array` points to the array of TX channels to be managed.
- :cpp:member:`rmt_sync_manager_config_t::array_size` sets the number of channels to be managed.
:cpp:func:`rmt_new_sync_manager` can return a manager handle on success. This function could also fail due to various errors such as invalid arguments, etc. Specially, when the sync manager has been installed before, and there're no hardware resources to create another manager, this function will report :c:macro:`ESP_ERR_NOT_FOUND` error. In addition, if the sync manager is not supported by the hardware, it will report :c:macro:`ESP_ERR_NOT_SUPPORTED` error. Please refer to [`TRM <{IDF_TARGET_TRM_EN_URL}#rmt>`__] before using the sync manager feature.
Start Transmission Simultaneously
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
For any managed TX channel, it won't start the machine until all the channels in the :cpp:member:`rmt_sync_manager_config_t::tx_channel_array` are called with :cpp:func:`rmt_transmit`. Before that, the channel is just put in a waiting state. Different channel usually take different time to finish the job if the transaction is different, which results in a loss of sync. So user needs to call :cpp:func:`rmt_sync_reset` to pull the channels back to the starting line again before restarting a simultaneous transmission.
Calling :cpp:func:`rmt_del_sync_manager` can recycle the sync manager and enable the channels to initiate transactions independently afterwards.
.. code:: c
rmt_channel_handle_t tx_channels[2] = {NULL}; // declare two channels
int tx_gpio_number[2] = {0, 2};
// install channels one by one
for (int i = 0; i < 2; i++) {
rmt_tx_channel_config_t tx_chan_config = {
.clk_src = RMT_CLK_SRC_DEFAULT, // select source clock
.gpio_num = tx_gpio_number[i], // GPIO number
.mem_block_symbols = 64, // memory block size, 64 * 4 = 256Bytes
.resolution_hz = 1 * 1000 * 1000, // 1MHz resolution
.trans_queue_depth = 1, // set the number of transactions that can pend in the background
};
ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_chan_config, &tx_channels[i]));
}
// install sync manager
rmt_sync_manager_handle_t synchro = NULL;
rmt_sync_manager_config_t synchro_config = {
.tx_channel_array = tx_channels,
.array_size = sizeof(tx_channels) / sizeof(tx_channels[0]),
};
ESP_ERROR_CHECK(rmt_new_sync_manager(&synchro_config, &synchro));
ESP_ERROR_CHECK(rmt_transmit(tx_channels[0], led_strip_encoders[0], led_data, led_num * 3, &transmit_config));
// tx_channels[0] won't start transmission until call of `rmt_transmit()` for tx_channels[1] returns
ESP_ERROR_CHECK(rmt_transmit(tx_channels[1], led_strip_encoders[1], led_data, led_num * 3, &transmit_config));
Initiate RX Transaction
^^^^^^^^^^^^^^^^^^^^^^^
As also discussed in the `Enable and Disable channel <#enable-and-disable-channel>`__, the RX channel still doesn't get ready to receive RMT symbols even user calls :cpp:func:`rmt_enable`. User needs to specify the basic characteristics of the incoming signals in :cpp:type:`rmt_receive_config_t`:
- :cpp:member:`rmt_receive_config_t::signal_range_min_ns` specifies the minimal valid pulse duration (either high or low level). A pulse whose width is smaller than this value will be treated as glitch and ignored by the hardware.
- :cpp:member:`rmt_receive_config_t::signal_range_max_ns` specifies the maximum valid pulse duration (either high or low level). A pulse whose width is bigger than this value will be treated as **Stop Signal**, and the receiver will generate receive complete event immediately.
The RMT receiver will start the RX machine after user calls :cpp:func:`rmt_receive` with the provided configuration above. Note that, this configuration is transaction specific, which means, to start a new round of reception, user needs to sets the :cpp:type:`rmt_receive_config_t` again. The receiver saves the incoming signals into its internal memory block or DMA buffer, in the format of :cpp:type:`rmt_symbol_word_t`.
.. only:: SOC_RMT_SUPPORT_RX_PINGPONG
Due to the limited size of memory block, the RMT receiver will notify the driver to copy away the accumulated symbols in a ping-pong way.
.. only:: not SOC_RMT_SUPPORT_RX_PINGPONG
Due to the limited size of memory block, the RMT receiver can only save short frames whose length is not longer than the memory block capacity. Long frames will be truncated by the hardware, and the driver will report an error message: ``hw buffer too small, received symbols truncated``.
The copy destination should be provided in the ``buffer`` parameter of :cpp:func:`rmt_receive` function. If this buffer size is not sufficient, the receiver can continue to work but later incoming symbols will be dropped and report an error message: ``user buffer too small, received symbols truncated``. Please take care of the lifecycle of the ``buffer`` parameter, user shouldn't recycle the buffer before the receiver finished or stopped working.
The receiver will be stopped by the driver when it finishes working (i.e. received a signal whose duration is bigger than :cpp:member:`rmt_receive_config_t::signal_range_max_ns`). User needs to call :cpp:func:`rmt_receive` again to restart the receiver, is necessary. User can get the received data in the :cpp:member:`rmt_rx_event_callbacks_t::on_recv_done` callback. See also `Register Event Callbacks <#register-event-callbacks>`__ for more information.
.. code:: c
static bool example_rmt_rx_done_callback(rmt_channel_handle_t channel, rmt_rx_done_event_data_t *edata, void *user_data)
{
BaseType_t high_task_wakeup = pdFALSE;
TaskHandle_t task_to_notify = (TaskHandle_t)user_data;
// send the received RMT symbols to the parser task
xTaskNotifyFromISR(task_to_notify, (uint32_t)edata, eSetValueWithOverwrite, &high_task_wakeup);
// return whether any task is woken up
return high_task_wakeup == pdTRUE;
}
TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
rmt_rx_event_callbacks_t cbs = {
.on_recv_done = example_rmt_rx_done_callback,
};
ESP_ERROR_CHECK(rmt_rx_register_event_callbacks(rx_channel, &cbs, cur_task));
// the following timing requirement is based on NEC protocol
rmt_receive_config_t receive_config = {
.signal_range_min_ns = 1250, // the shortest duration for NEC signal is 560us, 1250ns < 560us, valid signal won't be treated as noise
.signal_range_max_ns = 12000000, // the longest duration for NEC signal is 9000us, 12000000ns > 9000us, the receive won't stop early
};
rmt_symbol_word_t raw_symbols[64]; // 64 symbols should be sufficient for a standard NEC frame
// ready to receive
ESP_ERROR_CHECK(rmt_receive(rx_channel, raw_symbols, sizeof(raw_symbols), &receive_config));
// wait for RX done signal
rmt_rx_done_event_data_t *rx_data = NULL;
xTaskNotifyWait(0x00, ULONG_MAX, (uint32_t *)&rx_data, portMAX_DELAY);
// parse the receive symbols
example_parse_nec_frame(rx_data->received_symbols, rx_data->num_symbols);
RMT Encoder
^^^^^^^^^^^
An RMT encoder is part of the RMT TX transaction, whose responsibility is to generate and write the correct RMT symbols into hardware memory (or DMA buffer) at specific time. There're some special restrictions for an encoding function:
- An encoding function might be called for several times within a single transaction. This is because the target RMT memory block can't accommodate all the artifacts at once. We have to use the memory in a **ping-pong** way, thus the encoding session is divided into multiple parts. This requires the encoder to be **stateful**.
- The encoding function is running in the ISR context. To speed up the encoding session, it's high recommend to put the encoding function into IRAM. This can also avoid the cache miss during encoding.
To help get started with RMT driver faster, some commonly used encoders are provided out-of-the box. They can either work alone or chained together into a new encoder. See also `Composite Pattern <https://en.wikipedia.org/wiki/Composite_pattern>`__ for the principle behind. The driver has defined the encoder interface in :cpp:type:`rmt_encoder_t`, it contains the following functions:
- :cpp:member:`rmt_encoder_t::encode` is the fundamental function of an encoder. This is where the encoding session happens. Please note, the :cpp:member:`rmt_encoder_t::encode` function might be called for multiple times within a single transaction. The encode function should return the state of current encoding session. The supported states are listed in the :cpp:type:`rmt_encode_state_t`. If the result contains :cpp:enumerator:`RMT_ENCODING_COMPLETE`, it means the current encoder has finished work. If the result contains :cpp:enumerator:`RMT_ENCODING_MEM_FULL`, we need to yield from current session, as there's no space to save more encoding artifacts.
- :cpp:member:`rmt_encoder_t::reset` should reset the encoder state back to initial. The RMT encoder is stateful, if RMT transmitter stopped manually without its corresponding encoder being reset, then the following encoding session can be wrong. This function is also called implicitly in :cpp:func:`rmt_disable`.
- :cpp:member:`rmt_encoder_t::del` function should free the resources allocated by the encoder.
Copy Encoder
~~~~~~~~~~~~
A copy encoder is created by calling :cpp:func:`rmt_new_copy_encoder`. Copy encoder's main functionality is to copy the RMT symbols from user space into the driver layer. It's usually used to encode const data (i.e. data won't change at runtime after initialization), for example, the leading code in the IR protocol.
A configuration structure :cpp:type:`rmt_copy_encoder_config_t` should be provided in advance before calling :cpp:func:`rmt_new_copy_encoder`. Currently, this configuration is reserved for future expansion.
Bytes Encoder
~~~~~~~~~~~~~
A bytes encoder is created by calling :cpp:func:`rmt_new_bytes_encoder`. Bytes encoder's main functionality is to convert the user space byte stream into RMT symbols dynamically. It's usually used to encode dynamic data, for example, the address and command fields in the IR protocol.
A configuration structure :cpp:type:`rmt_bytes_encoder_config_t` should be provided in advance before calling :cpp:func:`rmt_new_bytes_encoder`:
- :cpp:member:`rmt_bytes_encoder_config_t::bit0` and :cpp:member:`rmt_bytes_encoder_config_t::bit1` are necessary to tell to the encoder how to represent bit zero and bit one in the format of :cpp:type:`rmt_symbol_word_t`.
- :cpp:member:`rmt_bytes_encoder_config_t::msb_first` sets the encoding order for of byte. If it is set to true, the encoder will encode the **Most Significant Bit** first. Otherwise, it will encode the **Least Significant Bit** first.
Besides the primitive encoders provided by the driver, user can implement his own encoder by chaining the existing encoders together. A common encoder chain is shown as follows:
.. blockdiag:: /../_static/diagrams/rmt/rmt_encoder_chain.diag
:caption: RMT Encoder Chain
:align: center
Customize RMT Encoder for NEC Protocol
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In this section, we will demonstrate on how to write an NEC encoder. The NEC IR protocol uses pulse distance encoding of the message bits. Each pulse burst is `562.5µs` in length, logical bits are transmitted as follows. It is worth mentioning, the bytes of data bits are sent least significant bit first.
- Logical ``0``: a `562.5µs` pulse burst followed by a `562.5µs` space, with a total transmit time of `1.125ms`
- Logical ``1``: a `562.5µs` pulse burst followed by a `1.6875ms` space, with a total transmit time of `2.25ms`
When a key is pressed on the remote controller, the message transmitted consists of the following, in order:
.. figure:: /../_static/ir_nec.png
:align: center
:alt: IR NEC Frame
IR NEC Frame
- `9ms` leading pulse burst (also called the "AGC pulse")
- `4.5ms` space
- 8-bit address for the receiving device
- 8-bit logical inverse of the address
- 8-bit command
- 8-bit logical inverse of the command
- a final `562.5µs` pulse burst to signify the end of message transmission
Then we can construct the NEC :cpp:member:`rmt_encoder_t::encode` function in the same order, for example:
.. code:: c
// IR NEC scan code representation
typedef struct {
uint16_t address;
uint16_t command;
} ir_nec_scan_code_t;
// construct a encoder by combining primitive encoders
typedef struct {
rmt_encoder_t base; // the base "class", declares the standard encoder interface
rmt_encoder_t *copy_encoder; // use the copy_encoder to encode the leading and ending pulse
rmt_encoder_t *bytes_encoder; // use the bytes_encoder to encode the address and command data
rmt_symbol_word_t nec_leading_symbol; // NEC leading code with RMT representation
rmt_symbol_word_t nec_ending_symbol; // NEC ending code with RMT representation
int state; // record the current encoding state (i.e. we're in which encoding phase)
} rmt_ir_nec_encoder_t;
static size_t rmt_encode_ir_nec(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)
{
rmt_ir_nec_encoder_t *nec_encoder = __containerof(encoder, rmt_ir_nec_encoder_t, base);
rmt_encode_state_t session_state = 0;
rmt_encode_state_t state = 0;
size_t encoded_symbols = 0;
ir_nec_scan_code_t *scan_code = (ir_nec_scan_code_t *)primary_data;
rmt_encoder_handle_t copy_encoder = nec_encoder->copy_encoder;
rmt_encoder_handle_t bytes_encoder = nec_encoder->bytes_encoder;
switch (nec_encoder->state) {
case 0: // send leading code
encoded_symbols += copy_encoder->encode(copy_encoder, channel, &nec_encoder->nec_leading_symbol,
sizeof(rmt_symbol_word_t), &session_state);
if (session_state & RMT_ENCODING_COMPLETE) {
nec_encoder->state = 1; // we can only switch to next state when current encoder finished
}
if (session_state & RMT_ENCODING_MEM_FULL) {
state |= RMT_ENCODING_MEM_FULL;
goto out; // yield if there's no free space to put other encoding artifacts
}
// fall-through
case 1: // send address
encoded_symbols += bytes_encoder->encode(bytes_encoder, channel, &scan_code->address, sizeof(uint16_t), &session_state);
if (session_state & RMT_ENCODING_COMPLETE) {
nec_encoder->state = 2; // we can only switch to next state when current encoder finished
}
if (session_state & RMT_ENCODING_MEM_FULL) {
state |= RMT_ENCODING_MEM_FULL;
goto out; // yield if there's no free space to put other encoding artifacts
}
// fall-through
case 2: // send command
encoded_symbols += bytes_encoder->encode(bytes_encoder, channel, &scan_code->command, sizeof(uint16_t), &session_state);
if (session_state & RMT_ENCODING_COMPLETE) {
nec_encoder->state = 3; // we can only switch to next state when current encoder finished
}
if (session_state & RMT_ENCODING_MEM_FULL) {
state |= RMT_ENCODING_MEM_FULL;
goto out; // yield if there's no free space to put other encoding artifacts
}
// fall-through
case 3: // send ending code
encoded_symbols += copy_encoder->encode(copy_encoder, channel, &nec_encoder->nec_ending_symbol,
sizeof(rmt_symbol_word_t), &session_state);
if (session_state & RMT_ENCODING_COMPLETE) {
nec_encoder->state = 0; // back to the initial encoding session
state |= RMT_ENCODING_COMPLETE; // telling the caller the NEC encoding has finished
}
if (session_state & RMT_ENCODING_MEM_FULL) {
state |= RMT_ENCODING_MEM_FULL;
goto out; // yield if there's no free space to put other encoding artifacts
}
}
out:
*ret_state = state;
return encoded_symbols;
}
A full sample code can be found in :example:`peripherals/rmt/ir_nec_transceiver`. In the above snippet, we use a ``switch-case`` plus several ``goto`` statements to implement a `state machine <https://en.wikipedia.org/wiki/Finite-state_machine>`__ . With this pattern, user can construct a lot more complex IR protocols.
Power Management
^^^^^^^^^^^^^^^^
When power management is enabled (i.e. :ref:`CONFIG_PM_ENABLE` is on), the system will adjust the APB frequency before going into light sleep, thus potentially changing the resolution of RMT internal counter.
However, the driver can prevent the system from changing APB frequency by acquiring a power management lock of type :cpp:enumerator:`ESP_PM_APB_FREQ_MAX`. Whenever user creates an RMT channel that has selected :cpp:enumerator:`RMT_CLK_SRC_APB` as the clock source, the driver will guarantee that the power management lock is acquired after the channel enabled by :cpp:func:`rmt_enable`. Likewise, the driver releases the lock after :cpp:func:`rmt_disable` is called for the same channel. This also reveals that the :cpp:func:`rmt_enable` and :cpp:func:`rmt_disable` should appear in pairs.
If the channel clock source is selected to others like :cpp:enumerator:`RMT_CLK_SRC_XTAL`, then the driver won't install power management lock for it, which is more suitable for a low power application as long as the source clock can still provide sufficient resolution.
IRAM Safe
^^^^^^^^^
By default, the RMT interrupt will be deferred when the Cache is disabled for reasons like writing/erasing the main Flash. Thus the transaction done interrupt will not get executed in time, which is not expected in a real-time application. What's worse, when the RMT transaction relies on **ping-pong** interrupt to successively encode or copy RMT symbols, such delayed response can lead to an unpredictable result.
There's a Kconfig option :ref:`CONFIG_RMT_ISR_IRAM_SAFE` that will:
1. Enable the interrupt being serviced even when cache is disabled
2. Place all functions that used by the ISR into IRAM [2]_
3. Place driver object into DRAM (in case it's mapped to PSRAM by accident)
This Kconfig option will allow the interrupt to run while the cache is disabled but will come at the cost of increased IRAM consumption.
Thread Safety
^^^^^^^^^^^^^
The factory function :cpp:func:`rmt_new_tx_channel`, :cpp:func:`rmt_new_rx_channel` and :cpp:func:`rmt_new_sync_manager` are guaranteed to be thread safe by the driver, which means, user can call them from different RTOS tasks without protection by extra locks.
Other functions that take the :cpp:type:`rmt_channel_handle_t` and :cpp:type:`rmt_sync_manager_handle_t` as the first positional parameter, are not thread safe. which means the user should avoid calling them from multiple tasks.
Kconfig Options
^^^^^^^^^^^^^^^
- :ref:`CONFIG_RMT_ISR_IRAM_SAFE` controls whether the default ISR handler can work when cache is disabled, see also `IRAM Safe <#iram-safe>`__ for more information.
- :ref:`CONFIG_RMT_ENABLE_DEBUG_LOG` is used to enabled the debug log at the cost of increased firmware binary size.
Application Examples
--------------------
* Using RMT to send morse code: :example:`peripherals/rmt/morse_code`.
* Using RMT to drive RGB LED strip: :example:`peripherals/rmt/led_strip`.
* NEC remote control TX and RX example: :example:`peripherals/rmt/ir_protocols`.
* Musical buzzer example: :example:`peripherals/rmt/musical_buzzer`.
* RMT based RGB LED strip customized encoder: :example:`peripherals/rmt/led_strip`
* RMT IR NEC protocol encoding and decoding: :example:`peripherals/rmt/ir_nec_transceiver`
* RMT transactions in queue: :example:`peripherals/rmt/musical_buzzer`
* RMT based stepper motor with S-curve algorithm: : :example:`peripherals/rmt/stepper_motor`
* RMT infinite loop for driving DShot ESC: :example:`peripherals/rmt/dshot_esc`
API Reference
-------------
.. include-build-file:: inc/rmt.inc
.. include-build-file:: inc/rmt_types.inc
.. include-build-file:: inc/rmt_tx.inc
.. include-build-file:: inc/rmt_rx.inc
.. include-build-file:: inc/rmt_common.inc
.. include-build-file:: inc/rmt_encoder.inc
.. include-build-file:: inc/components/driver/include/driver/rmt_types.inc
.. include-build-file:: inc/components/hal/include/hal/rmt_types.inc
.. [1]
Different ESP chip series might have different number of RMT channels. Please refer to the [`TRM <{IDF_TARGET_TRM_EN_URL}#rmt>`__] for details. The driver won't forbid you from applying for more RMT channels, but it will return error when there's no hardware resources available. Please always check the return value when doing `Resource Allocation <#resource-allocation>`__.
.. [2]
Callback function (e.g. :cpp:member:`rmt_tx_event_callbacks_t::on_trans_done`) and the functions invoked by itself should also reside in IRAM, users need to take care of this by themselves.

Wyświetl plik

@ -128,9 +128,58 @@ I2C
RMT Driver
----------
RMT driver has been redesigned (see :doc:`RMT transceiver <../api-reference/peripherals/rmt>`), which aims to unify and extend the usage of RMT peripheral. Although it's recommended to use the new driver APIs, the legacy driver is still available in the previous include path ``driver/rmt.h``. However, by default, including ``driver/rmt.h`` will bring a build warning like `The legacy RMT driver is deprecated, please use driver/rmt_tx.h and/or driver/rmt_rx.h`. The warning can be suppressed by the Kconfig option :ref:`CONFIG_RMT_SUPPRESS_DEPRECATE_WARN`.
The major breaking changes in concept and usage are listed as follows:
Breaking Changes in Concepts
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- ``rmt_channel_t`` which used to identify the hardware channel are removed from user space. In the new driver, RMT channel is represented by :cpp:type:`rmt_channel_handle_t`. The channel is dynamic allocated by the driver, instead of designated by user.
- ``rmt_item32_t`` is replaced by :cpp:type:`rmt_symbol_word_t`, which avoids a nested union inside a struct.
- ``rmt_mem_t`` is removed, as we don't allow users to access RMT memory block (a.k.an RMTMEM) directly. Direct access to RMTMEM doesn't make sense but make mistakes, especially when the RMT channel also connected with a DMA channel.
- ``rmt_mem_owner_t`` is removed, as the ownership is controller by driver, not by user anymore.
- ``rmt_source_clk_t`` is replaced by :cpp:type:`rmt_clock_source_t`, note they're not binary compatible.
- ``rmt_data_mode_t`` is removed, the RMT memory access mode is configured to always use Non-FIFO and DMA mode.
- ``rmt_mode_t`` is removed, as the driver has stand alone install functions for TX and RX channels.
- ``rmt_idle_level_t`` is removed, setting IDLE level for TX channel is available in :cpp:member:`rmt_transmit_config_t::eot_level`.
- ``rmt_carrier_level_t`` is removed, setting carrier polarity is available in :cpp:member:`rmt_carrier_config_t::polarity_active_low`.
- ``rmt_channel_status_t`` and ``rmt_channel_status_result_t`` are removed, they're not used anywhere.
- transmitting by RMT channel doesn't expect user to prepare the RMT symbols, instead, user needs to provide an RMT Encoder to tell the driver how to convert user data into RMT symbols.
Breaking Changes in Usage
~~~~~~~~~~~~~~~~~~~~~~~~~
- Channel installation has been separated for TX and RX channels into :cpp:func:`rmt_new_tx_channel` and :cpp:func:`rmt_new_rx_channel`.
- ``rmt_set_clk_div`` and ``rmt_get_clk_div`` are removed. Channel clock configuration can only be done during channel installation.
- ``rmt_set_rx_idle_thresh`` and ``rmt_get_rx_idle_thresh`` are removed. In the new driver, the RX channel IDLE threshold is redesigned into a new concept :cpp:member:`rmt_receive_config_t::signal_range_max_ns`.
- ``rmt_set_mem_block_num`` and ``rmt_get_mem_block_num`` are removed. In the new driver, the memory block number is determined by :cpp:member:`rmt_tx_channel_config_t::mem_block_symbols` and :cpp:member:`rmt_rx_channel_config_t::mem_block_symbols`.
- ``rmt_set_tx_carrier`` is removed, the new driver uses :cpp:func:`rmt_apply_carrier` to set carrier behavior.
- ``rmt_set_mem_pd`` and ``rmt_get_mem_pd`` are removed. The memory power is managed by the driver automatically.
- ``rmt_memory_rw_rst``, ``rmt_tx_memory_reset`` and ``rmt_rx_memory_reset`` are removed. Memory reset is managed by the driver automatically.
- ``rmt_tx_start`` and ``rmt_rx_start`` are merged into a single function :cpp:func:`rmt_enable`, for both TX and RX channels.
- ``rmt_tx_stop`` and ``rmt_rx_stop`` are merged into a single function :cpp:func:`rmt_disable`, for both TX and RX channels.
- ``rmt_set_memory_owner`` and ``rmt_get_memory_owner`` are removed. RMT memory owner guard is added automatically by the driver.
- ``rmt_set_tx_loop_mode`` and ``rmt_get_tx_loop_mode`` are removed. In the new driver, the loop mode is configured in :cpp:member:`rmt_transmit_config_t::loop_count`.
- ``rmt_set_source_clk`` and ``rmt_get_source_clk`` are removed. Configuring clock source is only possible during channel installation by :cpp:member:`rmt_tx_channel_config_t::clk_src` and :cpp:member:`rmt_rx_channel_config_t::clk_src`.
- ``rmt_set_rx_filter`` is removed. In the new driver, the filter threshold is redesigned into a new concept :cpp:member:`rmt_receive_config_t::signal_range_min_ns`.
- ``rmt_set_idle_level`` and ``rmt_get_idle_level`` are removed. Setting IDLE level for TX channel is available in :cpp:member:`rmt_transmit_config_t::eot_level`.
- ``rmt_set_rx_intr_en``, ``rmt_set_err_intr_en``, ``rmt_set_tx_intr_en``, ``rmt_set_tx_thr_intr_en`` and ``rmt_set_rx_thr_intr_en`` are removed. The new driver doesn't allow user to turn on/off interrupt from user space. Instead, it provides callback functions.
- ``rmt_set_gpio`` and ``rmt_set_pin`` are removed. The new driver doesn't support to switch GPIO dynamically at runtime.
- ``rmt_config`` is removed. In the new driver, basic configuration is done during the channel installation stage.
- ``rmt_isr_register`` and ``rmt_isr_deregister`` are removed, the interrupt is allocated by the driver itself.
- ``rmt_driver_install`` is replaced by :cpp:func:`rmt_new_tx_channel` and :cpp:func:`rmt_new_rx_channel`.
- ``rmt_driver_uninstall`` is replaced by :cpp:func:`rmt_del_channel`.
- ``rmt_fill_tx_items``, ``rmt_write_items`` and ``rmt_write_sample`` are removed. In the new driver, user needs to provide an encoder to "translate" the user data into RMT symbols.
- ``rmt_get_counter_clock`` is removed, as the channel clock resolution is configured by user from :cpp:member:`rmt_tx_channel_config_t::resolution_hz`.
- ``rmt_wait_tx_done`` is replaced by :cpp:func:`rmt_tx_wait_all_done`.
- ``rmt_translator_init``, ``rmt_translator_set_context`` and ``rmt_translator_get_context`` are removed. In the new driver, the translator has been replaced by the RMT encoder.
- ``rmt_get_ringbuf_handle`` is removed. The new driver doesn't use Ringbuffer to save RMT symbols. Instead, the incoming data are saved to the user provided buffer directly. The user buffer can even be mounted to DMA link internally.
- ``rmt_register_tx_end_callback`` is replaced by :cpp:func:`rmt_tx_register_event_callbacks`, where user can register :cpp:member:`rmt_tx_event_callbacks_t::on_trans_done` event callback.
- ``rmt_set_intr_enable_mask`` and ``rmt_clr_intr_enable_mask`` are removed, as the interrupt is handled by the driver, user doesn't need to take care of it.
- ``rmt_set_pin`` is removed, as ``rmt_set_gpio`` can do the same thing.
- ``rmt_memory_rw_rst`` is removed, user can use ``rmt_tx_memory_reset`` and ``rmt_rx_memory_reset`` for TX and RX channel respectively.
- ``rmt_add_channel_to_group`` and ``rmt_remove_channel_from_group`` are replaced by RMT sync manager. Please refer to :cpp:func:`rmt_new_sync_manager`.
- ``rmt_set_tx_loop_count`` is removed. The loop count in the new driver is configured in :cpp:member:`rmt_transmit_config_t::loop_count`.
- ``rmt_enable_tx_loop_autostop`` is removed. In the new driver, TX loop auto stop is always enabled if available, it's not configurable anymore.
LCD
---

Wyświetl plik

@ -70,6 +70,8 @@ If type alias or template alias:
void() esp_spp_cb_t (esp_spp_cb_event_t event, esp_spp_cb_param_t *param)
----^
rmt_encoder.inc:line: WARNING: Duplicate C++ declaration, also defined at api-reference/peripherals/rmt:line.
Declaration is '.. cpp:type:: struct rmt_encoder_t rmt_encoder_t'.
spi_master.inc:line: WARNING: Duplicate C++ declaration, also defined at api-reference/peripherals/spi_master:line.
Declaration is '.. cpp:type:: struct spi_transaction_t spi_transaction_t'.
spi_slave.inc:line: WARNING: Duplicate C++ declaration, also defined at api-reference/peripherals/spi_slave:line.