diff options
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 |
