esp32/machine_bitstream: Reinstate bitstream bit-bang implementation.

The bit-bang implementation was replaced with the RMT implementation in
599b61c086.  This commit brings back that
bit-bang code, and allows it to be selected via the new static method:

    esp32.RMT.bitstream_channel(None)

The bit-bang implementation may be useful if the RMT needs to be used for
something else, or if bit-banging is more stable in certain applications.

Signed-off-by: Damien George <damien@micropython.org>
This commit is contained in:
Damien George
2022-01-11 17:21:14 +11:00
parent e754c2e84f
commit a3bbd5332b
5 changed files with 119 additions and 9 deletions

View File

@@ -573,6 +573,9 @@ For low-level driving of a NeoPixel::
400kHz) devices by passing ``timing=0`` when constructing the 400kHz) devices by passing ``timing=0`` when constructing the
``NeoPixel`` object. ``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. APA102 (DotStar) uses a different driver as it has an additional clock pin.
Capacitive touch Capacitive touch

View File

@@ -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 new sequence of pulses. Looping sequences longer than 126 pulses is not
supported by the hardware. 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 Ultra-Low-Power co-processor
---------------------------- ----------------------------

View File

@@ -66,6 +66,10 @@ typedef struct _rmt_install_state_t {
esp_err_t ret; esp_err_t ret;
} rmt_install_state_t; } 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) { STATIC void rmt_install_task(void *pvParameter) {
rmt_install_state_t *state = pvParameter; rmt_install_state_t *state = pvParameter;
state->ret = rmt_driver_install(state->channel_id, 0, 0); state->ret = rmt_driver_install(state->channel_id, 0, 0);
@@ -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_uint_t idle_level = args[3].u_bool;
mp_obj_t tx_carrier_obj = args[4].u_obj; mp_obj_t tx_carrier_obj = args[4].u_obj;
if (channel_id == MICROPY_HW_ESP32_RMT_CHANNEL_BITSTREAM) { if (esp32_rmt_bitstream_channel_id >= 0 && channel_id == esp32_rmt_bitstream_channel_id) {
mp_raise_ValueError(MP_ERROR_TEXT("reserved channel id")); mp_raise_ValueError(MP_ERROR_TEXT("channel used by bitstream"));
} }
if (clock_div < 1 || clock_div > 255) { if (clock_div < 1 || clock_div > 255) {
@@ -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_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[] = { 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___del__), MP_ROM_PTR(&esp32_rmt_deinit_obj) },
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&esp32_rmt_deinit_obj) }, { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&esp32_rmt_deinit_obj) },
@@ -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_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_loop), MP_ROM_PTR(&esp32_rmt_loop_obj) },
{ MP_ROM_QSTR(MP_QSTR_write_pulses), MP_ROM_PTR(&esp32_rmt_write_pulses_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); STATIC MP_DEFINE_CONST_DICT(esp32_rmt_locals_dict, esp32_rmt_locals_dict_table);

View File

@@ -26,12 +26,70 @@
#include "py/mpconfig.h" #include "py/mpconfig.h"
#include "py/mphal.h" #include "py/mphal.h"
#include "modesp32.h"
#if MICROPY_PY_MACHINE_BITSTREAM #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) #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 1, 0)
// This convenience macro was not available in earlier IDF versions. // This convenience macro was not available in earlier IDF versions.
@@ -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. // 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) { 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, MICROPY_HW_ESP32_RMT_CHANNEL_BITSTREAM); rmt_config_t config = RMT_DEFAULT_CONFIG_TX(pin, channel_id);
// Use 40MHz clock (although 2MHz would probably be sufficient). // Use 40MHz clock (although 2MHz would probably be sufficient).
config.clk_div = 2; config.clk_div = 2;
@@ -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); 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 #endif // MICROPY_PY_MACHINE_BITSTREAM

View File

@@ -26,14 +26,13 @@
#define RTC_LAST_EXT_PIN 39 #define RTC_LAST_EXT_PIN 39
#define RTC_IS_VALID_EXT_PIN(pin_id) ((1ll << (pin_id)) & RTC_VALID_EXT_PINS) #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_nvs_type;
extern const mp_obj_type_t esp32_partition_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_rmt_type;
extern const mp_obj_type_t esp32_ulp_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); esp_err_t rmt_driver_install_core1(uint8_t channel_id);
#endif // MICROPY_INCLUDED_ESP32_MODESP32_H #endif // MICROPY_INCLUDED_ESP32_MODESP32_H