There are some quite good explanations on how to add a serial communication interface on a SAM D21 based board. I will link three from "big players" in the IoT world: Arduino: Adding more Serial Interfaces to SAMD microcontrollers, Sparkfun: Adding More SERCOM Ports for SAMD Boards and Adafruit: Using ATSAMD21 SERCOM for more SPI, I2C and Serial ports. They are certainly worth reading, but they are not germane to the Seeeduino XIAO. I will explain why and how to add a supplementary SPI, I²C or USART port on the XIAO if it is possible to forego one of the other predefined ports.
Thanks to Elaine Wu who included a link to this page in the Tutorials section of her blog entitled Seeeduino XIAO Resources Roundup. What will be your next project idea made by Seeeduino XIAO?.
Table of contents
- SAM D21 Serial Communication Interfaces
- Seeeduino XIAO SERCOM
- Two I²C Ports on the Seeeduino XIAO
- Modifying the XIAO
variant
Files - Using the Modified XIAO
variant
in PlatformIO - Using the Modified XIAO
variant
in the Arduino IDE - Everything but the Kitchen Sink
- A Word or Two to the Wise
SAM D21 Serial Communication Interfaces
Here is a very clear overview of the architecture of serial peripherals on SAM D21 microcontrollers by Atmel found in a 2015 application note AT11628: SAM D21 SERCOM I2C Configuration.
Generally microcontrollers will have separate serial communication modules with different pinouts for each module. Separate dedicated peripherals and user registers will be available for each module. For example USART will be a separate peripheral with dedicated pins for its function and I²C will be a separate peripheral with its own dedicated pins.
In SAM D microcontrollers, all the serial peripherals are designed into a single module as serial communication interface (SERCOM). A SERCOM module can be either configured as USART or I²C or SPI selectable by user. Each SERCOM will be assigned four pads from PAD0 to PAD3. The functionality of each pad is configurable depending on the SERCOM mode used. Unused pads can be used for other purpose and the SERCOM module will not control them unless it is configured to be used by the SERCOM module.
For example, SERCOM0 can be configured as USART mode with PAD0 as transmit pad and PAD1 as receive pad. Other unused pads (PAD2 and PAD3) can be either used as GPIO pins or can be assigned to some other peripherals. The assignment of SERCOM functionality for different pads is highly flexible making the SERCOM module more advantageous compared to the typical serial communication peripheral implementation.There are six serial communication interfaces (SERCOMx, x=0,...,5) on the SAM D21 (the SAM D51 has more interfaces). Each SERCOM can handle any one of four protocols: classic serial communication (full-duplex USART or half-duplex single wire), I²C (2 or 4 wire, master or slave), SPI (with optional harware slave select) or Lin (slave). I had to look up LIN (Local Interconnect Network) which was not mentioned in the 2015 note. It turned out to be a serial communication protocol used in vehicles. Furthermore, not only can the protocol of a serial communication interface be changed, but the four "pads" of each SERCOM can be associated with a primary set of four pins and with a second, alternate, set of pins. Actually, SERCOM 4 has two and SERCOM 5 has three complete sets of alternate pad assignments while SERCOM3 has a partial second alternate assignment. This is often referred to as multiplexing. The two tables below present the pertinent information about the serial interfaces in Table 7-1. PORT Function Multiplexing for SAM D21 A/B.CD Variant Devices and SAM DA1 A/B Variant Devices.
Interface | Pads | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 2 | 3 | 0' | 1' | 2' | 3' | 0" | 1" | 2" | 3" | |
SERCOM0 | PA08 | PA09 | PA10 | PA11 | ||||||||
ALT-SERCOM0 | PA04 | PA05 | PA06 | PA07 | ||||||||
SERCOM1 | PA16 | PA17 | PA18 | PA19 | ||||||||
ALT-SERCOM1 | PA00 | PA01 | PA30 | PA31 | ||||||||
SERCOM2 | PA12 | PA13 | PA14 | PA15 | ||||||||
ALT-SERCOM2 | PA08 | PA09 | PA10 | PA11 | ||||||||
SERCOM3 | PA22 | PA23 | PA24 | PA25 | ||||||||
ALT-SERCOM3 | PA16 | PA17 | PA18 | PA19 | PA20 | PA21 | ||||||
SERCOM4 | PB12 | PB13 | PB14 | PB15 | ||||||||
ALT-SERCOM4 | PA12 | PA13 | PA14 | PA15 | PB08 | PB09 | PB10 | PB11 | ||||
SERCOM5 | PB16 | PB17 | PA20 | PA21 | ||||||||
ALT-SERCOM5 | PA22 | PA23 | PA24 | PA25 | PB02 | PB03 | PB00 | PB01 | PB30 | PB31 | PB22 | PB23 |
Be careful when interpreting the data in the tables. SERCOM0 and ALT-SERCOM0 refer to the same serial interface; the ALT is just an indication that the interface pads are multiplexed onto an alternate set of pins.
There are additional constraints. Only 8 pins (PA08, PA09, PA12, PA13, PA16, PA17, PA22 and PA23) support I²C. Note that these connect to pads 0 and 1 of SERCOM 0, 1, 2 and 3 respectively because the data (SDA) signal must be mapped to pad 0 of a SERCOM, while the clock signal (SCL) must be mapped to pad 1 (reference: Table 7-5. SERCOM Pins Supporting I²C. When it comes to USARTs, the TX line must be mapped to either pad 0 or pad 2 of serial interface on the SAM D21 but on the SAM D51 family TX can only be on pad 0 (Reference: Creating a new Serial in Using ATSAMD21 SERCOM for more SPI, I2C and Serial ports By lady ada).
Seeeduino XIAO SERCOM
Of course the flexibility is much reduced with the Seeeduino XIAO which brings out only 11 pins. Here is the relevant information from the previous table, using the Arduino pin labels.
Seeedino XIAO | |||||
---|---|---|---|---|---|
Interface | SERCOM Pads | Default Protocol | |||
0 | 1 | 2 | 3 | ||
SERCOM0 | A4 | A5 | A2 | A3 | |
ALT-SERCOM0 | A1 | A9 | A10 | A8 | SPI |
ALT-SERCOM2 | A4 | A5 | A2 | A3 | I²C |
ALT-SERCOM4 | A6 | A7 | USART |
As can be seen the XIAO firmware creates three serial communication interfaces, USART, I²C and SPI, with three of the SERCOMs. Had Seeed Studio decided to use SERCOM0 with its primary pad assignment for the I²C bus, then it would not have been possible to have a SPI bus since SERCOM0 and ALT-SERCOM0 are, as stated before, the same serial communication interface. Here is representation of the serial interfaces based on the physical layout of the XIA0.
Seeedino XIAO | ||||||
---|---|---|---|---|---|---|
Function | SERCOM PADS | PINS | SERCOM PADS | Function | ||
DAC | A0 | 5V | ||||
ATL-SERCOM0_PAD0 | A1 | GND | ||||
SERCOM0_PAD2, ALT-SERCOM2_PAD2 | A2 | 3.3V | ||||
SERCOM0_PAD3, ALT-SERCOM2_PAD3 | A3 | A10 | ALT-SERCOM0_PAD2 | MOSI | ||
SDA | ALT-SERCOM2_PAD0 | A4 | A9 | ALT-SERCOM0_PAD1 | MISO | |
SCL | ALT-SERCOM2_PAD1, SERCOM0_PAD1 | A5 | A8 | ALT-SERCOM0_PAD3 | SCK | |
TX | ALT-SERCOM4_PAD0 | A6 | A7 | ALT-SERCOM4_PAD1 | RX |
There are two other serial connections:
- The USB port which is mapped to the
Serial
(=SerialUSB
) device. - The Single Wire Debug (SWD) interface which is brought out as two solder pads on the back of the XIAO ().
Let’s try to add these to our table.
Serial Interface | Pad | Protocol | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 2 | 3 | ||||||||||
ALT-SERCOM0 | PA04 | A1 | - | PA05 | A9 | MISO | PA06 | A10 | MOSI | PA07 | A8 | SCK | SPI |
ALT-SERCOM2 | PA08 | A4 | SDA | PA09 | A5 | SCL | PA10 | A2 | - | PA11 | A3 | - | I²C |
ALT-SERCOM4 | PB08 | A6 | RX | PB09 | A7 | TX | USART | ||||||
ALT-SERCOM1 | PA30 | SWCLK | PA31 | SWDIO | SWD | ||||||||
SERCOM3 / ALT-SERCOM5 ?? | PA24 | D- | PA25 | D+ | USB |
A blank cell means that the corresponding pad is connected to a pin that is not brought out on the XIAO, while a dash '-' means that the pin is available but the pad is not used and therefore not connected to the pin. Those pins can thus be used for other purposes.
The SWD needs to be used in conjunction with a debug probe which costs an order of magnitude more than the XIAO at a minimum. The SWD pins are shown as multiplexed to SERCOM1, but I cannot find any mention of that in the variant.cpp
file for the XIAO which is part of the SeeedStudio SAMD Arduino core. However it is in the arduino_zero variant.cpp file in the same repository. Elsewhere, I show that the pins used for the SWD pin multiplexed to SERCOM1 can be used for as a two wire (TX and RX) serial interface.
The USB connection uses pins PA24 and PA25. These could be connected as pads 2 and 3 respectively of either SERCOM3 or ALT-SERCOM5. Even though the USB port is mapped to a serial device, it is independent of the SERCOM interfaces. I seem to recall reading in an application note that if the USB stack is loaded, then the two pins (or rather three pins because there's a third enable pin) are exclusively used for that purpose and cannot be reassigned. That is why removing the declarations of SERCOM3 and SERCOM5 changes absolutely nothing. A blink sketch that also writes "ON" and "OFF" to Serial
while toggling the state of a LED works just as well as when the two SERCOMs were declared. Presumably, if one did not need to use the USB stack, it could be disabled and the USB connector could be used as a TTL level serial port. Because I currently do not have an ICE probe which would be needed to reload the bootloader, I have no intention of investigating this topic any further.
Forgetting about the USB and SWD pins, the XIAO exposes three, or more precisely two and a half, serial interfaces: SERCOM0, SERCOM2 and (half of) SERCOM4. So if more than one SPI or USART channel is needed, all that can be done is to modify use configuration of a preexisting interface. Given that the only "legal" (more on that in the next section) I²C pin assignment available on the XIAO is with SERCOM2 and that a full-duplex SPI connection cannot be set up on SERCOM4, there are 6 ways to assign the mode of the three available SERCOM, assuming that all three are used.
Protocols | SERCOM0 | SERCOM2 | SERCOM4 |
---|---|---|---|
3×USART | USART | USART | USART |
2×SPI + 1×USART | SPI | SPI | USART |
2×USART + (1×I²C or 1×SPI) | USART | I²C | USART |
SPI | USART | USART | |
USART | SPI | USART | |
1×SPI + 1×I²C + 1×USART | SPI | I²C | USART |
There's not much to be gained from interchanging the SPI and USART assignments, so in effect there are potentially four other ways of defining the use of SERCOM0 and SERCOM2: 2 USART, 2 SPI, SPI+USART USART+I²C. I must admit here that I have only tested the two USART + I²C configuration. Just to complicate things, I will mention that it might be possible to set up a half SPI interface on SERCOM4 with just a SCLK and MISO or MOSI signal. That's another possibility that I have not investigated.
Two I²C Ports on the Seeeduino XIAO
Before diving into the details of the serial communication interfaces on the SAM D21 chip, I forged ahead and connected two I²C devices to the XIAO without realizing that this was not recommended. The "standard" I²C pins A4 and A5 (SDA and SCL respectively) are used to drive an I²C OLED display. The second I²C port is used to connect the XIA to a Raspberry Pi as a slave I²C device. Since the default I²C interface is on SERCOM2, the second port can be on SERCOM0 (SDA on A1, SCL on A9) sacrificing the SPI interface or on SERCOM4 (SDA on A6, SCL on A7) sacrificing the USART interface. The two circuits are shown below. Note how the XIAO and display are powered from the 5 volt output of the Pi.
The funny thing is that this worked (albeit with a slight problem to be discussed). So for those willing to break the rules, the set of possibilities is much larger.
Protocols | SERCOM0 | SERCOM2 | SERCOM4 |
---|---|---|---|
3×USART | USART | USART | USART |
3×I²C | I²C | I²C | I²C |
2×SPI + (1×USART or 1×I²C) | SPI | SPI | USART |
SPI | SPI | I²C | |
2×I²C + (1×USART or 1×SPI) | I²C | I²C | USART |
SPI | I²C | I²C | |
2×USART + (1×SPI or 1×I²C) | USART | I²C | USART |
SPI | USART | USART | |
1×SPI + 1×I²C + 1×USART | SPI | I²C | USART |
It must be stressed that most of these combinations have not been thoroughly tested. The example discussed here was done on a breadboard with short connections between the Pi, the XIAO and the OLED display. The I²C channel between the Pi and the XIAO required very little bandwidth; there was an exchange of only two bytes every five seconds. So setting up a second I²C interface on the XIAO in any sort of "production" setting needs to be thoroughly investigated. Nevertheless, the example is interesting from the programming point of view.
Here is a sketch that replaces the SPI protocol on SERCOM0 or the USART channel on SERCOM4 with a second I²C instance.
This sketch can be downloaded by clicking on this link: two_hdw_i2c.ino. A Python script such as light_sensor.py
described in I²C Light Sensor using a Seeeduino XIAO On the Raspberry Pi will display the data generated by the sketch.
Note how easy it was to set up the second I²C instance in lieu of the SPI instance on SERCOM0:
- Create a
TwoWire
instance specifying the serial communication interface and the pad 0 and pad 1 connections:TwoWire myWire(sercom0, A1, A9)
. - Set the
TwoWire onService
method as the SERCOM0 interrupt handler. - Start the
TwoWire
object in slave mode specifying the I²C address:myWire.begin(4);
. - Assign the physical pins to the serial interface:
pinPeripheral(A1, PIO_SERCOM_ALT); pinPeripheral(A2, PIO_SERCOM_ALT);
.
Unfortunately, when the very same thing is attempted, except for adding the extra I²C instance on SERCOM4, there is a linking problem.
Indeed we do find the following assignment in .../packages/Seeeduino/hardware/samd/1.7.0/variants/XIAO_m0/variant.cpp
.
If those few lines are removed from the variant.cpp
file, then the sketch will compile and run correctly as long as the OLED SDA and SCL pins are now connected to pins A6 and A7 of the XIAO. The lesson learned from this experiment is that it was necessary to do some "surgery" to the variant.cpp
file to prevent the creation of the Serial1
object and assignment of its interrupt handler.
Modifying the XIAO variant
Files
variant.h
, variant.cpp
and boards.txt
as explained in this section. Consequently, when version 1.8.3 of core is used, then Serial1
can be disabled or enabled with the additional SERCOM4
option for the "Seeeduino XIAO"
in the Tools menu
.
Many thanks to Geoff Evans (geocom) who took on the task of submitting a pull request for these changes, and thanks to the Seeed Studio SAMDx1 Arduino Core team for committing that change to the core.
Wholesale removal of the Serial1 Uart
from the variant
files is not the best way to go. What if the USART is needed in another project? Setting up 9 different variant.h
and variant.cpp
files to take care of all the possible permutations of serial protocols on the XIAO is not practical. Then I thought of using macros to select one of the nine possibilities outlined above, but that's just as awkward. Instead, I decided to add a single macro NO_USART_INTERFACE
to excise the Serial1
object when another type of serial interface is needed on SERCOM4. As shown in the above sketch, it is always possible to modify the serial instance of SERCOM0 and SERCOM2 if needed.
Here is the last part of the variant.h
file with the preprocessor macro appearing twice to avoid moving the couple of lines defining the SERIAL_PORT_HARDWARE
and SERIAL_PORT_HARDWARE_OPEN
macros to the first appearance of NO_USART_INTERFACE
.
The changes to variant.cpp
is straightforward.
Of course, the NO_USART_INTERFACE
macro is not defined in these files because it needs to be defined only when needed.
Using the Modified XIAO variant
Files in PlatformIO
variant
files in the <.platformio>/packages/framework-arduino-samd/variants/XIAO_m0/
directory will have to be done manually in order to use the NO_USART_INTERFACE
build flag to disable Serial1
as described below.
In PlatformIO (see "Hello XIAO" in PlatformIO), those changes to the two variant
files in the <.platformio>/packages/framework-arduino-samd/variants/XIAO_m0/
directory are just about the only thing needed. The two_hdw_i2c
sketch will compile and run in PlatformIO using SERCOM4 (remove or comment out the USE_SERCOM0
macro in the sketch) with the following platformio.ini
configuration file.
Using the Modified XIAO variant
Files in the Arduino IDE
boards.txt
file as described in this section. With this new version of the core, Serial1
can be disabled or enabled with the additional SERCOM4
option for the "Seeeduino XIAO" in the Tools
menu. The screen capture below shows how the serial port is disabled.
It is a bit more complicated to use the modified variant
file in the Arduino IDE. The simplest is to add a platform.local.txt
file in the .../arduino-1.8.10/portable/packages/Seeeduino/hardware/samd/1.7.2/
directory alongside the platform.txt
file to override a number of directives in the later file.
This works, but it is not very convenient because this configuration file is in force at the board level. Something equivalent to the platformio.ini
file which is in effect at the sketch level only would be preferable. The best I could come up with was adding an option in the Tools
menu for the XIAO board.
Setting SERCOM4
to "None" instead of "USART" means that the NO_USART_INTERFACE
macro will be defined. Adding this menu option requires changes to the boards.txt
file.
- The new menu entry was added at the start of the
boards.txt
. The part to the right of the equal sign is the option text displayed in theTools
IDE menu.menu.sercom4=SERCOM4 - The possible option values were added at the end of the
Seeed XIAO M0 (SAMD21)
definition.seeed_XIAO_m0.menu.sercom4.include=USART seeed_XIAO_m0.menu.sercom4.include.sercom4_flag= seeed_XIAO_m0.menu.sercom4.exclude=None seeed_XIAO_m0.menu.sercom4.exclude.sercom4_flag=-DNO_USART_INTERFACEThe first and third lines define the two values, "USART" and "None", which appear in the
SERCOM4
submenu. The second and fourth line take care of assigning a value tosercom4_flag
depending on which choice is made. That value will either be an empty string or the compiler directive-DNO_USART_INTERFACE
. - The
sercom4_flag
variable is added to theseeed_XIAO_m0.build.extra_flags=
line which is found about 15 lines above the added menu choices.seeed_XIAO_m0.build.extra_flags= -DARDUINO_SAMD_ZERO -D__SAMD21__ -D__SAMD21G18A__ -DARM_MATH_CM0PLUS -DSEEED_XIAO_M0 {build.usb_flags} {sercom4_flag}
These changes are better than manually editing the platform.local.txt
file but definitely not the best solution. And that's because the IDE does not save the menu choices for each sketch, but only for the last sketch edited in the IDE.
Everything but the Kitchen Sink
If you like the idea of modifying the variant.h
file to accommodate all the possible modes that can be assigned to the 3 SERCOM interfaces, including not assigning any serial interface, then take a look at Use the Seeeduino XIAO with Multiple UART Ports by gears-computer-workshop.
The site is in Japanese, but Google Translate does a good job of translating into English, or French for that matter. Besides, the code is easily understood.
A Word or Two to the Wise
I'll end with two warnings.
- Things are not as simple as portrayed above. There is another "gotcha" when an attempt is made to assign an "unusual" pin to a SERCOM pad. Said in another way, the default GPIO pin configuration in
variant.cpp
is not as flexible as the SAM D21/D51 architecture allows. Actually, I got that all wrong and it's not that complicated at all to use any GPIO pin configuration that the microcontroller architecture allows. It's all explained in a follow on post: Three, Nay, Four Hardware Serial Ports on a SAM D21 XIAO. - In the first version of this post, I included modified versions of
variant.h
,variant.cpp
andboards.txt
. But I decided against doing that because all modified files will be overwritten when the board definition is updated. How do I know? While writting the first version of this post on May 4, 2020, the Arduino core has been updated from version 1.7.0 to 1.7.1 and then 1.7.2 by Seeed Studio. As of March 2022 the version is up to 1.8.1. So if you modified these files according to the above suggestions, be careful when updating the board definition in the Arduino IDE or the platform definition in PlatformIO. You will have to modify the new versions of the files installed with the update. Do not assume that the old modified versions ofvariant.h
,variant.cpp
andboards.txt
will work with the new core. Of course, this applies to changes proposed by gears-computer-workshop also.