Merge pull request #5100 from tannewt/fix_cp_irremote

Switch SAMD21 ticks to PER event
This commit is contained in:
Scott Shawcroft 2021-08-12 09:52:29 -07:00 committed by GitHub
commit d294692c4e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 185 additions and 98 deletions

View File

@ -149,7 +149,7 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input
mp_hal_stdout_tx_strn("\x04", 1); mp_hal_stdout_tx_strn("\x04", 1);
} }
// check for SystemExit // check for SystemExit
if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(((mp_obj_base_t *)nlr.ret_val)->type), MP_OBJ_FROM_PTR(&mp_type_SystemExit))) { if (mp_obj_is_subclass_fast(mp_obj_get_type((mp_obj_t)nlr.ret_val), MP_OBJ_FROM_PTR(&mp_type_SystemExit))) {
// at the moment, the value of SystemExit is unused // at the moment, the value of SystemExit is unused
ret = pyexec_system_exit; ret = pyexec_system_exit;
#if CIRCUITPY_ALARM #if CIRCUITPY_ALARM

View File

@ -273,13 +273,13 @@ audio_dma_result audio_dma_setup_playback(audio_dma_t *dma,
#ifdef SAM_D5X_E5X #ifdef SAM_D5X_E5X
int irq = dma->event_channel < 4 ? EVSYS_0_IRQn + dma->event_channel : EVSYS_4_IRQn; int irq = dma->event_channel < 4 ? EVSYS_0_IRQn + dma->event_channel : EVSYS_4_IRQn;
// Only disable and clear on SAMD51 because the SAMD21 shares EVSYS with ticks.
NVIC_DisableIRQ(irq);
NVIC_ClearPendingIRQ(irq);
#else #else
int irq = EVSYS_IRQn; int irq = EVSYS_IRQn;
#endif #endif
NVIC_DisableIRQ(irq);
NVIC_ClearPendingIRQ(irq);
DmacDescriptor *first_descriptor = dma_descriptor(dma_channel); DmacDescriptor *first_descriptor = dma_descriptor(dma_channel);
setup_audio_descriptor(first_descriptor, dma->beat_size, output_spacing, output_register_address); setup_audio_descriptor(first_descriptor, dma->beat_size, output_spacing, output_register_address);
if (single_buffer) { if (single_buffer) {
@ -374,7 +374,7 @@ STATIC void dma_callback_fun(void *arg) {
audio_dma_load_next_block(dma); audio_dma_load_next_block(dma);
} }
void evsyshandler_common(void) { void audio_evsys_handler(void) {
for (uint8_t i = 0; i < AUDIO_DMA_CHANNEL_COUNT; i++) { for (uint8_t i = 0; i < AUDIO_DMA_CHANNEL_COUNT; i++) {
audio_dma_t *dma = audio_dma_state[i]; audio_dma_t *dma = audio_dma_state[i];
if (dma == NULL) { if (dma == NULL) {
@ -388,26 +388,4 @@ void evsyshandler_common(void) {
} }
} }
#ifdef SAM_D5X_E5X
void EVSYS_0_Handler(void) {
evsyshandler_common();
}
void EVSYS_1_Handler(void) {
evsyshandler_common();
}
void EVSYS_2_Handler(void) {
evsyshandler_common();
}
void EVSYS_3_Handler(void) {
evsyshandler_common();
}
void EVSYS_4_Handler(void) {
evsyshandler_common();
}
#else
void EVSYS_Handler(void) {
evsyshandler_common();
}
#endif
#endif #endif

View File

@ -99,4 +99,6 @@ void audio_dma_background(void);
uint8_t find_sync_event_channel_raise(void); uint8_t find_sync_event_channel_raise(void);
void audio_evsys_handler(void);
#endif // MICROPY_INCLUDED_ATMEL_SAMD_AUDIO_DMA_H #endif // MICROPY_INCLUDED_ATMEL_SAMD_AUDIO_DMA_H

View File

@ -2,6 +2,7 @@
#define MICROPY_HW_MCU_NAME "samd21e18" #define MICROPY_HW_MCU_NAME "samd21e18"
#define MICROPY_HW_NEOPIXEL (&pin_PA15) #define MICROPY_HW_NEOPIXEL (&pin_PA15)
#define MICROPY_HW_NEOPIXEL_COUNT (2)
#define IGNORE_PIN_PA01 1 #define IGNORE_PIN_PA01 1
#define IGNORE_PIN_PA02 1 #define IGNORE_PIN_PA02 1

View File

@ -14,6 +14,7 @@ CIRCUITPY_FULL_BUILD = 0
CIRCUITPY_ANALOGIO = 0 CIRCUITPY_ANALOGIO = 0
CIRCUITPY_AUDIOCORE = 0 CIRCUITPY_AUDIOCORE = 0
CIRCUITPY_BUSIO_SPI = 0 CIRCUITPY_BUSIO_SPI = 0
CIRCUITPY_BUSIO_UART = 0
CIRCUITPY_PULSEIO = 0 CIRCUITPY_PULSEIO = 0
CIRCUITPY_PWMIO = 0 CIRCUITPY_PWMIO = 0
CIRCUITPY_ROTARYIO = 0 CIRCUITPY_ROTARYIO = 0

View File

@ -16,3 +16,16 @@
// USB is always used internally so skip the pin objects for it. // USB is always used internally so skip the pin objects for it.
#define IGNORE_PIN_PA24 1 #define IGNORE_PIN_PA24 1
#define IGNORE_PIN_PA25 1 #define IGNORE_PIN_PA25 1
// USD ID
#define IGNORE_PIN_PA18 1
// Hooked to the external crystal
#define IGNORE_PIN_PA00 1
#define IGNORE_PIN_PA01 1
// SWD only
#define IGNORE_PIN_PA30 1
#define IGNORE_PIN_PA31 1
// Not connected
#define IGNORE_PIN_PA28 1

View File

@ -16,3 +16,11 @@
// USB is always used internally so skip the pin objects for it. // USB is always used internally so skip the pin objects for it.
#define IGNORE_PIN_PA24 1 #define IGNORE_PIN_PA24 1
#define IGNORE_PIN_PA25 1 #define IGNORE_PIN_PA25 1
// Not connected
#define IGNORE_PIN_PA00 1
#define IGNORE_PIN_PA01 1
// SWD only
#define IGNORE_PIN_PA30 1
#define IGNORE_PIN_PA31 1

View File

@ -272,6 +272,9 @@ void common_hal_pulseio_pulsein_deinit(pulseio_pulsein_obj_t *self) {
void common_hal_pulseio_pulsein_pause(pulseio_pulsein_obj_t *self) { void common_hal_pulseio_pulsein_pause(pulseio_pulsein_obj_t *self) {
uint32_t mask = 1 << self->channel; uint32_t mask = 1 << self->channel;
EIC->INTENCLR.reg = mask << EIC_INTENSET_EXTINT_Pos; EIC->INTENCLR.reg = mask << EIC_INTENSET_EXTINT_Pos;
#ifdef SAMD21
rtc_end_pulse();
#endif
} }
void common_hal_pulseio_pulsein_resume(pulseio_pulsein_obj_t *self, void common_hal_pulseio_pulsein_resume(pulseio_pulsein_obj_t *self,
@ -299,6 +302,9 @@ void common_hal_pulseio_pulsein_resume(pulseio_pulsein_obj_t *self,
EIC->INTFLAG.reg = mask << EIC_INTFLAG_EXTINT_Pos; EIC->INTFLAG.reg = mask << EIC_INTFLAG_EXTINT_Pos;
EIC->INTENSET.reg = mask << EIC_INTENSET_EXTINT_Pos; EIC->INTENSET.reg = mask << EIC_INTENSET_EXTINT_Pos;
#ifdef SAMD21
rtc_start_pulse();
#endif
pulsein_set_config(self, true); pulsein_set_config(self, true);
} }

View File

@ -91,26 +91,19 @@
#if CIRCUITPY_PEW #if CIRCUITPY_PEW
#include "common-hal/_pew/PewPew.h" #include "common-hal/_pew/PewPew.h"
#endif #endif
volatile bool hold_interrupt = false; static volatile bool sleep_ok = true;
#ifdef SAMD21 #ifdef SAMD21
static void rtc_set_continuous(bool continuous) { static uint8_t _tick_event_channel = 0;
while (RTC->MODE0.STATUS.bit.SYNCBUSY) {
;
}
RTC->MODE0.READREQ.reg = (continuous ? RTC_READREQ_RCONT : 0) | 0x0010;
while (RTC->MODE0.STATUS.bit.SYNCBUSY) {
;
}
}
// Sleeping requires a register write that can stall interrupt handling. Turning
// off sleeps allows for more accurate interrupt timing. (Python still thinks
// it is sleeping though.)
void rtc_start_pulse(void) { void rtc_start_pulse(void) {
rtc_set_continuous(true); sleep_ok = false;
hold_interrupt = true;
} }
void rtc_end_pulse(void) { void rtc_end_pulse(void) {
hold_interrupt = false; sleep_ok = true;
rtc_set_continuous(false);
} }
#endif #endif
@ -161,6 +154,20 @@ static void save_usb_clock_calibration(void) {
} }
#endif #endif
static void rtc_continuous_mode(void) {
#ifdef SAMD21
while (RTC->MODE0.STATUS.bit.SYNCBUSY) {
}
RTC->MODE0.READREQ.reg = RTC_READREQ_RCONT | 0x0010;
while (RTC->MODE0.STATUS.bit.SYNCBUSY) {
}
// Do the first request and wait for it.
RTC->MODE0.READREQ.reg = RTC_READREQ_RREQ | RTC_READREQ_RCONT | 0x0010;
while (RTC->MODE0.STATUS.bit.SYNCBUSY) {
}
#endif
}
static void rtc_init(void) { static void rtc_init(void) {
#ifdef SAMD21 #ifdef SAMD21
_gclk_enable_channel(RTC_GCLK_ID, GCLK_CLKCTRL_GEN_GCLK2_Val); _gclk_enable_channel(RTC_GCLK_ID, GCLK_CLKCTRL_GEN_GCLK2_Val);
@ -168,9 +175,17 @@ static void rtc_init(void) {
while (RTC->MODE0.CTRL.bit.SWRST != 0) { while (RTC->MODE0.CTRL.bit.SWRST != 0) {
} }
// Turn on periodic events to use as tick. We control whether it interrupts
// us with the EVSYS INTEN register.
RTC->MODE0.EVCTRL.reg = RTC_MODE0_EVCTRL_PEREO2;
RTC->MODE0.CTRL.reg = RTC_MODE0_CTRL_ENABLE | RTC->MODE0.CTRL.reg = RTC_MODE0_CTRL_ENABLE |
RTC_MODE0_CTRL_MODE_COUNT32 | RTC_MODE0_CTRL_MODE_COUNT32 |
RTC_MODE0_CTRL_PRESCALER_DIV2; RTC_MODE0_CTRL_PRESCALER_DIV2;
// Turn on continuous sync of the count register. This will speed up all
// tick reads.
rtc_continuous_mode();
#endif #endif
#ifdef SAM_D5X_E5X #ifdef SAM_D5X_E5X
hri_mclk_set_APBAMASK_RTC_bit(MCLK); hri_mclk_set_APBAMASK_RTC_bit(MCLK);
@ -363,6 +378,9 @@ void reset_port(void) {
#endif #endif
reset_event_system(); reset_event_system();
#ifdef SAMD21
_tick_event_channel = EVSYS_SYNCH_NUM;
#endif
reset_all_pins(); reset_all_pins();
@ -430,21 +448,14 @@ uint32_t port_get_saved_word(void) {
// TODO: Move this to an RTC backup register so we can preserve it when only the BACKUP power domain // TODO: Move this to an RTC backup register so we can preserve it when only the BACKUP power domain
// is enabled. // is enabled.
static volatile uint64_t overflowed_ticks = 0; static volatile uint64_t overflowed_ticks = 0;
#ifdef SAMD21
static volatile bool _ticks_enabled = false;
#endif
static uint32_t _get_count(uint64_t *overflow_count) { static uint32_t _get_count(uint64_t *overflow_count) {
#ifdef SAM_D5X_E5X #ifdef SAM_D5X_E5X
while ((RTC->MODE0.SYNCBUSY.reg & (RTC_MODE0_SYNCBUSY_COUNTSYNC | RTC_MODE0_SYNCBUSY_COUNT)) != 0) { while ((RTC->MODE0.SYNCBUSY.reg & (RTC_MODE0_SYNCBUSY_COUNTSYNC | RTC_MODE0_SYNCBUSY_COUNT)) != 0) {
} }
#endif #endif
#ifdef SAMD21 // SAMD21 does continuous sync so we don't need to wait here.
// Request a read so we don't stall the bus later. See section 14.3.1.5 Read Request
RTC->MODE0.READREQ.reg = RTC_READREQ_RREQ | 0x0010;
while (RTC->MODE0.STATUS.bit.SYNCBUSY != 0) {
}
#endif
// Disable interrupts so we can grab the count and the overflow. // Disable interrupts so we can grab the count and the overflow.
common_hal_mcu_disable_interrupts(); common_hal_mcu_disable_interrupts();
uint32_t count = RTC->MODE0.COUNT.reg; uint32_t count = RTC->MODE0.COUNT.reg;
@ -458,29 +469,6 @@ static uint32_t _get_count(uint64_t *overflow_count) {
volatile bool _woken_up; volatile bool _woken_up;
static void _port_interrupt_after_ticks(uint32_t ticks) {
uint32_t current_ticks = _get_count(NULL);
if (ticks > 1 << 28) {
// We'll interrupt sooner with an overflow.
return;
}
#ifdef SAMD21
if (hold_interrupt) {
return;
}
#endif
uint32_t target = current_ticks + (ticks << 4);
RTC->MODE0.COMP[0].reg = target;
#ifdef SAM_D5X_E5X
while ((RTC->MODE0.SYNCBUSY.reg & (RTC_MODE0_SYNCBUSY_COMP0)) != 0) {
}
#endif
RTC->MODE0.INTFLAG.reg = RTC_MODE0_INTFLAG_CMP0;
RTC->MODE0.INTENSET.reg = RTC_MODE0_INTENSET_CMP0;
current_ticks = _get_count(NULL);
_woken_up = current_ticks >= target;
}
void RTC_Handler(void) { void RTC_Handler(void) {
uint32_t intflag = RTC->MODE0.INTFLAG.reg; uint32_t intflag = RTC->MODE0.INTFLAG.reg;
if (intflag & RTC_MODE0_INTFLAG_OVF) { if (intflag & RTC_MODE0_INTFLAG_OVF) {
@ -497,19 +485,10 @@ void RTC_Handler(void) {
} }
#endif #endif
if (intflag & RTC_MODE0_INTFLAG_CMP0) { if (intflag & RTC_MODE0_INTFLAG_CMP0) {
// Clear the interrupt because we may have hit a sleep and _ticks_enabled // Clear the interrupt because we may have hit a sleep
RTC->MODE0.INTFLAG.reg = RTC_MODE0_INTFLAG_CMP0; RTC->MODE0.INTFLAG.reg = RTC_MODE0_INTFLAG_CMP0;
_woken_up = true; _woken_up = true;
#ifdef SAMD21 // SAMD21 ticks are handled by EVSYS
if (_ticks_enabled) {
// Do things common to all ports when the tick occurs.
supervisor_tick();
// Check _ticks_enabled again because a tick handler may have turned it off.
if (_ticks_enabled) {
_port_interrupt_after_ticks(1);
}
}
#endif
#ifdef SAM_D5X_E5X #ifdef SAM_D5X_E5X
RTC->MODE0.INTENCLR.reg = RTC_MODE0_INTENCLR_CMP0; RTC->MODE0.INTENCLR.reg = RTC_MODE0_INTENCLR_CMP0;
#endif #endif
@ -526,6 +505,39 @@ uint64_t port_get_raw_ticks(uint8_t *subticks) {
return overflow_count + current_ticks / 16; return overflow_count + current_ticks / 16;
} }
void evsyshandler_common(void) {
#ifdef SAMD21
if (_tick_event_channel < EVSYS_SYNCH_NUM && event_interrupt_active(_tick_event_channel)) {
supervisor_tick();
}
#endif
#if CIRCUITPY_AUDIOIO || CIRCUITPY_AUDIOBUSIO
audio_evsys_handler();
#endif
}
#ifdef SAM_D5X_E5X
void EVSYS_0_Handler(void) {
evsyshandler_common();
}
void EVSYS_1_Handler(void) {
evsyshandler_common();
}
void EVSYS_2_Handler(void) {
evsyshandler_common();
}
void EVSYS_3_Handler(void) {
evsyshandler_common();
}
void EVSYS_4_Handler(void) {
evsyshandler_common();
}
#else
void EVSYS_Handler(void) {
evsyshandler_common();
}
#endif
// Enable 1/1024 second tick. // Enable 1/1024 second tick.
void port_enable_tick(void) { void port_enable_tick(void) {
#ifdef SAM_D5X_E5X #ifdef SAM_D5X_E5X
@ -533,9 +545,23 @@ void port_enable_tick(void) {
RTC->MODE0.INTENSET.reg = RTC_MODE0_INTENSET_PER2; RTC->MODE0.INTENSET.reg = RTC_MODE0_INTENSET_PER2;
#endif #endif
#ifdef SAMD21 #ifdef SAMD21
// TODO: Switch to using the PER *event* from the RTC to generate an interrupt via EVSYS. // SAMD21 ticks won't survive port_reset(). This *should* be ok since it'll
_ticks_enabled = true; // be triggered by ticks and no Python will be running.
_port_interrupt_after_ticks(1); if (_tick_event_channel >= EVSYS_SYNCH_NUM) {
turn_on_event_system();
_tick_event_channel = find_sync_event_channel();
}
// This turns on both the event detected interrupt (EVD) and overflow (OVR).
init_event_channel_interrupt(_tick_event_channel, CORE_GCLK, EVSYS_ID_GEN_RTC_PER_2);
// Disable overflow interrupt because we ignore it.
if (_tick_event_channel >= 8) {
uint8_t value = 1 << (_tick_event_channel - 8);
EVSYS->INTENCLR.reg = EVSYS_INTENSET_OVRp8(value);
} else {
uint8_t value = 1 << _tick_event_channel;
EVSYS->INTENCLR.reg = EVSYS_INTENSET_OVR(value);
}
NVIC_EnableIRQ(EVSYS_IRQn);
#endif #endif
} }
@ -545,21 +571,48 @@ void port_disable_tick(void) {
RTC->MODE0.INTENCLR.reg = RTC_MODE0_INTENCLR_PER2; RTC->MODE0.INTENCLR.reg = RTC_MODE0_INTENCLR_PER2;
#endif #endif
#ifdef SAMD21 #ifdef SAMD21
_ticks_enabled = false; if (_tick_event_channel >= 8) {
RTC->MODE0.INTENCLR.reg = RTC_MODE0_INTENCLR_CMP0; uint8_t value = 1 << (_tick_event_channel - 8);
EVSYS->INTENCLR.reg = EVSYS_INTENSET_EVDp8(value);
} else {
uint8_t value = 1 << _tick_event_channel;
EVSYS->INTENCLR.reg = EVSYS_INTENSET_EVD(value);
}
#endif #endif
} }
// This is called by sleep, we ignore it when our ticks are enabled because
// they'll wake us up earlier. If we don't, we'll mess up ticks by overwriting
// the next RTC wake up time.
void port_interrupt_after_ticks(uint32_t ticks) { void port_interrupt_after_ticks(uint32_t ticks) {
uint32_t current_ticks = _get_count(NULL);
if (ticks > 1 << 28) {
// We'll interrupt sooner with an overflow.
return;
}
#ifdef SAMD21 #ifdef SAMD21
if (_ticks_enabled) { if (!sleep_ok) {
return; return;
} }
#endif #endif
_port_interrupt_after_ticks(ticks);
uint32_t target = current_ticks + (ticks << 4);
#ifdef SAMD21
// Try and avoid a bus stall when writing COMP by checking for an obvious
// existing sync.
while (RTC->MODE0.STATUS.bit.SYNCBUSY == 1) {
}
#endif
// Writing the COMP register can take up to 180us to synchronize. During
// this time, the bus will stall and no interrupts will be serviced.
RTC->MODE0.COMP[0].reg = target;
#ifdef SAM_D5X_E5X
while ((RTC->MODE0.SYNCBUSY.reg & (RTC_MODE0_SYNCBUSY_COMP0)) != 0) {
}
#endif
RTC->MODE0.INTFLAG.reg = RTC_MODE0_INTFLAG_CMP0;
RTC->MODE0.INTENSET.reg = RTC_MODE0_INTENSET_CMP0;
// Set continuous mode again because setting COMP may disable it.
rtc_continuous_mode();
current_ticks = _get_count(NULL);
_woken_up = current_ticks >= target;
} }
void port_idle_until_interrupt(void) { void port_idle_until_interrupt(void) {
@ -571,7 +624,7 @@ void port_idle_until_interrupt(void) {
} }
#endif #endif
common_hal_mcu_disable_interrupts(); common_hal_mcu_disable_interrupts();
if (!tud_task_event_ready() && !hold_interrupt && !_woken_up) { if (!tud_task_event_ready() && sleep_ok && !_woken_up) {
__DSB(); __DSB();
__WFI(); __WFI();
} }

View File

@ -121,9 +121,15 @@ CFLAGS += -DCIRCUITPY_BUILTINS_POW3=$(CIRCUITPY_BUILTINS_POW3)
CIRCUITPY_BUSIO ?= 1 CIRCUITPY_BUSIO ?= 1
CFLAGS += -DCIRCUITPY_BUSIO=$(CIRCUITPY_BUSIO) CFLAGS += -DCIRCUITPY_BUSIO=$(CIRCUITPY_BUSIO)
# These two flags pretend to implement their class but raise a ValueError due to
# unsupported pins. This should be used sparingly on boards that don't break out
# generic IO but need parts of busio.
CIRCUITPY_BUSIO_SPI ?= 1 CIRCUITPY_BUSIO_SPI ?= 1
CFLAGS += -DCIRCUITPY_BUSIO_SPI=$(CIRCUITPY_BUSIO_SPI) CFLAGS += -DCIRCUITPY_BUSIO_SPI=$(CIRCUITPY_BUSIO_SPI)
CIRCUITPY_BUSIO_UART ?= 1
CFLAGS += -DCIRCUITPY_BUSIO_UART=$(CIRCUITPY_BUSIO_UART)
CIRCUITPY_CAMERA ?= 0 CIRCUITPY_CAMERA ?= 0
CFLAGS += -DCIRCUITPY_CAMERA=$(CIRCUITPY_CAMERA) CFLAGS += -DCIRCUITPY_CAMERA=$(CIRCUITPY_CAMERA)
@ -184,7 +190,7 @@ CFLAGS += -DCIRCUITPY_FREQUENCYIO=$(CIRCUITPY_FREQUENCYIO)
CIRCUITPY_GAMEPADSHIFT ?= 0 CIRCUITPY_GAMEPADSHIFT ?= 0
CFLAGS += -DCIRCUITPY_GAMEPADSHIFT=$(CIRCUITPY_GAMEPADSHIFT) CFLAGS += -DCIRCUITPY_GAMEPADSHIFT=$(CIRCUITPY_GAMEPADSHIFT)
CIRCUITPY_GETPASS ?= 1 CIRCUITPY_GETPASS ?= $(CIRCUITPY_FULL_BUILD)
CFLAGS += -DCIRCUITPY_GETPASS=$(CIRCUITPY_GETPASS) CFLAGS += -DCIRCUITPY_GETPASS=$(CIRCUITPY_GETPASS)
CIRCUITPY_GNSS ?= 0 CIRCUITPY_GNSS ?= 0
@ -330,7 +336,7 @@ CFLAGS += -DCIRCUITPY_TOUCHIO_USE_NATIVE=$(CIRCUITPY_TOUCHIO_USE_NATIVE)
CIRCUITPY_TOUCHIO ?= 1 CIRCUITPY_TOUCHIO ?= 1
CFLAGS += -DCIRCUITPY_TOUCHIO=$(CIRCUITPY_TOUCHIO) CFLAGS += -DCIRCUITPY_TOUCHIO=$(CIRCUITPY_TOUCHIO)
CIRCUITPY_TRACEBACK ?= 1 CIRCUITPY_TRACEBACK ?= $(CIRCUITPY_FULL_BUILD)
CFLAGS += -DCIRCUITPY_TRACEBACK=$(CIRCUITPY_TRACEBACK) CFLAGS += -DCIRCUITPY_TRACEBACK=$(CIRCUITPY_TRACEBACK)
# For debugging. # For debugging.

View File

@ -104,7 +104,7 @@ STATIC mp_obj_t busio_spi_make_new(const mp_obj_type_t *type, size_t n_args, con
common_hal_busio_spi_construct(self, clock, mosi, miso); common_hal_busio_spi_construct(self, clock, mosi, miso);
return MP_OBJ_FROM_PTR(self); return MP_OBJ_FROM_PTR(self);
#else #else
mp_raise_NotImplementedError(NULL); mp_raise_ValueError(translate("Invalid pins"));
#endif // CIRCUITPY_BUSIO_SPI #endif // CIRCUITPY_BUSIO_SPI
} }

View File

@ -72,13 +72,16 @@ typedef struct {
extern const busio_uart_parity_obj_t busio_uart_parity_even_obj; extern const busio_uart_parity_obj_t busio_uart_parity_even_obj;
extern const busio_uart_parity_obj_t busio_uart_parity_odd_obj; extern const busio_uart_parity_obj_t busio_uart_parity_odd_obj;
#if CIRCUITPY_BUSIO_UART
STATIC void validate_timeout(mp_float_t timeout) { STATIC void validate_timeout(mp_float_t timeout) {
if (timeout < (mp_float_t)0.0f || timeout > (mp_float_t)100.0f) { if (timeout < (mp_float_t)0.0f || timeout > (mp_float_t)100.0f) {
mp_raise_ValueError(translate("timeout must be 0.0-100.0 seconds")); mp_raise_ValueError(translate("timeout must be 0.0-100.0 seconds"));
} }
} }
#endif // CIRCUITPY_BUSIO_UART
STATIC mp_obj_t busio_uart_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { STATIC mp_obj_t busio_uart_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
#if CIRCUITPY_BUSIO_UART
// Always initially allocate the UART object within the long-lived heap. // Always initially allocate the UART object within the long-lived heap.
// This is needed to avoid crashes with certain UART implementations which // This is needed to avoid crashes with certain UART implementations which
// cannot accomodate being moved after creation. (See // cannot accomodate being moved after creation. (See
@ -141,8 +144,12 @@ STATIC mp_obj_t busio_uart_make_new(const mp_obj_type_t *type, size_t n_args, co
args[ARG_baudrate].u_int, bits, parity, stop, timeout, args[ARG_baudrate].u_int, bits, parity, stop, timeout,
args[ARG_receiver_buffer_size].u_int, NULL, false); args[ARG_receiver_buffer_size].u_int, NULL, false);
return (mp_obj_t)self; return (mp_obj_t)self;
#else
mp_raise_ValueError(translate("Invalid pins"));
#endif // CIRCUITPY_BUSIO_UART
} }
#if CIRCUITPY_BUSIO_UART
// Helper to ensure we have the native super class instead of a subclass. // Helper to ensure we have the native super class instead of a subclass.
busio_uart_obj_t *native_uart(mp_obj_t uart_obj) { busio_uart_obj_t *native_uart(mp_obj_t uart_obj) {
@ -358,6 +365,7 @@ STATIC mp_obj_t busio_uart_obj_reset_input_buffer(mp_obj_t self_in) {
return mp_const_none; return mp_const_none;
} }
STATIC MP_DEFINE_CONST_FUN_OBJ_1(busio_uart_reset_input_buffer_obj, busio_uart_obj_reset_input_buffer); STATIC MP_DEFINE_CONST_FUN_OBJ_1(busio_uart_reset_input_buffer_obj, busio_uart_obj_reset_input_buffer);
#endif // CIRCUITPY_BUSIO_UART
//| class Parity: //| class Parity:
//| """Enum-like class to define the parity used to verify correct data transfer.""" //| """Enum-like class to define the parity used to verify correct data transfer."""
@ -400,6 +408,7 @@ const mp_obj_type_t busio_uart_parity_type = {
}; };
STATIC const mp_rom_map_elem_t busio_uart_locals_dict_table[] = { STATIC const mp_rom_map_elem_t busio_uart_locals_dict_table[] = {
#if CIRCUITPY_BUSIO_UART
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&busio_uart_deinit_obj) }, { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&busio_uart_deinit_obj) },
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&busio_uart_deinit_obj) }, { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&busio_uart_deinit_obj) },
{ MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) },
@ -417,12 +426,14 @@ STATIC const mp_rom_map_elem_t busio_uart_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_baudrate), MP_ROM_PTR(&busio_uart_baudrate_obj) }, { MP_ROM_QSTR(MP_QSTR_baudrate), MP_ROM_PTR(&busio_uart_baudrate_obj) },
{ MP_ROM_QSTR(MP_QSTR_in_waiting), MP_ROM_PTR(&busio_uart_in_waiting_obj) }, { MP_ROM_QSTR(MP_QSTR_in_waiting), MP_ROM_PTR(&busio_uart_in_waiting_obj) },
{ MP_ROM_QSTR(MP_QSTR_timeout), MP_ROM_PTR(&busio_uart_timeout_obj) }, { MP_ROM_QSTR(MP_QSTR_timeout), MP_ROM_PTR(&busio_uart_timeout_obj) },
#endif // CIRCUITPY_BUSIO_UART
// Nested Enum-like Classes. // Nested Enum-like Classes.
{ MP_ROM_QSTR(MP_QSTR_Parity), MP_ROM_PTR(&busio_uart_parity_type) }, { MP_ROM_QSTR(MP_QSTR_Parity), MP_ROM_PTR(&busio_uart_parity_type) },
}; };
STATIC MP_DEFINE_CONST_DICT(busio_uart_locals_dict, busio_uart_locals_dict_table); STATIC MP_DEFINE_CONST_DICT(busio_uart_locals_dict, busio_uart_locals_dict_table);
#if CIRCUITPY_BUSIO_UART
STATIC const mp_stream_p_t uart_stream_p = { STATIC const mp_stream_p_t uart_stream_p = {
MP_PROTO_IMPLEMENT(MP_QSTR_protocol_stream) MP_PROTO_IMPLEMENT(MP_QSTR_protocol_stream)
.read = busio_uart_read, .read = busio_uart_read,
@ -445,3 +456,11 @@ const mp_obj_type_t busio_uart_type = {
.protocol = &uart_stream_p, .protocol = &uart_stream_p,
), ),
}; };
#else
const mp_obj_type_t busio_uart_type = {
{ &mp_type_type },
.name = MP_QSTR_UART,
.make_new = busio_uart_make_new,
.locals_dict = (mp_obj_dict_t *)&busio_uart_locals_dict,
};
#endif // CIRCUITPY_BUSIO_UART