Arduino Zero Sercom1 SPI Slave

I have problems getting the SPI Slave to work in the Sercom1 module. SPI Master is another Arduino Zero. I measured the transmission lines (SCK, MISO, MOSI, SS) with an oscilloscope where I see that data is correctly transmitted. I use SPI mode 0 and send an character (0xAA).

What works:

  • Data transmission (oscilloscope measurement) correct
  • I get interrupts on slave select low, high and receive register empty(SSL, TXC and DRE Interrupt)

What doesn't work:

  • I don't get a Data received interrupt (RXC)
  • the data register is empty

What I tried:
-measured with an oscilloscope
-checking different SPI modes
-reading the configured registers to check if the initialisation where done correctly
-reading the datasheet multiple times for any hints. especially page 484

Here is my initialisation code for the spi module:

void spiSlave_init()
{
	
	//Configure SERCOM1 SPI PINS
	PORTA.DIR.reg &= ~PORT_PA16; //Set PA16 as input (MOSI)
	PORTA.DIR.reg &= ~PORT_PA17; //Set PA17 as input (SCK)
	PORTA.DIR.reg &= ~PORT_PA18; //Set PA18 as input (SS)
	PORTA.DIR.reg |= PORT_PA19; //Set PA19 as output (MISO)

	
	PORTA.PINCFG[16].bit.PMUXEN = 0x1; //Enable Peripheral Multiplexing for SERCOM1 SPI PA18 Arduino PIN10
	PORTA.PMUX[0].bit.PMUXE = 0x2; //SERCOM 1 is selected for peripherial use of this pad
	PORTA.PINCFG[17].bit.PMUXEN = 0x1; //Enable Peripheral Multiplexing for SERCOM1 SPI PA18 Arduino PIN10
	PORTA.PMUX[0].bit.PMUXO = 0x2; //SERCOM 1 is selected for peripherial use of this pad
	PORTA.PINCFG[18].bit.PMUXEN = 0x1; //Enable Peripheral Multiplexing for SERCOM1 SPI PA18 Arduino PIN10
	PORTA.PMUX[9].bit.PMUXE = 0x2; //SERCOM 1 is selected for peripherial use of this pad
	PORTA.PINCFG[19].bit.PMUXEN = 0x1; //Enable Peripheral Multiplexing for SERCOM1 SPI PA18 Arduino PIN10
	PORTA.PMUX[9].bit.PMUXO = 0x2; //SERCOM 1 is selected for peripherial use of this pad
	
	
	//Disable SPI 1
	SERCOM1->SPI.CTRLA.bit.ENABLE =0;
	while(SERCOM1->SPI.SYNCBUSY.bit.ENABLE);
	
	//Reset SPI 1
	SERCOM1->SPI.CTRLA.bit.SWRST = 1;
	while(SERCOM1->SPI.CTRLA.bit.SWRST || SERCOM1->SPI.SYNCBUSY.bit.SWRST);
	
	//Setting up NVIC
	NVIC_EnableIRQ(SERCOM1_IRQn);
	NVIC_SetPriority(SERCOM1_IRQn,2);
	
	//Setting Generic Clock Controller!!!!
	GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(GCM_SERCOM1_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
	SERCOM1->SPI.CTRLA.bit.DORD = 0; //MSB first
	SERCOM1->SPI.CTRLA.bit.CPOL = 0; //SCK is low when idle, leading edge is rising edge
	SERCOM1->SPI.CTRLA.bit.CPHA = 0; //data sampled on leading sck edge and changed on a trailing sck edge
	SERCOM1->SPI.CTRLA.bit.FORM = 0x0; //Frame format = SPI
	SERCOM1->SPI.CTRLA.bit.DIPO = 0; //DATA PAD0 MOSI is used as input (slave mode)
	SERCOM1->SPI.CTRLA.bit.DOPO = 0x2; //DATA PAD3 MISO is used as output
	SERCOM1->SPI.CTRLA.bit.MODE = 0x2; //SPI in Slave mode
	SERCOM1->SPI.CTRLA.bit.IBON = 0x1; //Buffer Overflow notification
	SERCOM1->SPI.CTRLA.bit.RUNSTDBY = 1; //wake on receiver complete
	
	//Set up SPI control B register
	//SERCOM1->SPI.CTRLB.bit.RXEN = 0x1; //Enable Receiver
	SERCOM1->SPI.CTRLB.bit.SSDE = 0x1; //Slave Selecte Detection Enabled
	SERCOM1->SPI.CTRLB.bit.CHSIZE = 0; //character size 8 Bit
	//SERCOM1->SPI.CTRLB.bit.PLOADEN = 0x1; //Enable Preload Data Register
	//while (SERCOM1->SPI.SYNCBUSY.bit.CTRLB);	
	
	//Set up SPI interrupts
	SERCOM1->SPI.INTENSET.bit.SSL = 0x1; //Enable Slave Select low interrupt				
	SERCOM1->SPI.INTENSET.bit.RXC = 0x1; //Receive complete interrupt
	SERCOM1->SPI.INTENSET.bit.TXC = 0x1; //Receive complete interrupt
	SERCOM1->SPI.INTENSET.bit.ERROR = 0x1; //Receive complete interrupt
	SERCOM1->SPI.INTENSET.bit.DRE = 0x1; //Data Register Empty interrupt
	//init SPI CLK	
	//SERCOM1->SPI.BAUD.reg = SERCOM_FREQ_REF / (2*4000000u)-1;
	//Enable SPI
	SERCOM1->SPI.CTRLA.bit.ENABLE = 1;
	while(SERCOM1->SPI.SYNCBUSY.bit.ENABLE);
	SERCOM1->SPI.CTRLB.bit.RXEN = 0x1; //Enable Receiver, this is done here due to errate issue
	while(SERCOM1->SPI.SYNCBUSY.bit.CTRLB); //wait until receiver is enabled

}

Any ideas or hints?

1 Like

okay just by posting this my brain went out of filtering my code mode, to look at your code correctly mode :slight_smile:

I found the issue right away the problem was in the pinmux section, i didnt initialise MISO and SCK correctly. Here is my working example code with interrupt handler. You can use it for the Arduino Zero/Zero Pro to use Pin 10,11,12,13 (SS, MOSI, MISO, SCK)as SPI in slave mode. Have fun!

Interrupt Service Routine:

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

SPI init function:

void spiSlave_init()
{
	
	//Configure SERCOM1 SPI PINS
	PORTA.DIR.reg &= ~PORT_PA16; //Set PA16 as input (MOSI)
	PORTA.DIR.reg &= ~PORT_PA17; //Set PA17 as input (SCK)
	PORTA.DIR.reg &= ~PORT_PA18; //Set PA18 as input (SS)
	PORTA.DIR.reg |= PORT_PA19; //Set PA19 as output (MISO)

	
	PORTA.PINCFG[16].bit.PMUXEN = 0x1; //Enable Peripheral Multiplexing for SERCOM1 SPI PA18 Arduino PIN10
	PORTA.PMUX[8].bit.PMUXE = 0x2; //SERCOM 1 is selected for peripherial use of this pad
	PORTA.PINCFG[17].bit.PMUXEN = 0x1; //Enable Peripheral Multiplexing for SERCOM1 SPI PA18 Arduino PIN10
	PORTA.PMUX[8].bit.PMUXO = 0x2; //SERCOM 1 is selected for peripherial use of this pad
	PORTA.PINCFG[18].bit.PMUXEN = 0x1; //Enable Peripheral Multiplexing for SERCOM1 SPI PA18 Arduino PIN10
	PORTA.PMUX[9].bit.PMUXE = 0x2; //SERCOM 1 is selected for peripherial use of this pad
	PORTA.PINCFG[19].bit.PMUXEN = 0x1; //Enable Peripheral Multiplexing for SERCOM1 SPI PA18 Arduino PIN10
	PORTA.PMUX[9].bit.PMUXO = 0x2; //SERCOM 1 is selected for peripherial use of this pad
	
	
	//Disable SPI 1
	SERCOM1->SPI.CTRLA.bit.ENABLE =0;
	while(SERCOM1->SPI.SYNCBUSY.bit.ENABLE);
	
	//Reset SPI 1
	SERCOM1->SPI.CTRLA.bit.SWRST = 1;
	while(SERCOM1->SPI.CTRLA.bit.SWRST || SERCOM1->SPI.SYNCBUSY.bit.SWRST);
	
	//Setting up NVIC
	NVIC_EnableIRQ(SERCOM1_IRQn);
	NVIC_SetPriority(SERCOM1_IRQn,2);
	
	//Setting Generic Clock Controller!!!!
	GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(GCM_SERCOM1_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
	SERCOM1->SPI.CTRLA.bit.DORD = 0; //MSB first
	SERCOM1->SPI.CTRLA.bit.CPOL = 0; //SCK is low when idle, leading edge is rising edge
	SERCOM1->SPI.CTRLA.bit.CPHA = 0; //data sampled on leading sck edge and changed on a trailing sck edge
	SERCOM1->SPI.CTRLA.bit.FORM = 0x0; //Frame format = SPI
	SERCOM1->SPI.CTRLA.bit.DIPO = 0; //DATA PAD0 MOSI is used as input (slave mode)
	SERCOM1->SPI.CTRLA.bit.DOPO = 0x2; //DATA PAD3 MISO is used as output
	SERCOM1->SPI.CTRLA.bit.MODE = 0x2; //SPI in Slave mode
	SERCOM1->SPI.CTRLA.bit.IBON = 0x1; //Buffer Overflow notification
	SERCOM1->SPI.CTRLA.bit.RUNSTDBY = 1; //wake on receiver complete
	
	//Set up SPI control B register
	//SERCOM1->SPI.CTRLB.bit.RXEN = 0x1; //Enable Receiver
	SERCOM1->SPI.CTRLB.bit.SSDE = 0x1; //Slave Selecte Detection Enabled
	SERCOM1->SPI.CTRLB.bit.CHSIZE = 0; //character size 8 Bit
	//SERCOM1->SPI.CTRLB.bit.PLOADEN = 0x1; //Enable Preload Data Register
	//while (SERCOM1->SPI.SYNCBUSY.bit.CTRLB);	
	
	//Set up SPI interrupts
	SERCOM1->SPI.INTENSET.bit.SSL = 0x1; //Enable Slave Select low interrupt				
	SERCOM1->SPI.INTENSET.bit.RXC = 0x1; //Receive complete interrupt
	SERCOM1->SPI.INTENSET.bit.TXC = 0x1; //Receive complete interrupt
	SERCOM1->SPI.INTENSET.bit.ERROR = 0x1; //Receive complete interrupt
	SERCOM1->SPI.INTENSET.bit.DRE = 0x1; //Data Register Empty interrupt
	//init SPI CLK	
	//SERCOM1->SPI.BAUD.reg = SERCOM_FREQ_REF / (2*4000000u)-1;
	//Enable SPI
	SERCOM1->SPI.CTRLA.bit.ENABLE = 1;
	while(SERCOM1->SPI.SYNCBUSY.bit.ENABLE);
	SERCOM1->SPI.CTRLB.bit.RXEN = 0x1; //Enable Receiver, this is done here due to errate issue
	while(SERCOM1->SPI.SYNCBUSY.bit.CTRLB); //wait until receiver is enabled

}
2 Likes

Hi,

Thanks for sharing your code ! :slight_smile:

Do you have the code for the master on pin 10,11,12,13 (SS, MOSI, MISO, SCK) as well ?

I have to change a few register bits. I will do this in the next few days and then test if it works. I will post it afterwards. :slight_smile:

Great! Thanks!

Tom_the_banana - Thank you for posting, I am trying to do something very similar and was completely stuck until I came across this.

AloyseTech - There is a good explanation of how you can setup the master here:

I am trying to use Tom_the_banana's SPI Slave code on an Adafruit Feather M0 with the 1.6.7 IDE and getting multiple identical compile errors -

error: request for member 'DIR' in 'PORTA', which is of non-class type '_EPortType'

On the following lines of code in SPI init function:

PORTA.DIR.reg &= ~PORT_PA16; //Set PA16 as input (MOSI)
.
.
.
PORTA.PMUX[9].bit.PMUXO = 0x2; //SERCOM 1 is selected for peripherial use of this pad

Here's the full code listing:

#include <SPI.h>

const int slaveAPin = 10;

void setup() {

  Serial.begin(9600);

  pinMode(slaveAPin, INPUT_PULLUP);

  attachInterrupt(10, SERCOM1_Handler, FALLING);

  spiSlave_init();

}

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

}


void spiSlave_init()
{
  
  //Configure SERCOM1 SPI PINS
  PORTA.DIR.reg &= ~PORT_PA16; //Set PA16 as input (MOSI)
  PORTA.DIR.reg &= ~PORT_PA17; //Set PA17 as input (SCK)
  PORTA.DIR.reg &= ~PORT_PA18; //Set PA18 as input (SS)
  PORTA.DIR.reg |= PORT_PA19; //Set PA19 as output (MISO)

  
  PORTA.PINCFG[16].bit.PMUXEN = 0x1; //Enable Peripheral Multiplexing for SERCOM1 SPI PA18 Arduino PIN10
  PORTA.PMUX[8].bit.PMUXE = 0x2; //SERCOM 1 is selected for peripherial use of this pad
  PORTA.PINCFG[17].bit.PMUXEN = 0x1; //Enable Peripheral Multiplexing for SERCOM1 SPI PA18 Arduino PIN10
  PORTA.PMUX[8].bit.PMUXO = 0x2; //SERCOM 1 is selected for peripherial use of this pad
  PORTA.PINCFG[18].bit.PMUXEN = 0x1; //Enable Peripheral Multiplexing for SERCOM1 SPI PA18 Arduino PIN10
  PORTA.PMUX[9].bit.PMUXE = 0x2; //SERCOM 1 is selected for peripherial use of this pad
  PORTA.PINCFG[19].bit.PMUXEN = 0x1; //Enable Peripheral Multiplexing for SERCOM1 SPI PA18 Arduino PIN10
  PORTA.PMUX[9].bit.PMUXO = 0x2; //SERCOM 1 is selected for peripherial use of this pad
  
  
  //Disable SPI 1
  SERCOM1->SPI.CTRLA.bit.ENABLE =0;
  while(SERCOM1->SPI.SYNCBUSY.bit.ENABLE);
  
  //Reset SPI 1
  SERCOM1->SPI.CTRLA.bit.SWRST = 1;
  while(SERCOM1->SPI.CTRLA.bit.SWRST || SERCOM1->SPI.SYNCBUSY.bit.SWRST);
  
  //Setting up NVIC
  NVIC_EnableIRQ(SERCOM1_IRQn);
  NVIC_SetPriority(SERCOM1_IRQn,2);
  
  //Setting Generic Clock Controller!!!!
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(GCM_SERCOM1_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
  SERCOM1->SPI.CTRLA.bit.DORD = 0; //MSB first
  SERCOM1->SPI.CTRLA.bit.CPOL = 0; //SCK is low when idle, leading edge is rising edge
  SERCOM1->SPI.CTRLA.bit.CPHA = 0; //data sampled on leading sck edge and changed on a trailing sck edge
  SERCOM1->SPI.CTRLA.bit.FORM = 0x0; //Frame format = SPI
  SERCOM1->SPI.CTRLA.bit.DIPO = 0; //DATA PAD0 MOSI is used as input (slave mode)
  SERCOM1->SPI.CTRLA.bit.DOPO = 0x2; //DATA PAD3 MISO is used as output
  SERCOM1->SPI.CTRLA.bit.MODE = 0x2; //SPI in Slave mode
  SERCOM1->SPI.CTRLA.bit.IBON = 0x1; //Buffer Overflow notification
  SERCOM1->SPI.CTRLA.bit.RUNSTDBY = 1; //wake on receiver complete
  
  //Set up SPI control B register
  //SERCOM1->SPI.CTRLB.bit.RXEN = 0x1; //Enable Receiver
  SERCOM1->SPI.CTRLB.bit.SSDE = 0x1; //Slave Selecte Detection Enabled
  SERCOM1->SPI.CTRLB.bit.CHSIZE = 0; //character size 8 Bit
  //SERCOM1->SPI.CTRLB.bit.PLOADEN = 0x1; //Enable Preload Data Register
  //while (SERCOM1->SPI.SYNCBUSY.bit.CTRLB);  
  
  //Set up SPI interrupts
  SERCOM1->SPI.INTENSET.bit.SSL = 0x1; //Enable Slave Select low interrupt        
  SERCOM1->SPI.INTENSET.bit.RXC = 0x1; //Receive complete interrupt
  SERCOM1->SPI.INTENSET.bit.TXC = 0x1; //Receive complete interrupt
  SERCOM1->SPI.INTENSET.bit.ERROR = 0x1; //Receive complete interrupt
  SERCOM1->SPI.INTENSET.bit.DRE = 0x1; //Data Register Empty interrupt
  //init SPI CLK  
  //SERCOM1->SPI.BAUD.reg = SERCOM_FREQ_REF / (2*4000000u)-1;
  //Enable SPI
  SERCOM1->SPI.CTRLA.bit.ENABLE = 1;
  while(SERCOM1->SPI.SYNCBUSY.bit.ENABLE);
  SERCOM1->SPI.CTRLB.bit.RXEN = 0x1; //Enable Receiver, this is done here due to errate issue
  while(SERCOM1->SPI.SYNCBUSY.bit.CTRLB); //wait until receiver is enabled

}


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

I've searched Google and the forum posts without success. I suspect there I'm missing a header file but not sure. Any help or input would be appreciated.

Hi Scotty454,

First of all you don't need the lines:

//Configure SERCOM1 SPI PINS
PORTA.DIR.reg &= ~PORT_PA16; //Set PA16 as input (MOSI)
PORTA.DIR.reg &= ~PORT_PA17; //Set PA17 as input (SCK)
PORTA.DIR.reg &= ~PORT_PA18; //Set PA18 as input (SS)
PORTA.DIR.reg |= PORT_PA19; //Set PA19 as output (MISO)

This sets/clears bits in port A's data direction (DIR) register, but as the code is subsequently switching these pins over the SERCOM1, setting GPIO data direction is unnecessary.

Regarding the syntax, there are probably several ways, but I know the following works:

PORT->Group[PORTA].PINCFG[16].bit.PMUXEN = 0x1; //Enable Peripheral Multiplexing for SERCOM1 SPI PA18 Arduino PIN10
PORT->Group[PORTA].PMUX[8].bit.PMUXE = 0x2; //SERCOM 1 is selected for peripherial use of this pad
PORT->Group[PORTA].PINCFG[17].bit.PMUXEN = 0x1; //Enable Peripheral Multiplexing for SERCOM1 SPI PA18 Arduino PIN10
PORT->Group[PORTA].PMUX[8].bit.PMUXO = 0x2; //SERCOM 1 is selected for peripherial use of this pad
PORT->Group[PORTA].PINCFG[18].bit.PMUXEN = 0x1; //Enable Peripheral Multiplexing for SERCOM1 SPI PA18 Arduino PIN10
PORT->Group[PORTA].PMUX[9].bit.PMUXE = 0x2; //SERCOM 1 is selected for peripherial use of this pad
PORT->Group[PORTA].PINCFG[19].bit.PMUXEN = 0x1; //Enable Peripheral Multiplexing for SERCOM1 SPI PA18 Arduino PIN10
PORT->Group[PORTA].PMUX[9].bit.PMUXO = 0x2; //SERCOM 1 is selected for peripherial use of this pad

MartinL,

Thanks for explanation on the redundant GPIO setup and the syntax changes. The code complies and runs now without errors after these changes. I've learned a lot from your other posts and threads in the Zero forum!

Hi MartinL,

I have been trying to set the Feather M0 up as an SPI slave, using SCK (PB11), MOSI (PB10) and MISO (PA12). Attached is some code by Nick Gammon, appearing to work on boards based on ATmega328 chips. However lines

SPCR |= _BV(SPE);
ISR (SPI_STC_vect)

don't seem to work on the M0.

Could you help me to figure out how to use the M0 as an SPI slave? Should I use code similar to SercomSPI.ino with different pin numbers?

SPI.ino (1.24 KB)

SercomSPI.ino (566 Bytes)

Hi Maverick123,

Unfortunately, I don't have experience of using the SAMD21 as a SPI slave.

Hi MartinL,

thank you for your reply. I hope someone else could help me a bit further then. I will elaborate on my problem:
I am trying to connect two Adafruit Feather M0's (SAMD21) to a Raspberry Pi 3B over SPI. One connected to the Raspberry's main SPI, the other to the Raspberry's auxiliary SPI. For this I am planning to use the pigpio-library on the Raspberry, which allows to use both the main and auxiliary SPI. Each Feather M0 should create 8 independent PWM signals. For setting up the PWM channels MartinL has helped me out in this thread:
Changing Arduino PWM Frequency - Page 8

Since I am not very experienced in programming for the Arduino, I have tried to adapt the file posted by Scotty454 to set the M0 up as an SPI slave. I would like to use the default SPI pins: PB11 (SCK), PB10 (MOSI), PA12 (MISO). Since I am using one SPI bus per Feather, I think I don't need to use a SS pin.

I have looked into it myself a bit more, and found out that I can use Sercom4 to create an SPI Slave. Sercom4 is also used by default for SPI, as can be found on this website

sercom: muxing-it-up

I have changed the code from Tom_the_banana and Scotty454:

  • To use Sercom4 in stead of Sercom1.
  • Changed the port select from 0x2 to 0x3, to select the Sercom-alt
PORT->Group[PORTB].PMUX[5].bit.PMUXE = 0x3;
  • Changed DIPO to 2, to select PAD2 for MOSI (corresponding to PB10:MOSI on the board)
  • Changed DOPO to 0, to select PAD0 for MISO (corresponding to PA12:MISO on the board)
  • Defined slaveAPin = 16 (corresponding to PB09:A2 on the board), which is Sercom4 PAD1

Could someone give me some feedback? My Feather M0 hasn't arrived yet, so any feedback before that time would be very helpful. I will post the results of the test with the Feather M0 here once it has arrived.

Sercom4_SPI_Slave.ino (5.06 KB)

I am new bee here. May I ask where do you find all these register setting?? And the programming style does not looks like arduino style. Where can I found any materiel about it? Thanks.

Hi hhbcd,

On my Windows based machine the register definitions are located at:

C:\Users\Computer\AppData\Local\Arduino15\packages\tools\arduino\CMSIS\4.0.0-atmel\Device\ATMEL\samd21\include\

In the "include" directory, there are two further directories: "instance" and "component".

Instance contains the register definitions for each peripheral instance, for example tcc0.h, tcc1.h, etc...

Component contains the structures and definitions for the peripheral's register bitfields, for example tcc.h.

Thanks!

It's really helpful.

MartinL:
Hi hhbcd,

On my Windows based machine the register definitions are located at:

C:\Users\Computer\AppData\Local\Arduino15\packages\tools\arduino\CMSIS\4.0.0-atmel\Device\ATMEL\samd21\include\

In the "include" directory, there are two further directories: "instance" and "component".

Instance contains the register definitions for each peripheral instance, for example tcc0.h, tcc1.h, etc...

Component contains the structures and definitions for the peripheral's register bitfields, for example tcc.h.

Maverick123:

  • Changed DOPO to 0, to select PAD0 for MISO (corresponding to PA12:MISO on the board)

Hi Maverick123,

DOPO should be set to 0x3, which corresponds to PAD[0]: MISO , PAD[3]: SCK, PAD[1]: SS. The other option (0x0) will not select the correct SCK and SS pads.

SERCOM4->SPI.CTRLA.bit.DOPO = 0x3;

More info can be found on page 438 of the complete datasheet.

1 Like

void spi_slave_init()
{
/* MOSI(DI)-PAD0,SCK-PAD1,SS-PAD2,MISO(DO)-PAD3
SPI in slave mode */
spi_slave_pin_init();
SERCOM1->SPI.CTRLA.reg = SERCOM_SPI_CTRLA_DOPO(0x2) |
SERCOM_SPI_CTRLA_MODE_SPI_SLAVE ;

/* SPI receiver enabled /
SERCOM1->SPI.CTRLB.reg = SERCOM_SPI_CTRLB_RXEN ;
SERCOM1->SPI.CTRLB.bit.PLOADEN = 0x1;
/
synchronization busy /
while(SERCOM1->SPI.SYNCBUSY.bit.CTRLB);
/
SERCOM1 enabled /
SERCOM1->SPI.CTRLA.reg |= SERCOM_SPI_CTRLA_ENABLE;
/
synchronization busy /
// while(SERCOM1->SPI.SYNCBUSY.reg & SERCOM_SPI_SYNCBUSY_ENABLE);
/
SERCOM1 interrupt handler mapped to callback handler ‘SERCOM1_App_Handler’ /
// _sercom_set_handler (SERCOM_INTRANCE_INDEX, (sercom_handler_t) SERCOM1_App_Handler);
/
SERCOM1 handler enabled */
// system_interrupt_enable(SERCOM1_IRQn);

SERCOM1->SPI.CTRLA.bit.DORD = 0; //MSB first
//SerialUSB.println( SERCOM1->SPI.CTRLA.bit.DORD);
SERCOM1->SPI.CTRLA.bit.CPOL = 0; //SCK is low when idle, leading edge is rising edge
SERCOM1->SPI.CTRLA.bit.CPHA = 0; //data sampled on leading sck edge and changed on a trailing sck edge
SERCOM1->SPI.CTRLA.bit.FORM = 0x0; //Frame format = SPI
SERCOM1->SPI.CTRLA.bit.DIPO = 0x3; //DATA PAD0 MOSI is used as input (slave mode)
SERCOM1->SPI.CTRLA.bit.DOPO = 0x0; //DATA PAD3 MISO is used as output
SERCOM1->SPI.CTRLA.bit.MODE = 0x2; //SPI in Slave mode

//SERCOM1->SPI.CTRLA.bit.IBON = 0x1; //Buffer Overflow notification
//SERCOM1->SPI.CTRLA.bit.RUNSTDBY = 1; //wake on receiver complete

//Set up SPI control B register
SERCOM1->SPI.CTRLB.bit.RXEN = 0x1; //Enable Receiver
SERCOM1->SPI.CTRLB.bit.SSDE = 0x1; //Slave Selecte Detection Enabled
SERCOM1->SPI.CTRLB.bit.CHSIZE = 0; //character size 8 Bit
//SERCOM1->SPI.CTRLB.bit.PLOADEN = 0x1;
//SerialUSB.println(SERCOM1->SPI.CTRLB.bit.PLOADEN);
//SERCOM1->SPI.CTRLB.reg = SERCOM_SPI_CTRLB_RXEN | SERCOM_SPI_CTRLB_PLOADEN;

}

void spi_slave_pin_init()
{
/* pin18 pull-resistor is set into high /
PORT->Group[0].PINCFG[18].reg = PORT_PINCFG_INEN | PORT_PINCFG_PULLEN;
PORT->Group[0].OUTSET.reg = (1u << 18);
pin_set_peripheral_function(PINMUX_PA16C_SERCOM1_PAD0); //MOSI
pin_set_peripheral_function(PINMUX_PA17C_SERCOM1_PAD1); //SCK
pin_set_peripheral_function(PINMUX_PA18C_SERCOM1_PAD2); //SS
pin_set_peripheral_function(PINMUX_PA19C_SERCOM1_PAD3); //MISO
}
static void pin_set_peripheral_function(uint32_t pinmux)
{
uint8_t port = (uint8_t)((pinmux >> 16)/32);
PORT->Group[port].PMUX[((pinmux >> 16) - (port
32))/2].reg &= ~(0xF << (4 * ((pinmux >>
16) & 0x01u)));
PORT->Group[port].PMUX[((pinmux >> 16) - (port32))/2].reg |= (uint8_t)((pinmux &
0x0000FFFF) << (4 * ((pinmux >> 16) & 0x01u)));
PORT->Group[port].PINCFG[((pinmux >> 16) - (port
32))].bit.PMUXEN = 1;
}

This is the code i had used for slave setting
From master Im sending 1 byte in slave side 3 bytes has to be transfer as response for 1 byte of master.
But in master im getting first byte as master data which is sent by master initially..so that slave's third byte will be lost can any one help me out ....

Hi everyone, I am new to all this and am trying to configure out the SPI code that was posted here to my application. I understand all of it except for the following lines:

PORT->Group[PORTA].PINCFG[16].bit.PMUXEN = 0x1; //Enable Peripheral Multiplexing for SERCOM2 SPI PA18 Arduino PIN10
  PORT->Group[PORTA].PMUX[8].bit.PMUXE = 0x2; //SERCOM 1 is selected for peripherial use of this pad
  PORT->Group[PORTA].PINCFG[17].bit.PMUXEN = 0x1; //Enable Peripheral Multiplexing for SERCOM2 SPI PA18 Arduino PIN10
  PORT->Group[PORTA].PMUX[8].bit.PMUXO = 0x2; //SERCOM 1 is selected for peripherial use of this pad
  PORT->Group[PORTA].PINCFG[18].bit.PMUXEN = 0x1; //Enable Peripheral Multiplexing for SERCOM2 SPI PA18 Arduino PIN10
  PORT->Group[PORTA].PMUX[9].bit.PMUXE = 0x2; //SERCOM 1 is selected for peripherial use of this pad
  PORT->Group[PORTA].PINCFG[19].bit.PMUXEN = 0x1; //Enable Peripheral Multiplexing for SERCOM2 SPI PA18 Arduino PIN10
  PORT->Group[PORTA].PMUX[9].bit.PMUXO = 0x2; //SERCOM 1 is selected for peripherial use of this pad

I am trying to use SERCOM2 on pins PA08 -MISO, PA09 -SCK, PA10-CS0, and PA11-MOSI.

It would be greatly appreciated if someone could explain the code (or at least point me in the right direction to figure it out) above since I believe I will need to modify it to get it working with the pins I am using. I have DOPO set to 0x0 and DIPO to 0x3.

I am also confused as to how to write to the buffer that the master can read from. I see no location in the code where a write buffer is used/filled with data.

Thanks

Hi all,

to answer some of your questions regarding setting up a Sercom SPI slave I have created the following repository on GitHub: SercomSPISlave.

It includes an Arduino library that can be used to setup an SPI slave using Sercom1 or Sercom4.

In case you notice any bugs or issues, or if you have any requests please file an issue on GitHub.

Regards