esp32/esp32_rmt: Extend RMT to support carrier feature.

The ESP32 RMT peripheral has hardware support for a carrier frequency, and
this commit exposes it to Python with the keyword arguments carrier_freq
and carrier_duty_percent in the constructor.  Example usage:

    r = esp32.RMT(0, pin=Pin(2), clock_div=80, carrier_freq=38000, carrier_duty_percent=50)
This commit is contained in:
Jon Rob 2020-06-08 06:29:43 +00:00 committed by Damien George
parent a51eef4471
commit 1678f41744
2 changed files with 40 additions and 7 deletions

View File

@ -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 = esp32.RMT(0, pin=Pin(18), clock_div=8)
r # RMT(channel=0, pin=18, source_freq=80000000, 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=38000)
r # RMT(channel=0, pin=18, source_freq=80000000, clock_div=8, carrier_freq=38000, carrier_duty_percent=50)
# The channel resolution is 100ns (1/(source_freq/clock_div)). # 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 r.write_pulses((1, 20, 2, 40), start=0) # Send 0 for 100ns, 1 for 2000ns, 0 for 200ns, 1 for 4000ns
@ -164,6 +169,9 @@ define the pulses.
multiplying the resolution by a 15-bit (0-32,768) number. There are eight 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. 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`` as something like 38000, a typical IR carrier frequency.
So, in the example above, the 80MHz clock is divided by 8. Thus the 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 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, with each number, the bitstream is ``0101`` with durations of [100ns, 2000ns,
@ -174,17 +182,20 @@ For more details see Espressif's `ESP-IDF RMT documentation.
.. Warning:: .. Warning::
The current MicroPython RMT implementation lacks some features, most notably The current MicroPython RMT implementation lacks some features, most notably
receiving pulses and carrier transmit. RMT should be considered a receiving pulses. RMT should be considered a
*beta feature* and the interface may change in the future. *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=0, carrier_duty_percent=50)
This class provides access to one of the eight RMT channels. *channel* is 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*, 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* 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 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* is used to
enable the carrier feature and specify its frequency, default value is ``0``
(not enabled). To enable, specify a positive integer. *carrier_duty_percent*
defaults to 50.
.. method:: RMT.source_freq() .. method:: RMT.source_freq()

View File

@ -52,6 +52,8 @@ typedef struct _esp32_rmt_obj_t {
uint8_t channel_id; uint8_t channel_id;
gpio_num_t pin; gpio_num_t pin;
uint8_t clock_div; uint8_t clock_div;
uint16_t carrier_duty_percent;
uint32_t carrier_freq;
mp_uint_t num_items; mp_uint_t num_items;
rmt_item32_t *items; rmt_item32_t *items;
} esp32_rmt_obj_t; } esp32_rmt_obj_t;
@ -61,6 +63,8 @@ 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_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_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_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, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
}; };
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; 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_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
@ -68,6 +72,16 @@ STATIC mp_obj_t esp32_rmt_make_new(const mp_obj_type_t *type, size_t n_args, siz
gpio_num_t pin_id = machine_pin_get_id(args[1].u_obj); gpio_num_t pin_id = machine_pin_get_id(args[1].u_obj);
mp_uint_t clock_div = args[2].u_int; mp_uint_t clock_div = args[2].u_int;
bool carrier_en = false;
mp_uint_t carrier_duty_percent = 0;
mp_uint_t carrier_freq = 0;
if (args[4].u_int > 0) {
carrier_en = true;
carrier_duty_percent = args[3].u_int;
carrier_freq = args[4].u_int;
}
if (clock_div < 1 || clock_div > 255) { if (clock_div < 1 || clock_div > 255) {
mp_raise_ValueError(MP_ERROR_TEXT("clock_div must be between 1 and 255")); mp_raise_ValueError(MP_ERROR_TEXT("clock_div must be between 1 and 255"));
} }
@ -77,6 +91,8 @@ 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->channel_id = channel_id;
self->pin = pin_id; self->pin = pin_id;
self->clock_div = clock_div; self->clock_div = clock_div;
self->carrier_duty_percent = carrier_duty_percent;
self->carrier_freq = carrier_freq;
rmt_config_t config; rmt_config_t config;
config.rmt_mode = RMT_MODE_TX; config.rmt_mode = RMT_MODE_TX;
@ -85,11 +101,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.mem_block_num = 1;
config.tx_config.loop_en = 0; config.tx_config.loop_en = 0;
config.tx_config.carrier_en = 0; config.tx_config.carrier_en = carrier_en;
config.tx_config.idle_output_en = 1; config.tx_config.idle_output_en = 1;
config.tx_config.idle_level = 0; config.tx_config.idle_level = 0;
config.tx_config.carrier_duty_percent = 0; config.tx_config.carrier_duty_percent = self->carrier_duty_percent;
config.tx_config.carrier_freq_hz = 0; config.tx_config.carrier_freq_hz = self->carrier_freq;
config.tx_config.carrier_level = 1; config.tx_config.carrier_level = 1;
config.clk_div = self->clock_div; config.clk_div = self->clock_div;
@ -103,8 +119,14 @@ 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) { 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); esp32_rmt_obj_t *self = MP_OBJ_TO_PTR(self_in);
if (self->pin != -1) { if (self->pin != -1) {
mp_printf(print, "RMT(channel=%u, pin=%u, source_freq=%u, clock_div=%u)", 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); self->channel_id, self->pin, APB_CLK_FREQ, self->clock_div);
if (self->carrier_freq > 0) {
mp_printf(print, ", carrier_freq=%u, carrier_duty_percent=%u)",
self->carrier_freq, self->carrier_duty_percent);
} else {
mp_printf(print, ")");
}
} else { } else {
mp_printf(print, "RMT()"); mp_printf(print, "RMT()");
} }