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:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user