Using Multiple SPI Buses on the Zero

Hi there!

Due to some component placements, I need to use two SPI buses on my Arduino Zero.
I checked out this guide to get a sense of Muxing the serial ports to be used for SPI.

I then changed SPI_INTERFACES_COUNT (variant.h, line 133) to 2

 #define SPI_INTERFACES_COUNT 2

Now, SPI.h will be defining my second SPI bus as SPI1:

 extern SPIClass SPI1;

I am using the following pins:

/*
PA16 - MOSI - SER1 [0] - ARDUINO PIN 11
PA17 - SCK - SER1 [1] - ARDUINO PIN 13
PA18 - MISO - SER1 [2] - ARDUINO PIN 12
*/

I am changing the external SPIClass definition in SPI.cpp to:

#if SPI_INTERFACES_COUNT > 1
  SPIClass SPI1(&sercom1, 12, 13, 11, SPI_PAD_0_SCK_1, SERCOM_RX_PAD_2)
#endif

So far so good...at this point, the code compiles and runs

I then open this second SPI port by calling

char buf[3] = {0};

SPI1.begin(); 
pinPeripheral(11, PIO_SERCOM);
pinPeripheral(12, PIO_SERCOM);
pinPeripheral(13, PIO_SERCOM);

pinMode(CSpin, OUTPUT);
digitalWrite(CSpin, HIGH); //CS is active low

SPI1.beginTransaction(SPISettings(20000000, MSBFIRST, SPI_MODE0);
digitalWrite(CSpin, LOW); //assert CS
SPI1.transfer(0x6E); //send a command to the unit

buf[0] = SPI1.transfer(0); //get a response
buf[1] = SPI1.transfer(0); //get a response
buf[2] = SPI1.transfer(0); //get a response

digitalWrite(CSpin, HIGH); //release CS
SPI1.endTransaction();

And I get nothing. Nothing goes into buf the way it does on the default SPI port, and it doesn't seem like any signal is being sent across. I checked the pins with my logic analyzer and I also see no change - the only thing that moves is CS.

Is there still something in the register that I am missing? CTRLA and CTRLB seem to be enabled, I don't see any kind of buffer overflow or register error - which would be surprising since I'm simply initializing the device.

Does anyone have any experience with this or any suggestions as to how I can properly configure these pins?

Thank you!

Hi masoandro,

It isn't necessary to start editing the "variant.h" file, just simply cut and paste the sketch code from Adafruit tutorial into the Arduino IDE:

#include <SPI.h>
#include "wiring_private.h" // pinPeripheral() function
  
SPIClass mySPI (&sercom1, 12, 13, 11, SPI_PAD_0_SCK_1, SERCOM_RX_PAD_3);

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

  // do this first, for Reasons
  mySPI.begin();

  // Assign pins 11, 12, 13 to SERCOM functionality
  pinPeripheral(11, PIO_SERCOM);
  pinPeripheral(12, PIO_SERCOM);
  pinPeripheral(13, PIO_SERCOM);
}

uint8_t i=0;
void loop() {
  Serial.println(i);
  mySPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0));
  mySPI.transfer(i++);
  mySPI.endTransaction();
}

In any case, when Arduino update the SAMD21 core to a new, these files will be overwritten.

Also, in your code:

SPIClass SPI1(&sercom1, 12, 13, 11, SPI_PAD_0_SCK_1, SERCOM_RX_PAD_2)

...should connect to SERCOM_RX_PAD_3 and instead of SERCOM_RX_PAD_2.

Thank you for your reply.
Can you explain why it should be SERCOM_RX_PAD_3? The MISO line is connected to PA18, which according to the datasheet is SERCOM1/PAD[2] or SERCOM-ALT3/PAD[2].

I did try to put it on SERCOM_RX_PAD_3 and take it out of the SPI peripheral variant. But why would this make a difference either? The only mention of SPI_INTERFACES_COUNT in all library references is in SPI.h, SPI.cpp and variant.h. I don't think it should make a difference where the SPIClass is defined.

In any case I did try doing both of these things you suggested but there is still no output from the pins.

Ok, I've shrunk my example down to just a few lines. However, I'm getting pretty messed up results.
Mainly, the data on the line is more or less incomprehensible, and spi transfer does not return any values.

I am using

SPI1.beginTransaction(SPISettings(2000000, MSBFIRST, SPIMODE0));

I went ahead and disconnected everything from the chip. I just want to see what is coming out of the SPI pins (at least MOSI and SCK).
The clock (again without any line interference) is all over the place. Maybe it's my logic analyzer, because I can't see why the clock would send such inconsistent signals. I've attached a picture of the clock signal over 4 total SPI transfers.

Hi masoandro,

Can you explain why it should be SERCOM_RX_PAD_3? The MISO line is connected to PA18, which according to the datasheet is SERCOM1/PAD[2] or SERCOM-ALT3/PAD[2].

The MISO line is on D12 which is PA19 on sercom1, pad 3.

Here's a basic example using the Adafruit SPI code, it writes to the MPU-6000 gyroscope's device ID register at address 0x76 (hex) with the digital pins 10, 11, 12 and 13:

#include <SPI.h>
#include "wiring_private.h" // pinPeripheral() function

#define WHO_AM_I_MPU6000 0x75 // Should return 0x68 (hex)
 
SPIClass mySPI (&sercom1, 12, 13, 11, SPI_PAD_0_SCK_1, SERCOM_RX_PAD_3);

void setup() {
  SerialUSB.begin(115200);                      // Initialise serial communications
  while(!SerialUSB);                            // Wait for the serial communications to initialise
  mySPI.begin();                                // Initialise the SPI port
  pinPeripheral(11, PIO_SERCOM);                // Assign MOSI
  pinPeripheral(12, PIO_SERCOM);                // Assign MISO
  pinPeripheral(13, PIO_SERCOM);                // Assign SCK
  pinMode(10, OUTPUT);                          // Assign CS
  digitalWrite(10, HIGH);                       // Pull the CS line HIGH
  mySPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));  // Start an SPI transaction at 1MHz
  digitalWrite(10, LOW);                        // Pull the CS line LOW
  mySPI.transfer(WHO_AM_I_MPU6000 | 0x80);      // Access the MPU-6000 device ID register
  uint8_t device = mySPI.transfer(0x00);        // Read the register contents
  digitalWrite(10, HIGH);                       // Pull the CS line HIGH
  mySPI.endTransaction();                       // End the transaction
  SerialUSB.print(F("Result: "));               // Output the result
  SerialUSB.println(device, HEX);               // Returns 0x68 (hex)   
}

void loop() {}

The code outputs the result:

Result: 68

This is a stand-alone sketch that doesn't require modification of the "variant.h" or "variant.cpp" files.

Thanks for your help. You are right, I was mistaking the pins. My final code is

SPIClass SPI1 (&sercom1, 10, 13, 11, SPI_PAD_0_SCK_1, SERCOM_RX_PAD_2);
#define CSPIN 6

void setup(void){
  SPI1.begin(115200);
  pinPeripheral(10, PIO_SERCOM);
  pinPeripheral(11, PIO_SERCOM);
  pinPeripheral(13, PIO_SERCOM);
  pinMode(CSPIN, OUTPUT);
  digitalWrite(CSPIN, HIGH);
}

void loop(void){
  char buf[3] = {0};
  int i;
  while(1){
    SPI1.beginTransaction(SPISettings(2000000, MSBFIRST, SPI_MODE0));
    digitalWrite(CSPIN, LOW);
    SPI1.transfer(0x6F);
    buf[0] = SPI1.transfer(0);
    buf[1] = SPI1.transfer(0);
    buf[2] = SPI1.transfer(0);
    digitalWrite(CSpin, HIGH);
    SPI1.endTransaction();
  }
}

And sure enough, the device serial number is now in the buf pointer (no print statements since I am using a debugger). I had really misread the pins on the schematic and in variant. Looks good on the logic analyzer now too.