Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

esp32 RMT: Extend Micropython use of hardware carrier freq feature. #6127

Closed
wants to merge 15 commits into from
18 changes: 14 additions & 4 deletions docs/library/esp32.rst
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,11 @@ used to transmit or receive many other types of digital signals::

r = esp32.RMT(0, pin=Pin(18), clock_div=8)
r # RMT(channel=0, pin=18, source_freq=80000000, clock_div=8)

# To use carrier frequency
r = esp32.RMT(0, pin=Pin(18), clock_div=8, carrier_freq_hz=38000)
r # RMT(channel=0, pin=18, source_freq=80000000, clock_div=8, carrier_freq_hz=38000, carrier_duty_percent=50)

# The channel resolution is 100ns (1/(source_freq/clock_div)).
r.write_pulses((1, 20, 2, 40), start=0) # Send 0 for 100ns, 1 for 2000ns, 0 for 200ns, 1 for 4000ns

Expand All @@ -164,27 +169,32 @@ define the pulses.
multiplying the resolution by a 15-bit (0-32,768) number. There are eight
channels (0-7) and each can have a different clock divider.

To enable the carrier frequency feature of the esp32 hardware, specify the ``carrier_freq_hz`` to someting like 38000,
a typical IR carrier frequency.

So, in the example above, the 80MHz clock is divided by 8. Thus the
resolution is (1/(80Mhz/8)) 100ns. Since the ``start`` level is 0 and toggles
with each number, the bitstream is ``0101`` with durations of [100ns, 2000ns,
100ns, 4000ns].


For more details see Espressif's `ESP-IDF RMT documentation.
<https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/rmt.html>`_.

.. Warning::
The current MicroPython RMT implementation lacks some features, most notably
receiving pulses and carrier transmit. RMT should be considered a
The current MicroPython RMT implementation lacks some features. Most noteably the recieve feature. RMT should be considered a
*beta feature* and the interface may change in the future.


.. class:: RMT(channel, \*, pin=None, clock_div=8)
.. class:: RMT(channel, \*, pin=None, clock_div=8, carrier_freq_hz=None, carrier_duty_percent=50)

This class provides access to one of the eight RMT channels. *channel* is
required and identifies which RMT channel (0-7) will be configured. *pin*,
also required, configures which Pin is bound to the RMT channel. *clock_div*
is an 8-bit clock divider that divides the source clock (80MHz) to the RMT
channel allowing the resolution to be specified.
channel allowing the resolution to be specified. *carrier_freq_hz* is used to enable the carrier feature
and specify its frequency, default value is ``None`` (not enabled). To enable, specify a 32-bit interger.
*carrier_duty_percent* is default to 50.

.. method:: RMT.source_freq()

Expand Down
33 changes: 28 additions & 5 deletions ports/esp32/esp32_rmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ typedef struct _esp32_rmt_obj_t {
uint8_t channel_id;
gpio_num_t pin;
uint8_t clock_div;
bool carrier_en;
JonRob812 marked this conversation as resolved.
Show resolved Hide resolved
uint16_t carrier_duty_percent;
uint32_t carrier_freq_hz;
mp_uint_t num_items;
rmt_item32_t *items;
} esp32_rmt_obj_t;
Expand All @@ -61,13 +64,25 @@ STATIC mp_obj_t esp32_rmt_make_new(const mp_obj_type_t *type, size_t n_args, siz
{ MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_pin, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
{ MP_QSTR_clock_div, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} }, // 100ns resolution
{ MP_QSTR_carrier_duty_percent, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 50} },
{ MP_QSTR_carrier_freq_hz, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
JonRob812 marked this conversation as resolved.
Show resolved Hide resolved
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
mp_uint_t channel_id = args[0].u_int;
gpio_num_t pin_id = machine_pin_get_id(args[1].u_obj);
mp_uint_t clock_div = args[2].u_int;

mp_obj_t carrier_en = mp_const_false;
JonRob812 marked this conversation as resolved.
Show resolved Hide resolved
mp_uint_t carrier_duty_percent = 0;
mp_uint_t carrier_freq_hz = 0;

if (args[4].u_obj != MP_OBJ_NULL) {
carrier_en = mp_const_true;
carrier_duty_percent = args[3].u_int;
carrier_freq_hz = mp_obj_get_int(args[4].u_obj);
}

if (clock_div < 1 || clock_div > 255) {
mp_raise_ValueError(MP_ERROR_TEXT("clock_div must be between 1 and 255"));
}
Expand All @@ -77,6 +92,9 @@ STATIC mp_obj_t esp32_rmt_make_new(const mp_obj_type_t *type, size_t n_args, siz
self->channel_id = channel_id;
self->pin = pin_id;
self->clock_div = clock_div;
self->carrier_en = mp_obj_is_true(carrier_en);
self->carrier_duty_percent = carrier_duty_percent;
self->carrier_freq_hz = carrier_freq_hz;

rmt_config_t config;
config.rmt_mode = RMT_MODE_TX;
Expand All @@ -85,11 +103,11 @@ STATIC mp_obj_t esp32_rmt_make_new(const mp_obj_type_t *type, size_t n_args, siz
config.mem_block_num = 1;
config.tx_config.loop_en = 0;

config.tx_config.carrier_en = 0;
config.tx_config.carrier_en = self->carrier_en;
JonRob812 marked this conversation as resolved.
Show resolved Hide resolved
config.tx_config.idle_output_en = 1;
config.tx_config.idle_level = 0;
config.tx_config.carrier_duty_percent = 0;
config.tx_config.carrier_freq_hz = 0;
config.tx_config.carrier_duty_percent = self->carrier_duty_percent;
config.tx_config.carrier_freq_hz = self->carrier_freq_hz;
config.tx_config.carrier_level = 1;

config.clk_div = self->clock_div;
Expand All @@ -103,8 +121,13 @@ STATIC mp_obj_t esp32_rmt_make_new(const mp_obj_type_t *type, size_t n_args, siz
STATIC void esp32_rmt_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
esp32_rmt_obj_t *self = MP_OBJ_TO_PTR(self_in);
if (self->pin != -1) {
mp_printf(print, "RMT(channel=%u, pin=%u, source_freq=%u, clock_div=%u)",
self->channel_id, self->pin, APB_CLK_FREQ, self->clock_div);
if (self->carrier_en) {
JonRob812 marked this conversation as resolved.
Show resolved Hide resolved
mp_printf(print, "RMT(channel=%u, pin=%u, source_freq=%u, clock_div=%u, carrier_freq_hz=%u, carrier_duty_percent=%u)",
self->channel_id, self->pin, APB_CLK_FREQ, self->clock_div, self->carrier_freq_hz, self->carrier_duty_percent);
} else {
mp_printf(print, "RMT(channel=%u, pin=%u, source_freq=%u, clock_div=%u)",
self->channel_id, self->pin, APB_CLK_FREQ, self->clock_div);
}
JonRob812 marked this conversation as resolved.
Show resolved Hide resolved
} else {
mp_printf(print, "RMT()");
}
Expand Down