Electronics Design AU
nRF

How Do You Configure the nRF52's TWI/TWIM I2C Peripheral?

Last updated 19 August 2026 · 10 min read

Direct Answer

The nRF52 series exposes I2C through two peripheral variants sharing the same underlying hardware block: the legacy TWI, a blocking, interrupt-per-byte master that requires firmware to service TXDREADY/RXDREADY for every byte, and TWIM, an EasyDMA-based master that transfers an entire buffer autonomously once started, with the CPU only needing to respond to a STOPPED or ERROR event. TWIM is the recommended choice for any new nRF52 design: lower CPU overhead, lower power (a transfer can run while the CPU sleeps if started via PPI/DPPI), and it's the variant Nordic's nrfx driver and SDKs actively develop. Both nRF52832 and nRF52840 provide exactly two TWI/TWIM-capable instances, each sharing its hardware ID with an SPI/SPIM/SPIS instance, so enabling TWI0 makes SPI0 unavailable on that same ID and vice versa. Pins are freely assignable to SCL/SDA via PSEL registers rather than fixed to an alternate-function table, though P0.09/P0.10 default to NFC antenna use and need a UICR change before they're usable as TWI pins. Clock stretching from a slave is handled correctly in hardware, but there's no hardware timeout for a slave that stretches indefinitely or a bus wedged by a device holding SDA low, so firmware needs its own timeout and a GPIO-based bus-clear recovery sequence to handle that case.

Detailed Explanation

Every nRF52 device implements I2C as one of two register-compatible peripheral personalities sharing the same physical hardware block: TWI, the original blocking implementation, and TWIM, an EasyDMA-based master added later in the nRF52 generation. Both speak standard I2C on the wire; the difference is entirely in how firmware moves data through them. This page covers configuring TWIM as the primary path, where TWI still fits, and the bus-recovery sequence every design with removable or hot-pluggable I2C peripherals needs. For the I2C protocol itself, addressing, and pull-up sizing, see What Is I2C?; this page assumes that background and focuses on the nRF52-specific peripheral.

TWI vs TWIM: Blocking vs EasyDMA

The legacy TWI peripheral moves one byte at a time. Firmware writes a byte to TXD, waits for the TXDREADY event, writes the next byte, and repeats for the length of the transfer, either polling the event register or servicing it in an interrupt handler. Every byte costs a CPU round trip, and the CPU cannot sleep meaningfully mid-transfer without added complexity.

TWIM replaces this with EasyDMA. Firmware sets up a source or destination buffer once, triggers a single task, and the peripheral moves the entire buffer autonomously, raising one STOPPED event (or an ERROR event on a NACK, overrun, or other fault) when the whole transfer finishes rather than one event per byte.

/* nrfx_twim: EasyDMA-based write to a device register, then read back */
nrfx_twim_t twim = NRFX_TWIM_INSTANCE(0);
nrfx_twim_config_t config = NRFX_TWIM_DEFAULT_CONFIG(SCL_PIN, SDA_PIN);
nrfx_twim_init(&twim, &config, NULL, NULL);
nrfx_twim_enable(&twim);

uint8_t reg = 0x00;
uint8_t data;
nrfx_twim_tx(&twim, DEVICE_ADDR, &reg, sizeof(reg), true);   /* true: no STOP, repeated start follows */
nrfx_twim_rx(&twim, DEVICE_ADDR, &data, sizeof(data));

Nordic's nrfx driver layer and the nRF Connect SDK both treat TWIM as the primary API; recent nrfx releases have deprecated the legacy blocking TWI driver rather than continuing to develop it alongside TWIM. Unless a design has a specific reason to stay on the older API, new nRF52 firmware should target TWIM directly.

Instance and Pin Constraints

Both the nRF52832 and the nRF52840 provide exactly two TWI/TWIM-capable hardware instances (commonly labelled instance 0 and instance 1 in Nordic's documentation and the nrfx driver), and this is a fixed silicon limit rather than a configuration choice, so check the specific part's product specification before assuming otherwise. Each instance shares its underlying register block and interrupt with an SPI/SPIM/SPIS instance of the same number: enabling TWIM0 makes SPIM0/SPIS0/legacy SPI0 unavailable at the same time, because only one personality of a shared hardware ID can be enabled at once. A design that needs both an I2C sensor and a dedicated SPI flash or display running concurrently needs to plan which peripheral goes on which instance ID before committing to a pin-out, not after.

Pin assignment is considerably more flexible than on MCUs with a fixed alternate-function table. SCL and SDA are set through the PSEL.SCL and PSEL.SDA registers (or the equivalent nrfx config struct fields), and in most designs any GPIO pin can be assigned to either signal, rather than being restricted to a small fixed set of AF-mapped pins. The practical exception worth knowing: P0.09 and P0.10 default to the NFC antenna function (NFC1/NFC2) and need the UICR.NFCPINS register configured before they behave as plain GPIO usable for TWI. The nRF52840 additionally exposes a second GPIO port (P1.00 through P1.15) alongside P0, giving more routing flexibility than the nRF52832's single port; the actual pins physically broken out still depend on the specific package, so confirm against the pinout for the exact variant in use, not just the die's full pin count.

EasyDMA Buffer Setup

TWIM's transfer buffers are configured through the TXD.PTR/TXD.MAXCNT and RXD.PTR/RXD.MAXCNT register pairs (or the buffer arguments to nrfx_twim_tx()/nrfx_twim_rx()), each holding a RAM address and a byte count. EasyDMA on the nRF52 series accesses RAM directly over the bus fabric; buffers must live in RAM, not flash, which matters for any code that tries to source a TWIM write directly from a const array placed in flash by the linker rather than a RAM-resident copy. This is the same EasyDMA constraint that applies to the SAADC and the other EasyDMA peripherals on the same silicon family.

A repeated-start sequence (write a register pointer, then read its value without releasing the bus) is a single logical transaction across two calls: the write call is issued with the stop condition suppressed, and the following read call completes it with a STOP. Getting this wrong, issuing a full STOP between the pointer write and the read, is a common cause of a device that intermittently NACKs or returns stale data, since some I2C peripherals rely on the repeated start to guarantee the internal register pointer hasn't been touched by another bus transaction in between.

Clock Stretching

TWI and TWIM both implement the I2C specification's clock-stretching mechanism correctly as a master: if a slave holds SCL low after the master releases it, the nRF52 peripheral waits rather than proceeding with the next clock edge, exactly as the I2C specification requires. What the peripheral does not provide is a hardware timeout on how long it's willing to wait. A slave that stretches indefinitely, or a bus that's actually wedged rather than legitimately stretching, will hold the peripheral's transfer in progress with no automatic recovery. Firmware needs its own bounded timeout around every TWI/TWIM transaction; a TIMER-based watchdog is a natural fit here, and the same PPI/DPPI interconnect used for other autonomous chains can trigger a forced peripheral disable if a transaction runs longer than the slowest legitimate stretch the design expects, without the CPU needing to poll for it.

Bus Lockup and Recovery

The most common nRF52 I2C bus lockup follows the same pattern seen on any I2C master: a slave is interrupted mid-transaction (hot-unplugged, browning out, or externally reset) while it's holding SDA low, and because I2C is open-drain, no other device on the bus can force the line high again on its own. The TWI/TWIM peripheral's ERRORSRC register reports a NACK, overrun, or similar transaction-level fault, but it has no mechanism to clear a bus that's stuck at the electrical level; the peripheral is simply waiting on a line that will never release itself.

Recovery uses the same GPIO-based bus-clear technique described in the I2C bus-recovery specification, adapted to the nRF52's register set:

/* Disable TWIM, hand SCL/SDA back to plain GPIO, and clock the bus free */
nrfx_twim_disable(&twim);

nrf_gpio_cfg_output(SCL_PIN);
nrf_gpio_cfg_input(SDA_PIN, NRF_GPIO_PIN_NOPULL);

for (int i = 0; i < 9; i++) {
    if (nrf_gpio_pin_read(SDA_PIN)) break;   /* slave has released the bus */
    nrf_gpio_pin_clear(SCL_PIN);
    nrf_delay_us(5);
    nrf_gpio_pin_set(SCL_PIN);
    nrf_delay_us(5);
}

/* Manual STOP: SDA low-to-high while SCL is high */
nrf_gpio_cfg_output(SDA_PIN);
nrf_gpio_pin_clear(SDA_PIN);
nrf_delay_us(5);
nrf_gpio_pin_set(SDA_PIN);

/* Hand the pins back to TWIM and re-enable */
nrfx_twim_init(&twim, &config, NULL, NULL);
nrfx_twim_enable(&twim);

Because the nRF52's pin assignment is register-based rather than a fixed alternate-function mapping, reclaiming the pins for TWI/TWIM afterward is simply re-running the peripheral's normal init sequence with the same PSEL.SCL/PSEL.SDA values, with no separate alternate-function remap step to worry about, unlike MCUs where the pin has to be explicitly switched back to its peripheral mode.

Design Considerations

  • Default to TWIM over legacy TWI for new designs. EasyDMA's per-transfer rather than per-byte CPU involvement, combined with active driver support in nrfx and the nRF Connect SDK, makes TWIM the better default outside of code that must stay compatible with the older blocking API for legacy reasons.
  • Plan instance allocation before finalising a pin-out. With only two TWI/TWIM-capable IDs on both the nRF52832 and nRF52840, and each one shared with an SPI personality, a design using I2C plus a dedicated SPI device needs to confirm early which peripheral occupies which ID rather than discovering the conflict during bring-up.
  • Confirm P0.09/P0.10 aren't still reserved for NFC before routing TWI to them. The UICR NFC-pin default is a frequent first-bring-up surprise on designs that don't use NFC at all but still inherit the pins' default antenna assignment.
  • Implement a transaction timeout independent of the peripheral. Because TWI/TWIM has no built-in timeout for an indefinitely stretching or wedged slave, a design without an application-level timeout can hang a task indefinitely waiting on a transfer that will never complete on its own.
  • Keep the bus-clear recovery path ready for any hot-pluggable or field-serviceable I2C connector, not just as a rare fallback. The recovery sequence above only takes a few microseconds and is worth running automatically whenever a transaction times out, rather than requiring a manual reset. Firmware that pairs this kind of fault-tolerant peripheral configuration with the platform's low-power PPI/DPPI-driven design patterns is exactly the kind of nRF52 integration work Zeus Design's embedded firmware team delivers for connected hardware products.

Common Mistakes

  • Using legacy TWI in new code without a specific reason to avoid TWIM. This adds unnecessary per-byte CPU overhead and interrupt load compared to an EasyDMA transfer, and works against every other low-power design decision made elsewhere in the firmware.
  • Pointing a TWIM buffer at flash-resident const data. EasyDMA needs a RAM address; a buffer pointer into flash produces a transfer that silently fails or reads garbage rather than a clear compile-time error, since the register accepts any 32-bit address without validating it points at RAM.
  • Issuing a full STOP between a register-pointer write and the following read, breaking the repeated-start sequence many sensor and EEPROM devices rely on to guarantee an uninterrupted register access, and producing intermittent stale reads or NACKs that look like a flaky device rather than a protocol-sequencing bug.
  • Assuming the peripheral will time out on its own if a slave stretches too long or the bus wedges. It won't; there is no hardware timeout, so any code that calls a blocking TWIM transfer without an application-level bound can hang the calling task indefinitely on a single bad transaction.
  • Forgetting that TWI0/TWIM0 and SPI0/SPIM0 (and the 1-numbered equivalents) cannot run at the same time. A design that assigns an I2C sensor to instance 0 and later adds an SPI display on the same instance number without checking will hit a peripheral-enable conflict that's easy to miss until integration, since each personality's registers exist independently in the SDK until the shared hardware ID is actually enabled.

Frequently Asked Questions

Should new nRF52 designs still use the legacy TWI driver at all?
Generally no. Nordic's nrfx driver layer and the nRF Connect SDK both treat TWIM (and TWIS for slave mode) as the primary implementation, and recent nrfx releases have deprecated or removed the legacy blocking TWI driver in favour of it. The legacy TWI peripheral still exists in silicon and can be selected instead of TWIM on the same hardware ID, but there's rarely a reason to choose it for a new design over TWIM's EasyDMA transfers and lower CPU overhead.
Can TWIM run while the CPU is asleep?
A TWIM transfer that's already running continues via EasyDMA without CPU involvement, and the CPU can be asleep for the duration if the transfer was started by a PPI/DPPI-triggered TASKS_STARTTX rather than direct firmware action. See [how does PPI/DPPI work on the nRF52 and nRF53?](/questions/nrf52-ppi-dppi-peripheral-interconnect) for the event-to-task chaining mechanism this depends on. The CPU still needs to wake to process the result once the transfer's STOPPED or ERROR event fires, unless that too is chained into a further autonomous action.

References

Related Questions

Related Forum Discussions