SAMD21 Arduino Zero: trouble creating new Uart on pins 15/16

I have this custom board that uses a SAMD21 MCU and (so far) most closely behaves like an Arduino Zero. I have been using boards similar to this one (also programmed as an Arduino Zero) in other projects, so far without issues. Now I have this problem.

This particular board has a LoRaWAN chip connected via a pair of UART pins to the SAMD21. It is supposed to use pins 15/16 (PB08/PB09) for TX/RX respectively. However, I have been unable to get an answer from the other side no matter how I configure the pins. Below is my sample program that is supposed to bridge the debug uart and the problematic uart together:

#include <Arduino.h>
#include "wiring_private.h"

#define DEBUG_SERIAL    SerialUSB

#define PIN_TESTSERIAL_RX (16ul)  // PB09
#define PIN_TESTSERIAL_TX (15ul)  // PB08
#define PAD_TESTSERIAL_RX (SERCOM_RX_PAD_1)
#define PAD_TESTSERIAL_TX (UART_TX_PAD_0)
Uart TESTSERIAL(&sercom4, PIN_TESTSERIAL_RX, PIN_TESTSERIAL_TX, PAD_TESTSERIAL_RX, PAD_TESTSERIAL_TX);
void SERCOM4_Handler() { TESTSERIAL.IrqHandler(); }

// EDIT: 2025/07/09 this macro definition is a copy-paste error and was not supposed to be active.
// #define TESTSERIAL Serial1

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

    uint32_t t1 = millis();
    while (!DEBUG_SERIAL && millis() - t1 <= 100);

    TESTSERIAL.begin(9600);

    pinPeripheral(PIN_TESTSERIAL_RX, PIO_SERCOM_ALT);
    pinPeripheral(PIN_TESTSERIAL_TX, PIO_SERCOM_ALT);
}

void loop()
{
    int c;
    while (TESTSERIAL.available()) {
        c = TESTSERIAL.read();
        if (c >= 0) DEBUG_SERIAL.write((uint8_t)c);
    }
    while (DEBUG_SERIAL.available()) {
        c = DEBUG_SERIAL.read();
        if (c >= 0) TESTSERIAL.write((uint8_t)c);
    }
}

By searching through this very forum, I found a post asking for exactly this scenario: SAMD21 SERCOM Serial1 & Serial2 not working . The post was closed in May 2021 without any satisfactory answer.

From what I can gather from the variant.[h|c] files, sercom4 is supposed to be already in use for SPI. My particular project does not make use of SPI, and I am perfectly fine with disconnecting any conflicting SPI functionality, as long as I can use an UART on those particular pins (that is, pins 15/16). Also, those pines are marked as "analog pins" A1 and A2. Again, I do not use analog pins on this particular project, and I am fine with disconnecting anything that conflicts on those pins.

The board can be physically patched to move the LoRaWAN connections to the pins matching Serial1, and has been done for one board. However, this is but one of a batch of boards and I would not like to have all of the boards patched if this can be fixed in firmware instead.

Why would the above program not work in my setup? Am I missing some step? Are the steps done in the correct order? Do I have to explicitly rip out SPI and/or analog functionality from the target pins in order to initialize the UART?

Presumably, the person who designed the mysterious board would be able to answer the question.

You aren't giving forum members enough information to do anything but make wild guesses. For example:

uses a SAMD21 MCU and (so far) most closely behaves like an Arduino Zero

There are many variants of the SAMD21xxxx. Is it too much trouble to state exactly which SAMD21 chip is on the mysterious board?

According to the circuit specs, it is an ATSAMD21G15B-AFT .

@jremington So you think the sample program should work as-is on a true official Arduino Zero, without issue?

Hmm. Are you sure that whatever board variant you are using has pins 15 and 16 mapped to pb09 and pb08? That’s true of the arduino zero, but by no means true of all samd21 boards.

Otherwise, it looks OK to me. I've used quite similar code to get 6 Sercoms implementing UARTs on a SAMD21. See GitHub - WestfW/WestfW_SerComLib
(But I didn't use that particular combination of pins/pads.)

Perhaps also useful: SAMD21-PMUX - Google Sheets

My interpretation of the PMUX sheet linked above is that on the D21G, pins 15 and 16 are mapped to PA10 and PA11. Correct me if I'm wrong.

I am using the GPIO numbering from Arduino, as derived from the arduino_zero variant.cpp file. In this numbering, Arduino pin numbers 15/16 are PB08/PB09.

A correction: after examination of the board, the actual chip is shown to be an ATSAMD21G18A-U. The -G15 variant would not have enough flash space for the project firmware.

So are you using "zero" as the board type, or have you (or your vendor) created a new variant?
If the latter, did you create the variant.cpp, or was it provided? Did you check that specific variant to make sure the pin mapping is the same as the Arduino Zero?

Yes, it's important to note that the pin numbers used in the Uart constructor are "Arduino pin numbers" and not chip pin numbers, but it was pretty clear from the additional context in the first post that this was understood.

I am using "zero" as the board type. Specifically, in Arduino IDE 2.x, I have the following board type selected at menu Tools-->Board-->Arduino SAMD Boards (32-bits ARM Cortex-M0+)-->Arduino Zero (Native USB Port). Also, when plugging in the board, it identifies in the system log as an Arduino Zero.

I have not created a new variant, or installed any custom variant code for the sole purpose of using this particular board. As far as development goes, I am building for the Arduino Zero.

My team has performed some tests today with the custom board. The sketch I posted as part of the first message of this thread fails to make any activity appear in the oscilloscope attached to pins PB08/PB09. However, a different program that programs the pins as PWM and cycles through values does result in oscilloscope activity. In the Arduino Zero variant.cpp, I see a mention of PIN_ATTR_PWM, so I consider that PWM being successful on this board is completely expected.

I am trying to find an actual Arduino Zero (so far without success) in order to repeat the experiments and check whether the UART code works on an actual Arduino Zero.

I still believe that there is some missing step, or some other misconfiguration in my UART code that prevents the code from working. Maybe something about ripping out analog/PWM support that was by default connected to those pins.

Otherwise, I would appreciate an explanation of why the posted UART code could never possibly work, despite the SAMD21 documentation apparently suggesting that it could work using sercom4.

Hi @a_villacis.

The Arduino Zero board is available for purchase from the Arduino Store:

From the official Arduino Amazon store:

And from various authorized distributors:

https://www.mouser.com/ProductDetail/Arduino/ABX00003?qs=uFNwyRpLCSLuiMWQhDVCBA%3D%3D

Are you still there?

I've finally gotten around to trying your code on a SAMD21 board I have here (a Sparkfun "SAMD21G breakout", which is mostly compatible with an Arduino M0)

It doesn't work there either.

I added some instrumentation. The first clue that something is weird comes from adding a dump of the PMUX values, using code from GitHub - WestfW/ArduinoZero-PMUX-report: Generate a user-readable report of the current state of the pins (what they're doing) on Arduino Zero and other SAMD21 based systems

This shows:

test pmux values
00      PA11  PMUX(02) SERCOM0 P3(UART)
01      PA10  PMUX(02) SERCOM0 P2(UART)
03      PA09  GPIO I
04      PA08  GPIO I
11      PA16  GPIO I
13      PA17  GPIO O
15 (A01) PB08  PMUX(03) SERCOM4 P0(USART)
16 (A02) PB09  PMUX(03) SERCOM4 P1(USART)
26      PA22  GPIO I
27      PA23  GPIO I
29      PB10  GPIO I
28      PA12  GPIO I

Note that pins 0/1 (Serial1) show as a UART, while 15/16 show as USART.
It turns out that this is because the Sercom "mode" is set to zero, which is a UART with external clock.

That's not what it should be!

Further investigation shows that all of Sercom4 seems to be uninitialized!

REG_SERCOM4_USART_CTRLA   = 00000000
REG_SERCOM4_USART_CTRLB   = 00000000
REG_SERCOM4_USART_BAUD    = 00000000
REG_SERCOM4_USART_RXPL    = 00000000
REG_SERCOM4_USART_INTENCLR= 00000000
REG_SERCOM4_USART_INTENSET= 00000000
REG_SERCOM4_USART_INTFLAG = 00000000
REG_SERCOM4_USART_STATUS  = 00000000
REG_SERCOM4_USART_SYNCBUSY= 00000000
REG_SERCOM4_USART_DATA    = 00000000
REG_SERCOM4_USART_DBGCTRL = 00000000

Still investigating how THAT might have happened!

Still here - I still have the issue with my board. Sorry for the lack of activity, but I was busy with other projects of my day job (of which the SAMD21 is but one).

Thanks for pointing me to the github repo for the register dump. I will try this on my board as soon as I have some free time.

REG_SERCOM4_USART_CTRLA   = 00000000
REG_SERCOM4_USART_CTRLB   = 00000000
REG_SERCOM4_USART_BAUD    = 00000000
REG_SERCOM4_USART_RXPL    = 00000000
REG_SERCOM4_USART_INTENCLR= 00000000
REG_SERCOM4_USART_INTENSET= 00000000
REG_SERCOM4_USART_INTFLAG = 00000000
REG_SERCOM4_USART_STATUS  = 00000000
REG_SERCOM4_USART_SYNCBUSY= 00000000
REG_SERCOM4_USART_DATA    = 00000000
REG_SERCOM4_USART_DBGCTRL = 00000000

Is this block of REG_SERCOM4_USART_* supposed to be part of the output of the dump program? Or is this part of some header file that is supposed to be supplied by Arduino?

It's additional code that I added.

void dump_sercom4() {
  sprintf(buffer, "REG_SERCOM4_USART_CTRLA   = %08x\n", REG_SERCOM4_USART_CTRLA);
  Serial.print(buffer);
  sprintf(buffer, "REG_SERCOM4_USART_CTRLB   = %08x\n", REG_SERCOM4_USART_CTRLB);
  Serial.print(buffer);
  sprintf(buffer, "REG_SERCOM4_USART_BAUD    = %08x\n", REG_SERCOM4_USART_BAUD);
  Serial.print(buffer);
  sprintf(buffer, "REG_SERCOM4_USART_RXPL    = %08x\n", REG_SERCOM4_USART_RXPL);
  Serial.print(buffer);
  sprintf(buffer, "REG_SERCOM4_USART_INTENCLR= %08x\n", REG_SERCOM4_USART_INTENCLR);
  Serial.print(buffer);
  sprintf(buffer, "REG_SERCOM4_USART_INTENSET= %08x\n", REG_SERCOM4_USART_INTENSET);
  Serial.print(buffer);
  sprintf(buffer, "REG_SERCOM4_USART_INTFLAG = %08x\n", REG_SERCOM4_USART_INTFLAG);
  Serial.print(buffer);
  sprintf(buffer, "REG_SERCOM4_USART_STATUS  = %08x\n", REG_SERCOM4_USART_STATUS);
  Serial.print(buffer);
  sprintf(buffer, "REG_SERCOM4_USART_SYNCBUSY= %08x\n", REG_SERCOM4_USART_SYNCBUSY);
  Serial.print(buffer);
  sprintf(buffer, "REG_SERCOM4_USART_DATA    = %08x\n", REG_SERCOM4_USART_DATA);
  Serial.print(buffer);
  sprintf(buffer, "REG_SERCOM4_USART_DBGCTRL = %08x\n", REG_SERCOM4_USART_DBGCTRL);
  Serial.print(buffer);
}
test pmux values
00      PA11  PMUX(02) SERCOM0 P3(UART)
01      PA10  PMUX(02) SERCOM0 P2(UART)
03      PA09  GPIO I

Come to think of it, that's weird too, since the sketch doesn't use Serial on pin 0/1.

Oh!

void SERCOM4_Handler() { TESTSERIAL.IrqHandler(); }

/******  this is clearly wrong ****/
#define TESTSERIAL Serial1
/*********  You want to use the TESTSERIAL Uart you just created! *****/

void setup()

Still not working, though :frowning:

Hmm. The datasheet says that TXPO=0 (UART_TX_PAD_0) means that XCK (if applicable) is on PAD 1.

But it doesn't say what happens if RXD is also assigned to PAD1, and/or what happens if XCK is not applicable (as in this case, where you're using the internal clock.)

There don't seem to be a TXPO that sets the TX to pad 1 without "complications."

I'm not sure if this is what is causing problems :frowning:

Ok, I feel a bit dumb. @westfw You are right. The #TESTSERIAL macro is a copy-paste error that should not have been there.

Once I realized this mistake, I fixed my test program and redid the passthru test. Now it does work, with no issues. I have now fixed and restructured my firmware project so that it now works through the intended pins correctly.

Thanks again for pointing this out. However, I have no idea why your own board still won't work yet.

Please post the working code. It would help others who come across this or a similar problem

I also fixed the sample code I quoted in the original post. But for the sake of clarity, here is the final sample code that works on my board:

#include <Arduino.h>
#include "wiring_private.h"

#define DEBUG_SERIAL    SerialUSB

#define PIN_TESTSERIAL_RX (16ul)  // PB09
#define PIN_TESTSERIAL_TX (15ul)  // PB08
#define PAD_TESTSERIAL_RX (SERCOM_RX_PAD_1)
#define PAD_TESTSERIAL_TX (UART_TX_PAD_0)
Uart TESTSERIAL(&sercom4, PIN_TESTSERIAL_RX, PIN_TESTSERIAL_TX, PAD_TESTSERIAL_RX, PAD_TESTSERIAL_TX);
void SERCOM4_Handler() { TESTSERIAL.IrqHandler(); }

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

    uint32_t t1 = millis();
    while (!DEBUG_SERIAL && millis() - t1 <= 100);

    TESTSERIAL.begin(9600);

    pinPeripheral(PIN_TESTSERIAL_RX, PIO_SERCOM_ALT);
    pinPeripheral(PIN_TESTSERIAL_TX, PIO_SERCOM_ALT);
}

void loop()
{
    int c;
    while (TESTSERIAL.available()) {
        c = TESTSERIAL.read();
        if (c >= 0) DEBUG_SERIAL.write((uint8_t)c);
    }
    while (DEBUG_SERIAL.available()) {
        c = DEBUG_SERIAL.read();
        if (c >= 0) TESTSERIAL.write((uint8_t)c);
    }
}

Thanks, but in the future please don't modify the original post, as that can make the thread difficult or even impossible for others to follow.