Skip to content

angeloINTJ/TwoWirePIO_RP2040

Repository files navigation

TwoWirePIO_RP2040

TwoWire-compatible I2C using PIO+DMA for RP2040. Unlimited buses on any GPIO pins.

License: MIT PlatformIO Platform: RP2040

Overview

The RP2040 has only two hardware I2C controllers (I2C0 and I2C1). In large projects this quickly becomes a limitation — many I2C devices have only two possible addresses, requiring multiplexers like the TCA9548A.

TwoWirePIO_RP2040 eliminates this limitation using the RP2040's PIO (Programmable I/O) to create as many I2C buses as needed, with an API fully compatible with Arduino's Wire.h (TwoWire).

Features

  • Unlimited I2C buses — up to 4 independent buses on any GPIO pins
  • PIO+DMA burstRead — zero-CPU 8-byte burst transfers via DMA
  • Drop-in Wire replacementWirePIO bus(2,3); bus.begin();
  • GPIO bit-bang fallback — works even without PIO resources
  • Master + Slave modes
  • Async DMA transfers with callback
  • Timeout + NACK detection
  • Compatible with BMx280PIO_RP2040, Adafruit_BME280, SSD1306, U8g2, and more

Requirements

  • Raspberry Pi Pico or any RP2040-based board
  • Earle Philhower's arduino-pico core (install via Boards Manager)
    • In Arduino IDE: File → Preferences → Additional Boards Manager URLs: https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
    • Then: Tools → Board → Boards Manager → search "Raspberry Pi Pico/RP2040"
  • Arduino IDE 1.8.10+ or PlatformIO

Note: This library uses RP2040 PIO hardware. Only compatible with the earlephilhower/arduino-pico core, not the Arduino Mbed OS core.

Quick Start

#include <WirePIO.h>

// Create a bus on any GPIO pins
WirePIO bus(2, 3);  // SDA=GPIO2, SCL=GPIO3

void setup() {
    Serial.begin(115200);
    bus.begin();

    // Use exactly like Wire:
    bus.beginTransmission(0x76);
    bus.write(0xD0);         // Chip ID register
    bus.endTransmission(false);

    bus.requestFrom(0x76, 1);
    if (bus.available()) {
        Serial.println(bus.read(), HEX);
    }
}

Multiple Buses

Each WirePIO bus uses 2 PIO state machines (TX + RX). The RP2040 has 8 SMs total across pio0 and pio1, supporting up to 4 independent buses.

WirePIO bus1(2, 3);   // Sensors on pio0
WirePIO bus2(4, 5);   // Display on pio0
WirePIO bus3(6, 7);   // ADC on pio1
WirePIO bus4(8, 9);   // EEPROM on pio1

bus1.begin();
bus2.begin();
bus3.begin();
bus4.begin();

API

WirePIO implements the full TwoWire / HardwareI2C interface:

Method Description
begin() Initialize as I2C master
begin(uint8_t addr) Initialize as I2C slave
end() Shut down and release resources
setClock(freqHz) Set bus frequency (100000 or 400000)
getClock() Get current bus frequency
setSDA(pin) / setSCL(pin) Change pins (before begin())
setPIO(pio) Set PIO block (pio0/pio1, before begin())
setBufferSize(size) Set TX/RX buffer size (default 256)
beginTransmission(addr) Start a master write transaction
write(byte) / write(buf, len) Append data to TX buffer
endTransmission([stop]) Send TX buffer, return error code
requestFrom(addr, n, [stop]) Read n bytes from slave
available() Bytes available in RX buffer
read() Read one byte, -1 if empty
peek() Peek next byte without consuming
onReceive(callback) Slave receive callback
onRequest(callback) Slave request callback
scan() Scan bus and print addresses
burstRead(addr, reg, buf, len) PIO+DMA burst read (≤8 bytes)

Async API

Method Description
writeAsync(addr, buf, len) Non-blocking write
readAsync(addr, buf, len) Non-blocking read
writeReadAsync(addr, wbuf, wlen, rbuf, rlen) Combined non-blocking
finishedAsync() Check if async transfer done
onFinishedAsync(callback) Completion callback

Error Codes

Code Meaning
0 Success
1 Data too long for buffer
2 NACK on address
3 NACK on data
4 Other error
5 Timeout

Architecture

Application
    │
├─── Adafruit_BME280
├─── Adafruit_SSD1306
├─── INA219
├─── ADS1115
│
    ▼
WirePIO (TwoWire-compatible API)
    │
    ├── WirePIOTransport
    │   ├── GPIO bit-bang (any pin pair)
    │   └── PIO + DMA (zero-CPU burst)
    │
    └── WirePIOSlave
        └── Hardware I2C peripheral

Pin Mapping

  • SDA: Any GPIO pin. The PIO drives it LOW actively, releases to Hi-Z for HIGH (open-drain emulation). External pull-up resistor (2.2k–4.7kΩ) required.
  • SCL: Any GPIO pin. The PIO drives it actively (push-pull via side-set) for clean clock edges. No external pull-up strictly required for master mode, but recommended for multi-master or slave compatibility.

Performance

  • SCL frequency: ~65–87 kHz (conservative, spec-compliant timing)
  • PIO clock divider: sys_clk / (freq * 20) — 20 PIO cycles per I2C bit
  • DMA burst: 2 DMA channels per bus (TX → PIO, PIO → RX)
  • CPU usage during transfer: 0% (DMA handles everything)

Limitations

  • PIO program: 31 instructions (fits within RP2040's 32-instruction limit)
  • Max 8 PIO state machines per RP2040 → up to 8 I2C buses simultaneously (or fewer if other PIO programs are loaded)
  • Slave mode uses hardware I2C peripheral (requires I2C-capable pins)
  • No clock stretching in PIO master

Installation

Arduino IDE

  1. Open Library Manager (Tools → Manage Libraries...)
  2. Search for "WirePIO"
  3. Click Install

Manual install: Download this repository as ZIP and use Sketch → Include Library → Add .ZIP Library...

PlatformIO

lib_deps = TwoWirePIO_RP2040

Or with a specific version:

lib_deps = angeloINTJ/TwoWirePIO_RP2040 @ ^1.3.2

Pico SDK (CMake)

add_subdirectory(path/to/TwoWirePIO_RP2040)
target_link_libraries(your_target TwoWirePIO_RP2040)

Changelog

v1.3.5 (2026-07-14)

  • I2C clock divider correction — Fix PIO clock divider formula (*20.0f*18.0f) for accurate I2C timing. Previous value produced slightly slower SCL than specified.

v1.3.4 (2026-07-12)

  • Fix library name consistency (TwoWirePIO_RP2040WirePIO) across all metadata files
  • Complete README rewrite: fix title, API gaps, misleading examples
  • Fix author name encoding (Angelo Moises AlvesÂngelo Moisés Alves)

v1.3.2 (2026-07-12)

  • Prepare for Arduino Library Manager submission
  • Add error statistics counters to WirePIO
  • Restore PIO transport to v1.2.0 for BMx280PIO compatibility
  • Revert unstable DMA IRQ changes

License

MIT — see LICENSE

Author

Ângelo Moisés Alves — @angeloINTJ

About

TwoWire-compatible I2C library for RP2040 using PIO+DMA — unlimited buses on any GPIO pins.

Topics

Resources

License

Stars

3 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors