Arduino Uno as SPI slave

Hello, I'm having some troubles sending data from my SAMD21g18a master to Arduino uno (slave). This is my master code:


#include "sam.h"
#include "t/Core.h"

#define BUFFER_SIZE 32

#define SPI_CLK_FREQ 8000000
#define SPI_BAUD 50000

void init_SPI_tutorial(){

	while(SERCOM1->SPI.SYNCBUSY.bit.ENABLE);
	SERCOM1->SPI.CTRLA.bit.ENABLE = 0;

	while(SERCOM1->SPI.SYNCBUSY.bit.SWRST);
	SERCOM1->SPI.CTRLA.bit.SWRST = 1;

	while(SERCOM1->SPI.CTRLA.bit.SWRST);
	while(SERCOM1->SPI.SYNCBUSY.bit.SWRST || SERCOM1->SPI.SYNCBUSY.bit.ENABLE);

	PORT->Group[0].WRCONFIG.reg =
	PORT_WRCONFIG_WRPINCFG |											//Enables the configuration of PINCFG
	PORT_WRCONFIG_WRPMUX |												//Enables the configuration of the PMUX for the selected pins
	PORT_WRCONFIG_PMUXEN |												//Enables the PMUX for the pins
	PORT_WRCONFIG_PMUX(MUX_PA19C_SERCOM1_PAD3) |						                        //Bulk configuration for PMUX "C" for SERCOM1
	PORT_WRCONFIG_HWSEL |
	PORT_WRCONFIG_INEN  |												//Enable input on this pin MISO
	PORT_WRCONFIG_PINMASK((uint16_t)((PORT_PA19) >> 16));				                                //Selecting which pin is configured  PB16  This bit needs to shift to fit the 16 bit macro requirements

	//Using the WRCONFIG register to bulk configure both PB22 and PB23 for being configured the SERCOM1 SPI MASTER MOSI and SCK pins
	PORT->Group[0].WRCONFIG.reg =
	PORT_WRCONFIG_WRPINCFG |											//Enables the configuration of PINCFG
	PORT_WRCONFIG_WRPMUX |												//Enables the configuration of the PMUX for the selected pins
	PORT_WRCONFIG_PMUX(MUX_PA16C_SERCOM1_PAD0) |						                        //Bulk configuration for PMUX
	PORT_WRCONFIG_HWSEL |
	PORT_WRCONFIG_PMUXEN |												//Enables the PMUX for the pins
	PORT_WRCONFIG_PINMASK ((uint16_t)((PORT_PA16 | PORT_PA17) >> 16));	                                        //Selecting which pin is configured

	PM->APBCMASK.reg |= PM_APBCMASK_SERCOM1;							                //Enable the SERCOM 1 under the PM

	GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(SERCOM1_GCLK_ID_CORE) |			                                //Provide necessary clocks to the peripheral
	GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(0);

	while(GCLK->STATUS.bit.SYNCBUSY);									//Wait for clock sync

	SERCOM1->SPI.CTRLA.reg = SERCOM_SPI_CTRLA_MODE_SPI_MASTER|			                        //Configure the Peripheral as SPI Master
	SERCOM_SPI_CTRLA_DOPO(0)|
	SERCOM_SPI_CTRLA_DIPO(0x3);
	SERCOM1->SPI.CTRLB.reg = SERCOM_SPI_CTRLB_RXEN;						                //Enable receive on SPI

	uint16_t BAUD_REG = ((float)SPI_CLK_FREQ / (float)(2 * SPI_BAUD)) - 1;	                                //Calculate BAUD value
	SERCOM1->SPI.BAUD.reg =	SERCOM_SPI_BAUD_BAUD(BAUD_REG);				                        //Set the SPI baud rate
	SERCOM1->SPI.CTRLA.reg |= SERCOM_SPI_CTRLA_ENABLE;				 			//Enable the Sercom SPI
	while(SERCOM1->SPI.SYNCBUSY.bit.ENABLE);
}

uint8_t spiSend(uint8_t data){

	while(SERCOM1->SPI.INTFLAG.bit.DRE == 0);
	SERCOM1->SPI.DATA.reg = data;
	while(SERCOM1->SPI.INTFLAG.bit.RXC == 0);
	return (uint8_t)SERCOM1->SPI.DATA.reg;
}



int main(void)
{
    /* Replace with your application code */
	SystemInit();
	_delay_init(SysTick);
	
	PORT->Group[0].DIRSET.reg = (1 << 18);
	//PORT->Group[0].OUTCLR.reg = (1 << 18);							  // set SPI slave select LOW;
	PORT->Group[0].OUTSET.reg = (1 << 18);							  // set SPI slave select HIGH
	init_SPI_tutorial();
	while (1) 
    {
		PORT->Group[0].OUTCLR.reg = (1 << 18);
		DelayMS(10);
		for(int i = 0; i < 10; ++i)spiSend(0xaa), DelayMS(100);
		PORT->Group[0].OUTSET.reg = (1 << 18);	
		DelayMS(1000);
    }
}

When I check with a scope this code "works" aka it does provide clock and it send the correct message as well as it sets the slave select to low and high.

Slave code:

#include<SPI.h>

volatile boolean received;
volatile byte receivedData;

ISR (SPI_STC_vect)        //Inerrrput routine function 
{
  receivedData = SPDR;   // Get the received data from SPDR register
  received = true;       // Sets received as True 
}

void setup()
{
  Serial.begin(115200);

  pinMode(MISO,OUTPUT);   //Sets MISO as OUTPUT
  SPCR |= _BV(SPE);       //Turn on SPI in Slave Mode
  received = false;
  SPI.attachInterrupt();  //Activate SPI Interuupt 
}


void loop()
{ 
  if(received) {                        
    SPDR = receivedData;    // send back the received data, this is not necessary, only for demo purpose
    received = false;
    Serial.print(receivedData, HEX);
  }
}

I'm not really familiar with UNO so this is just something I copy pasted but as far as I can tell it should be correct. However I don't get any data on the slave side and it never enters the interrupt.
I connected the devices like this:
SAMD21 UNO
GND <-----> GND
PA19 <-----> PIN 12
PA18 <-----> PIN 11
PA17 <-----> PIN13
PA16 <-----> PIN 10
Any help is appreciated.

First the hardware: a SAMD21 runs on 3.3V while the UNO runs on 5V. Your wiring might work (given very short connections) for SCKL, CS and MOSI but the MISO connection probably damages the SAMD21. You should use level converters for all these signals!

Additionally the SPI frequency of 8MHz is too high for the UNO. Reduce it to 1 MHz and try again. If there isn't too much data to be transferred this frequency might work. Otherwise decrease the frequency more.

1 Like

Thanks, lowering the frequency did the trick. And thanks for letting me know about the voltage difference before I broke something.

I tried goin in reverse now, using UNO as the master and SAMD as the slave. But again I'm running in to problems. I used this code for the SAMD21 slave. And this for the master:

#include <SPI.h>
#include<Arduino.h>

void setup (void)
  {
  digitalWrite(SS, HIGH);  // ensure SS stays high
  SPI.begin ();
  SPI.setClockDivider(SPI_CLOCK_DIV4);    //Sets clock for SPI communication at 8 (16/8=2Mhz)
  Serial.begin(115200);
  } // end of setup

void loop (void)
  {
  byte c;

  // enable Slave Select
  digitalWrite(SS, LOW);    // SS is pin 10

  // send test string
  for (const char * p = "Fab" ; c = *p; p++)
    SPI.transfer (c);

  // disable Slave Select
  digitalWrite(SS, HIGH);
  delay (100);
  } // end of loop

I tried changing the clock div to all values but it doesn't help, any hints on what is wrong assuming the connections are correct?

In addition to that I tried sending from SAMD21 to SAMD21 using the code that worked after lowering frequency but the slave never gets any data.

As I haven't seen the code of the other side I would expect the error to be there. Or the connections are not correct.

Why?

The slave code you provided in the original post won't work on the SAMD21, i won't even compile.

I wanted to try SAMD21 in slave mode no particular reason, just wanted to know how it works.
My message was badly formatted I used a SPI slave library (for SAMD21) here is how the slave is implemented:

/*  
  Copyright (C) 2021 lenvm
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
  For the GNU General Public License see https://www.gnu.org/licenses/
  Contact Information
  -------------------
  lenvm
  GitHub   : https://github.com/lenvm
*/

/*
  Example code for the SercomSPISlave library.
  This code initializes a Sercom1 SPI Slave and prints the data received.
  Written 2020 July 15 
  by lenvm
  Updated 2021 June 8
  by lenvm
*/

#include <SercomSPISlave.h>
SercomSPISlave SPISlave;

#define DEBUG // comment this line out to not print debug data on the serial bus

// initialize variables
byte buf[1];

void setup()
{  
  Serial.begin(115200);
  Serial.println("Serial started");
  SPISlave.Sercom1init();
  Serial.println("Sercom1 SPI slave initialized");
}

void loop()
{
  Serial.println(buf[0]);
  delay(1);
}

void SERCOM1_Handler() // 25.7 Register Summary, page 454 atmel 42181, samd21
{
  #ifdef DEBUG
    Serial.println("In SPI Interrupt");
  #endif
  uint8_t data = 0;
  uint8_t interrupts = SERCOM1->SPI.INTFLAG.reg; //Read SPI interrupt register
  #ifdef DEBUG
    Serial.print("Interrupt: "); Serial.println(interrupts);
  #endif
  
  if(interrupts & (1<<3)) // 8 = 1000 = SSL
  {
    #ifdef DEBUG
      Serial.println("SPI SSL Interupt");
    #endif
    SERCOM1->SPI.INTFLAG.bit.SSL = 1; //clear slave select interrupt
    //data = SERCOM1->SPI.DATA.reg; //Read data register
    #ifdef DEBUG
      Serial.print("DATA: "); Serial.println(data);
    #endif
    //SERCOM1->SPI.INTFLAG.bit.RXC = 1; //clear receive complete interrupt
  }
  
  // This is where data is received, and is written to a buffer, which is used in the main loop
  if(interrupts & (1<<2)) // 4 = 0100 = RXC
  {
    #ifdef DEBUG
      Serial.println("SPI Data Received Complete Interrupt");
    #endif
    data = SERCOM1->SPI.DATA.reg; //Read data register
    buf[0] = data; // copy data to buffer
    #ifdef DEBUG
      Serial.print("DATA: ");
      Serial.println(data);
    #endif
    SERCOM1->SPI.INTFLAG.bit.RXC = 1; //clear receive complete interrupt
  }
  
  if(interrupts & (1<<1)) // 2 = 0010 = TXC
  {
    #ifdef DEBUG
      Serial.println("SPI Data Transmit Complete Interrupt");
    #endif
    SERCOM1->SPI.INTFLAG.bit.TXC = 1; //clear receive complete interrupt
  }
  
  if(interrupts & (1<<0)) // 1 = 0001 = DRE
  {
    #ifdef DEBUG
      Serial.println("SPI Data Register Empty Interrupt");
    #endif
    SERCOM1->SPI.DATA.reg = 0xAA;
    //SERCOM1->SPI.INTFLAG.bit.DRE = 1;
  }
  
  #ifdef DEBUG
    Serial.println("----------");
  #endif
}

And this is the setup for the SPI on the slave:

/*
  Copyright (C) 2021 lenvm
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
  For the GNU General Public License see https://www.gnu.org/licenses/
  Contact Information
  -------------------
  lenvm
  GitHub   : https://github.com/lenvm
*/

#include "SercomSPISlave.h"

// Constructors //
SercomSPISlave::SercomSPISlave()
{
    
}

// Public Methods //
void SercomSPISlave::Sercom0init()
{
    //Configure SERCOM0  SPI PINS  PAD
    //Set PA08 as input  (MOSI)    00
    //Set PA09 as input  (SCK)     01
    //Set PA10 as input  (SS)      02
    //Set PA11 as output (MISO)    03
    PORT->Group[PORTA].PINCFG[8].bit.PMUXEN = 0x1; //Enable Peripheral Multiplexing for SERCOM0 SPI PA08
    PORT->Group[PORTA].PMUX[4].bit.PMUXE = 0x2; //SERCOM 0 is selected for peripheral use of this pad (0x2 selects peripheral function C: SERCOM)
    PORT->Group[PORTA].PINCFG[9].bit.PMUXEN = 0x1; //Enable Peripheral Multiplexing for SERCOM0 SPI PA09
    PORT->Group[PORTA].PMUX[4].bit.PMUXO = 0x2; //SERCOM 0 is selected for peripheral use of this pad (0x2 selects peripheral function C: SERCOM)
    PORT->Group[PORTA].PINCFG[10].bit.PMUXEN = 0x1; //Enable Peripheral Multiplexing for SERCOM0 SPI PA10
    PORT->Group[PORTA].PMUX[5].bit.PMUXE = 0x2; //SERCOM 0 is selected for peripheral use of this pad (0x2 selects peripheral function C: SERCOM)
    PORT->Group[PORTA].PINCFG[11].bit.PMUXEN = 0x1; //Enable Peripheral Multiplexing for SERCOM0 SPI PA11
    PORT->Group[PORTA].PMUX[5].bit.PMUXO = 0x2; //SERCOM 0 is selected for peripheral use of this pad (0x2 selects peripheral function C: SERCOM)
    /*
    Explanation:
    PMUXEN stands for Peripheral Multiplexing Enable
    PMUXE stands for Even bits in the Peripheral Multiplexing register
    PMUXO stands for Odd bits in the Peripheral Multiplexing register
    The selection of peripheral function A to H is done by writing to the Peripheral Multiplexing Odd and Even bits in the Peripheral Multiplexing register (PMUXn.PMUXE/O) in the PORT.
    Reference: Atmel-42181G-SAM-D21_Datasheet section 6.1 on page 21
    PA08 corresponds to: PORTA, PMUX[4] Even
    PA09 corresponds to: PORTA, PMUX[4] Odd
    PA10 corresponds to: PORTA, PMUX[5] Even
    PA11 corresponds to: PORTA, PMUX[5] Odd
    In general:
    Px(2n+0/1) corresponds to Portx, PMUX[n] Even=0/Odd=1
    */
    
  //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 = 0x0; //DATA PAD 00 MOSI is used as slave input (slave mode) // page 492
  SERCOM0->SPI.CTRLA.bit.DOPO = 0x2; //DATA PAD 03 MISO is used as slave 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 Selecte 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 errate issue
  while(SERCOM0->SPI.SYNCBUSY.bit.CTRLB); //wait until receiver is enabled

}

void SercomSPISlave::Sercom1init()
{
    //Configure SERCOM1 SPI PINS
    //Set PA16 as input (MOSI)
    //Set PA17 as input (SCK)
    //Set PA18 as input (SS)
    //Set PA19 as output (MISO)
    PORT->Group[PORTA].PINCFG[16].bit.PMUXEN = 0x1; //Enable Peripheral Multiplexing for SERCOM1 SPI PA16 Arduino PIN11
    PORT->Group[PORTA].PMUX[8].bit.PMUXE = 0x2; //SERCOM 1 is selected for peripheral use of this pad (0x2 selects peripheral function C: SERCOM)
    PORT->Group[PORTA].PINCFG[17].bit.PMUXEN = 0x1; //Enable Peripheral Multiplexing for SERCOM1 SPI PA17 Arduino PIN13
    PORT->Group[PORTA].PMUX[8].bit.PMUXO = 0x2; //SERCOM 1 is selected for peripheral use of this pad (0x2 selects peripheral function C: SERCOM)
    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 peripheral use of this pad (0x2 selects peripheral function C: SERCOM)
    PORT->Group[PORTA].PINCFG[19].bit.PMUXEN = 0x1; //Enable Peripheral Multiplexing for SERCOM1 SPI PA19 Arduino PIN12
    PORT->Group[PORTA].PMUX[9].bit.PMUXO = 0x2; //SERCOM 1 is selected for peripheral use of this pad (0x2 selects peripheral function C: SERCOM)
/*
Explanation:
PMUXEN stands for Peripheral Multiplexing Enable
PMUXE stands for Even bits in the Peripheral Multiplexing register
PMUXO stands for Odd bits in the Peripheral Multiplexing register
The selection of peripheral function A to H is done by writing to the Peripheral Multiplexing Odd and Even bits in the Peripheral Multiplexing register (PMUXn.PMUXE/O) in the PORT.
Reference: Atmel-42181G-SAM-D21_Datasheet section 6.1 on page 21
PA16 corresponds to: PORTA, PMUX[8] Even
PA17 corresponds to: PORTA, PMUX[8] Odd
PA18 corresponds to: PORTA, PMUX[9] Even
PA19 corresponds to: PORTA, PMUX[9] Odd
In general:
Px(2n+0/1) corresponds to Portx, PMUX[n] Even=0/Odd=1
*/
    
  //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 = 0x0; //DATA PAD0 MOSI is used as slave input (slave mode) // page 492
  SERCOM1->SPI.CTRLA.bit.DOPO = 0x2; //DATA PAD2 MISO is used as slave 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 SercomSPISlave::Sercom4init()
{
  //Configure SERCOM4 SPI PINS  
    //Set PA12 as input (MOSI) - on D22 / MISO
    //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[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 peripheral use of this pad (0x3 selects peripheral function D: SERCOM-ALT)
  PORT->Group[PORTB].PINCFG[9].bit.PMUXEN = 0x1; //Enable Peripheral Multiplexing for SERCOM4 SPI PB09 Arduino PIN16
  PORT->Group[PORTB].PMUX[4].bit.PMUXO = 0x3; //SERCOM 4 is selected for peripheral use of this pad (0x3 selects peripheral function D: SERCOM-ALT)
  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 peripheral use of this pad (0x3 selects peripheral function D: SERCOM-ALT)
  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 peripheral use of this pad (0x3 selects peripheral function D: SERCOM-ALT)
/*
Explanation:
PMUXEN stands for Peripheral Multiplexing Enable
PMUXE stands for Even bits in the Peripheral Multiplexing register
PMUXO stands for Odd bits in the Peripheral Multiplexing register
The selection of peripheral function A to H is done by writing to the Peripheral Multiplexing Odd and Even bits in the Peripheral Multiplexing register (PMUXn.PMUXE/O) in the PORT.
Reference: Atmel-42181G-SAM-D21_Datasheet section 6.1 on page 21
PA12 corresponds to: PORTA, PMUX[6] Even
PB09 corresponds to: PORTB, PMUX[4] Odd
PB10 corresponds to: PORTB, PMUX[5] Even
PB11 corresponds to: PORTB, PMUX[5] Odd
In general:
Px(2n+0/1) corresponds to Portx, PMUX[n] Even=0/Odd=1
*/

  //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 = 0x0; //DATA PAD0 MOSI is used as slave input (slave mode) // page 492
  SERCOM4->SPI.CTRLA.bit.DOPO = 0x2; //DATA PAD2 MISO is used as slave 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

}

And the code I use for arduino as master is in the message above, I'll paste it here just so everything is in one place.

#include <SPI.h>
#include<Arduino.h>

void setup (void)
  {
  digitalWrite(SS, HIGH);  // ensure SS stays high
  SPI.begin ();
  SPI.setClockDivider(SPI_CLOCK_DIV4);    //Sets clock for SPI communication at 8 (16/8=2Mhz)
  Serial.begin(115200);
  } // end of setup

void loop (void)
  {
  byte c;

  // enable Slave Select
  digitalWrite(SS, LOW);    // SS is pin 10

  // send test string
  for (const char * p = "Fab" ; c = *p; p++)
    SPI.transfer (c);

  // disable Slave Select
  digitalWrite(SS, HIGH);
  delay (100);
  } // end of loop

As far as I can tell the connections should remain the same.

Do you use level converters between the UNO and the SAMD21? If you don't you probably damaged the SAMD21 board already as it isn't 5V tolerant and you now fired with 3 5V pins at it.

1 Like

I am using them now but I guess it's too late since I didn't use them before you told me :/. Well thanks for the input, I'll try to get my hands on a new chip and test the code again. Once again I appreciate the help.