Adding More SERCOM Ports for SAMD Boards

Pages
Contributors: M-Short
Favorited Favorite 1

Adding an SPI

1.) Figure Out Which Pins to Use.

Let's start by checking out the SERCOM.h file in Arduino’s SAMD21 core files. Specifically we are looking at lines 73-79 (yep, we looked at those when we added the UART) and lines 102-108. You should see the code listed below. But what does it mean? There are 2 parts, the first part defines which pads you can use for an RX (MISO) pad. It looks like you can use pads 0, 1, 2, and 3 which is all of them. Next, it defines which pads you can use for TX (MOSI, and SCK) and in what configuration. So, we'll need to keep that in mind when we select our pins.

language:c
typedef enum
{
    SERCOM_RX_PAD_0 = 0,
    SERCOM_RX_PAD_1,
    SERCOM_RX_PAD_2,
    SERCOM_RX_PAD_3
} SercomRXPad;

//...

typedef enum
{
    SPI_PAD_0_SCK_1 = 0,
    SPI_PAD_2_SCK_3,
    SPI_PAD_3_SCK_1,
    SPI_PAD_0_SCK_3
} SercomSpiTXPad;

Since none of the boards I've come across use SERCOM 2 for anything we're going to use it for our examples. Let's start with our outputs since those pins are limited compared to the MISO pins. It looks like SCK can only be on pad 1 or 3, and MOSI can be on 0, 2, or 3 depending on the configuration. So, looking at our table, let's start by removing the pins that are not broken out to our board or already in use. That removes MISO, D38, D1/TX, and D0/RX, leaving use with D4, D3, D2, and D5. Any of those look like good options, but lets go with D3 for MISO, D5 for SCK, and D4 for MOSI. Again, we'll need to dig into our fancy charts to figure out what is where.

2.) Add Your Code.

Next lets figure out what code we need. Let's take a look at the SPI.cpp file again. All the way near the bottom on line 261, you'll see the following line. This is what we are trying to duplicate in our code. The variant.h file defines all those macros, but their names give us a good idea of what should go there.

language:c
 SPIClass SPI (&PERIPH_SPI, PIN_SPI_MISO, PIN_SPI_SCK, PIN_SPI_MOSI, PAD_SPI_TX, PAD_SPI_RX);

Let's start with the definition. Let's pick a name. "SPI2" sounds good. We also know we are going to use SERCOM 2, that we are going to use D3 for MISO, D5 for SCK, and D4 for MOSI. So, we'll add the following to our code.

language:c
SPIClass SPI2 (&sercom2, 3, 5, 4, SPI_PAD_0_SCK_3, SERCOM_RX_PAD_1);

3.) Update the Pin Definitions Based on the Pin Mux.

Next, we need to set up the mux. Right now, the pins are defined as general I/O pins. We want them to act as SERCOM pins. The first thing we need to do is to add the pin peripheral library. Then we use the pinPeripheral command to set up the pin defintion.

language:c
#include "wiring_private.h" // pinPeripheral() function

SPI2.begin();
pinPeripheral(3, PIO_SERCOM_ALT);
pinPeripheral(4, PIO_SERCOM_ALT);
pinPeripheral(5, PIO_SERCOM);

You'll notice that one uses the argument PIO_SEROM and the others PIO_SERCOM_ALT. If you look on the datasheet, you'll notice that pins can have a SERCOM port listed under SERCOM or SERCOM-ALT. For D5, we are using the SERCOM port in the SERCOM column. For D3 and D4, we are using the SERCOM port in the SERCOM-ALT column.

4.) Putting It All Together.

Step 4 is running the code, testing, and troubleshooting. For this example, we are going to grab a Serial LCD screen and connect it to our new SPI port. Make sure that you have soldered headers to the LCD if you have not already.

Circuit Diagram SAMD21 SERCOM SPI to SerLCD

The next step is to test out the SPI port with the code listed below. The code is pretty bare bones and shows you where all your new code should go. The code also includes a pin definition for the CSPIN. Copy and paste the code in your Arduino IDE. Select your board, COM port, and hit upload.

language:c
/*********************************************************************
   Sample code for setting up additional Serial ports on a SamD21 board
   In this example the Redboard Turbo is used with the 16x2 SerLCD display
   For more information on the SerLCD code check out the github repo
   https://github.com/sparkfun/OpenLCD
   https://www.sparkfun.com/products/14812
   https://www.sparkfun.com/products/14072
   By: Michelle Shorter - SparkFun Electronics
   License: This code is public domain but you buy me a burger
   if you use this and we meet someday (Beefware license).
**********************************************************************/
#include <SPI.h>
#include "wiring_private.h" // pinPeripheral() function
#define CSPIN 6
#define Time 25

//D3-MISO, D4-MOSI, D5-SCK
SPIClass SPI2 (&sercom2, 3, 5, 4, SPI_PAD_0_SCK_3, SERCOM_RX_PAD_1); 

int i = 0;
void setup() {
  // put your setup code here, to run once:

  //Get all pins and SPI ports setup
  SPI2.begin();
  pinPeripheral(3, PIO_SERCOM_ALT);
  pinPeripheral(4, PIO_SERCOM_ALT);
  pinPeripheral(5, PIO_SERCOM);
  pinMode(CSPIN, OUTPUT);
  digitalWrite(CSPIN, HIGH); //make sure it is high to start
  SPI2.setClockDivider(SPI_CLOCK_DIV128); //Slow down the master a bit

  //Reset the screen, set backlight, etc.
  digitalWrite(CSPIN, LOW);// Select the screen before sending
  SPI2.transfer('|');//Setting character
  SPI2.transfer('-');//Clear display
  SPI2.transfer('|');//Put LCD into setting mode
  SPI2.transfer(158 + 0); //Set green backlight amount to 0%
  SPI2.transfer('|');//Put LCD into setting mode
  SPI2.transfer(188 + 10); //Set blue backlight amount to 0%
  SPI2.transfer('|');//Put LCD into setting mode
  SPI2.transfer(128 + 5); //Set white/red backlight amount to (15=51% 100%=+29)
  delay(Time);
  digitalWrite(CSPIN, HIGH);// Deselect the screen after sending
  delay(1000);//Each setting change prints an output, this delay allows them to be printed before trying to keep going

  //Send Welcome Text
  char tempString[50]; //Needs to be large enough to hold the entire string with up to 5 digits
  sprintf(tempString, "Welcome ");
  spiSendString(tempString);
  delay(1500);
}

void loop() {    
  // put your main code here, to run repeatedly:

  //Clear the screen, then send the Counting string
  digitalWrite(CSPIN, LOW);// Select the screen before sending
  SPI2.transfer('|');//Setting character
  SPI2.transfer('-');//Clear display
  delay(Time);
  digitalWrite(CSPIN, HIGH);// Deselect the screen after sending
  char tempString[50]; //Needs to be large enough to hold the entire string with up to 5 digits
  sprintf(tempString,"Counting: %d ", i);
  spiSendString(tempString);
  i++;
  delay(1000-Time);

}

//Sends a string over SPI
void spiSendString(char* data)
{
  digitalWrite(CSPIN, LOW); //Drive the CS pin low to select OpenLCD
  for(byte x = 0 ; data[x] != '\0' ; x++) //Send chars until we hit the end of the string
    SPI2.transfer(data[x]);
  digitalWrite(CSPIN, HIGH); //Release the CS pin to de-select OpenLCD
}