I've a device which acts as a SPI Master and sends data over to my microcontroller which is acting as a slave. Simple, I thought, there'll be some stuff in the Arduino SPI library that will help with that. But no, that's just for the master case. When I googled it, I found many examples like this one https://gist.github.com/chrismeyersfsu/3317769 from Chris Meyers which uses the SPCR and SPDR registers and an ISR interupt to process the data. The problem is, that all three of these things are AVR specific so don't exist on the Arduino MKR Zero which is SAMD21 based.
Sercom
Digging further, I found that the SAMD range of microcontrollers use a serial communication module or Sercom which can implement several types of communication.
The SAM D21 chip has 6 of these SERCOM modules available. However, these do have some default configurations such as use by the onboard SDCard reader.
The default configuration for the MKR Zero is as follows:
Sercom | Use |
---|---|
0 | I2C |
1 | SPI |
2 | SPI for SDCard |
3 | Available |
4 | Avalable |
5 | Serial 1 / Programming debugging port |
The SPI pins are also defined as follows:
#define PIN_SPI_MISO (10u) #define PIN_SPI_MOSI (8u) #define PIN_SPI_SCK (9u) #define PIN_SPI_SS (4u)
So it looks like Sercom1 is configured against some default pins we can use and for the right type of communication.
SPI Slave
The Microchip example code for the C21 Microcontroller has a clear definition of what we need to do to initialise the SERCOM peripheral as an SPI slave. If we cross reference this against the SPI library, we should be able to get it working.
https://microchipdeveloper.com/32arm:samc21-code-gcc-sercom-spi-slave-example
* SERCOM PERIPHERAL INITIALIZATION MUST PERFORM THE FOLLOWING FUNCTIONS:
- CONFIGURE THE SERCOM SPI PINS USING THE PMUX
- PROVIDE A BUS CLOCK TO THE PERIPHERAL USING MCLK
- PROVIDE A GENERIC GLOCK TO THE PERIPHERAL USING GCLK
- CONFIGURE THE SERCOM SPI CONTROL REGISTERS FOR SPI SLAVE OPERATION
- CONFIGURE INTERRUPTS
- ENABLE MODULE
That code also goes into the details of what is needed.
Pin Multiplexing
Many modern micro-controllers have a pin multiplexer. This allows you to map different functionality to different pins. However there is not a totally free option of which pins go where so
This is expressed in the following tables from the Variants.cpp file. So for example, if we look at Pins 8,9 and 10 we see that they can be mapped to Sercom1 or Sercom3. But if we look for pad2 which is the slave select, then on the MKR board, only Sercom3 is available on the output pins. So it looks like Sercom3 / peripheral D is the combination we need with Pins 6, 8, 9 and 10.
+------------+------------------+--------+-----------------+--------+-----------------------+---------+---------+--------+--------+----------+----------+ | Pin number | MKR Board pin | PIN | Notes | Peri.A | Peripheral B | Perip.C | Perip.D | Peri.E | Peri.F | Periph.G | Periph.H | | | | | | EIC | ADC | AC | PTC | DAC | SERCOMx | SERCOMx | TCCx | TCCx | COM | AC/GLCK | | | | | |(EXTINT)|(AIN)|(AIN)| | | (x/PAD) | (x/PAD) | (x/WO) | (x/WO) | | | +------------+------------------+--------+-----------------+--------+-----+-----+-----+-----+---------+---------+--------+--------+----------+----------+ | 00 | D0 | PA22 | | *06 | | | X10 | | 3/00 | 5/00 |* TC4/0 | TCC0/4 | | GCLK_IO6 | | 01 | D1 | PA23 | | *07 | | | X11 | | 3/01 | 5/01 |* TC4/1 | TCC0/5 | USB/SOF | GCLK_IO7 | | 02 | D2 | PA10 | | 10 | *18 | | X02 | | 0/02 | 2/02 |*TCC1/0 | TCC0/2 | I2S/SCK0 | GCLK_IO4 | | 03 | D3 | PA11 | | 11 | *19 | | X03 | | 0/03 | 2/03 |*TCC1/1 | TCC0/3 | I2S/FS0 | GCLK_IO5 | | 04 | D4 | PB10 | | *10 | | | | | | 4/02 |* TC5/0 | TCC0/4 | I2S/MCK1 | GCLK_IO4 | | 05 | D5 | PB11 | | *11 | | | | | | 4/03 |* TC5/1 | TCC0/5 | I2S/SCK1 | GCLK_IO5 | | 06 | D6 | PA20 | | *04 | | | X08 | | 5/02 | 3/02 | |*TCC0/6 | I2S/SCK0 | GCLK_IO4 | | 07 | D7 | PA21 | | *05 | | | X09 | | 5/03 | 3/03 | |*TCC0/7 | I2S/FS0 | GCLK_IO5 | +------------+------------------+--------+-----------------+--------+-----+-----+-----+-----+---------+---------+--------+--------+----------+----------+ | | SPI | | | | | | | | | | | | | | | 08 | MOSI | PA16 | | *00 | | | X04 | | *1/00 | 3/00 |*TCC2/0 | TCC0/6 | | GCLK_IO2 | | 09 | SCK | PA17 | | *01 | | | X05 | | *1/01 | 3/01 | TCC2/1 | TCC0/7 | | GCLK_IO3 | | 10 | MISO | PA19 | | 03 | | | X07 | | *1/03 | 3/03 |* TC3/1 | TCC0/3 | I2S/SD0 | AC/CMP1 | +------------+------------------+--------+-----------------+--------------------+-----+-----+---------+---------+--------+--------+----------+----------+ | | Wire | | | | | | | | | | | | | | | 11 | SDA | PA08 | | NMI | *16 | | X00 | | *0/00 | 2/00 | TCC0/0 | TCC1/2 | I2S/SD1 | | | 12 | SCL | PA09 | | 09 | *17 | | X01 | | *0/01 | 2/01 | TCC0/1 | TCC1/3 | I2S/MCK0 | | +------------+------------------+--------+-----------------+--------+-----+-----+-----+-----+---------+---------+--------+--------+----------+----------+ | | Serial1 | | | | | | | | | | | | | | | 13 | RX | PB23 | | 07 | | | | | | *5/03 | | | | GCLK_IO1 | | 14 | TX | PB22 | | 06 | | | | | | *5/02 | | | | GCLK_IO0 | +------------+------------------+--------+-----------------+--------+-----+-----+-----+-----+---------+---------+--------+--------+----------+----------+ | 15 | A0 / DAC0 | PA02 | | 02 | *00 | | Y00 | OUT | | | | | | | | 16 | A1 | PB02 | | *02 | *10 | | Y08 | | | 5/00 | | | | | | 17 | A2 | PB03 | | *03 | *11 | | Y09 | | | 5/01 | | | | | | 18 | A3 | PA04 | | 04 | *04 | 00 | Y02 | | | 0/00 |*TCC0/0 | | | | | 19 | A4 | PA05 | | 05 | *05 | 01 | Y03 | | | 0/01 |*TCC0/1 | | | | | 20 | A5 | PA06 | | 06 | *06 | 02 | Y04 | | | 0/02 | TCC1/0 | | | | | 21 | A6 | PA07 | | 07 | *07 | 03 | Y05 | | | 0/03 | TCC1/1 | | I2S/SD0 | | +------------+------------------+--------+-----------------+--------+-----+-----+-----+-----+---------+---------+--------+--------+----------+----------+ | | USB | | | | | | | | | | | | | | | 22 | | PA24 | USB N | 12 | | | | | 3/02 | 5/02 | TC5/0 | TCC1/2 | USB/DM | | | 23 | | PA25 | USB P | 13 | | | | | 3/03 | 5/03 | TC5/1 | TCC1/3 | USB/DP | | | 24 | | PA18 | USB ID | 02 | | | X06 | | 1/02 | 3/02 | TC3/0 | TCC0/2 | | AC/CMP0 | +------------+------------------+--------+-----------------+--------+-----+-----+-----+-----+---------+---------+--------+--------+----------+----------+ | 25 | AREF | PA03 | | 03 | 01 | | Y01 | | | | | | | | +------------+------------------+--------+-----------------+--------+-----+-----+-----+-----+---------+---------+--------+--------+----------+----------+ | | SD SPI | | | | | | | | | | | | | | | 26 | | PA12 | SD SCK | 12 | | | | | *2/00 | 4/00 | TCC2/0 | TCC0/6 | | AC/CMP0 | | 27 | | PA13 | SD MOSI | 13 | | | | | *2/01 | 4/01 | TCC2/1 | TCC0/7 | | AC/CMP1 | | 28 | | PA14 | SD SS | 14 | | | | | 2/02 | 4/02 | TC3/0 | TCC0/4 | | GCLK_IO0 | | 29 | | PA15 | SD MISO | 15 | | | | | *2/03 | 4/03 | TC3/1 | TCC0/5 | | GCLK_IO1 | | 30 | | PA27 | SD CD | 15 | | | | | | | | | | GCLK_IO0 | +------------+------------------+--------+-----------------+--------+-----+-----+-----+-----+---------+---------+--------+--------+----------+----------+ | 31 | | PA28 | BOTTOM PAD | 08 | | | | | | | | | | GCLK_IO0 | | 32 | | PB08 | LED_BUILTIN | 08 | 02 | | Y14 | | | 4/00 | TC4/0 | | | | | 33 | | PB09 | ADC_BATTERY | *09 | 03 | | Y15 | | | 4/01 | TC4/1 | | | | +------------+------------------+--------+-----------------+--------+-----+-----+-----+-----+---------+---------+--------+--------+----------+----------+ | | 32768Hz Crystal | | | | | | | | | | | | | | | 34 | | PA00 | XIN32 | 00 | | | | | | 1/00 | TCC2/0 | | | | | 35 | | PA01 | XOUT32 | 01 | | | | | | 1/01 | TCC2/1 | | | | +------------+------------------+--------+-----------------+--------+-----+-----+-----+-----+---------+---------+--------+--------+----------+----------+
Unfortunately that's as far as I got. Not knowing if my device was faulty or if my SPI Slave was faulty.
I found a UART image that could be used on the device instead so I've swapped to that instead.
Additional Reference
This thread also has a bunch of people who were trying to get this to work. https://forum.arduino.cc/index.php?topic=360026.0
That thread also reference the NVIC or Nested Vectored Interrupt Controller which is covered in more details here http://www.lucadavidian.com/2017/08/08/arduino-m0-pro-le-interfacce-seriali-e-gli-interrupt/
I also found a good example from Adafruit about adding more SPI ports if you wanted to have several master ports on your device. https://learn.adafruit.com/using-atsamd21-sercom-to-add-more-spi-i2c-serial-ports/creating-a-new-spi
That in turn points to the board manager file for the SAMD21 https://github.com/arduino/ArduinoCore-samd which contains the pin mappings and macros in variant.cpp and variant.h