Trying to make the MKR ZERO (ATSAMD21) an SPI slave

Hello,

I have been looking into how to make the MKR ZERO a slave, as from what I am aware of they don’t like to be slaves ‘straight out of the box’.

What I am trying to achieve is a MKR ZERO slave to a MKR ZERO (or UNO) master, over SPI.

I initially tried to upload Nick Gammon’s SPI master/slave examples to the boards (two MKR ZEROs), but the slave sketch wouldn’t build for the board. I subsequently did some research and found out the aforementioned fact about them not liking being a slave. I uploaded the code to an UNO and it worked fine. So far, I have a MKR ZERO being a master to an UNO slave.

Unfortunately, because of the speeds at which I need the Arduino to run, I need the MKR ZERO as the slave.

I found some very good advice and some handy code for making an Arduino ZERO a slave here:

Arduino Zero Sercom1 SPI Slave

but, of course, because it is a ‘standard’ Arduino ZERO, the ports don’t match up. Code is as follows:

#include <SPI.h>

const int slaveAPin = 16;

void setup() {

  Serial.begin(9600);

  pinMode(slaveAPin, INPUT_PULLUP);

  attachInterrupt(16, SERCOM4_Handler, FALLING);

  spiSlave_init();

}

void loop() {
  //waste some time in a loop
  int var = 0;
  while(var < 200){
  var++;
  }

}


void spiSlave_init()
{
  //Configure SERCOM4 SPI PINS  
    //Set PB08 as input (MOSI) - on A1
    //Set PB09 as input (SCK) - on A2
    //Set PB10 as input (SS) - on D23 / MOSI
    //Set PB11 as output (MISO) - on D24 / SCK
  PORT->Group[PORTB].PINCFG[9].bit.PMUXEN = 0x1; //Enable Peripheral Multiplexing for SERCOM4 SPI PB08 Arduino PIN16
  //PORT->Group[g_APinDescription[A2].ulPort].PINCFG[g_APinDescription[A2].ulPin].bit.PMUXEN = 1;
  PORT->Group[PORTB].PMUX[4].bit.PMUXO = 0x3; //SERCOM 4 is selected for peripherial use of this pad (pheripherial D=?3)
  //PORT->Group[g_APinDescription[A2].ulPort].PMUX[g_APinDescription[A2].ulPin >> 1].reg = PORT_PMUX_PMUXO_D;
  PORT->Group[PORTB].PINCFG[10].bit.PMUXEN = 0x1; //Enable Peripheral Multiplexing for SERCOM4 SPI PB10 Arduino PIN23
  PORT->Group[PORTB].PMUX[5].bit.PMUXE = 0x3; //SERCOM 4 is selected for peripherial use of this pad (pheripherial D=?3)
  PORT->Group[PORTB].PINCFG[11].bit.PMUXEN = 0x1; //Enable Peripheral Multiplexing for SERCOM4 SPI PB11 Arduino PIN24
  PORT->Group[PORTB].PMUX[5].bit.PMUXO = 0x3; //SERCOM 4 is selected for peripherial use of this pad (pheripherial D=?3)
  PORT->Group[PORTA].PINCFG[12].bit.PMUXEN = 0x1; //Enable Peripheral Multiplexing for SERCOM4 SPI PA12 Arduino PIN22
  PORT->Group[PORTA].PMUX[6].bit.PMUXE = 0x3; //SERCOM 4 is selected for peripherial use of this pad (pheripherial D=?3)
  
  //Disable SPI 1
  SERCOM4->SPI.CTRLA.bit.ENABLE =0;
  while(SERCOM4->SPI.SYNCBUSY.bit.ENABLE);
  
  //Reset SPI 1
  SERCOM4->SPI.CTRLA.bit.SWRST = 1;
  while(SERCOM4->SPI.CTRLA.bit.SWRST || SERCOM4->SPI.SYNCBUSY.bit.SWRST);
  
  //Setting up NVIC
  NVIC_EnableIRQ(SERCOM4_IRQn);
  NVIC_SetPriority(SERCOM4_IRQn,2);
  
  //Setting Generic Clock Controller!!!!
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(GCM_SERCOM4_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
  SERCOM4->SPI.CTRLA.bit.DORD = 0; //MSB first
  SERCOM4->SPI.CTRLA.bit.CPOL = 0; //SCK is low when idle, leading edge is rising edge
  SERCOM4->SPI.CTRLA.bit.CPHA = 0; //data sampled on leading sck edge and changed on a trailing sck edge
  SERCOM4->SPI.CTRLA.bit.FORM = 0x0; //Frame format = SPI
  SERCOM4->SPI.CTRLA.bit.DIPO = 0x2; //DATA PAD2 MOSI is used as input (slave mode)
  SERCOM4->SPI.CTRLA.bit.DOPO = 0x3; //DATA PAD0 MISO is used as output
  SERCOM4->SPI.CTRLA.bit.MODE = 0x2; //SPI in Slave mode
  SERCOM4->SPI.CTRLA.bit.IBON = 0x1; //Buffer Overflow notification
  SERCOM4->SPI.CTRLA.bit.RUNSTDBY = 1; //wake on receiver complete
  
  //Set up SPI control B register
  //SERCOM4->SPI.CTRLB.bit.RXEN = 0x1; //Enable Receiver
  SERCOM4->SPI.CTRLB.bit.SSDE = 0x1; //Slave Selecte Detection Enabled
  SERCOM4->SPI.CTRLB.bit.CHSIZE = 0; //character size 8 Bit
  //SERCOM4->SPI.CTRLB.bit.PLOADEN = 0x1; //Enable Preload Data Register
  //while (SERCOM4->SPI.SYNCBUSY.bit.CTRLB);  
  
  //Set up SPI interrupts
  SERCOM4->SPI.INTENSET.bit.SSL = 0x1; //Enable Slave Select low interrupt        
  SERCOM4->SPI.INTENSET.bit.RXC = 0x1; //Receive complete interrupt
  SERCOM4->SPI.INTENSET.bit.TXC = 0x1; //Receive complete interrupt
  SERCOM4->SPI.INTENSET.bit.ERROR = 0x1; //Receive complete interrupt
  SERCOM4->SPI.INTENSET.bit.DRE = 0x1; //Data Register Empty interrupt
  //init SPI CLK  
  //SERCOM4->SPI.BAUD.reg = SERCOM_FREQ_REF / (2*4000000u)-1;
  //Enable SPI
  SERCOM4->SPI.CTRLA.bit.ENABLE = 1;
  while(SERCOM4->SPI.SYNCBUSY.bit.ENABLE);
  SERCOM4->SPI.CTRLB.bit.RXEN = 0x1; //Enable Receiver, this is done here due to errate issue
  while(SERCOM4->SPI.SYNCBUSY.bit.CTRLB); //wait until receiver is enabled

}


void SERCOM4_Handler()
{
  Serial.println("In SPI Interrupt"); 
  uint8_t data = 0;
  data =(uint8_t)SERCOM4->SPI.DATA.reg;
  uint8_t interrupts = SERCOM4->SPI.INTFLAG.reg; //Read SPI interrupt register
  Serial.println(interrupts);
  if(interrupts & (1<<3))
  {
    Serial.println("SPI SSL Interupt");
    SERCOM4->SPI.INTFLAG.bit.SSL = 1; //clear slave select interrupt
  }
  if(interrupts & (1<<2))
  {
    Serial.println("SPI Data Received Complete Interrupt");
    data = SERCOM4->SPI.DATA.reg; //Read data register
    Serial.print("DATA: "); Serial.println(data);
    SERCOM4->SPI.INTFLAG.bit.RXC = 1; //clear receive complete interrupt
  }
  if(interrupts & (1<<1))
  {
    Serial.println("SPI Data Transmit Complete Interrupt");
    SERCOM4->SPI.INTFLAG.bit.TXC = 1; //clear receive complete interrupt
  }
  
  if(interrupts & (1<<0))
  {
    Serial.println("SPI Data Register Empty Interrupt");
    SERCOM4->SPI.DATA.reg = 0xAA;
  }
  Serial.print("DATA: "); Serial.println(data); 
  //Serial.print("CTRLA: "); Serial.println(SERCOM4->SPI.CTRLA.reg);  
}

I am pretty new to coding as a whole, with electrical engineering being my strength, so have been trying to get my head around the addresses and SERCOMs, etc, and have found the chip’s manual to be of some use, especially pages 21-23 (PORT Function Multiplexing) and 435 (SERCOM modes (following on from what was said in the previously linked forum post)):

Atmel | SMART SAM D21 Datasheet

In addition to this, the MKR ZERO schematics, here:

MKR ZERO Schematics

and Adafruit’s ‘Muxing it up’ page:

Muxing it up

I suppose my end goal is to have the MOSI, MISO and SCK lines on the same pins as are on the MKR ZERO, with slave select on pin D7, just to have it in slave mode.

Any takers?

Thank you in advance!

Chris

Did you ever find a solution? Are you still looking?