summaryrefslogtreecommitdiff
path: root/ports/esp32/machine_bitstream.c
diff options
context:
space:
mode:
Diffstat (limited to 'ports/esp32/machine_bitstream.c')
-rw-r--r--ports/esp32/machine_bitstream.c141
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