Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SAMD51 SPI Secondary Mode #9385

Open
wants to merge 44 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
639fed2
Add slave_mode parameter to the SPI bus constructor
Randall-Scharpf Nov 7, 2023
b459802
Update SPI construction usages
Randall-Scharpf Nov 7, 2023
58e1ee7
Attempt to change MODE to peripheral
Randall-Scharpf Nov 7, 2023
8444193
fix inversion bitmath error
Randall-Scharpf Dec 4, 2023
b98ff01
add build script
Randall-Scharpf Dec 4, 2023
c969a64
working edits to spi and can attempts
Randall-Scharpf Dec 4, 2023
82a6f95
SPI responds to clock with this change, data transfer still ununcerta…
Randall-Scharpf Dec 6, 2023
9c8401b
build interpreter (mac version)
anniexiang01 Dec 6, 2023
8a552f1
potential fix for busio SPI parameters
anniexiang01 Dec 6, 2023
b983bf9
add SS pin and fix DIPO/DOPO assignment
Randall-Scharpf Apr 2, 2024
1728222
Merge branch '9.0.x' of https://github.com/adafruit/circuitpython int…
Randall-Scharpf Apr 12, 2024
09d2c66
Merge branch 'adafruit-9.0.x' into main
Randall-Scharpf Apr 12, 2024
c06f9e1
hey wait we dont need a crystal
Randall-Scharpf May 4, 2024
bd10666
update dma code to use new allocation interface
Randall-Scharpf May 4, 2024
5490a82
add async spi interface
Randall-Scharpf May 4, 2024
cfa4425
enable preloading data register
Randall-Scharpf May 6, 2024
5c21495
update samd-peripherals submodule to point to a bruinspace-owned fork
Randall-Scharpf May 20, 2024
4d151a0
update reference submodule commit hash
Randall-Scharpf May 20, 2024
26e838b
added board configurations for RAPID-0 CDH, ADCS, and EPS boards. rev…
anniexiang01 May 20, 2024
0ae5be8
corrected board family on rapid0_adcs mpconfigboard.mk file
anniexiang01 May 21, 2024
a73df58
Updated USB VID, PID, and PRODUCT to reflect sublicense given by Micr…
anniexiang01 May 21, 2024
d4df61e
updated adcs board files to be based on the bdmicro vina d51, new bui…
anniexiang01 May 24, 2024
dcc97d7
fixed rapid0-cdh pin error
anniexiang01 May 25, 2024
14136fc
restore metro_m4_express configuration
Randall-Scharpf Jun 30, 2024
555f1a3
remove extraneous build files
Randall-Scharpf Jun 30, 2024
d048667
remove extraneous build files
Randall-Scharpf Jun 30, 2024
4696d07
set SPI drive strength to high
Randall-Scharpf Jul 28, 2024
1fc4d72
haha! adcs works now
Randall-Scharpf Oct 18, 2024
2ba0b56
add annie's new rapid-0 firmware build workflow
Randall-Scharpf Oct 31, 2024
a7b3de4
add SPITarget interface (HAL still unimplemented)
Randall-Scharpf Nov 1, 2024
289f420
implement the spitarget methods
Randall-Scharpf Nov 1, 2024
616ec37
fix the type of self within spitarget
Randall-Scharpf Nov 1, 2024
114d49e
fix import errors and transfer_close return typing
Randall-Scharpf Nov 1, 2024
5657f8a
fix typing and naming mismatches
Randall-Scharpf Nov 1, 2024
1aad399
ok this is the one
Randall-Scharpf Nov 1, 2024
ff2fb90
fix function name mismatch between call and declaration
Randall-Scharpf Nov 1, 2024
dd7056b
return none from bound functions
Randall-Scharpf Nov 1, 2024
1875e70
fix buffer mishandling in spitarget bindings
Randall-Scharpf Nov 5, 2024
0a68f43
remove busio.SPI
Randall-Scharpf Nov 6, 2024
ea344c3
repair constructors referencing old API
Randall-Scharpf Nov 6, 2024
100a045
remove asynchronous interface under busio.SPI
Randall-Scharpf Nov 6, 2024
821b7f2
merge implementation of new spitarget api
Randall-Scharpf Nov 14, 2024
022e191
add missing files not found by wildcard
Randall-Scharpf Nov 14, 2024
d059736
push samd-peripherals to merge commit in adafruit repository
Randall-Scharpf Nov 26, 2024
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
1 change: 1 addition & 0 deletions docs/porting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ as a natural "TODO" list. An example minimal build list is shown below:
CIRCUITPY_FRAMEBUFFERIO = 0
CIRCUITPY_FREQUENCYIO = 0
CIRCUITPY_I2CTARGET = 0
CIRCUITPY_SPITARGET = 0
# Requires SPI, PulseIO (stub ok):
CIRCUITPY_DISPLAYIO = 0
Expand Down
25 changes: 2 additions & 23 deletions ports/atmel-samd/audio_dma.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,6 @@ static audio_dma_t *audio_dma_state[AUDIO_DMA_CHANNEL_COUNT];
// This cannot be in audio_dma_state because it's volatile.
static volatile bool audio_dma_pending[AUDIO_DMA_CHANNEL_COUNT];

static bool audio_dma_allocated[AUDIO_DMA_CHANNEL_COUNT];

uint8_t find_sync_event_channel_raise() {
uint8_t event_channel = find_sync_event_channel();
if (event_channel >= EVSYS_SYNCH_NUM) {
Expand All @@ -60,24 +58,6 @@ uint8_t find_sync_event_channel_raise() {
return event_channel;
}

uint8_t dma_allocate_channel(void) {
uint8_t channel;
for (channel = 0; channel < AUDIO_DMA_CHANNEL_COUNT; channel++) {
if (!audio_dma_allocated[channel]) {
audio_dma_allocated[channel] = true;
return channel;
}
}
return channel; // i.e., return failure
}

void dma_free_channel(uint8_t channel) {
assert(channel < AUDIO_DMA_CHANNEL_COUNT);
assert(audio_dma_allocated[channel]);
audio_dma_disable_channel(channel);
audio_dma_allocated[channel] = false;
}

void audio_dma_disable_channel(uint8_t channel) {
if (channel >= AUDIO_DMA_CHANNEL_COUNT) {
return;
Expand Down Expand Up @@ -211,7 +191,7 @@ audio_dma_result audio_dma_setup_playback(audio_dma_t *dma,
bool output_signed,
uint32_t output_register_address,
uint8_t dma_trigger_source) {
uint8_t dma_channel = dma_allocate_channel();
uint8_t dma_channel = dma_allocate_channel(true);
if (dma_channel >= AUDIO_DMA_CHANNEL_COUNT) {
return AUDIO_DMA_DMA_BUSY;
}
Expand Down Expand Up @@ -362,8 +342,7 @@ void audio_dma_reset(void) {
for (uint8_t i = 0; i < AUDIO_DMA_CHANNEL_COUNT; i++) {
audio_dma_state[i] = NULL;
audio_dma_pending[i] = false;
audio_dma_allocated[i] = false;
audio_dma_disable_channel(i);
dma_free_channel(i);
dma_descriptor(i)->BTCTRL.bit.VALID = false;
MP_STATE_PORT(playing_audio)[i] = NULL;
}
Expand Down
3 changes: 0 additions & 3 deletions ports/atmel-samd/audio_dma.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,6 @@ uint8_t audiosample_channel_count(mp_obj_t sample_obj);
void audio_dma_init(audio_dma_t *dma);
void audio_dma_reset(void);

uint8_t dma_allocate_channel(void);
void dma_free_channel(uint8_t channel);

// This sets everything up but doesn't start the timer.
// Sample is the python object for the sample to play.
// loop is true if we should loop the sample.
Expand Down
2 changes: 1 addition & 1 deletion ports/atmel-samd/common-hal/audiobusio/PDMIn.c
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ static uint16_t filter_sample(uint32_t pdm_samples[4]) {
// output_buffer_length is the number of slots, not the number of bytes.
uint32_t common_hal_audiobusio_pdmin_record_to_buffer(audiobusio_pdmin_obj_t *self,
uint16_t *output_buffer, uint32_t output_buffer_length) {
uint8_t dma_channel = dma_allocate_channel();
uint8_t dma_channel = dma_allocate_channel(true);
pdmin_event_channel = find_sync_event_channel_raise();
pdmin_dma_block_done = false;

Expand Down
7 changes: 6 additions & 1 deletion ports/atmel-samd/common-hal/busio/SPI.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@

#include "hal/include/hal_gpio.h"
#include "hal/include/hal_spi_m_sync.h"
#include "hal/include/hpl_spi_m_sync.h"

#include "samd/dma.h"
#include "samd/sercom.h"
Expand Down Expand Up @@ -96,6 +95,7 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self,
if (!samd_peripherals_valid_spi_clock_pad(clock_pad)) {
continue;
}
// find mosi_pad first, since it corresponds to dopo which takes limited values
for (int j = 0; j < NUM_SERCOMS_PER_PIN; j++) {
if (!mosi_none) {
if (sercom_index == mosi->sercom[j].index) {
Expand Down Expand Up @@ -145,6 +145,8 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self,

// Pads must be set after spi_m_sync_init(), which uses default values from
// the prototypical SERCOM.

hri_sercomspi_write_CTRLA_MODE_bf(sercom, 3);
hri_sercomspi_write_CTRLA_DOPO_bf(sercom, dopo);
hri_sercomspi_write_CTRLA_DIPO_bf(sercom, miso_pad);

Expand All @@ -161,6 +163,7 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self,
gpio_set_pin_pull_mode(clock->number, GPIO_PULL_OFF);
gpio_set_pin_function(clock->number, clock_pinmux);
claim_pin(clock);
hri_port_set_PINCFG_DRVSTR_bit(PORT, (enum gpio_port)GPIO_PORT(clock->number), GPIO_PIN(clock->number));
self->clock_pin = clock->number;

if (mosi_none) {
Expand All @@ -171,6 +174,7 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self,
gpio_set_pin_function(mosi->number, mosi_pinmux);
self->MOSI_pin = mosi->number;
claim_pin(mosi);
hri_port_set_PINCFG_DRVSTR_bit(PORT, (enum gpio_port)GPIO_PORT(mosi->number), GPIO_PIN(mosi->number));
}

if (miso_none) {
Expand All @@ -181,6 +185,7 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self,
gpio_set_pin_function(miso->number, miso_pinmux);
self->MISO_pin = miso->number;
claim_pin(miso);
hri_port_set_PINCFG_DRVSTR_bit(PORT, (enum gpio_port)GPIO_PORT(miso->number), GPIO_PIN(miso->number));
}

spi_m_sync_enable(&self->spi_desc);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ void common_hal_imagecapture_parallelimagecapture_singleshot_capture(imagecaptur
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(buffer, &bufinfo, MP_BUFFER_RW);

uint8_t dma_channel = dma_allocate_channel();
uint8_t dma_channel = dma_allocate_channel(true);

uint32_t *dest = bufinfo.buf;
size_t count = bufinfo.len / 4; // PCC receives 4 bytes (2 pixels) at a time
Expand Down
235 changes: 235 additions & 0 deletions ports/atmel-samd/common-hal/spitarget/SPITarget.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
#include "common-hal/spitarget/SPITarget.h"
#include "common-hal/busio/__init__.h"

#include "shared-bindings/spitarget/SPITarget.h"
#include "shared-bindings/microcontroller/Pin.h"
#include "py/mperrno.h"
#include "py/runtime.h"

#include "hpl_sercom_config.h"
#include "peripheral_clk_config.h"

#include "hal/include/hal_gpio.h"
#include "hal/include/hal_spi_m_sync.h"

#include "hpl_sercom_config.h"
#include "samd/sercom.h"

void common_hal_spitarget_spi_target_construct(spitarget_spi_target_obj_t *self,
const mcu_pin_obj_t *sck, const mcu_pin_obj_t *mosi,
const mcu_pin_obj_t *miso, const mcu_pin_obj_t *ss) {
Sercom *sercom = NULL;
uint8_t sercom_index;
uint32_t clock_pinmux = 0;
uint32_t mosi_pinmux = 0;
uint32_t miso_pinmux = 0;
uint32_t ss_pinmux = 0;
uint8_t clock_pad = 0;
uint8_t mosi_pad = 0;
uint8_t miso_pad = 0;
uint8_t dopo = 255;

// Ensure the object starts in its deinit state.
self->clock_pin = NO_PIN;

// Special case for SAMR21 boards. (feather_radiofruit_zigbee)
#if defined(PIN_PC19F_SERCOM4_PAD0)
if (miso == &pin_PC19) {
if (mosi == &pin_PB30 && sck == &pin_PC18) {
sercom = SERCOM4;
sercom_index = 4;
clock_pinmux = MUX_F;
mosi_pinmux = MUX_F;
miso_pinmux = MUX_F;
clock_pad = 3;
mosi_pad = 2;
miso_pad = 0;
dopo = samd_peripherals_get_spi_dopo(clock_pad, mosi_pad);
}
// Error, leave SERCOM unset to throw an exception later.
} else
#endif
{
for (int i = 0; i < NUM_SERCOMS_PER_PIN; i++) {
sercom_index = sck->sercom[i].index; // 2 for SERCOM2, etc.
if (sercom_index >= SERCOM_INST_NUM) {
continue;
}
Sercom *potential_sercom = sercom_insts[sercom_index];
if (potential_sercom->SPI.CTRLA.bit.ENABLE != 0) {
continue;
}
clock_pinmux = PINMUX(sck->number, (i == 0) ? MUX_C : MUX_D);
clock_pad = sck->sercom[i].pad;
if (!samd_peripherals_valid_spi_clock_pad(clock_pad)) {
continue;
}
// find miso_pad first, since it corresponds to dopo which takes limited values
for (int j = 0; j < NUM_SERCOMS_PER_PIN; j++) {
if (sercom_index == miso->sercom[j].index) {
miso_pinmux = PINMUX(miso->number, (j == 0) ? MUX_C : MUX_D);
miso_pad = miso->sercom[j].pad;
dopo = samd_peripherals_get_spi_dopo(clock_pad, miso_pad);
if (dopo > 0x3) {
continue; // pad combination not possible
}
} else {
continue;
}
for (int k = 0; k < NUM_SERCOMS_PER_PIN; k++) {
if (sercom_index == mosi->sercom[k].index) {
mosi_pinmux = PINMUX(mosi->number, (k == 0) ? MUX_C : MUX_D);
mosi_pad = mosi->sercom[k].pad;
for (int m = 0; m < NUM_SERCOMS_PER_PIN; m++) {
if (sercom_index == ss->sercom[m].index) {
ss_pinmux = PINMUX(ss->number, (m == 0) ? MUX_C : MUX_D);
sercom = potential_sercom;
break;
}
}
if (sercom != NULL) {
break;
}
}
}
if (sercom != NULL) {
break;
}
}
if (sercom != NULL) {
break;
}
}
}
if (sercom == NULL) {
raise_ValueError_invalid_pins();
}

// Set up SPI clocks on SERCOM.
samd_peripherals_sercom_clock_init(sercom, sercom_index);

if (spi_m_sync_init(&self->spi_desc, sercom) != ERR_NONE) {
mp_raise_OSError(MP_EIO);
}

// Pads must be set after spi_m_sync_init(), which uses default values from
// the prototypical SERCOM.

hri_sercomspi_write_CTRLA_MODE_bf(sercom, 2);
hri_sercomspi_write_CTRLA_DOPO_bf(sercom, dopo);
hri_sercomspi_write_CTRLA_DIPO_bf(sercom, mosi_pad);
hri_sercomspi_write_CTRLB_PLOADEN_bit(sercom, 1);

// Always start at 250khz which is what SD cards need. They are sensitive to
// SPI bus noise before they are put into SPI mode.
uint8_t baud_value = samd_peripherals_spi_baudrate_to_baud_reg_value(250000);
if (spi_m_sync_set_baudrate(&self->spi_desc, baud_value) != ERR_NONE) {
// spi_m_sync_set_baudrate does not check for validity, just whether the device is
// busy or not
mp_raise_OSError(MP_EIO);
}

gpio_set_pin_direction(sck->number, GPIO_DIRECTION_IN);
gpio_set_pin_pull_mode(sck->number, GPIO_PULL_OFF);
gpio_set_pin_function(sck->number, clock_pinmux);
claim_pin(sck);
self->clock_pin = sck->number;

gpio_set_pin_direction(mosi->number, GPIO_DIRECTION_IN);
gpio_set_pin_pull_mode(mosi->number, GPIO_PULL_OFF);
gpio_set_pin_function(mosi->number, mosi_pinmux);
self->MOSI_pin = mosi->number;
claim_pin(mosi);

gpio_set_pin_direction(miso->number, GPIO_DIRECTION_OUT);
gpio_set_pin_pull_mode(miso->number, GPIO_PULL_OFF);
gpio_set_pin_function(miso->number, miso_pinmux);
self->MISO_pin = miso->number;
claim_pin(miso);

gpio_set_pin_direction(ss->number, GPIO_DIRECTION_IN);
gpio_set_pin_pull_mode(ss->number, GPIO_PULL_OFF);
gpio_set_pin_function(ss->number, ss_pinmux);
self->SS_pin = ss->number;
claim_pin(ss);

self->running_dma.failure = 1; // not started

spi_m_sync_enable(&self->spi_desc);
}

bool common_hal_spitarget_spi_target_deinited(spitarget_spi_target_obj_t *self) {
return self->clock_pin == NO_PIN;
}

void common_hal_spitarget_spi_target_deinit(spitarget_spi_target_obj_t *self) {
if (common_hal_spitarget_spi_target_deinited(self)) {
return;
}
allow_reset_sercom(self->spi_desc.dev.prvt);

spi_m_sync_disable(&self->spi_desc);
spi_m_sync_deinit(&self->spi_desc);
reset_pin_number(self->clock_pin);
reset_pin_number(self->MOSI_pin);
reset_pin_number(self->MISO_pin);
reset_pin_number(self->SS_pin);
self->clock_pin = NO_PIN;
}

void common_hal_spitarget_spi_target_transfer_start(spitarget_spi_target_obj_t *self,
uint8_t *mosi_packet, const uint8_t *miso_packet, size_t len) {
if (len == 0) {
return;
}
if (self->running_dma.failure != 1) {
mp_raise_RuntimeError(MP_ERROR_TEXT("Async SPI transfer in progress on this bus, keep awaiting."));
}
Sercom* sercom = self->spi_desc.dev.prvt;
self->running_dma = shared_dma_transfer_start(sercom, miso_packet, &sercom->SPI.DATA.reg, &sercom->SPI.DATA.reg, mosi_packet, len, 0);

// There is an issue where if an unexpected SPI transfer is received before the user calls "end" for the in-progress, expected
// transfer, the SERCOM has an error and gets confused. This can be detected from INTFLAG.ERROR. I think the code in
// ports/atmel-samd/peripherals/samd/dma.c at line 277 (as of this commit; it's the part that reads s->SPI.INTFLAG.bit.RXC and
// s->SPI.DATA.reg) is supposed to fix this, but experimentation seems to show that it does not in fact fix anything. Anyways, if
// the ERROR bit is set, let's just reset the peripheral and then setup the transfer again -- that seems to work.
if (hri_sercomspi_get_INTFLAG_ERROR_bit(sercom)) {
shared_dma_transfer_close(self->running_dma);

// disable the sercom
spi_m_sync_disable(&self->spi_desc);
hri_sercomspi_wait_for_sync(sercom, SERCOM_SPI_SYNCBUSY_MASK);

// save configurations
hri_sercomspi_ctrla_reg_t ctrla_saved_val = hri_sercomspi_get_CTRLA_reg(sercom, -1); // -1 mask is all ones: save all bits
hri_sercomspi_ctrlb_reg_t ctrlb_saved_val = hri_sercomspi_get_CTRLB_reg(sercom, -1); // -1 mask is all ones: save all bits
hri_sercomspi_baud_reg_t baud_saved_val = hri_sercomspi_get_BAUD_reg(sercom, -1); // -1 mask is all ones: save all bits
// reset
hri_sercomspi_set_CTRLA_SWRST_bit(sercom);
hri_sercomspi_wait_for_sync(sercom, SERCOM_SPI_SYNCBUSY_MASK);
// re-write configurations
hri_sercomspi_write_CTRLA_reg(sercom, ctrla_saved_val);
hri_sercomspi_write_CTRLB_reg(sercom, ctrlb_saved_val);
hri_sercomspi_write_BAUD_reg (sercom, baud_saved_val);
hri_sercomspi_wait_for_sync(sercom, SERCOM_SPI_SYNCBUSY_MASK);

// re-enable the sercom
spi_m_sync_enable(&self->spi_desc);
hri_sercomspi_wait_for_sync(sercom, SERCOM_SPI_SYNCBUSY_MASK);

self->running_dma = shared_dma_transfer_start(sercom, miso_packet, &sercom->SPI.DATA.reg, &sercom->SPI.DATA.reg, mosi_packet, len, 0);
}
}

bool common_hal_spitarget_spi_target_transfer_is_finished(spitarget_spi_target_obj_t *self) {
return self->running_dma.failure == 1 || shared_dma_transfer_finished(self->running_dma);
}

int common_hal_spitarget_spi_target_transfer_close(spitarget_spi_target_obj_t *self) {
if (self->running_dma.failure == 1) {
return 0;
}
int res = shared_dma_transfer_close(self->running_dma);
self->running_dma.failure = 1;
return res;
}
21 changes: 21 additions & 0 deletions ports/atmel-samd/common-hal/spitarget/SPITarget.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#ifndef MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_BUSIO_SPI_TARGET_H
#define MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_BUSIO_SPI_TARGET_H

#include "common-hal/microcontroller/Pin.h"
#include "hal/include/hal_spi_m_sync.h"
#include "py/obj.h"

typedef struct {
mp_obj_base_t base;

struct spi_m_sync_descriptor spi_desc;

uint8_t clock_pin;
uint8_t MOSI_pin;
uint8_t MISO_pin;
uint8_t SS_pin;

dma_descr_t running_dma;
} spitarget_spi_target_obj_t;

#endif // MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_BUSIO_SPI_TARGET_H
1 change: 1 addition & 0 deletions ports/atmel-samd/common-hal/spitarget/__init__.c
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// No spitarget module functions.
Loading