Skip to content

esp32/machine_bitstream.c: Replace with RMT-based driver. #7985

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

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions ports/esp32/esp32_rmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
#include "mphalport.h"
#include "driver/rmt.h"

#include "modesp32.h"

// This exposes the ESP32's RMT module to MicroPython. RMT is provided by the Espressif ESP-IDF:
//
// https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/rmt.html
Expand Down Expand Up @@ -73,6 +75,10 @@ STATIC mp_obj_t esp32_rmt_make_new(const mp_obj_type_t *type, size_t n_args, siz
mp_uint_t idle_level = args[3].u_bool;
mp_obj_t tx_carrier_obj = args[4].u_obj;

if (channel_id == MICROPY_HW_ESP32_RMT_CHANNEL_BITSTREAM) {
mp_raise_ValueError(MP_ERROR_TEXT("reserved channel id"));
}

if (clock_div < 1 || clock_div > 255) {
mp_raise_ValueError(MP_ERROR_TEXT("clock_div must be between 1 and 255"));
}
Expand Down
114 changes: 69 additions & 45 deletions ports/esp32/machine_bitstream.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,63 +24,87 @@
* THE SOFTWARE.
*/

// This is a translation of the cycle counter implementation in ports/stm32/machine_bitstream.c.

#include "py/mpconfig.h"
#include "py/mphal.h"

#if MICROPY_PY_MACHINE_BITSTREAM

#define NS_TICKS_OVERHEAD (6)

void IRAM_ATTR machine_bitstream_high_low(mp_hal_pin_obj_t pin, uint32_t *timing_ns, const uint8_t *buf, size_t len) {
uint32_t pin_mask, gpio_reg_set, gpio_reg_clear;
#if !CONFIG_IDF_TARGET_ESP32C3
if (pin >= 32) {
pin_mask = 1 << (pin - 32);
gpio_reg_set = GPIO_OUT1_W1TS_REG;
gpio_reg_clear = GPIO_OUT1_W1TC_REG;
} else
#endif
{
pin_mask = 1 << pin;
gpio_reg_set = GPIO_OUT_W1TS_REG;
gpio_reg_clear = GPIO_OUT_W1TC_REG;
}
#include "driver/rmt.h"

// Convert ns to cpu ticks [high_time_0, period_0, high_time_1, period_1].
uint32_t fcpu_mhz = ets_get_cpu_frequency();
for (size_t i = 0; i < 4; ++i) {
timing_ns[i] = fcpu_mhz * timing_ns[i] / 1000;
if (timing_ns[i] > NS_TICKS_OVERHEAD) {
timing_ns[i] -= NS_TICKS_OVERHEAD;
}
if (i % 2 == 1) {
// Convert low_time to period (i.e. add high_time).
timing_ns[i] += timing_ns[i - 1];
}
}
#include "modesp32.h"

uint32_t irq_state = mp_hal_quiet_timing_enter();
// 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 }}};

for (size_t i = 0; i < len; ++i) {
uint8_t b = buf[i];
for (size_t j = 0; j < 8; ++j) {
GPIO_REG_WRITE(gpio_reg_set, pin_mask);
uint32_t start_ticks = mp_hal_ticks_cpu();
uint32_t *t = &timing_ns[b >> 6 & 2];
while (mp_hal_ticks_cpu() - start_ticks < t[0]) {
;
}
GPIO_REG_WRITE(gpio_reg_clear, pin_mask);
b <<= 1;
while (mp_hal_ticks_cpu() - start_ticks < t[1]) {
;
// 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++;
}

mp_hal_quiet_timing_exit(irq_state);
*translated_size = size;
*item_num = num;
}

// Use the reserved RMT channel to stream high/low data on the specified pin.
void machine_bitstream_high_low(mp_hal_pin_obj_t pin, uint32_t *timing_ns, const uint8_t *buf, size_t len) {
rmt_config_t config = RMT_DEFAULT_CONFIG_TX(pin, MICROPY_HW_ESP32_RMT_CHANNEL_BITSTREAM);

// 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(config.channel, 0, 0));

// 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));
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));
}

#endif // MICROPY_PY_MACHINE_BITSTREAM
3 changes: 3 additions & 0 deletions ports/esp32/modesp32.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,7 @@ extern const mp_obj_type_t esp32_partition_type;
extern const mp_obj_type_t esp32_rmt_type;
extern const mp_obj_type_t esp32_ulp_type;

// Reserve the last channel for machine.bitstream.
#define MICROPY_HW_ESP32_RMT_CHANNEL_BITSTREAM (RMT_CHANNEL_MAX - 1)

#endif // MICROPY_INCLUDED_ESP32_MODESP32_H