Skip to content
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
40 changes: 40 additions & 0 deletions ports/atmel-samd/common-hal/busio/SPI.c
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,9 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self,

setup_pin(clock, clock_pinmux, GPIO_DIRECTION_OUT);
self->clock_pin = clock->number;
#if CIRCUITPY_BUSIO_SPI_ASYNC
self->async_active = false;
#endif

if (mosi_none) {
self->MOSI_pin = NO_PIN;
Expand Down Expand Up @@ -192,6 +195,9 @@ void common_hal_busio_spi_deinit(busio_spi_obj_t *self) {
if (common_hal_busio_spi_deinited(self)) {
return;
}
#if CIRCUITPY_BUSIO_SPI_ASYNC
common_hal_busio_spi_end(self);
#endif
allow_reset_sercom(self->spi_desc.dev.prvt);

spi_m_sync_disable(&self->spi_desc);
Expand Down Expand Up @@ -285,6 +291,40 @@ bool common_hal_busio_spi_write(busio_spi_obj_t *self,
return status >= 0; // Status is number of chars read or an error code < 0.
}

#if CIRCUITPY_BUSIO_SPI_ASYNC
void common_hal_busio_spi_write_start(busio_spi_obj_t *self, const uint8_t *data, size_t len,
circuitpy_async_flag_t *done) {
common_hal_busio_spi_end(self);
CIRCUITPY_ASYNC_FLAG_INIT(done);
self->async_done = done;
// One descriptor holds at most 65535 beats; longer writes and short ones go the normal way.
if (len >= 32 && len <= 65535) {
Sercom *sercom = self->spi_desc.dev.prvt;
shared_dma_transfer_start(&self->async_xfer, sercom, data, &sercom->SPI.DATA.reg,
NULL, NULL, len, 0);
if (self->async_xfer.failure == 0) {
self->async_active = true;
return;
}
}
common_hal_busio_spi_write(self, data, len);
CIRCUITPY_ASYNC_FLAG_SET(done);
}

void common_hal_busio_spi_end(busio_spi_obj_t *self) {
if (!self->async_active) {
return;
}
// finished() also clears the RX overflow a TX-only transfer leaves on the SERCOM.
// No background tasks here: the caller holds the bus, and one of them may want it.
while (!shared_dma_transfer_finished(&self->async_xfer)) {
}
shared_dma_transfer_close(&self->async_xfer);
self->async_active = false;
CIRCUITPY_ASYNC_FLAG_SET(self->async_done);
}
#endif

bool common_hal_busio_spi_read(busio_spi_obj_t *self,
uint8_t *data, size_t len, uint8_t write_value) {
if (len == 0) {
Expand Down
10 changes: 10 additions & 0 deletions ports/atmel-samd/common-hal/busio/SPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,16 @@

#pragma once

#include "py/mpconfig.h"
#if CIRCUITPY_BUSIO_SPI_ASYNC
#include "peripherals/samd/dma.h"
#endif
#include "common-hal/microcontroller/Pin.h"

#include "hal/include/hal_spi_m_sync.h"

#include "py/obj.h"
#include "supervisor/shared/async_flag.h"

typedef struct {
mp_obj_base_t base;
Expand All @@ -19,4 +24,9 @@ typedef struct {
uint8_t clock_pin;
uint8_t MOSI_pin;
uint8_t MISO_pin;
#if CIRCUITPY_BUSIO_SPI_ASYNC
bool async_active; // a write_start DMA transfer may still be running
dma_transfer_t async_xfer;
circuitpy_async_flag_t *async_done;
#endif
} busio_spi_obj_t;
1 change: 1 addition & 0 deletions ports/atmel-samd/mpconfigport.mk
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ endif # samd21
# Put samx5x-only choices here.

ifneq ($(filter $(CHIP_FAMILY),samd51 same51 same54),)
CIRCUITPY_BUSIO_SPI_ASYNC ?= 1

# The 24 KB stack has room for a larger display refresh buffer.
CIRCUITPY_DISPLAY_AREA_BUFFER_SIZE ?= 2048
Expand Down
49 changes: 49 additions & 0 deletions ports/espressif/common-hal/busio/SPI.c
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self,

// Ensure the object starts in its deinit state.
common_hal_busio_spi_mark_deinit(self);
#if CIRCUITPY_BUSIO_SPI_ASYNC
self->async_count = 0;
#endif

const spi_bus_config_t bus_config = {
.mosi_io_num = mosi != NULL ? mosi->number : -1,
Expand Down Expand Up @@ -176,6 +179,10 @@ void common_hal_busio_spi_deinit(busio_spi_obj_t *self) {
RUN_BACKGROUND_TASKS;
}

#if CIRCUITPY_BUSIO_SPI_ASYNC
common_hal_busio_spi_end(self);
#endif

// Mark as deinit early in case we are used in an interrupt.
common_hal_reset_pin(self->clock);
common_hal_busio_spi_mark_deinit(self);
Expand All @@ -200,6 +207,9 @@ bool common_hal_busio_spi_configure(busio_spi_obj_t *self,
bits == self->bits) {
return true;
}
#if CIRCUITPY_BUSIO_SPI_ASYNC
common_hal_busio_spi_end(self);
#endif
spi_bus_remove_device(spi_handle[self->host_id]);
set_spi_config(self, baudrate, polarity, phase, bits);
return true;
Expand Down Expand Up @@ -324,6 +334,45 @@ bool common_hal_busio_spi_transfer(busio_spi_obj_t *self,
return true;
}

#if CIRCUITPY_BUSIO_SPI_ASYNC
void common_hal_busio_spi_write_start(busio_spi_obj_t *self, const uint8_t *data, size_t len,
circuitpy_async_flag_t *done) {
common_hal_busio_spi_end(self);
CIRCUITPY_ASYNC_FLAG_INIT(done);
self->async_done = done;
size_t chunks = (len + SPI_MAX_DMA_LEN - 1) / SPI_MAX_DMA_LEN;
// Short writes, other word sizes and writes needing more transactions than we keep go the
// normal way.
if (len <= 4 || self->bits != 8 || chunks > MP_ARRAY_SIZE(self->async_trans)) {
common_hal_busio_spi_write(self, data, len);
CIRCUITPY_ASYNC_FLAG_SET(done);
return;
}
for (size_t i = 0; i < chunks; i++) {
size_t offset = i * SPI_MAX_DMA_LEN;
spi_transaction_t *trans = &self->async_trans[i];
memset(trans, 0, sizeof(*trans));
trans->length = MIN(len - offset, SPI_MAX_DMA_LEN) * 8;
trans->tx_buffer = data + offset;
spi_device_queue_trans(spi_handle[self->host_id], trans, portMAX_DELAY);
}
self->async_count = chunks;
}

void common_hal_busio_spi_end(busio_spi_obj_t *self) {
if (self->async_count == 0) {
return;
}
// No background tasks here: the caller holds the bus, and one of them may want it.
spi_transaction_t *trans;
while (self->async_count > 0) {
spi_device_get_trans_result(spi_handle[self->host_id], &trans, portMAX_DELAY);
self->async_count--;
}
CIRCUITPY_ASYNC_FLAG_SET(self->async_done);
}
#endif

uint32_t common_hal_busio_spi_get_frequency(busio_spi_obj_t *self) {
return self->baudrate;
}
Expand Down
7 changes: 7 additions & 0 deletions ports/espressif/common-hal/busio/SPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

#pragma once

#include "py/mpconfig.h"
#include "supervisor/shared/async_flag.h"
#include "driver/spi_master.h"
#include "shared-bindings/microcontroller/Pin.h"

Expand All @@ -25,4 +27,9 @@ typedef struct {
uint32_t requested_baudrate; // Value passed to configure(); used for the cache-hit check.

SemaphoreHandle_t mutex;
#if CIRCUITPY_BUSIO_SPI_ASYNC
spi_transaction_t async_trans[2]; // queued by write_start, collected by end
uint8_t async_count;
circuitpy_async_flag_t *async_done;
#endif
} busio_spi_obj_t;
1 change: 1 addition & 0 deletions ports/espressif/mpconfigport.mk
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ CIRCUITPY_FULL_BUILD ?= 1

# The 24 KB stack has room for a larger display refresh buffer.
CIRCUITPY_DISPLAY_AREA_BUFFER_SIZE ?= 2048
CIRCUITPY_BUSIO_SPI_ASYNC ?= 1

# If SSL is enabled, it's mbedtls
CIRCUITPY_SSL_MBEDTLS = 1
Expand Down
Loading
Loading