Muon: D9/D10 Pins Stuck at 0.7V When Using MCP23S17 IO Expander - Hardware Constraint

I wish this post had existed before, so I didn’t have to spend hours trying to debug. Posting it for anyone in the future.

The problem:

When using the Muon's built-in MCP23S17 IO expander (or any device on the primary SPI bus), pins D9 and D10 cannot be used as GPIO. They remain stuck at ~0.7V instead of outputting proper HIGH (3.3V) or LOW (0V) voltages.

Symptoms:

  • digitalWrite(D9, HIGH) results in ~0.7V instead of 3.3V

  • Pin appears to be floating/tri-state

  • Issue persists across firmware reflashes (only power cycle fixes it)

  • Occurs on all Muon boards tested (not board-specific)

Root cause: RTL8721DM Pin Multiplexing Constraint. This is not a software bug. It's a hardware design constraint of the RTL8721DM chip.

Technical Explanation

The RTL8721DM has two SPI peripherals with the following pin mappings:

Primary SPI (used by Muon's built-in MCP23S17):

  • D11 = SPI_MISO

  • D12 = SPI_MOSI

  • D13 = SPI_SCK

  • D8 = SPI_SS (MCP23S17 chip select)

SPI1 Primary Pins:

  • D3 = SPI1_MISO

  • D2 = SPI1_MOSI

  • D4 = SPI1_SCK

SPI1 Alternate Pins (THE PROBLEM):

  • D9 = SPI1_MOSI (alternate)

  • D10 = SPI1_MISO (alternate)

When the primary SPI is active (e.g., communicating with the MCP23S17), the RTL8721DM's pin multiplexing hardware reserves the SPI1 alternate pins (D9/D10) at the silicon level. This appears to be a design decision by Realtek to allow fast switching between SPI1 pin configurations, but it creates an undocumented constraint for users.

The SPI1 primary pins are fundamentally incompatible with using the MCP23S17 You must use different pins.

1 Like

Which version of DeviceOS did you test this on?
This is not the expected behaviour, and could likely be addressed (pending engineering investigation).

2 Likes

Thank you for looking into this! I really appreciate the quick response. Here are the details:

I tested on versions 5.9.0, and 6.3.3 on 3 Muon boards.

Minimal Reproduction Code

#include "MCP23S17.h"
#include "Particle.h"

#define IOEX_PA0_PIN 1  
#define IOEX_PB7_PIN 16 
#define MCP_CS_PIN D23 

MCP ioExpander(0, MCP_CS_PIN);

void setup()
{
    ioExpander.pinMode(0x0000);  // All 16 pins as outputs
    ioExpander.digitalWrite(IOEX_PA0_PIN, LOW);
    ioExpander.digitalWrite(IOEX_PB7_PIN, LOW);
}

void loop() {
  // digitalWrite(D9,HIGH) ... or D10
}

Observed Behavior

What happens:

  1. Flash firmware that initializes MCP23S17
  2. Measure D9 with multimeter
  3. Expected: D9 toggles between 0V (LOW) and 3.3V (HIGH)
  4. Actual: D9 permanently stuck at ~0.7V regardless of digitalWrite() calls

Pin voltage measurements:

  • digitalWrite(D9, HIGH) → ~0.9V (should be 3.3V)
  • digitalWrite(D9, LOW) → ~0.7V (should be 0V)
  • Voltage remains constant at ~0.7V (floating/tri-state condition)

Key Finding: D9 is Permanently Locked (Not Re-triggered)

Initially we thought MCP23S17 SPI operations were "re-triggering" the lock, but testing shows D9 is permanently locked from the moment ioExpander.pinMode() is called.

  • Calling SPI1.end() has NO EFFECT
  • Calling pinMode(D9, OUTPUT) has NO EFFECT
  • Once MCP23S17 is initialized, D9 and D10 cannot be used as GPIO at all

Persistence Across Reflashes

The lock persists across software resets but not hardware resets:

This suggests the pin configuration registers are not being reset during software resets.

Hardware Tested

Pin Configuration Details

Primary SPI (used by MCP23S17):

  • D11 = SPI_MISO
  • D12 = SPI_MOSI
  • D13 = SPI_SCK
  • D23 = MCP23S17 chip select

SPI1 Alternate Pins (where issue occurs):

  • D9 = SPI1_MOSI (alternate) ← Problem pin
  • D10 = SPI1_MISO (alternate) ← Likely same issue

Suspected cause: When primary SPI is initialized, the RTL8721DM also configures SPI1 alternate pins at the hardware level, preventing GPIO control.

Attempted Workarounds (All Failed)

Test 1: Call SPI1.end() to release pins

void setup() {
    ioExpander.pinMode(0x0000);  // Init MCP
    SPI1.end();                  // Try to release SPI1
    pinMode(D9, OUTPUT);
    digitalWrite(D9, HIGH);
}

Result: NO EFFECT - D9 still reads 0.7V

Test 2: Call SPI.end() and SPI1.end() before MCP init

void setup() {
    SPI.end();
    SPI1.end();
    delay(100);
    SPI.begin();
    ioExpander.pinMode(0x0000);
    pinMode(D9, OUTPUT);
}

Result: NO EFFECT - D9 still locked

Thank you for investigating this! Understanding whether this is fixable or a hardware limitation would be very helpful.

Reporting back that setting GPB1 to high on setup seems to have fixed the issue. Able to get 3.3V from D9/D10.

We dove into the BL1530 chip documentation https://www.belling.com.cn/media/file_object/bel_product/BL1530/datasheet/BL1530_V1.2_en.pdf to find a solution.

#define IOEX_PB1_PIN 10 
ioExpander.digitalWrite(IOEX_PB1_PIN, HIGH);

I would appreciate it if this were added to the documentation.

Thanks