Using more than 4 GPIO pins on the XIAO nRF52840 Sense

I need 6 distinct PWM outputs, multiple simultaneously. I’m able to use pins 0-3 just fine, however when also using any pin 4-10 the board becomes unresponsive and I need to get it into bootloader mode for recovery. The board is connected via USB and has nothing connected to its pins.

I realised that pins 4-5, 6-7 and 8-10 are used for I2C, UART and SPI respectively and thought that writing to them might mess up some internal communication. But when just using one of these pins without also using pins 0-3, everything is fine.

Here’s a small example sketch for reproducing the issue:

const int TEST_PIN = 10; // MOSI for SPI

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(0, OUTPUT);
  pinMode(1, OUTPUT);
  pinMode(2, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(TEST_PIN, OUTPUT);
}

void loop() {
  byte value = millis() % 255;
  digitalWrite(LED_BUILTIN, value < 128 ? HIGH : LOW);
  analogWrite(0, value);
  analogWrite(1, value);
  analogWrite(2, value);
  analogWrite(3, value);
  //analogWrite(TEST_PIN, value); // this will make the board unresponsive
}

Commenting in the last analog write will cause the issue. I don’t understand what’s going on here, any clarification would be appreciated.

according to this image the left pins are the only analog pins on that device

All analog pins on Xiao SAMD21

Yes, but as mentioned the issue also occurs when using e.g. pin 4 (which is labeled as analog) in combination with pins 0-3.

Hi,
Correct me If I am wrong, but I think analogWrite in Arduino means a PWM output on digital pins and potentially an analog output on DAC pins. So issue can be with timers or PWM generators so this can be a bug. Check if all pins of your XIAO can generate PWM.

It has nothing to do with analog pins at all until you try to analogRead them.

Best regards,
Electry

All pins should support PWM according to this:

And it’s working fine as long as the pins are used individually. But that’s not enough for my use case.

Besides an explanation for why that’s the case, I’d also appreciate any workaround ideas that allow me to output more PWM signals than I have usable pins (I don’t have much experience with electronics hardware).

I already considered using an 8 channel analog multiplexer, however that seems to only allow me to control one output at a time. I then checked if I could make use of an 8 bit shift register which allows simultaneous outputs, but these seem to not work well with PWM signals. Is there some other integrated circuit that works like a mix between these two?

might be a power brownout issue too

I consulted the Nordic webpage. The issue is the nRF52840 has only 4 hardware PWM generators. Maybe someone did not implement error handling correctly, so if you try to use more, the program crashes. You can try to find software-generated PWM or explore PWM generation using timers. It will not be as precise as PWM generators.

If additional board to generate PWM is not an issue, I would consider something like: Adafruit 16-Channel 12-bit PWM/Servo Driver - I2C interface [PCA9685] : ID 815 : Adafruit Industries, Unique & fun DIY electronics and kits

If you will not drive a larger load with the PWM signal, the manufacturer is not so important, the IC is. So you can find a PCA9685 breakout board that suits your needs. This is not the only solution you can use, there are more solutions around to generate PWM. You can try to google them.

2 Likes

If you use non-mbed for BSP, you can use 16 PWMs without any problem.
ThisTry this.

\Arduino15\packages\Seeeduino\hardware\nrf52\1.1.8\cores\nRF5\HardwarePWM.h and .cpp

//----------------------------------------------------------------------------------------------
//Board Library : Seeed nRF52 Borads 1.1.8
//Board Select  : Seeed nRF52 Borads / Seeed XIAO nRF52840 Sense 
//----------------------------------------------------------------------------------------------
#include <Arduino.h>
#include <Adafruit_TinyUSB.h>

void setup() {
  Serial.begin(115200);
  while(!Serial);
  delay(2000);
}

void loop() {
  for(uint8_t i = 0; i < 255; i++) {
    Serial.print("PWM0 usedChannel "); Serial.println(HwPWM0.usedChannelCount());
    Serial.print("PWM0 freeChannel "); Serial.println(HwPWM0.freeChannelCount());
    Serial.print("PWM1 usedChannel "); Serial.println(HwPWM1.usedChannelCount());
    Serial.print("PWM1 freeChannel "); Serial.println(HwPWM1.freeChannelCount());
    Serial.print("PWM2 usedChannel "); Serial.println(HwPWM2.usedChannelCount());
    Serial.print("PWM2 freeChannel "); Serial.println(HwPWM2.freeChannelCount());
    Serial.print("PWM3 usedChannel "); Serial.println(HwPWM3.usedChannelCount());
    Serial.print("PWM3 freeChannel "); Serial.println(HwPWM3.freeChannelCount());

    Serial.print("PWM Value "); Serial.println(i);
    analogWrite(LED_RED, i);
    analogWrite(LED_GREEN, i*2);
    analogWrite(LED_BLUE, i*3);
    analogWrite(D10, i*4);
    analogWrite(D9, i);
    analogWrite(D8, i*2);
    analogWrite(D7, i*3);
    analogWrite(D6, i*4);  
    delay(20); 
  }
  delay(1000);
}

Maybe time to consider the PCA9685 16-Channel, 12-Bit PWM Fm+ I²C-Bus LED Controller operating on I²C.

Shouldn’t be a problem if nothing is connected to the pins I guess?

Thanks for investigating, that checks out! I modified my example sketch to verify if I can use pins 4-10 as long as I don’t use more than 4 pins in total - and I can!

Unfortunately I don’t understand what that means, could you elaborate? I tried running your example but only got an error from TinyUSB telling me that the board (Seeed XIAO BLE Sense - nRF52840) is not supported.

Thanks for pointing me to this. As @electry suggested, I opted for an IC not manufactured by Adafruit, as I can get 5 for the same price on AliExpress.

I appreciate the input from everyone! To sum up:

It seems to not be possible to output more than 4 PWM signals due to hardware limitations. To work around that, either generate the PWM signal using software or use an external IC like the PCA9685.

Unfortunately I don’t understand what that means, could you elaborate?

XIAO_nRF52840 has two different BSP(Board Service Package)…
If you search for “nRF52” in the ArduinoIDE Board Manager, you will find “Seeed nRF52 Boards (non-mbed)” and “Seeed nRF52 mbed-enabled Boards (mbed)”.
The “Seeed nRF52 Boards (non-mbed)” will run 16 PWMs. On my desk XIAO is running 8 PWMs with 2 modules (4 channels/module).

1 Like

Hi there,
You are using the wrong BSP. Switch to the correct one and it works fine.
HTH
GL :slight_smile: PJ
:v:

Thanks for the hint & clarification @msfujino. I tried switching to the non-mbed version but that caused a series of compilation issues. First was

undefined reference to Serial

which could be resolved by manually including

#include “Adafruit_TinyUSB.h”

However that caused

adafruit-nrfutil/macos/adafruit-nrfutil: permission denied

which could be resolved by manually granting the permission using chmod.

And then I finally ended up with this issue, which I wasn’t motivated enough to resolve yet:

Man, why is the Arduino related tooling always such a pain to work with. Glad that I’m used to quirky issues from developing Android apps since a decade :smiling_face_with_tear:

1 Like

I compiled it with ArduinoIDE 2.3.2 on Linux and it uploaded without any problems. The only thing is that I did the following operation when I installed IDE2.3.2.

sudo apt install python3-pip
sudo pip install pyserial
pip install --user adafruit-nrfutil
sudo usermod -a -G dialout ‘xxxx’

So I finally got around the last remaining issue:

Traceback (most recent call last):
  File "__main__.py", line 317, in <module>
  File "click/core.py", line 1134, in __call__
  File "click/core.py", line 1040, in main
  File "click/_unicodefun.py", line 100, in _verify_python_env
RuntimeError: Click will abort further execution because Python was configured to use ASCII as encoding for the environment. Consult https://click.palletsprojects.com/unicode-support/ for mitigation steps.

This system lists some UTF-8 supporting locales that you can pick from. The following suitable locales were discovered: af_ZA.UTF-8, am_ET.UTF-8, be_BY.UTF-8, bg_BG.UTF-8, ca_ES.UTF-8, cs_CZ.UTF-8, da_DK.UTF-8, de_AT.UTF-8, de_CH.UTF-8, de_DE.UTF-8, el_GR.UTF-8, en_AU.UTF-8, en_CA.UTF-8, en_GB.UTF-8, en_IE.UTF-8, en_NZ.UTF-8, en_US.UTF-8, es_ES.UTF-8, et_EE.UTF-8, eu_ES.UTF-8, fi_FI.UTF-8, fr_BE.UTF-8, fr_CA.UTF-8, fr_CH.UTF-8, fr_FR.UTF-8, he_IL.UTF-8, hr_HR.UTF-8, hu_HU.UTF-8, hy_AM.UTF-8, is_IS.UTF-8, it_CH.UTF-8, it_IT.UTF-8, ja_JP.UTF-8, kk_KZ.UTF-8, ko_KR.UTF-8, lt_LT.UTF-8, nl_BE.UTF-8, nl_NL.UTF-8, no_NO.UTF-8, pl_PL.UTF-8, pt_BR.UTF-8, pt_PT.UTF-8, ro_RO.UTF-8, ru_RU.UTF-8, sk_SK.UTF-8, sl_SI.UTF-8, sr_YU.UTF-8, sv_SE.UTF-8, tr_TR.UTF-8, uk_UA.UTF-8, zh_CN.UTF-8, zh_HK.UTF-8, zh_TW.UTF-8
[38295] Failed to execute script __main__

exit status 1

Compilation error: exit status 1

It’s an open issue in the Arduino IDE issue tracker since 2022:

Setting values for LC_ALL and LANG (as suggested by the page linked in the error message) did nothing. Downgrading the Arduino IDE seems to work, but the less drastic fix turned out to be replacing the adafruit-nrfutil executable that is shipped by Seeed nRF52 Boards with the one that comes with Seeed nRF52 mbed-enabled Boards. In my case, these were located here:

~/Library/Arduino15/packages/Seeeduino/hardware/nrf52/1.1.8/tools/adafruit-nrfutil/
~/Library/Arduino15/packages/Seeeduino/hardware/mbed/2.9.2/tools/adafruit-nrfutil/

I initially tried overwriting it with the version you get from pip install --user adafruit-nrfutil as suggested by @msfujino and also with the versions from the official GitHub releases, but none of these worked for me. Luckily the version that ships with mbed 2.9.2 did the trick.

Steppschuh,
Did you finally achieve “more than four PWMs”?

Yes, I already marked your hint to switch to the non-embed BSP as solution. Thanks again everyone for all your input!

1 Like

That’s because the mbed (sense) version needs spi/i2c for the embedded sensors, i assume.