Seeeduino XIAO SPI Slave - not working

Hello!

I am trying to use the Seeeduino XIAO as an SPI slave, with very little success. After much searching, I have pieced together the code below, while using the default SERCOM0 SPI pins (also below).

However, I have two issues.

  1. The interrupt does not fire - not at all, regardless of accurate data.

  2. The master device slows down significantly, I know this because I am trying to act as a device in the middle, only sniffing the line (pure slave). Seems like a clock issue? Why does the atmel datasheet tell us to set a clock on slave mode when the slave is not supposed to set a clock?

Any assistance is greatly appreciated. A working SPI slave code example for this board (or on SERCOM0) would be fantastic. 3 hours of googling have turned up a lot on Sercom 1 and Sercom 4, but not Sercom 0.

void Seeeduino_spiSlave_init()
{
// Pin
// 4 - SS(CS)
// 8 - SCK - SERCOM0 - PAD 3
// 9 - MISO - SERCOM0 - PAD 1
// 10 - MOSI - SERCOM0 - PAD 2

pinMode(SS, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(SS), SERCOM0_Handler, FALLING);

//Disable SPI 1
SERCOM0->SPI.CTRLA.bit.ENABLE = 0;
while (SERCOM0->SPI.SYNCBUSY.bit.ENABLE);

//Reset SPI 1
SERCOM0->SPI.CTRLA.bit.SWRST = 1;
while (SERCOM0->SPI.CTRLA.bit.SWRST || SERCOM0->SPI.SYNCBUSY.bit.SWRST);

//Setting up NVIC
NVIC_EnableIRQ(SERCOM0_IRQn);
NVIC_SetPriority(SERCOM0_IRQn, 2);

//Setting Generic Clock Controller!!!
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(GCM_SERCOM0_CORE) | //Generic Clock 0
GCLK_CLKCTRL_GEN_GCLK0 | // Generic Clock Generator 0 is the source
GCLK_CLKCTRL_CLKEN; // Enable Generic Clock Generator

while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY); //Wait for synchronisation

//Set up SPI Control A Register
SERCOM0->SPI.CTRLA.bit.DORD = 0; //MSB first
SERCOM0->SPI.CTRLA.bit.CPOL = 0; //SCK is low when idle, leading edge is rising edge
SERCOM0->SPI.CTRLA.bit.CPHA = 0; //data sampled on leading sck edge and changed on a trailing sck edge
SERCOM0->SPI.CTRLA.bit.FORM = 0x0; //Frame format = SPI
SERCOM0->SPI.CTRLA.bit.DIPO = 2; //DATA PAD0 MOSI is used as input (slave mode)
SERCOM0->SPI.CTRLA.bit.DOPO = 1; //DATA PAD3 MISO is used as output
SERCOM0->SPI.CTRLA.bit.MODE = 0x2; //SPI in Slave mode
SERCOM0->SPI.CTRLA.bit.IBON = 0x1; //Buffer Overflow notification
SERCOM0->SPI.CTRLA.bit.RUNSTDBY = 1; //wake on receiver complete

//Set up SPI control B register
SERCOM0->SPI.CTRLB.bit.SSDE = 0x1; //Slave Select Detection Enabled
SERCOM0->SPI.CTRLB.bit.CHSIZE = 0; //character size 8 Bit

//Set up SPI interrupts
SERCOM0->SPI.INTENSET.bit.SSL = 0x1; //Enable Slave Select low interrupt
SERCOM0->SPI.INTENSET.bit.RXC = 0x1; //Receive complete interrupt
SERCOM0->SPI.INTENSET.bit.TXC = 0x1; //Receive complete interrupt
SERCOM0->SPI.INTENSET.bit.ERROR = 0x1; //Receive complete interrupt
SERCOM0->SPI.INTENSET.bit.DRE = 0x1; //Data Register Empty interrupt

//Enable SPI
SERCOM0->SPI.CTRLA.bit.ENABLE = 1;
while (SERCOM0->SPI.SYNCBUSY.bit.ENABLE);
SERCOM0->SPI.CTRLB.bit.RXEN = 0x1; //Enable Receiver, this is done here due to errant issue
while (SERCOM0->SPI.SYNCBUSY.bit.CTRLB); //wait until receiver is enabled
}
void SERCOM0_Handler()
{
noInterrupts();

uint8_t data = 0;
data = (uint8_t)SERCOM0->SPI.DATA.reg;
uint8_t interrupts = SERCOM0->SPI.INTFLAG.reg; //Read SPI interrupt register

if (interrupts & (1 << 3))
{
SERCOM0->SPI.INTFLAG.bit.SSL = 1; //clear slave select interrupt
}
if (interrupts & (1 << 2))
{
data = SERCOM0->SPI.DATA.reg; //Read data register
SERCOM0->SPI.INTFLAG.bit.RXC = 1; //clear receive complete interrupt
}
if (interrupts & (1 << 1))
{
SERCOM0->SPI.INTFLAG.bit.TXC = 1; //clear receive complete interrupt
}

if (interrupts & (1 << 0))
{
SERCOM0->SPI.DATA.reg = 0xAA;
}
char _data = data;
Serial.print(_data); // print received data

interrupts();
}

Anybody have any experience with this? I hope you can share.

Alright, I figured it out. See my working example at the link below.
https://github.com/ramiss/SeeeduinoXIAO_SPISlave

Thank you for sharing. Have you tested the transmission speed?

No, but it is using the SPI hardware, so there is no reason to believe it won’t reach the top SPI speed of the SAMD21. Which, from what I’ve found, is 12Mhz.

Thanks again for your sharing, I hope it can help more people.