Skip to content

esp32/machine_bitstream: Reinstate bit-bang implementation. #8158

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

Merged
Merged
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
3 changes: 3 additions & 0 deletions docs/esp32/quickref.rst
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,9 @@ For low-level driving of a NeoPixel::
400kHz) devices by passing ``timing=0`` when constructing the
``NeoPixel`` object.

The low-level driver uses an RMT channel by default. To configure this see
`RMT.bitstream_channel`.

APA102 (DotStar) uses a different driver as it has an additional clock pin.

Capacitive touch
Expand Down
11 changes: 11 additions & 0 deletions docs/library/esp32.rst
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,17 @@ For more details see Espressif's `ESP-IDF RMT documentation.
new sequence of pulses. Looping sequences longer than 126 pulses is not
supported by the hardware.

.. staticmethod:: RMT.bitstream_channel([value])

Select which RMT channel is used by the `machine.bitstream` implementation.
*value* can be ``None`` or a valid RMT channel number. The default RMT
channel is the highest numbered one.

Passing in ``None`` disables the use of RMT and instead selects a bit-banging
implementation for `machine.bitstream`.

Passing in no argument will not change the channel. This function returns
the current channel number.

Ultra-Low-Power co-processor
----------------------------
Expand Down
32 changes: 30 additions & 2 deletions ports/esp32/esp32_rmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ typedef struct _rmt_install_state_t {
esp_err_t ret;
} rmt_install_state_t;

// Current channel used for machine.bitstream, in the machine_bitstream_high_low_rmt
// implementation. A value of -1 means do not use RMT.
int8_t esp32_rmt_bitstream_channel_id = RMT_CHANNEL_MAX - 1;

STATIC void rmt_install_task(void *pvParameter) {
rmt_install_state_t *state = pvParameter;
state->ret = rmt_driver_install(state->channel_id, 0, 0);
Expand Down Expand Up @@ -104,8 +108,8 @@ 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 (esp32_rmt_bitstream_channel_id >= 0 && channel_id == esp32_rmt_bitstream_channel_id) {
mp_raise_ValueError(MP_ERROR_TEXT("channel used by bitstream"));
}

if (clock_div < 1 || clock_div > 255) {
Expand Down Expand Up @@ -314,6 +318,27 @@ STATIC mp_obj_t esp32_rmt_write_pulses(size_t n_args, const mp_obj_t *args) {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp32_rmt_write_pulses_obj, 2, 3, esp32_rmt_write_pulses);

STATIC mp_obj_t esp32_rmt_bitstream_channel(size_t n_args, const mp_obj_t *args) {
if (n_args > 0) {
if (args[0] == mp_const_none) {
esp32_rmt_bitstream_channel_id = -1;
} else {
mp_int_t channel_id = mp_obj_get_int(args[0]);
if (channel_id < 0 || channel_id >= RMT_CHANNEL_MAX) {
mp_raise_ValueError(MP_ERROR_TEXT("invalid channel"));
}
esp32_rmt_bitstream_channel_id = channel_id;
}
}
if (esp32_rmt_bitstream_channel_id < 0) {
return mp_const_none;
} else {
return MP_OBJ_NEW_SMALL_INT(esp32_rmt_bitstream_channel_id);
}
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp32_rmt_bitstream_channel_fun_obj, 0, 1, esp32_rmt_bitstream_channel);
STATIC MP_DEFINE_CONST_STATICMETHOD_OBJ(esp32_rmt_bitstream_channel_obj, MP_ROM_PTR(&esp32_rmt_bitstream_channel_fun_obj));

STATIC const mp_rom_map_elem_t esp32_rmt_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&esp32_rmt_deinit_obj) },
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&esp32_rmt_deinit_obj) },
Expand All @@ -322,6 +347,9 @@ STATIC const mp_rom_map_elem_t esp32_rmt_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_wait_done), MP_ROM_PTR(&esp32_rmt_wait_done_obj) },
{ MP_ROM_QSTR(MP_QSTR_loop), MP_ROM_PTR(&esp32_rmt_loop_obj) },
{ MP_ROM_QSTR(MP_QSTR_write_pulses), MP_ROM_PTR(&esp32_rmt_write_pulses_obj) },

// Static methods
{ MP_ROM_QSTR(MP_QSTR_bitstream_channel), MP_ROM_PTR(&esp32_rmt_bitstream_channel_obj) },
};
STATIC MP_DEFINE_CONST_DICT(esp32_rmt_locals_dict, esp32_rmt_locals_dict_table);

Expand Down
77 changes: 73 additions & 4 deletions ports/esp32/machine_bitstream.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,70 @@

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

#if MICROPY_PY_MACHINE_BITSTREAM

#include "driver/rmt.h"
/******************************************************************************/
// Bit-bang implementation

#include "modesp32.h"
#define NS_TICKS_OVERHEAD (6)

// This is a translation of the cycle counter implementation in ports/stm32/machine_bitstream.c.
STATIC void IRAM_ATTR machine_bitstream_high_low_bitbang(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;
}

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

uint32_t irq_state = mp_hal_quiet_timing_enter();

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]) {
;
}
}
}

mp_hal_quiet_timing_exit(irq_state);
}

/******************************************************************************/
// RMT implementation

#include "driver/rmt.h"

#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 1, 0)
// This convenience macro was not available in earlier IDF versions.
Expand Down Expand Up @@ -93,8 +151,8 @@ STATIC void IRAM_ATTR bitstream_high_low_rmt_adapter(const void *src, rmt_item32
}

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

// Use 40MHz clock (although 2MHz would probably be sufficient).
config.clk_div = 2;
Expand Down Expand Up @@ -138,4 +196,15 @@ void machine_bitstream_high_low(mp_hal_pin_obj_t pin, uint32_t *timing_ns, const
gpio_matrix_out(pin, SIG_GPIO_OUT_IDX, false, false);
}

/******************************************************************************/
// 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 (esp32_rmt_bitstream_channel_id < 0) {
machine_bitstream_high_low_bitbang(pin, timing_ns, buf, len);
} else {
machine_bitstream_high_low_rmt(pin, timing_ns, buf, len, esp32_rmt_bitstream_channel_id);
}
}

#endif // MICROPY_PY_MACHINE_BITSTREAM
5 changes: 2 additions & 3 deletions ports/esp32/modesp32.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,13 @@
#define RTC_LAST_EXT_PIN 39
#define RTC_IS_VALID_EXT_PIN(pin_id) ((1ll << (pin_id)) & RTC_VALID_EXT_PINS)

extern int8_t esp32_rmt_bitstream_channel_id;

extern const mp_obj_type_t esp32_nvs_type;
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)

esp_err_t rmt_driver_install_core1(uint8_t channel_id);

#endif // MICROPY_INCLUDED_ESP32_MODESP32_H