The Seeed Studio XIAO SAMD21 (formerly known as the Seeeduino XIAO) arrived in my mail box on March 18, 2020. About two weeks later, I published the first version of this post. After making some corrections in the following month, the post remained unchanged until the start of 2022 when I started correcting some parts of the post in view of recent developments and things learned. While adding more and more corrections I became dissatisfied with the original post and decided that a major rewrite was in order. So this is an opportunity to clarify some things and to look at some topics not covered before such as the capacitance touch inputs and the hardware watchdog.
This post is not for the neophyte that has never used the Arduino framework. If you are in that situation, welcome to the fascinating world of microcontroller enthusiasts, and let me suggest that you start with the Seeed Studio XIAO Starter Kit Courses or the original Seeeduino XIAO Free Course by Seeed Studio. Once you have worked through the course, you may want to return to this page if you are curious about some particular topics.
The author of this post is a hobbyist who has tinkered with classic Atmel AVR, STM32 and ESP32 microcontrollers and has somewhat more experience with ESP8266 based development boards. That background obviously tainted the first encounter with the SAM D21 microcontroller found on the Seeed Studio board and it explains some references to the ESP. There were mistakes in the first version of this overview, but welcomed help from readers, notably D. J. Park, made it possible to correct some of the more egregious errors. No doubt, old errors remain while new ones were introduced in this new version. Corrections and suggestions are heartily sought; there is an email link at the bottom of the page for that purpose. Questions adressed to me are answered as best a "non expert" can.
Apologies for the length of this introduction, but that is in keeping with the length of this page which is one of the longest on this site where long posts abound. Hopefully, the table of content will be of use for those looking for a particular topic.
Table of contents
- The XIAO Form Factor
- The SAM D21 Microcontroller
- Meet Some Seeed Studio XIAO SAMD21 Competitors
- Web Resources
- Development Environments
- Uploading/Bootloader/COM Port/Reset Problem
- Only a Blink Sketch, But Do Read Me
- A Proper
setup()
Function - XIAO SAMD21 Input Output Pins
- Analogue Input and Output
- Digital Input and Output
- Interrupts
- Timers
- Watchdog
- Serial Communication
The XIAO Form Factor
When SeeedStudio announced the XIAO SAMD21 in late 2019, it was actually launching the first of a family of boards that share a common form factor. Aside from a common size, all the members of the family share a common general pin assignment and a full speed (12Mbsp) USB 2.0 interface with a USB-C type connector. The first four boards in the family have a 32-bit RISC ARM Cortex-M processor from three different chip makers. The latest board is based on the Risc-V architecture. Each member of the complete family is described in the Seeed Studio XIAO Series Web page which also contains a very clear and detailed comparison table. There is another comparison table that is worth looking at.
XIAO (小 in Chinese) means small, as Paul Battley kindly informed me in a private correspondence. There is no doubt that the boards are very small (22x18 mm). Given their tiny size, the two breadboard-compatible headers along the long sides of the board contain only 7 pins each. Three of these are for ground and power (3.3 and 5 volts) while the other 11 pins are I/O pins. All boards share a common Arduino pin numbering and serial connections. By default they all offer one UART, one I²C, and one SPI channel (see the figure below). It is possible to reassign these serial connections in various ways depending on the microprocessor used.
XIAO Model | Architecture | |
---|---|---|
Seeeduino XIAO | ARM Cortex M0+ | link |
Seeed XIAO RP2040 | Dual-core ARM Cortex M0+ | link |
Seeed XIAO BLE nRF52840 | ARM Cortex M4F (M4 with FPU) | link |
Seeed XIAO BLE nRF52840 Sense | ARM Cortex M4F (M4 with FPU) | link |
All boards have pads on the underside. The number and layout of these is not common although the ARM based boards appear to implement the Serial Wire Debug (SWD) interface in similar fashion (not verified). Instead of the SWD interface, the Risc-V XIAO has pads for the JTAG debug interface on the underside.
Hardware Accessories
When purchasing the XIAO SAMD21, it is possible to add three hardware accessories:
- The Seeed Studio XIAO Expansion board which is a carrier board that adds a number of peripherals, including a buzzer, a real-time clock and a OLED display, as well as standard Grove connectors and a header for the Serial Wire Debug interface.
- The Grove Shield for Seeed Studio XIAO which is also a carrier board that adds 8 Grove connectors and battery management.
- The Seeed Studio XIAO Starter Kit which includes the XIAO Expansion board, nine Grove modules and additional components. It is meant to accompany the free Seeed Studio XIAO Series course already recommended above for neophytes.
I have not tested any of these products, so what I am about to say is based on reading the product descriptions and may not be accurate. First of all, I believe the two carrier boards were developed for the XIAO SAMD21 and I am confused about the compatibility of these with other members of the XIAO series. That's because pogo pins are used to connect to some of the pads on the underside of the XIAO, notably the SWD interface. It is unlikely that the debug interface would work on the XIAO ESP32C3 because it uses the JTAG standard instead of SWD. Even my first generation XIAO SAMD21 boards could have problems because they do not have the GND and RST pads just below the SWD pads. So before purchasing any of these accessories, make sure it will meet your expectations.
What's in a name?
A couple of months ago, Seeed Studio posted the following note: product names of the XIAO series might not be consistent enough, which is why we’ve changed the product names of all XIAO series products [so that] they have one unified, consistent name (source). Here is part of the table.
Original Name | New Name |
---|---|
Seeeduino XIAO | Seeed Studio XIAO SAMD21 |
Seeed XIAO RP2040 | Seeed Studio XIAO RP2040 |
Seeed XIAO BLE - nRF52840 | Seeed Studio XIAO nRF52840 |
Seeed XIAO BLE Sense - nRF52840 | Seeed Studio XIAO nRF52840 Sense |
XIAO WiFi/BLE - ESP32C3 | Seeed Studio XIAO ESP32C3 |
The logic behind the names makes sense.
- Manufacturer's name: Seeed Studio
- Product series (or form factor): XIAO
- Product ID (or microcontroller): SAMD21, RP2040, etc.
This new naming scheme is a good idea, as others have been introducing their own names such as XIAO M0 for the Seeeduino XIAO XIAO SAMD21. However, there is no going back for many libraries and packages that have been available for a long time already. Programming environments continue to identify the SAMD21 XIAO as the Seeeduino XIAO for example.
There will be no other mention of the newer members of the XIAO family in this post; XIAO will refer to the SAM D21 based board from now on.
The SAM D21 Microcontroller
The Atmel (now Microchip) SAM D21 is by no means a new microcontroller. It was introduced in 2012 as a low power and high performance 32-bit alternative to the mostly 8 bit devices used for IoT applications at the time. The high performance is relative, the core cannot perform floating point arithmetic in hardware. The same applies to 32-bit integer division and 32-bit integer multiplication with 64 bit results. Nevertheless, the M0+ core is a significant step up from AVR type microcontrollers.
ATTINY85-20MU | ATMEGA328P-MUR | ATSAMD21G18A-MU | |
---|---|---|---|
Core Size (bits) | 8 | 32 | |
Speed (MHz) | 20 | 48 | |
Flash Memory (K bytes) | 8 | 32 | 256 |
RAM (K bytes) | 0.5 | 2 | 32 |
I/O | 6 | 23 | 38 |
ADC (number/bits) | 4/10 | 8/10 | 14/12 |
DAC (number/bits) | 0 | 1/10 | |
Connectivity | I²C, SPI, UART | I²C, SPI, UART, LIN, USB | |
Unit price ($US) | 0.95 | 2.08 | 3.00 |
Prices were obtained from Digikey on March 20, 2020. At just under $5.00 from Seeed Studio, the XIAO looks like a bargain. I very much doubt one could purchase the CPU, an external crystal, a voltage regulator, four LEDs, various capacitors and resistors, metal can and a USB-C connector to make a similar board any cheaper. You could try, the schematic and more technical information is available in the Hardware Development Kit.
Meet Some Seeed Studio XIAO SAMD21 Competitors
The Seeeduino XIAO is not the first board based on the SAM D21 microprocessor. The major players in the hobbyist world have numerous boards in production:
- Arduino,
- See the Zero, MKR Zero and MKR 1000.
- Adafruit, and
- See the METRO M0, Feather M0 Basic Proto and the many other Feather devices based on the same processor.
- Sparkfun
- See the Redboard Turbo, SAMD21 Dev Breakout and SAMD21 Mini Breakout.
However the most direct competitor is the Adafruit QT Py. It has the same form factor as the XIAO, uses almost the same microcontroller (ATSAMD21E18 vs the ATSAMD21G18 on the XIAO), and adds some additional features such as an onboard Stemma QT connector for the I²C bus and solder pads on the back side for an optional SPI flash chip. The SWD interface is made available as two pads on the back side of the board just like the XIAO, but their location appears to be different.
There are others. Freescale (an offshoot of Motorola but now owned by NXP which is itself an offshoot of Phillips) may have been one of the first to offer boards based on the SAM D21. PjRC and avdweb produce small boards based on the same processor. Even Seeed Studio has at least four other SAM D21 based boards on offer (such as the Seeeduino Lotus Cortex-M0+ and Seeeduino LoRaWAN).
Web Resources
Information about the SAMD21 microcontroller, XIAO development board and other boards of the same type is accumulating rapidly on the Internet. There is now much more than can be read by any one person. So here is a partial list of sources that I have found useful.
General Information about the ARM architecture and the SAM D21 microcontroller. These references are quite technical and probably best avoided by those first starting out.
- Joseph Yiu, (September 2016)) Arm Cortex-M for Beginners.
- Microchip, (2020) SAM D21/DA1 Family, Complete Datasheet DS40001882E.
Information by SeeedStudio The information provided by the manufacturer is voluminous in comparison to others although most of it is from third parties. The main problem that I have is that it is dispersed and hence rather difficult to find.
- The catalogue of XIAO parts and the new Seeed Studio XIAO Series page already mentioned.
- The Seeed Studio XIAO SAMD21 product detail page. Scroll down the page to the Documentations section which contains many useful links. I am pleased and honoured to report that a link to this post is included in that page.
- The Seeeduino XIAO Free Course. That archive contains a 124 page PDF manual entitled Seeeduino XIAO in Action Minitype & Wearable Projects Step by Step by Yimeng Shi at SeeedStudio. As mentioned in the introduction, neophytes should begin with that resource. The course assumes that the XIAO SAMD21 is used along with the XIAO Starter Kit. That kit may be a valuable resource for a neophyte. I remember using a similar kit for the Arduino when starting out and it simplified the learning process which is always a bit daunting at first. Hobbyists on the other hand will already have a breadboard or two, Dupont wires and most of the devices found in the kit.
- The SeeedStudio Wiki. Click on
Platform
in the Seeed Wiki on the left of the page and then selectSeeed Studio XIAO
and thenSeeed Studio XIAO SAMD21
. Aside from the introductory page there are 8 other topics. I highly recommend Seeeduino XIAO Get Started By Nanase as a better introduction to the device than my attempt here. - Search the Seeed Studio Forum for XIAO.
- Search the The Seeed Studio Blog for XIAO.
Given the number of boards available, there is good information on the Web about the SAM D21. Adafruit has a tutorial Adafruit Feather M0 Basic Proto, Wanna play with the ARM Cortex M0 chipset? that is useful for XIAO users, particularly the section entitled Adapting Sketches to M0 & M4. The same is true at Sparkfun: RedBoard Turbo Hookup Guide and SAMD21 Mini/Dev Breakout Hookup Guide. The avdweb site by Albert van Dalen also has very useful information although it may be a little difficult to find. I use the search facility to find something I remember having seen previously.
A search of GitHub for "SEEED XIAO" will produce some interesting projects along with chaff. The search criteria "xiao m0" will yield the repository of sketches that accompanies this post as well as other collections of examples. The DroneBot Workshop published a Meet the Seeeduino XIAO blog post along with a YouTube video just yesterday (Dec 1, 2020). The author Bill (William) is a fellow Canadian born in Montréal as I was. Back to the topic, I have a number of additional posts about the XIAO SAMD21:
- "Adding the Seeeduino XIAO in PlatformIO (May 3, 2020). Ignore this post, the XIAO is fully supported in PlatformIO.
- "Hello XIAO" in PlatformIO (June 6, 2020)
- I²C Light Sensor using a Seeeduino XIAO
- Seeeduino XIAO Serial Communication Interfaces (SERCOM) (May 5, 2020)
- The XAIO as a USB-Serial Converter (November 2020)
- Three Nay, Four Hardware Serial Ports on a SAM D21 XIAO (26 March, 2022)
The increasing availability of good information about the XIAO and the SAM D21 microcontroller and other topics is a primary motivator for this second edition of the post.
Development Environments
Purists might want to try to program the XIAO using the Microchip (Atmel) Studio 7 IDE. There is copious documentation on the Microchip site although most appears to be based on the in-house development boards. This IDE is complex and I suspect that the learning curve is rather steep. There is another catch, at least for me, Studio 7 only runs in Windows.
Most readers of this post will probably choose to work with the XIAO in the Arduino framework. The original Arduino core for the SAM D21/D51 microcontrollers is available on GitHub. However it does not contain the board definition for the XIAO. Instead a fork by Seeed Studio must be used. When first introduced, only the Arduino Software (i.e. the Arduino integrated development environment or Arduino IDE) supported the XIAO, but it did not take long before PlatformIO supported the board as well.
There is another framework that can be used to program the XIAO in PlatformIO. Called Zephyr, an "OS [that] is based on a small-footprint kernel designed for use on resource-constrained and embedded systems: from simple embedded environmental sensors and LED wearables to sophisticated embedded controllers, smart watches, and IoT wireless applications." It does not appear to be as user-friendly as the Arduino framework, but that is just an uninformed judgment because I have absolutely no experience with Zephyr. Have a look at the source of its "blinky" sample program to get a little taste.
The programming language in the previous development frameworks is C/C++. Some, especially those that write programs for the Raspberry Pi, may prefer Python. There is a SAM D21 implementation of MicroPython which is largely compatible with Python 3. It is not entirely clear to me how well the XIAO is supported when looking at a recent entry in the forum. Adafruit has created CircuitPython which is a fork of MicroPython
that tries to make the language easier to learn. Unfortunately, Adafruit only supports its own boards but it looks as if the XIAO is supported. Read Installing Circuit Python on Seeeduino Xiao. Andy Warburton makes it look simple to set up and use. MicroPyhton and CircuitPython bring to mind the ESP8266 NodeMCU firmware development framework, only Python is used instead of Lua. I love Python, it takes me back to my first and only computer course which I quickly dropped. I just hated off-by-one column errors in punched cards. Yes, I am that old.
Unfortunately, I do not have any experience with any frameworks other than the Arduino core. Consequently, the sample code, sketches, or projects (the last two terms borrowed from the Arduino patois and PlatformIO jargon respectively) that follow must be compiled with the SAM D21/D51 Arduino core from Seeed Studio which is the only one that has the XIAO board definition. As far as possible, I have provided these examples in a format that can be used within the Arduino IDE or PlatformIO. In all there are 20 projects which can be obtained from the xiao_m0_sketches repository on GitHub.
For those that start digging into the source code of the SAMD microcontroller Arduino core, here is a hierarchy of the principal versions. It’s only a very small sample of the 644 forks on GitHub at this point.
arduino/ArduinoCore-samd (Release 1.8.13 2022/02/23) │ ├── adafruit/ArduinoCore-samd (Release 1.7.9 2022/02/05) │ │ │ ├── LynnL4/ArduinoCore-samd (-) │ │ │ │ │ ├── Seeed-Studio/ArduinoCore-samd (Release 1.8.3 2022/05/24)
Currently, the Arduino IDE uses version 1.8.3 of the Seeed Studio Arduino Core. PlatformIO uses version 8.1.0 of Atmel SAM Arduino framework which is based on version 1.8.1 of the Seeed Studio Arduino core. Discrepancies experienced when compiling a project in these two IDEs could be because PlatformIO is typically based on an older core.
Except for the lag in updates in the PlatformIO IDE, it usually does not matter which IDE is used, it's mostly a matter of personal preference. Neophytes should probably begin with the Arduino IDE. It must be added that the now officially released Arduino IDE 2.0 may prove to be all that is needed by many. See the Arduino IDE 2 Tutorials. For the time being, I will continue to use PlatformIO on VSCodium because it seems that the Arduino IDE does not yet implement a project-based configuration file. That is quite useful for those that play with many different boards.
Uploading/Bootloader/COM Port/Reset Problem
Those who, like me, worked mostly with ESP8266 based boards such as the nodeMCU
and the Lolin D1 mini
will have a few teething problems on starting to use the XIAO or other SAM D21/D51 boards. To be honest, uploading a new sketch to the XIAO is usually just as simple as with the D1 mini: plug the board to the computer with a USB cable, and click on the upload icon of the IDE. But sometimes, there is a problem. The sidebar below contains links to a few of the many reports of difficulties encountered when uploading to the XIAO.
- XIAO Boot issue.
- XIAO looses its Ports.
- Serial Monitor with XIAO not working
- An error occurred when uploading sketch to seeeduino lorawan board via arduino ide.
This is not a XIAO problem per se, here is a link about to the Adafruit Feather M0 and ItsyBitsy's, another SAM D21 board, with many questions along the same line.
The avdweb site also contains a discussion of the problem
One of the answers in the Adafruit FAQ provides a hint as to the cause of the problem.
UNO-type Arduinos have a separate serial port chip (aka "FTDI chip" or "Prolific PL2303" etc etc) which handles all serial port capability separately than the main chip. This way if the main chip fails, you can always use the COM port.M0 and 32u4-based Arduinos do not have a seperate (sic) chip, instead the main processor performs this task for you. It allows for a lower cost, higher power setup...but requires a little more effort since you will need to 'kick' into the bootloader manually once in a while.
The first image shows a typical ESP8266 development board using a USB to 3.3 volts TTY converter (here the CH340G, but it is often the CP1202) which sits between the USB connector and the hardware serial port of the microcontroller. As the next image shows there is no USB-TTY converter on the XIAO, the USB data signals from the USB-C socket are connected directly to two SAM D21 microcontroller I/O pins (PA25 and PA24). The USB signals are processed by a software USB stack which in effect replaces the hardware serial-USB converter. Consequently each time the XIAO is reset, any USB connection between the device and a computer will be lost which explains why the COM port (in Windows or device in Linux and MAC OS) disappears. A programming error could "trash" the USB stack or to put the microcontroller in some sort of tight loop so that the USB connection is lost. That should never happen with the ESP8266 board since the USB connection is between the computer and the CH370G and its own "hardware USB stack" which is independent of the ESP and will stay connected to the desktop even when the microcontroller is crashing.
Microcontrollers run in one of two modes: normal mode in which the loaded firmware is run and bootloader mode which makes it possible to change the firmware. On the ESP8266, the mode depends on the state of input/output pins when it boots. When an ESP is in bootloader mode, it expects to receive the new firmware over its hardware serial port on pins TX0 and RX0. These operations are quite different with the SAM D21. When the chip is powered up or when it is rebooted by grounding and then releasing the reset signal, the chip operates in normal mode and executes whatever firmware has already been uploaded. To put the SAM D21 in bootloader mode, the reset signal has to be asserted twice in quick succession. The microcontroller then appears as a USB storage device named Arduino in my file manager as can be seen below.
The firmware is the file named CURRENT.UF2
. Uploading a new firmware amounts to replacing that file with a new version. I will not go into details about that file because I know nothing about it, but hardy souls can consult the definitive document about the USB Flashing Format (UF2).
By the way, the date of the files is meaningless. It can be changed, but it will revert to the default 2018-12-24 20:00:00 on the next reboot. The size of CURRENT.UF2
does change as a function of the size of the firmware.
There is no reset button on the XIAO, only a couple of pads beside the USB connector. To mimic closing a button, the RST pad must be shorted to ground. This could be done with metal tweezers or a male-to-male Dupont wire or any other conductor. Some use shorted 0.1" header pins. It is not the most practical arrangement and if the board is in awkward position it is easy to short RST to ground twice when wanting to do it only once to reset the board. Luckily, the yellow (orange/amber) LED labelled L on the XIAO reveals its current mode.
Grounding of RST pad | Mode | Yellow LED |
---|---|---|
Once | Normal (boots from firmware in flash memory) | Flashes on once and then remains off unless the firmware turns it on later |
Twice | Bootloader (board appears as a USB storage device) | Continously pulses (cycles between dimming and brightening phases) |
The IDE (Arduino or PlatformIO) shields us very well from the details of uploading new firmware to the microcontroller. But if put in verbose mode, we can see what is going on behind the veil. Here is part of the output in the Arduino IDE.
and the similar sequence of commands in PlatformIO.
Note that the -R
(or --reset
) option means that once the firmware is uploaded, bossac
will reset the board which will then start to execute the new firmware. There is no need to "power cycle" the XIAO after uploading new firmware à la ESP8266.
BOSSA created by ShumaTech is a flash programming utility for [the] SAM family of flash-based ARM microcontrollers [by Microchip (formerly Atmel) such as the SAM D21]. The motivation behind BOSSA is to create a simple, easy-to-use, open source utility to replace Atmel's SAM-BA software. BOSSA is an acronym for Basic Open Source SAM-BA Application. The bossac
utility is a command-line version of the program. Version 1.9.1 was released in August 2018 but both IDEs use version 1.7.0 that is two years older. I just had to try using the utility. The -h
command line parameter displays the other command line parameters from which one learns that the -i
(--info
) command line parameter will identify the SAM device. However I had no luck.
But it did work after putting the XIAO in bootloader mode. Once the yellow LED was pulsing the following information was displayed using the utility.
Clearly bossac
can only work with a SAM device that is in bootloader mode. The Forcing reset using 1200bps open/close on port /dev/ttyACM0
reveals how the IDEs put the chip in that mode. Just opening the Arduino IDE serial monitor with a 1200 baud does indeed put the XIAO in bootloader mode.
The good news it that bossac
will usually manage to upload new firmware to the XIAO which has been put in bootloader mode with the double grounding of the RST button. Be patient, because the IDE will go through its Forcing reset...
routine before calling on bossac
to package and upload the new binary file. The next two sections are long-winded discussions of other aspects of the SAM D21 microcontroller that have an impact on the ease of uploading the firmware.
Only a Blink Sketch, But Do Read Me
The traditional "Blink" sketch must be just about the simplest program that does something visible: flashing a light-emitting diode (LED) on and off. The XIAO has four LEDs, three of which are connected to the microcontroller pins. Note that it is necessary to write a 0 (LOW
) to the pin to turn the corresponding LED on, This is contrary to the typical Arduino convention. Here is a simple sketch that flashes the yellow user LED.
Upload the sketch to the XIAO following the instructions of the IDE used. In both Arduino and PlatformiIO, this amounts to connecting the XIAO to the desktop or portable computer with a USB cable, ensuring that the IDE knows which communication port to use and then clicking on the appropriate upload icon in the IDE. This is explained in great detail in the Lesson 1 of the Seeeduino XIAO in Action book. My sketch is almost an exact copy of blink. ino
found in the lesson which, in turn, looks to be a copy of the classic Arduino example. However do note how the ON and OFF times differ in length which helps confirm that the LED is turned on when the I/O pin is set to LOW = 0 volts contrary to what the lesson says. While I am nitpicking, the serial port name of the XIAO on my Linux system is /dev/ttyACM0
. Both IDEs seem to spot the correct port so that should not be a problem.
Those two little errors are not why I strongly recommended reading this section. Let's modify the sketch slightly by adding a loop counter, n
, which will be incremented each time the loop()
is executed. And let's add two print statements that write out the state of the user LED to the serial monitor. Because this is done over the USB connection, the USB activity LEDs would be distracting, so the I/O pins connected to these two blue LEDs are put into INPUT
mode which, in effect, turns the LEDs off.
Here is part of the output as printed on the serial monitor:
That result is startling if, like me, your microcontroller experience was limited to classic Arduino and ESP8266 boards. How can the Serial
object be used when the serial port was not initialized with a Serial.begin(9600)
statement? The answer is that Serial
in the SAM D21 Arduino framework is not an instance of a Uart
class controlling a hardware universal asynchronous receiver-transmitter. Instead Serial
is an alias for USBSerial
which is a pseudo-serial instance wrapping a USB communications class device (USB CDC class, also called the universal serial bus class). Of course! we already documented that in the previous section: the USB data signals from the USB connector are connected directly to two I/O pins of the SAM D21 microcontroller.
As an experiment reboot the XIAO while it is running the sketch and look at the output in the serial monitor. Do not disconnect the USB cable, just reset the board by shorting the RST pad, making sure to short RST only once.
This is best done in the Arduino IDE because the monitor will automatically reconnect as soon as possible while, in my experience, most often it is necessary to manually reconect to the XIAO in PlatformIO.It's obvious that the board was rebooted after running for about 41 seconds. Why did it take a couple of seconds before the messages from the board started to show up in the serial monitor and where did the first two messages go? The answer has to do with the universal serial bus (USB).
I am old enough to recall the arrival of USB which was seen as a quite an advancement. Here was a true plug and play (self-configuring) hot swappable interface that meant that it was no longer necessary to locate elusive manuals and fiddle with tiny dip switches or, even worse, to find the correct configuration program for a card or device being added to a personal computer. We didn't even have to shut off the computer. When a USB device (the XIAO) is connected to a USB host (the computer), an interrupt will be raised on the latter indicating to the USB stack (i.e. the USB software package on the computer) that there is a new device. The stack will begin to query the XIAO to find out what its capabilities are. This is the USB enumeration process and it is not a trivial task. Shyamal Varma wrote a primer on the subject. No matter what the details are, in the end the computer will determine that the device class of the XIAO is a USB communications device class. The D-Bus service will then create a serial device (usually /dev/ttyACM0
on my machine as stated before) connected to the USB port. When the IDE serial monitor sees that the serial device is open, it will open it and start printing the data received from /dev/ttyACM0
which are the Serial.print
statements in our sketch sent by the XIAO. The USB enumeration, the creation of a device by D-Bus, opening that device by the IDE serial monitor, all this takes time; about two seconds on my system. The first two messages just ended up in the big bit bucket in the sky until the connection was made.
That process explains why it was not necessary to initialize the Serial
object. The sketch does not create a UART and initialize it, USBSerial
is created in the Arduino startup code that we never see that runs even before the setup()
function is reached. The communication channel with the computer is created and destroyed automatically when the physical connection between the device and host is established without any manual intervention needed on our part.
Before going on to expound on what I think the setup()
function should be, I must insist on the fact that I don't know what I am talking about. My little scenario makes sense to me but it is only a heuristic tool which hopefully is "close enough." There's an open invitation to anyone that feels something better should be said to send a message via the link at the bottom of the page.
A Proper setup()
Function
The setup()
function of many of my sketches contains Serial.print()
statements documenting the process as variables and objects are initialized, pin modes are set and so on. It's a simple debugging aid when creating a new sketch and it can be quite helpful for those of us that do not have very expensive in-circuit emulators (ICE) or debugging probes. Clearly, some care is needed when doing that with the XIAO. If Serial.print()
is used too quickly after the microcontroller boots, then the information will be lost. If the setup()
function waits until a USB connection is established, then the microcontroller will never execute its program unless it is tethered to a USB host which will rarely be the case in practice. The solution is a timeout for the USB connection. Here is a sketch, startup
, which provides some timing values.
For a first test, I used the Arduino IDE, connecting the XIAO board and starting the serial monitor setting the baud at 115200, 230400, 2500000 or even 9600 if wanted. Then the sketch was compiled and uploaded. This is the output displayed on the monitor. As can be seen, I chose 115200 out of habit.
The boolean test (Serial)
returns true
within half a second after the setup()
function is reached. However, that only means that the USB stack is up and running; there is no USB connection at this point. To test for the latter it is necessary to issue a Serial.begin()
command (with any value for the baud as that is ignored in the serial-over-cdc-usb Serial_
class) before testing the Serial
boolean operator. Those curious about the behaviour of the latter should take a look at the role of the _DTR
field in the Serial_::begin()
Serial_::operator bool()
functions in .../framework-arduino-samd-seeed/cores/arduino/USB/CDC.cpp
. It will become clear that Serial.beginWithoutDTR()
should not be used instead of Serial.begin()
when wanting to identify when a USB connection is available.
The results show that the Arduino serial monitor re-establishes a connection over USB with the XIAO in slightly more than a second after the microcontroller reboots. In platformIO, I simply cannot restart the monitor as quickly because the USB connection, which is lost after the XIAO is reset, is not restored automatically. After waiting for 8 seconds for a USB connection, the program will move on as shown below where I waited until the yellow LED was no longer on before opening the serial monitor just to prove the point.
With all that, I now believe that Albert van Dalen is correct and that the best start to the setup()
function is just an appropriate delay. I even created a little bootDelay
library complete with optional LED flashing and a USB connection timeout parameter (8 seconds by default which seems just right in my case). It can be found in the blink2
example (Source: 03_blink2). Frankly, a simple delay(8000)
at the very start of setup()
would suffice. The justification for this delay is twofold. First, that delay provides enough time to open the serial monitor to view messages printed during the rest of the setup()
function and the beginning cycles of the loop()
function if the setup()
is short. Usually Serial.print()
statements are just a debugging tool and not needed during the normal execution of the microcontroller firmware. On the other hand, that 8 second initial delay could prove very useful should I manage to put the SAM D21 into a state where the USB stack is no longer responsive. If it becomes impossible to upload a corrected version, I can disconnect the USB cable to the XIAO, reconnect it and after a couple of seconds the XIAO USB stack will function correctly which leaves another 6 seconds before the errant firmware starts which should be ample time to start the upload of a corrected firmware.
Many of the example projects presented below display their results on the serial monitor (or the serial plotter in the Arduino IDE). Accordingly, a blocking test for a USB connection will often follow the initial delay. Do not do that in normal circumstances.
As a final remark in this section, note that the NVIC_SystemReset();
statement is a software reset of the SAMD 21.
XIAO SAMD21 Input Output Pins
The following images show more details concerning the XIAO SAMD21. In addition to the "standard" XIAO connections (11 microcontroller input/output pins on the headers and the 2 microcontroller signals connected to SWD pads on the reverse of the board), 3 SAM D21 I/O pins are connected to 3 of the 4 LEDs to the right of the USB-C connector. The fourth LED, labelled P for power, is not connected to an I/O pin. A pad to the left of the USB-C connector is connected to the RST pin of the microcontroller while the pad directly below it is connected to ground. In later revisions of the XIAO, the RST and ground connections are replicated on the reverse side of the XIAO. There is also an additional pair of pads on the reverse side of the XIAO that could be used to power the board.
Connecting to the pads on the back side of the XIAO is not easy. The XIAO Expansion board uses four pogo pins to connect to the SWDIO, SWCLK, GND and RST pads to provide the debug connector and a reset push button (see Hardware Accessories above for more details).
Pin diagrams of the XIAO often show Dx
alongside the Arduino pin name Ax
. These additional variables are not defined anywhere and their addition is probably to highlight the flexibility of the microcontroller because each I/O pin can be a digital input or output or an analogue input. An Arduino name such as A3
is an unsigned 8-bit index to an entry in the pin description table named g_APinDescription[]
found in the variant.cpp
file. The value of the names are defined in the variant.h
header file in approximately the following fashion.
For clarity, some pins have more than one name such as MISO = A9 = 9
. The value of each variable is Ax
is x
(x = 0,1,...10
). It could be tempting to use the literal numeric value (the Arduino pin number) instead of the Arduino pin name. That should not be done in the very unlikely event that this mapping is changes in a future version of the variant
files when the SAM D21 Arduino core is updated.
WVariant.h
header although only A and B are commonly encountered) and a bit number (0 - 31, also called the bit mask) within the port. Each entry in the pin description table begins with the port and bit mask of the corresponding Arduino pin. For speed, the conversion from Arduino name to the microcontroller pin identity is implemented as two macros digitalPinToPort(P)
and digitalPinToBitMask(P)
that are defined in the variant.h
header file. The pin description table contains much more information about each I/O pin. Here is an "artistic" rendering of what is in the table.
Arduino Pin | SAM D21 Pin | Default Function | Digital | Analogue | IRQ | |||||
---|---|---|---|---|---|---|---|---|---|---|
Name | Number | In | Out | PWM | In | Out | QT | |||
A0 | 0 | PA2 | DAC | ✔ | ✔ | ✘ | ✔ | ✔ | ✔ | ✘ |
A1 | 1 | PA4 | I/O | ✔ | ✔ | ✔ | ✔ | ✘ | ✔ | ✔ |
A2 | 2 | PA10 | I/O | ✔ | ✔ | ✔ | ✔ | ✘ | ✘(*) | ✔ |
A3 | 3 | PA11 | I/O | ✔ | ✔ | ✔ | ✔ | ✘ | ✘(*) | ✔ |
A4 | 4 | PA8 | I²C - SDA | ✔ | ✔ | ✔ | ✔ | ✘ | ✘(*) | ✔ |
A5 | 5 | PA9 | I²C - SCL | ✔ | ✔ | ✔ | ✔ | ✘ | ✘(*) | ✔ (!) |
A6 | 6 | PB8 | UART - TX | ✔ | ✔ | ✔ | ✔ | ✘ | ✔ | ✔ |
A7 | 7 | PB9 | UART - RX | ✔ | ✔ | ✔ | ✔ | ✘ | ✔ | ✔ (!) |
A8 | 8 | PA7 | SPI - SCK | ✔ | ✔ | ✔ | ✔ | ✘ | ✔ | ✔ |
A9 | 9 | PA5 | SPI - MISO | ✔ | ✔ | ✔ | ✔ | ✘ | ✔ | ✔ |
A10 | 10 | PA6 | SPI - MOSI | ✔ | ✔ | ✔ | ✔ | ✘ | ✔ | ✔ |
- | 11 | PA19 | USB TX activity LED | ✘ | ✔ | ✔ | ||||
- | 12 | PA18 | USB RX activity LED | ✘ | ✔ | ✔ | ||||
- | 13 | PA17 | Free User LED | ✘ | ✔ | ✔ | ||||
- | 14 | PA28 | USB Host enable | ✔ | ✔ | |||||
- | 15 | PA24 | USB Data- | ✔ | ✔ | |||||
- | 16 | PA25 | USB Data+ | ✔ | ✔ | |||||
- | - | PA31 (§) | SWDIO | ✔ | ✔ | (?) | (?) | (?) | (?) | (?) |
- | - | PA30 (§) | SWCLCK | ✔ | ✔ | (?) | (?) | (?) | (?) | (?) |
Notes:
(*) - X drivers in an X/Y matrix of QT (capacitance touch) sensors.
(!) - pins A5 and A7 share an interrupt which cannot be attached to both at the same time.
(§) - The last two entries PA31 (SWDIO) and PA30 (SWCLK) used to implement the Serial Wire Debug Interface are not in the pin description array which explains why those entries do not have a pin number. According to avdweb, PA31 can be used safely for I/O without disturbing the debug interface, but if PA30 is used for I/O it will disable the debug interface. I have used these two pins as a fourth hardware serial port which was more of a stunt than anything else. I did not know about the avdweb "patch" needed to use PA30 for digital I/O, so did not apply it and the UART worked without it. Albert van Dalen hints that pins PA24, PA25 and PA28 should not be in the pin description table in order to minimize what he called thenative USB port virus
.
(?) - not tested
Three LEDs are connected to I/O pins which do not have Arduino pin names. However they all have macros that can be used as aliases to the Arduino pin number:
Arduino Pin Number | Macros | ||
---|---|---|---|
11 | PIN_LED3 | PIN_LED_TXL | |
12 | PIN_LED2 | PIN_LED_RXL | |
13 | PIN_LED_13 | PIN_LED | LED_BUILTIN |
The fourth LED is a power-on signal not connected to an I/O pin.
The sources for these tables are the XIAO schematic, variant.cpp
in the .../packages/Seeeduino/hardware/samd/1.7.0/variants/XIAO_m0/
directory and Table 7-1. PORT Function Multiplexing for SAM D21 A/B.CD Variant Devices and SAM DA1 A/B Variant Devices in the microcontroller datasheet.
Warning: The XIAO SAMD21 is a 3.3 volts device and applying 5 volts to an input will overload and damage the XIAO. There may be a 5-volt power connection on the board, but DO NOT use it to power say an I²C RTC clock because the latter's data connections would then be at the 5-volt level when in a HIGH state. Look at the absolute maximum ratings of VPIN
in the following table.
Table 41-2. Absolute Maximum Ratings | ||||
---|---|---|---|---|
Symbol | Description | Min. | Max. | Units |
VDD | Power supply voltage | 0 | 3.8 | V |
IVDD | Current into a VDD pin | - | 92 (1) | mA |
IGND | Current out of a GND pin | - | 130 (1) | mA |
VPIN | Pin voltage with respect to GND and VDD | GND-0.6V | VDD+0.6V | V |
Tstorage | Storage temperature | -60 | 150 | °C |
1. | Maximum source current is 46mA and maximum sink current is 65mA per cluster. A cluster is a group of GPIOs as shown in the table below. Also note that each VDD/GND pair is connected to two clusters so current consumption through the pair will be a sum of the clusters source/sink currents. |
Source: Microchip, SAM D21/DA1 Family, Complete Datasheet DS40001882E, p. 1008.
Analogue Input and Output
Up to now, I have had basically no experience with analogue input and output because I have mostly worked with ESP8266 based devices, many of which did not even use its single analogue input pin. The SAM D21 is quite different, as almost every I/O pin can be used for digital input/output or analogue input; there is a single analogue output pin. This was a good opportunity to learn some of the details about this aspect of microcontrollers.
First thing to check is that all
Ax
(x = 0,...10)
I/O pins of the XIAO can be analogue inputs. A simple voltage divider with two 2.2K resistors was used to test each input pin with 3 voltages: 3.3 volts, 3.3/2 volts and 0 volts. A running average measure of the input voltage at the pin being tested was automatically restarted by the program whenever the input volt changed. Here is a typical output of the program.
Here is the source code that produced that output.
The result was nearly identical for all the XIAO pins so that I am confident that all pins can be used as 12-bit or lower analogue to digital converters. Surprisingly, there is no analog on pins A4 and A5 of the QT Py which is the Adafruit clone of the XIAO. Perhaps the slightly different version of the SAM D21 on the QT Py (ATSAMD21E vs the ATSAMD21G on the XIAO) explains why the SDA and SCL pins would not be analogue inputs on the Adafruit clone.
A 12-bit digital analogue converter translates the input voltage measured at the pin (which must be in the range 0 to Vcc which is 3.3 volts maximum) into one of 2^12 = 4096 possible values. When the input voltage is 0 volts, the value read should be 0, when the voltage is equal to Vcc the input value should be 4095. That is not quite what was returned by the sketch. With one XIA0 the average returned value on pin A7 after 22150 readings was 4053.1. There can be quite a bit of variation in the values returned when measuring Vcc as can be seen with the following histogram established after 741 measurements on A2 of another XIAO.
Note how the highest returned value was 4067 which is short of 4095, the theoretical maximum value. It is possible to calibrate the analogue input, in other words, rescale the ADC so that it will more reliably output 4095 when Vcc is applied to the pin. This is covered in the next subsection: Analogue Input Calibration.
Looking at the source code (in .../packages/framework-arduino-samd-seeed/cores/arduino/wiring_analog.c
), it becomes obvious that the resolution of SAMD 21 analogue inputs can set to only one of 3 values: 8 (0 to 255), 10 (0 to 1023) and 12 bits (0 to 4095).
Even if the maximum ADC resolution is 12 bits, the statement analogReadResolution(24)
is perfectly legitimate. The analogRead()
function will return a scaled value in the range 0 to 16777215 (=2^24-1) which only provides the illusion of greater accuracy.
Finally, note that the ADC read resolution is common for all analogue input pins; sorry if this is belabouring the obvious.
Analogue Input Calibration
Among the libraries installed in the SeeedStudio SAMD Arduino Core version 1.7.0 there was an example sketch named CorrectADCResponse
in the SAMD_AnalogCorrection
library. I modified it to be able to run it in both the Arduino IDE and PlatformIO, connected pin A1 to ground and A2 to Vcc (3.3volts) as instructed at the top of the source file and ran it. Here was the output generated by the program.
I then reran the analog_in
sketch, setting the specified correction factor in the setup()
,
Quickly enough, the average settled at 4089.9 and the maximum value 4095 is rarely reached.
CorrectADCResponse
was removed from the SeeedStudio fork of the core in the next version. I wasn't aware of that fact until I began rewriting this post. Unfortunately, I did not see the December 2020 forum post by krzysiekf asking where the library had gone. Luckily msfujino explained how to get the library from the Arduino Core for SAMD microcontrollers by Arduino where it is still present. That's fine for SAM D21 boards like the XIAO, but I would recommend getting the SeedStudio 1.7.0 version which also handles SAM D51. The whole core can be obtained here and then steps 3 and 4 of the procedure by msfujino can be followed. However it is probably easier to just get the library from the GitHub repository SAMD21/SAMD51 Analog Correction based on the original Seeed Studio version.
Analogue Measure of Light with an LDR
Here is the simple circuit used to measure the quantity of light built around a light dependant resistor (LDR) and a fixed resistor. The 5K value of the fixed resistor is just an indication, it is not critical. Besides, I do not know the characteristics of the LDR. As verified in the previous section, any of the 11 I/O pins of the XIAO could be used, I chose A3.
I took these measures in the late afternoon when the light level in the room was decreasing as the sun was getting closer to the horizon.
Values over 4000 can be obtained by bringing a COB LED quite near the LDR. The measured value can drop quickly to 0, or very near to that level, by cupping a hand tightly around the LDR and thus shutting out most of the light.
The source code contains a macro which can be used to calculate a rolling average of the input voltage on the LDR pin.
Here is a graph showing how well a rolling average (in red) of the last 10 observations smooths out the raw data in blue.
This is based on artificial data. The LDR pin was left floating which produces something that looks very much like a random walk. The rolling average would probably do better with the light level measure which, unfortunately, I no longer had when adding this feature to the project. If the result is not smooth enough, then a longer FIFO buffer could be chosen. That has no impact on the performance, the calculation of the average is independent of the size of the buffer. Or a more sophisticated weighted average could be used, see testtouch sketch below.
Capacitance Touch Sensors
Like the ESP32, the SAM D21 has "touch pins". I was hoping that meant that the SAM D21 microcontroller had the equivalent of builtin TTP223 ICs. In other words, I thought it would be possible to connect an external electrode such as a copper disk or touch pad on a printed circuit board to one of these pins, set the mode and, voilà, there would be a clean 0/1 signal on the pin indicating when a finger was very close or in contact with the pad. Reality is always messier.
A major complication results from what Jeremy Gilbert (jgilbert20) called The PTC downfall: a big binary blob called QTouch. The Peripheral Touch Controller (PTC) subsystem is Microchip's hardware implementation of capacitive touch sensing as installed in the SAM D21 and other Microchip devices such as the ATmega328PB. The QTouch library is a royalty-free library for working with the PTC, but it is closed source. For that reason it is not included in any Arduino core. Hence the "official" QTouch library can only be used in development environments that are more accommodating, with the Microchip Studio 7 IDE being the most obvious choice. For mere hobbyists who prefer PlatformIO or the Arduino IDE, the only games in town are two reversed-engineered libraries: Jeremy Gilbert's LibrePTC and Adafruit_FreeTouch. Both libraries are available through the PlatformIO library manager but only the Adafruit library can be downloaded with the Arduino library manager. I have only used the Adafruit library and done just enough with it to check that the assignment of QT capabilities in the pin description table is correct.
Following the lead of juliusbangert asking for [h]elp with using Adafruit_FreeTouch library on SAMD21, I plotted the input from A0 and A1 set up as touch sensors in the Arduino Serial Plotter.
The plot is quite different from the one shown by Julius, but that could be down to differences in the sensor pad and in the version of the library. The sensor pad attached to each pin was very rudimentary, an 80 mm male-male Dupont wire arced from the pin to a free hole on the breadboard. Since the post by Julius in 2017, little information about the Adadfruit_FreeTouch
has surfaced. I did find three examples on Web that use the library.
- How to Make a Fruit Piano on XIAO SAMD21’s Q-Touch Function by Makerming. Clearly inspired by an Adafruit video about a touch Pi hat.
- Adafruit Neo Trinkey, Arduino Examples by Kattni Rembor. The board is a SAM D21 connected to 4 neo-pixels and two touch pads and a reset switch and basically nothing else.
- Capacitive Touch Unicorn Horn by Erin St Blaine. This has some debouncing code which might warrant additional attention.
These libraries share a common "brute force" approach that I reproduced in a sketch. My only improvement was the attempt to establish the "touched" threshold in a calibration step rather than playing a guessing game.
As can be seen from the output
The long answer by adafruit_support_mike in the Julius forum post contains information about the parameters used to construct the Adafruit_FreeTouch
class. It would be valuable to experiment with these values in a serious project. His last recommendation about the use of a running average could be misunderstood. A running average smooths out the fluctuations in the measured capacitance, but clearly from the plots, it is their greater amplitude that marks a touch. So I assume that the idea was to identify a touch when the current capacitance measure is too far from the running average. That also works from my test.
There's no pretence that either of these sketches provides a working solution to using the touch-sensing capabilities of the XIAO in a practical application where the board is used to perform resource-intensive tasks. But this is just an overview. If someone knows of a more complex example please get in touch that I may add it here.
Analogue Output
There is a single "true analogue" output signal on the XIAO which is available on pin A0. The term true analogue was in quotation marks because the signal is obtained through a 10 bit digital to analogue converter (DAC) so that the output voltage can be set to one of 2^10 = 1024 values. The output voltage is given by the following formula:
DAC output voltage Vout = (DATA/1023)*Vref
where DATA
is any value from 0 to 1023. The reference voltage, Vref
, can be one of many values, but by default it is the XIAO input voltage which is nominally 3.3V. Accordingly only discrete voltages can be obtained but the resolution, which by default is 0.003226 volts or 3.226 millivolts, is probably better than I will ever need. Using a Vref
of 1 volt, the step will fall to slightly less than 1 millivolt: 0.9775.
To test this analogue signal, I simply connected A0 to A2 but any other analogue input from A1 to A10 could be used although it is best to avoid A1. Here is a simple sketch that simply ramps the output analogue signal up and down in a linear fashion and reads the pin output. In essence it produces a triangular wave with code rather similar to the sketch using PWM to pulse the intensity of LEDs.
This is the output of the setup()
function when SCALE
is not defined (i.e., when #define SCALE
is removed or turned into a comment).
The loop()
function pumps out lines of output and input measures along with two reference values to the serial port.
The serial output of the sketch can be viewed in the IDE serial plotter (Tools/Serial Plotter
).
The graph could be surprising on first look, but remember this plot is not a direct measure of the output and input voltages. The triangular blue wave shows the numeric data written to the DAC to specify the output voltage on pin 0. The red wave (or higher amplitude wave, for colour-blind readers) shows the numeric data read from the ADC on pin 8 as it converts the output voltage read on pin A1. The ADC has a 12-bit resolution, so it divides the 0 to 3.3 voltage range into 4096 steps. Writing 64 to the output DAC will result in an output voltage of 0.2064 (=3.3*64/1023). The ADC reads that voltage as 4095*0.2064/3.3 = 256.12, which is 4 times 64 (when ignoring rounding and measurement errors). The input voltage is quite close to the output voltage notwithstanding the scale factor. This can be verified by scaling either the DAC or ADC measure. When the SCALE
macro is defined, the output value is scaled and the result is the following graph.
It is now obvious that the input value is (almost) equal to the output value.
The analogWriteResolution(DAC_BITS)
statement is not needed. Surprisingly, if it is included, the DAC_BITS
parameter has no influence at all on the program; testing with analogWriteResolution(6)
resulted in the exact same plot as before. Looking at analogWriteResolution()
analogWrite()
(in .../packages/framework-arduino-samd-seeed/cores/arduino/wiring_analog.c
), it is obvious that no constraints are placed on the value written to the DAC except that only the lower 10 bits of the value written are used.
In other words, the _writeResolution
variable that is modified with the analogWriteResolution()
function is not used in analogWrite()
when writing to the DAC pin (A0). It is used when writing to the other XIAO pins which are all capable of PWM. I think the take away here is do not set analogWriteResolution()
in the hope of modifying the resolution of the DAC pin (A0). It will do nothing with respect to the DAC, but it will modify the behaviour of analogWrite()
which sets the duty cycle of PWM outputs.
Important Note: when using pin 0 to output the DAC signal, DO NOT set the pin mode to OUTPUT
. Here is what happens if pinMode(DAC_PIN, OUTPUT);
statement is included the setup()
function. (See analogWrite() DAC on A0.)
Remember the red truncated wave is the actual voltage on the output pin, the blue wave is the set output voltage which is manifestly not obtained at times. Note that the DAC and ADC measures have been converted to volts. The sketch, dac2dac_v.ino
, that produced this plot can be found in the archive that accompanies this post.
Digital Input Output
All I/O pins on the XIAO can be used as digital pins, which means pins that should be in either a LOW state or HIGH state when used as inputs or outputs.
Digital Output
It is a simple matter to adapt the sketch blinking the XIAO yellow user LED (01_blink) to show that every pin on the XIAO can be used as a digital output pin. Here is a schematic for a perfectly good probe to use to check that each pin of the XIAO flashes on and off when using the following sketch. It is also possible to imitate an analogue output signal with a square wave by modifying the ratio of its ON time to its OFF time. This is called pulse width emulation (PWM).
It will be on sale very soon on a merchandizing site for the modest cost of $ 0.99 (plus $ 32.84 shipping and handling fee, US dollars of course). However some may want to wait for the high tech version.
Prototyping is complete, so a crowdfunding campaign will be launched, as soon as a patent has been obtained and the current chip shortage crisis is over.
Just connect "the probe" to each pin of the XIAO in turn and wait until the LED flashes on and off in tandem with the message on the serial monitor.
Pulse Width Modulation
Simple Arduino-style pulse width modulation is available on pins A1 to A10 and on the three I/O lines connected to the on-board LEDs.
Notice the setPwm()
function. In analogWrite() PWM range, lady ada highlights the difference between AVR and ARM cortex devices when setting the PWM duty cycle to 255. It turns out that the same problem exists when setting the duty cycle to 0. In both cases the output of the pin will not be a steady 0 or Vcc voltage; there will be a very short pulse. Hence the need to reset the pin mode to OUTPUT
and the use of digitalWrite()
instead of analogWrite()
in those two edge cases.
A PWM signal is a square wave (ON/OFF). It's duty ratio (which is set with the setPwm()
function in the above sketch is the proportion of ON time to the period of the signal. In other words, it is the ratio (ON time)/(ON time + OFF time). The period of the wave is 1/frequency. So the true length of time that the signal is on and off depends on the frequency of the signal. As far as I know, there is no function in the Arduino framework to set the PWM frequency. Doing that necessitates programming at the "bare metal" level, using assembly language in other words, which is beyond the scope of an overview of the XIAO and way above my pay scale in any case. I will point out that there is at least one library Arduino_SAMD21_turbo_PWM - A fast PWM library for SAMD21G-based Arduinos that points the way to do this. Unfortunately, it does not support the XIAO.
Digital Input
The original version of this post used a momentary close push-button to test the digital input function of the I/O pins. That required using a button library, which was good because it allowed me to advertise my own library. In retrospect, it all seemed much too complicated. A Dupont wire with an optional resistor is all that is needed to verify that all the I/O pins of the XIAO can be used as digital inputs. There is no need for an external pull up or pull down restore to ensure that the microcontroller input pin is in a steady default state when the I/O pin is floating. Setting the pin mode to
INPUT_PULLDOWN
connects a weak (20K to 60K but typically 40 KΩ) resistor between the pin and ground. If the mode is set to INPUT_PULLUP
then the weak resistor connects the pin to Vcc. If the mode is set to INPUT
, then the pin "floats" meaning its state (HIGH or LOW) is susceptible to electrical noise. This can be the cause of mysterious errors.
To run the following sketch, set INPUT_MODE
to one of INPUT_PULLUP
or INPUT_PULLDOWN
. If INPUT_PULLDOWN
is chosen, one end of the wire should be connected to Vcc (3.3 V) so that when the other end of the wire contacts a pin, it will overcome the weak pull down internal resistor and toggle the state of the input pin to HIGH. If the mode is set to INPUT_PULLUP
then the wire should be grounded to toggle an input pin to the LOW state.
The state of the button is continuously polled in this sketch, but it is possible to forego all this polling busy work by using an interrupt as explained in the next section.
With our simple probe, we can be sure that the signal to the input pin will be very close to 0 volts if the wire is grounded or very close to Vcc (3.3 volts on the XIAO) if the pin is connected to the 3.3 V output pin. But in practical applications, there's often no guarantee that such an unambiguous signal will be fed to a digital input pin. What is the minimum voltage level that the microcontroller will deem to be HIGH? The single true analogue output pin (A0) can be used to test this.
Here is the output to the Arduino plotter.
And this is the output if the PLOT
macro is not defined or if running in PlatformIO.
While a voltage inferior to 1.22 volts is applied to A2 on the particular XIAO on which this sketch was run, a LOW (=0
) will be returned by digitalRead(A2)
. Similarly a voltage above 1.59 volts will be read as a HIGH (=1
). The in between grey zone is a manifestation of hysteresis where digitalRead() will return HIGH or LOW depending on the state of the pin before. This observed grey zone is much smaller than what could be expected given the guaranteed characteristics which are 0.99 and 1.8 volts (at VDD = 3.3 volts) as per Table 41-12. Normal I/O Pins Characteristics in SAM D21/DA1 Family, Complete Datasheet pg 1009.
Parameter | Conditions | Min. | Max. | Unit |
---|---|---|---|---|
Input low-level voltage | VDD = 2.7V-3.63V | - | 0.3 × VDD | V |
Input high-level voltage | 0.55 × VDD | - |
Interrupts
The initial look at interrupts was not very detailed.
I was happy that it worked and then went on at length about the digitalPinToInterrupt()
macro. It is no longer defined twice so there's no point in repeating that discussion. However, I have now realized that the mapping between the Arduino pin number and the external interrupt attached to it is not as simple as I thought. So I modified the setup()
function to shows the mapping between the Arduino pin number and its external interrupt.
When two pins, A5 and A7, share the same interrupt it probably means that some restrictions apply. I decided to write a slightly more sophisticated program.
Just connect each pin to ground to test the interrupt service routine (ISR) for that pin. While a pin is grounded, the yellow LED should be on. When the pin is first connected to ground and then when it is disconnected, a State of pin Ax changed
message should appear in the serial monitor. Depending on the value of the EXT_INT9_PIN
directive, the interrupt attached to pin 5 and pin 7 may or may not work. An interrupt cannot be shared among pins so only one of pin 5 or 7 can have an active interrupt. On the other hand, distinct interrupts can have a common service routine. Look at pins 0 and 1 which have the same ISR in the program.
Timers
We have already worked with timers albeit without it being obvious. Pulse width modulation depends on timers.
There are two timer-based blink sketches in the Arduino IDE example sketches for the Seeeduino XAIO. Try them (File/Examples/
scroll down to Examples for Seeeduino XAIO
and select TimerTC3/ISRBlink
and TimerTCC0/ISRBlink
), they both work. I combined the two, with the choice of the timer done with a macro directive and tried to implement the asymmetric on and off behaviour of the previous blink sketches.
See the TimerOne & TimerThree Libraries example program from PJRC for an excellent explanation of the use of the volatile
type and of the need to disable interrupts in the main loop. Note how the mode of the PIN_LED_TXL
pin is set to INPUT
. Otherwise the blue TX LED will flash at each character written to the serial monitor which distracts from the flashing yellow LED. The output in the serial monitor confirms the timing values set.
While this may look good, to be honest, I am very dissatisfied with this presentation of the SAM timers. I seem to be running into trouble when using the timers. Just changing the timer interval gives unexpected results with either timer.
Even worse, intializing timer TimerTc3
with a 500 ms time will work, but it will crash when TimerTcc0
is used.
The print statement after the time initialization was ignored, but the yellow LED was turned on and remained on. The communication port, /dev/ttyACM0
on my system, was still up but firmware could not be uploaded to the XIAO until it was put in bootloader mode. In short, I have no idea what happened. Unfortunately, I cannot find information about the TimerTC3
and TimerTCC0
libraries which are only present in the Seeed Studio fork of the SAMD Arduino core. There are libraries that I should look at in the future.
- SAMD_TimerInterrupt by the prolific Khoi Hoang.
- Avdweb_SAMDtimer by Albert van Dalen whom we have already met before.
- Samd21-Timer-Arduino-library by Alberto Pimpo.
Again, any help would be much appreciated.
Watchdog
The SAMD 21 microcontroller does have a hardware watchdog, but it does not seem to be fully supported in the Arduino core. At least, I could not find a function to start it and another to reset it to prevent it from restarting the microcontroller. As often the case, others have stepped in and provided the missing bits which takes care of the awful details.
- The minimalist Sam D21 hardware watchdog by Adrian Adamski.
- The very useful SAMCrashMonitor by Chris Brunner (cyrusbuilt).
- The predictably more sophisticated Adafruit SleepyDog Arduino Library.
The last two libraries are obtainable with the PlatformIO manager. As for the Arduino IDE, but only the Adafruit library can be installed directly with the library manager; SAMCrashMonitor
must be installed manually. As for the code by Adrian Admaski, it was a simple task to transform it into a very small library. Here are the header file samdwdt.h
and implementation samdwdt.cpp
.
Here is the serial output from running the test code.
Serial Communication
The SAMD microcontroller comes with the usual array of serial communication buses. All boards of the XIAO family provide one UART, one I²C and one SPI bus by default. In this section each of these will be examined in turn. This is only scratching the surface of the capabilities of the chip which I have investigated at length in other posts, notably Seeeduino XIAO Serial Communication Interfaces (SERCOM)The XIAO and Three, Nay, Four Hardware Serial Ports on a SAM D21 XIAO.
UART
For a first look at the hardware serial port (UART), let's run a simple loop back test. That means that pin A6
which is the UART transmit pin must be connected to the receive pin A7
of the same UART
.
The result shows that the serial communication works.
It is possible to have more than one hardware serial port if the I²C or the SPI bus is not needed. See Three, Nay, Four Hardware Serial Ports on a SAM D21 XIAO.
SPI
In the original version of this section, the XIAO SPI bus was examined with a logic analyzer. I forgot that the SPI serial port can be tested in a loop back configuration just like the UART. My excuse is that I did not think to test the UART port at the time. As one would expect in this type of test, the serial output pin is connected to the serial input pin. More pedantically, the SPI data output pin, A10
or MOSI
(for Master Out Slave In), is connected to the SPI data input pin A9
or MISO
(for Master In Slave Out). There is no need to worry about the SPI clock signal and the chip select (also called the slave select) signal since these are meant for the slave device. Here is the output of the little test program.
The received message was exactly what was sent which is what was expected. Then the MISO
pin was disconnected from the MOSI
pin and left unconnected to anything for a short while (the pin was said to "float" in the jargon). Not surprisingly garbage data was read in as each character of the output message was transmitted. Most did not correspond to printable ASCII characters and their numeric value is shown in hexadecimal notation. Then the SPI data input pin was connected to the 3.3 volt rail so that the input value of each character was 0xFF corresponding to all bits being equal to 1. Finally, the MISO
was tied to ground and all received bytes were then equal to 0.
Here is the source of the SPI loop back test.
I²C
The I²C bus has a single bidirectional data signal which means that a simple loop back test cannot be performed. Instead of a test, I will present a digital clock program where the XIAO is connected to a couple of I²C devices: an 0.96" OLED display and a module with a real-time clock (RTC). The ubiquitous OLED display is a no name 128 by 64 pixels SSD1306 while the RTC is based on the DS3231. The connections between the devices are straight forward: connect all SDA pins together, all SCL pins together, ground pins together and Vcc pins together.
XIAO OLED RTC & EEPROM ------- ---- ------------ GND-----GND------GND 3.3V-----VCC------VCC A4(PA8)-----SDA------SDA A5(PA9)-----SCL------SCL
Let's start with a preliminary sketch to scan the I²C bus to ensure that the connected devices can be found and to obtain their I²C address. This can be done with most versions of the i2c_scanner
sketches that abound on the Web. I used a version by Thomas Feldmann with slight adjustments in the setup()
function. Also #include <Arduino.h>
was added so that the utility will compile and run in PlatformIO as well as in the Arduio IDE.
This is the output in the IDE serial monitor.
In the previous edition of this post, the U8g2 library by olikraus was used to control the display. This time around SSD1306Ascii by Bill Greiman is used. As for the real-time clock, the library used is RTC by Michael C. Miller. The code itself is a much simplified version of the DS3231_Simple
example sketch.
The size of the firmware when compiled with the two platforms is not the same. Thinking that this difference could result from the use of different versions of the SAMD Arduino Core in the two IDEs, I compiled the sketch with the Arduino IDE using the older version of the core that is installed in PlatformIO. Again the Arduino version was bigger which could perhaps be attributed to the number of libraries linked into the program. Or it could have something to do with optimization flags, debugging information and so on. I should test this again with Version 2.0 of the Arduino IDE before drawing any conclusions.
This program is by no means a true digital clock. Most obviously, there is no way to set or modify the time. Nevertheless, it does show that the I²C interface of the XIAO is functional.