diff options
| author | Elvis Pfutzenreuter <epxx@epxx.co> | 2024-11-22 20:38:58 -0300 |
|---|---|---|
| committer | Damien George <damien@micropython.org> | 2025-11-17 10:45:19 +1100 |
| commit | 2a3b9b0b4bed6ea51f1053e224278e9dbb266c35 (patch) | |
| tree | 7f87b28423868f94b5960202add77af51b8614d0 /ports/esp32/machine_bitstream.c | |
| parent | 27544a2d81da5b0d804a932d98d680f121a22b8f (diff) | |
esp32/esp32_rmt: Update RMT module to use the new RMT API.
The current `esp32.RMT` class uses a legacy API from ESP-IDF 4.x. The
ESP-IDF 5.x offers a new API, which is overall better, and easier to
implement the RX side in the future. This commit updates the module and
the documentation, preserving the current MicroPython RMT API as much as
possible.
The bitstream RMT implementation was updated as well, since ESP-IDF does
not allow firmware to reference legacy and new APIs at the same time (it
resets right after boot with an error message, even if neither module is
imported).
The documentation is updated accordingly.
Signed-off-by: Elvis Pfutzenreuter <elvis.pfutzenreuter@gmail.com>
Diffstat (limited to 'ports/esp32/machine_bitstream.c')
| -rw-r--r-- | ports/esp32/machine_bitstream.c | 141 |
1 files changed, 70 insertions, 71 deletions
diff --git a/ports/esp32/machine_bitstream.c b/ports/esp32/machine_bitstream.c index ed7fcc407..60addcc15 100644 --- a/ports/esp32/machine_bitstream.c +++ b/ports/esp32/machine_bitstream.c @@ -91,96 +91,95 @@ static void IRAM_ATTR machine_bitstream_high_low_bitbang(mp_hal_pin_obj_t pin, u } #if SOC_RMT_SUPPORTED + /******************************************************************************/ // RMT implementation -#include "driver/rmt.h" - -// Logical 0 and 1 values (encoded as a rmt_item32_t). -// The duration fields will be set later. -static rmt_item32_t bitstream_high_low_0 = {{{ 0, 1, 0, 0 }}}; -static rmt_item32_t bitstream_high_low_1 = {{{ 0, 1, 0, 0 }}}; - -// See https://github.com/espressif/esp-idf/blob/master/examples/common_components/led_strip/led_strip_rmt_ws2812.c -// This is called automatically by the IDF during rmt_write_sample in order to -// convert the byte stream to rmt_item32_t's. -static void IRAM_ATTR bitstream_high_low_rmt_adapter(const void *src, rmt_item32_t *dest, size_t src_size, size_t wanted_num, size_t *translated_size, size_t *item_num) { - if (src == NULL || dest == NULL) { - *translated_size = 0; - *item_num = 0; - return; - } - - size_t size = 0; - size_t num = 0; - uint8_t *psrc = (uint8_t *)src; - rmt_item32_t *pdest = dest; - while (size < src_size && num < wanted_num) { - for (int i = 0; i < 8; i++) { - // MSB first - if (*psrc & (1 << (7 - i))) { - pdest->val = bitstream_high_low_1.val; - } else { - pdest->val = bitstream_high_low_0.val; - } - num++; - pdest++; - } - size++; - psrc++; - } - - *translated_size = size; - *item_num = num; -} - -// Use the reserved RMT channel to stream high/low data on the specified pin. -static void machine_bitstream_high_low_rmt(mp_hal_pin_obj_t pin, uint32_t *timing_ns, const uint8_t *buf, size_t len, uint8_t channel_id) { - rmt_config_t config = RMT_DEFAULT_CONFIG_TX(pin, channel_id); +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0) +#include "rmt_private.h" +#endif +#include "driver/rmt_tx.h" +#include "driver/rmt_encoder.h" +static bool machine_bitstream_high_low_rmt(mp_hal_pin_obj_t pin, uint32_t *timing_ns, const uint8_t *buf, size_t len) { // Use 40MHz clock (although 2MHz would probably be sufficient). - config.clk_div = 2; - - // Install the driver on this channel & pin. - check_esp_err(rmt_config(&config)); - check_esp_err(rmt_driver_install_core1(config.channel)); + uint32_t clock_div = 2; + rmt_channel_handle_t channel = NULL; + rmt_tx_channel_config_t tx_chan_config = { + .clk_src = RMT_CLK_SRC_DEFAULT, + .gpio_num = pin, + .mem_block_symbols = SOC_RMT_MEM_WORDS_PER_CHANNEL, + .resolution_hz = APB_CLK_FREQ / clock_div, + .trans_queue_depth = 1, + }; + if (rmt_new_tx_channel(&tx_chan_config, &channel) != ESP_OK) { + return false; + } + check_esp_err(rmt_enable(channel)); // Get the tick rate in kHz (this will likely be 40000). - uint32_t counter_clk_khz = 0; - check_esp_err(rmt_get_counter_clock(config.channel, &counter_clk_khz)); - + uint32_t counter_clk_khz = APB_CLK_FREQ / clock_div; counter_clk_khz /= 1000; // Convert nanoseconds to pulse duration. - bitstream_high_low_0.duration0 = (counter_clk_khz * timing_ns[0]) / 1e6; - bitstream_high_low_0.duration1 = (counter_clk_khz * timing_ns[1]) / 1e6; - bitstream_high_low_1.duration0 = (counter_clk_khz * timing_ns[2]) / 1e6; - bitstream_high_low_1.duration1 = (counter_clk_khz * timing_ns[3]) / 1e6; - - // Install the bits->highlow translator. - rmt_translator_init(config.channel, bitstream_high_low_rmt_adapter); - - // Stream the byte data using the translator. - check_esp_err(rmt_write_sample(config.channel, buf, len, true)); - - // Wait 50% longer than we expect (if every bit takes the maximum time). - uint32_t timeout_ms = (3 * len / 2) * (1 + (8 * MAX(timing_ns[0] + timing_ns[1], timing_ns[2] + timing_ns[3])) / 1000); - check_esp_err(rmt_wait_tx_done(config.channel, pdMS_TO_TICKS(timeout_ms))); - - // Uninstall the driver. - check_esp_err(rmt_driver_uninstall(config.channel)); + // Example: 500ns = 40000 * 500 / 1e6 = 20 ticks + // 20 ticks / 40MHz = 500e-9 + rmt_bytes_encoder_config_t bytes_encoder_config = { + .bit0 = { + .level0 = 1, + .duration0 = (counter_clk_khz * timing_ns[0]) / 1e6, + .level1 = 0, + .duration1 = (counter_clk_khz * timing_ns[1]) / 1e6, + }, + .bit1 = { + .level0 = 1, + .duration0 = (counter_clk_khz * timing_ns[2]) / 1e6, + .level1 = 0, + .duration1 = (counter_clk_khz * timing_ns[3]) / 1e6, + }, + .flags.msb_first = 1 + }; + + // Install the bits->highlow encoder. + rmt_encoder_handle_t encoder; + check_esp_err(rmt_new_bytes_encoder(&bytes_encoder_config, &encoder)); + + rmt_transmit_config_t tx_config = { + .loop_count = 0, + .flags.eot_level = 0, + }; + + // Stream the byte data using the encoder. + rmt_encoder_reset(encoder); + check_esp_err(rmt_transmit(channel, encoder, buf, len, &tx_config)); + + // Wait until completion. + rmt_tx_wait_all_done(channel, -1); + + // Disable and release channel. + check_esp_err(rmt_del_encoder(encoder)); + rmt_disable(channel); + #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0) + channel->del(channel); + #else + rmt_del_channel(channel); + #endif // Cancel RMT output to GPIO pin. esp_rom_gpio_connect_out_signal(pin, SIG_GPIO_OUT_IDX, false, false); + + return true; } -#endif + +#endif // SOC_RMT_SUPPORTED + /******************************************************************************/ // Interface to machine.bitstream void machine_bitstream_high_low(mp_hal_pin_obj_t pin, uint32_t *timing_ns, const uint8_t *buf, size_t len) { #if SOC_RMT_SUPPORTED - if (esp32_rmt_bitstream_channel_id >= 0) { - machine_bitstream_high_low_rmt(pin, timing_ns, buf, len, esp32_rmt_bitstream_channel_id); + if (esp32_rmt_bitstream_enabled && machine_bitstream_high_low_rmt(pin, timing_ns, buf, len)) { + // Use of RMT was successful. return; } #endif |
