Merge pull request #3279 from hierophect/esp32-pulseinout

ESP32-S2: Add PulseOut and PulseIn
This commit is contained in:
Scott Shawcroft 2020-08-20 11:24:08 -07:00 committed by GitHub
commit 400701004b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 311 additions and 60 deletions

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-08-14 09:36-0400\n"
"POT-Creation-Date: 2020-08-18 11:19-0400\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -744,7 +744,7 @@ msgstr ""
#: shared-bindings/aesio/aes.c shared-bindings/busio/SPI.c
#: shared-bindings/microcontroller/Pin.c
#: shared-bindings/neopixel_write/__init__.c shared-bindings/pulseio/PulseOut.c
#: shared-bindings/neopixel_write/__init__.c
#: shared-bindings/terminalio/Terminal.c
msgid "Expected a %q"
msgstr ""
@ -1328,6 +1328,15 @@ msgstr ""
msgid "Polygon needs at least 3 points"
msgstr ""
#: ports/atmel-samd/common-hal/pulseio/PulseOut.c
#: ports/cxd56/common-hal/pulseio/PulseOut.c
#: ports/nrf/common-hal/pulseio/PulseOut.c
#: ports/stm/common-hal/pulseio/PulseOut.c
msgid ""
"Port does not accept pins or frequency. "
"Construct and pass a PWMOut Carrier instead"
msgstr ""
#: shared-bindings/_bleio/Adapter.c
msgid "Prefix buffer must be on the heap"
msgstr ""
@ -1340,6 +1349,10 @@ msgstr ""
msgid "Pull not used when direction is output."
msgstr ""
#: ports/stm/ref/pulseout-pre-timeralloc.c
msgid "PulseOut not supported on this chip"
msgstr ""
#: ports/stm/common-hal/os/__init__.c
msgid "RNG DeInit Error"
msgstr ""

View File

@ -96,7 +96,15 @@ void pulseout_reset() {
}
void common_hal_pulseio_pulseout_construct(pulseio_pulseout_obj_t* self,
const pulseio_pwmout_obj_t* carrier) {
const pulseio_pwmout_obj_t* carrier,
const mcu_pin_obj_t* pin,
uint32_t frequency,
uint16_t duty_cycle) {
if (!carrier || pin || frequency) {
mp_raise_NotImplementedError(translate("Port does not accept pins or frequency. \
Construct and pass a PWMOut Carrier instead"));
}
if (refcount == 0) {
// Find a spare timer.
Tc *tc = NULL;

View File

@ -58,8 +58,16 @@ static bool pulseout_timer_handler(unsigned int *next_interval_us, void *arg)
return true;
}
void common_hal_pulseio_pulseout_construct(pulseio_pulseout_obj_t *self,
const pulseio_pwmout_obj_t *carrier) {
void common_hal_pulseio_pulseout_construct(pulseio_pulseout_obj_t* self,
const pulseio_pwmout_obj_t* carrier,
const mcu_pin_obj_t* pin,
uint32_t frequency,
uint16_t duty_cycle) {
if (!carrier || pin || frequency) {
mp_raise_NotImplementedError(translate("Port does not accept pins or frequency. \
Construct and pass a PWMOut Carrier instead"));
}
if (pulse_fd < 0) {
pulse_fd = open("/dev/timer0", O_RDONLY);
}

View File

@ -35,10 +35,17 @@
#include "shared-module/displayio/__init__.h"
#endif
#if CIRCUITPY_PULSEIO
#include "common-hal/pulseio/PulseIn.h"
#endif
void port_background_task(void) {
// Zero delay in case FreeRTOS wants to switch to something else.
vTaskDelay(0);
#if CIRCUITPY_PULSEIO
pulsein_background();
#endif
}
void port_start_background_task(void) {}

View File

@ -93,6 +93,9 @@ void common_hal_neopixel_write (const digitalio_digitalinout_obj_t* digitalinout
// Reserve channel
uint8_t number = digitalinout->pin->number;
rmt_channel_t channel = esp32s2_peripherals_find_and_reserve_rmt();
if (channel == RMT_CHANNEL_MAX) {
mp_raise_RuntimeError(translate("All timers in use"));
}
// Configure Channel
rmt_config_t config = RMT_DEFAULT_CONFIG_TX(number, channel);

View File

@ -25,51 +25,184 @@
*/
#include "common-hal/pulseio/PulseIn.h"
#include "shared-bindings/microcontroller/__init__.h"
#include "py/runtime.h"
// STATIC void pulsein_handler(uint8_t num) {
// }
STATIC uint8_t refcount = 0;
STATIC pulseio_pulsein_obj_t * handles[RMT_CHANNEL_MAX];
// Requires rmt.c void esp32s2_peripherals_reset_all(void) to reset
STATIC void update_internal_buffer(pulseio_pulsein_obj_t* self) {
uint32_t length = 0;
rmt_item32_t *items = (rmt_item32_t *) xRingbufferReceive(self->buf_handle, &length, 0);
if (items) {
length /= 4;
for (size_t i=0; i < length; i++) {
uint16_t pos = (self->start + self->len) % self->maxlen;
self->buffer[pos] = items[i].duration0 * 3;
// Check if second item exists before incrementing
if (items[i].duration1) {
self->buffer[pos+1] = items[i].duration1 * 3;
if (self->len < (self->maxlen - 1)) {
self->len += 2;
} else {
self->start += 2;
}
} else {
if (self->len < self->maxlen) {
self->len++;
} else {
self->start++;
}
}
}
vRingbufferReturnItem(self->buf_handle, (void *) items);
}
}
// We can't access the RMT interrupt, so we need a global service to prevent
// the ringbuffer from overflowing and crashing the peripheral
void pulsein_background(void) {
for (size_t i = 0; i < RMT_CHANNEL_MAX; i++) {
if (handles[i]) {
update_internal_buffer(handles[i]);
UBaseType_t items_waiting;
vRingbufferGetInfo(handles[i]->buf_handle, NULL, NULL, NULL, NULL, &items_waiting);
}
}
}
void pulsein_reset(void) {
for (size_t i = 0; i < RMT_CHANNEL_MAX; i++) {
handles[i] = NULL;
}
supervisor_disable_tick();
refcount = 0;
}
void common_hal_pulseio_pulsein_construct(pulseio_pulsein_obj_t* self, const mcu_pin_obj_t* pin,
uint16_t maxlen, bool idle_state) {
mp_raise_NotImplementedError(translate("PulseIn not supported on this chip"));
self->buffer = (uint16_t *) m_malloc(maxlen * sizeof(uint16_t), false);
if (self->buffer == NULL) {
mp_raise_msg_varg(&mp_type_MemoryError, translate("Failed to allocate RX buffer of %d bytes"), maxlen * sizeof(uint16_t));
}
self->pin = pin;
self->maxlen = maxlen;
self->idle_state = idle_state;
self->start = 0;
self->len = 0;
self->paused = false;
// Set pull settings
gpio_pullup_dis(pin->number);
gpio_pulldown_dis(pin->number);
if (idle_state) {
gpio_pullup_en(pin->number);
} else {
gpio_pulldown_en(pin->number);
}
// Find a free RMT Channel and configure it
rmt_channel_t channel = esp32s2_peripherals_find_and_reserve_rmt();
if (channel == RMT_CHANNEL_MAX) {
mp_raise_RuntimeError(translate("All timers in use"));
}
rmt_config_t config = RMT_DEFAULT_CONFIG_RX(pin->number, channel);
config.rx_config.filter_en = true;
config.rx_config.idle_threshold = 30000; // 30*3=90ms idle required to register a sequence
config.clk_div = 240; // All measurements are divided by 3 to accomodate 65ms pulses
rmt_config(&config);
rmt_driver_install(channel, 1000, 0); //TODO: pick a more specific buffer size?
// Store this object and the buffer handle for background updates
self->channel = channel;
handles[channel] = self;
rmt_get_ringbuf_handle(channel, &(self->buf_handle));
// start RMT RX, and enable ticks so the core doesn't turn off.
rmt_rx_start(channel, true);
supervisor_enable_tick();
refcount++;
}
bool common_hal_pulseio_pulsein_deinited(pulseio_pulsein_obj_t* self) {
return false;
return handles[self->channel] ? false : true;
}
void common_hal_pulseio_pulsein_deinit(pulseio_pulsein_obj_t* self) {
handles[self->channel] = NULL;
esp32s2_peripherals_free_rmt(self->channel);
reset_pin_number(self->pin->number);
refcount--;
if (refcount == 0) {
supervisor_disable_tick();
}
}
void common_hal_pulseio_pulsein_pause(pulseio_pulsein_obj_t* self) {
self->paused = true;
rmt_rx_stop(self->channel);
}
void common_hal_pulseio_pulsein_resume(pulseio_pulsein_obj_t* self, uint16_t trigger_duration) {
// Make sure we're paused.
if ( !self->paused ) {
common_hal_pulseio_pulsein_pause(self);
}
if (trigger_duration > 0) {
gpio_set_direction(self->pin->number, GPIO_MODE_DEF_OUTPUT);
gpio_set_level(self->pin->number, !self->idle_state);
common_hal_mcu_delay_us((uint32_t)trigger_duration);
gpio_set_level(self->pin->number, self->idle_state);
gpio_set_direction(self->pin->number, GPIO_MODE_INPUT); // should revert to pull direction
}
self->paused = false;
rmt_rx_start(self->channel, false);
}
void common_hal_pulseio_pulsein_clear(pulseio_pulsein_obj_t* self) {
// Buffer only updates in BG tasks or fetches, so no extra protection is needed
self->start = 0;
self->len = 0;
}
uint16_t common_hal_pulseio_pulsein_get_item(pulseio_pulsein_obj_t* self, int16_t index) {
return false;
update_internal_buffer(self);
if (index < 0) {
index += self->len;
}
if (index < 0 || index >= self->len) {
mp_raise_IndexError(translate("index out of range"));
}
uint16_t value = self->buffer[(self->start + index) % self->maxlen];
return value;
}
uint16_t common_hal_pulseio_pulsein_popleft(pulseio_pulsein_obj_t* self) {
return false;
update_internal_buffer(self);
if (self->len == 0) {
mp_raise_IndexError(translate("pop from an empty PulseIn"));
}
uint16_t value = self->buffer[self->start];
self->start = (self->start + 1) % self->maxlen;
self->len--;
return value;
}
uint16_t common_hal_pulseio_pulsein_get_maxlen(pulseio_pulsein_obj_t* self) {
return false;
return self->maxlen;
}
bool common_hal_pulseio_pulsein_get_paused(pulseio_pulsein_obj_t* self) {
return false;
return self->paused;
}
uint16_t common_hal_pulseio_pulsein_get_len(pulseio_pulsein_obj_t* self) {
return false;
return self->len;
}

View File

@ -30,24 +30,27 @@
#include "common-hal/microcontroller/Pin.h"
#include "py/obj.h"
#include "driver/rmt.h"
#include "rmt.h"
typedef struct {
mp_obj_base_t base;
const mcu_pin_obj_t* pin;
rmt_channel_t channel;
bool idle_state;
bool paused;
volatile bool first_edge;
RingbufHandle_t buf_handle;
uint16_t* buffer;
uint16_t maxlen;
volatile uint16_t start;
volatile uint16_t len;
volatile uint32_t last_overflow;
volatile uint16_t last_count;
} pulseio_pulsein_obj_t;
void pulsein_reset(void);
void pulsein_background(void);
#endif // MICROPY_INCLUDED_ESP32S2_COMMON_HAL_PULSEIO_PULSEIN_H

View File

@ -29,32 +29,63 @@
#include "shared-bindings/pulseio/PWMOut.h"
#include "py/runtime.h"
// STATIC void turn_on(pulseio_pulseout_obj_t *pulseout) {
// }
// STATIC void turn_off(pulseio_pulseout_obj_t *pulseout) {
// }
// STATIC void start_timer(void) {
// }
// STATIC void pulseout_event_handler(void) {
// }
void pulseout_reset() {
}
// Requires rmt.c void esp32s2_peripherals_reset_all(void) to reset
void common_hal_pulseio_pulseout_construct(pulseio_pulseout_obj_t* self,
const pulseio_pwmout_obj_t* carrier) {
mp_raise_NotImplementedError(translate("PulseOut not supported on this chip"));
const pulseio_pwmout_obj_t* carrier,
const mcu_pin_obj_t* pin,
uint32_t frequency,
uint16_t duty_cycle) {
if (carrier || !pin || !frequency) {
mp_raise_NotImplementedError(translate("Port does not accept PWM carrier. \
Pass a pin, frequency and duty cycle instead"));
}
rmt_channel_t channel = esp32s2_peripherals_find_and_reserve_rmt();
if (channel == RMT_CHANNEL_MAX) {
mp_raise_RuntimeError(translate("All timers in use"));
}
// Configure Channel
rmt_config_t config = RMT_DEFAULT_CONFIG_TX(pin->number, channel);
config.tx_config.carrier_en = true;
config.tx_config.carrier_duty_percent = (duty_cycle * 100) / (1<<16);
config.tx_config.carrier_freq_hz = frequency;
config.clk_div = 80;
rmt_config(&config);
rmt_driver_install(channel, 0, 0);
self->channel = channel;
}
bool common_hal_pulseio_pulseout_deinited(pulseio_pulseout_obj_t* self) {
return false;
return (self->channel == RMT_CHANNEL_MAX);
}
void common_hal_pulseio_pulseout_deinit(pulseio_pulseout_obj_t* self) {
esp32s2_peripherals_free_rmt(self->channel);
self->channel = RMT_CHANNEL_MAX;
}
void common_hal_pulseio_pulseout_send(pulseio_pulseout_obj_t* self, uint16_t* pulses, uint16_t length) {
rmt_item32_t items[length];
// Circuitpython allows 16 bit pulse values, while ESP32 only allows 15 bits
// Thus, we use entire items for one pulse, rather than switching inside each item
for (size_t i = 0; i < length; i++) {
// Setting the RMT duration to 0 has undefined behavior, so avoid that pre-emptively.
if (pulses[i] == 0) {
pulses[i] = 1;
}
uint32_t level = (i % 2) ? 0 : 1;
const rmt_item32_t item = {{{ (pulses[i] & 0x8000 ? 0x7FFF : 1), level, (pulses[i] & 0x7FFF), level}}};
items[i] = item;
}
rmt_write_items(self->channel, items, length, true);
while (rmt_wait_tx_done(self->channel, 0) != ESP_OK) {
RUN_BACKGROUND_TASKS;
}
}

View File

@ -29,14 +29,14 @@
#include "common-hal/microcontroller/Pin.h"
#include "common-hal/pulseio/PWMOut.h"
#include "driver/rmt.h"
#include "rmt.h"
#include "py/obj.h"
typedef struct {
mp_obj_base_t base;
pulseio_pwmout_obj_t *pwmout;
rmt_channel_t channel;
} pulseio_pulseout_obj_t;
void pulseout_reset(void);
#endif // MICROPY_INCLUDED_ESP32S2_COMMON_HAL_PULSEIO_PULSEOUT_H

View File

@ -28,19 +28,18 @@
#ifndef ESP32S2_MPCONFIGPORT_H__
#define ESP32S2_MPCONFIGPORT_H__
#define CIRCUITPY_INTERNAL_NVM_SIZE (0)
#define MICROPY_NLR_THUMB (0)
#define CIRCUITPY_INTERNAL_NVM_SIZE (0)
#define MICROPY_NLR_THUMB (0)
#define MICROPY_PY_UJSON (0)
#define MICROPY_USE_INTERNAL_PRINTF (0)
#define MICROPY_PY_UJSON (0)
#define MICROPY_USE_INTERNAL_PRINTF (0)
#include "py/circuitpy_mpconfig.h"
#define MICROPY_PORT_ROOT_POINTERS \
CIRCUITPY_COMMON_ROOT_POINTERS
#define MICROPY_NLR_SETJMP (1)
#define CIRCUITPY_DEFAULT_STACK_SIZE 0x6000
#define MICROPY_NLR_SETJMP (1)
#define CIRCUITPY_DEFAULT_STACK_SIZE (0x6000)
#endif // __INCLUDED_ESP32S2_MPCONFIGPORT_H

View File

@ -29,6 +29,14 @@
bool rmt_reserved_channels[RMT_CHANNEL_MAX];
void esp32s2_peripherals_rmt_reset(void) {
for (size_t i = 0; i < RMT_CHANNEL_MAX; i++) {
if (rmt_reserved_channels[i]) {
esp32s2_peripherals_free_rmt(i);
}
}
}
rmt_channel_t esp32s2_peripherals_find_and_reserve_rmt(void) {
for (size_t i = 0; i < RMT_CHANNEL_MAX; i++) {
if (!rmt_reserved_channels[i]) {
@ -36,8 +44,8 @@ rmt_channel_t esp32s2_peripherals_find_and_reserve_rmt(void) {
return i;
}
}
mp_raise_RuntimeError(translate("All timers in use"));
return false;
// Returning the max indicates a reservation failure.
return RMT_CHANNEL_MAX;
}
void esp32s2_peripherals_free_rmt(rmt_channel_t chan) {

View File

@ -31,6 +31,7 @@
#include "driver/rmt.h"
#include <stdint.h>
void esp32s2_peripherals_rmt_reset(void);
rmt_channel_t esp32s2_peripherals_find_and_reserve_rmt(void);
void esp32s2_peripherals_free_rmt(rmt_channel_t chan);

View File

@ -39,9 +39,12 @@
#include "common-hal/busio/SPI.h"
#include "common-hal/busio/UART.h"
#include "common-hal/pulseio/PWMOut.h"
#include "common-hal/pulseio/PulseIn.h"
#include "supervisor/memory.h"
#include "supervisor/shared/tick.h"
#include "rmt.h"
STATIC esp_timer_handle_t _tick_timer;
void tick_timer_cb(void* arg) {
@ -66,7 +69,9 @@ void reset_port(void) {
vTaskDelay(4);
#if CIRCUITPY_PULSEIO
esp32s2_peripherals_rmt_reset();
pwmout_reset();
pulsein_reset();
#endif
#if CIRCUITPY_BUSIO
i2c_reset();

View File

@ -94,7 +94,10 @@ void pulseout_reset() {
}
void common_hal_pulseio_pulseout_construct(pulseio_pulseout_obj_t* self,
const pulseio_pwmout_obj_t* carrier) {
const pulseio_pwmout_obj_t* carrier,
const mcu_pin_obj_t* pin,
uint32_t frequency,
uint16_t duty_cycle) {
// if (refcount == 0) {
// // Find a spare timer.
// Tc *tc = NULL;

View File

@ -100,7 +100,15 @@ void pulseout_reset() {
}
void common_hal_pulseio_pulseout_construct(pulseio_pulseout_obj_t* self,
const pulseio_pwmout_obj_t* carrier) {
const pulseio_pwmout_obj_t* carrier,
const mcu_pin_obj_t* pin,
uint32_t frequency,
uint16_t duty_cycle) {
if (!carrier || pin || frequency) {
mp_raise_NotImplementedError(translate("Port does not accept pins or frequency. \
Construct and pass a PWMOut Carrier instead"));
}
if (refcount == 0) {
timer = nrf_peripherals_allocate_timer_or_throw();
}

View File

@ -113,7 +113,15 @@ void pulseout_reset() {
}
void common_hal_pulseio_pulseout_construct(pulseio_pulseout_obj_t* self,
const pulseio_pwmout_obj_t* carrier) {
const pulseio_pwmout_obj_t* carrier,
const mcu_pin_obj_t* pin,
uint32_t frequency,
uint16_t duty_cycle) {
if (!carrier || pin || frequency) {
mp_raise_NotImplementedError(translate("Port does not accept pins or frequency. \
Construct and pass a PWMOut Carrier instead"));
}
// Add to active PulseOuts
refcount++;
TIM_TypeDef * tim_instance = stm_peripherals_find_timer();

View File

@ -64,20 +64,29 @@
//| pulse.send(pulses)"""
//| ...
//|
STATIC mp_obj_t pulseio_pulseout_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
mp_arg_check_num(n_args, kw_args, 1, 1, false);
mp_obj_t carrier_obj = args[0];
STATIC mp_obj_t pulseio_pulseout_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
if (!MP_OBJ_IS_TYPE(carrier_obj, &pulseio_pwmout_type)) {
mp_raise_TypeError_varg(translate("Expected a %q"), pulseio_pwmout_type.name);
}
// create Pulse object from the given pin
pulseio_pulseout_obj_t *self = m_new_obj(pulseio_pulseout_obj_t);
self->base.type = &pulseio_pulseout_type;
common_hal_pulseio_pulseout_construct(self, (pulseio_pwmout_obj_t *)MP_OBJ_TO_PTR(carrier_obj));
mp_obj_t carrier_obj = pos_args[0];
if (MP_OBJ_IS_TYPE(carrier_obj, &pulseio_pwmout_type)) {
// Use a PWMOut Carrier
mp_arg_check_num(n_args, kw_args, 1, 1, false);
common_hal_pulseio_pulseout_construct(self, (pulseio_pwmout_obj_t *)MP_OBJ_TO_PTR(carrier_obj), NULL, 0, 0);
} else {
// Use a Pin, frequency, and duty cycle
enum { ARG_pin, ARG_frequency};
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_pin, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_frequency, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 38000} },
{ MP_QSTR_duty_cycle, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1<<15} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
const mcu_pin_obj_t* pin = validate_obj_is_free_pin(args[ARG_pin].u_obj);
common_hal_pulseio_pulseout_construct(self, NULL, pin, args[ARG_frequency].u_int, args[ARG_frequency].u_int);
}
return MP_OBJ_FROM_PTR(self);
}

View File

@ -34,7 +34,11 @@
extern const mp_obj_type_t pulseio_pulseout_type;
extern void common_hal_pulseio_pulseout_construct(pulseio_pulseout_obj_t* self,
const pulseio_pwmout_obj_t* carrier);
const pulseio_pwmout_obj_t* carrier,
const mcu_pin_obj_t* pin,
uint32_t frequency,
uint16_t duty_cycle);
extern void common_hal_pulseio_pulseout_deinit(pulseio_pulseout_obj_t* self);
extern bool common_hal_pulseio_pulseout_deinited(pulseio_pulseout_obj_t* self);
extern void common_hal_pulseio_pulseout_send(pulseio_pulseout_obj_t* self,