On an LPC55S69-EVK, I2C_MasterTransferBlocking() hangs permanently (no timeout, confirmed via SDK source inspection) on the very first transaction attempted after I2C_MasterInit(). Every peripheral configuration register I can inspect - CFG, PSELID, CLKDIV, MSTTIME, IOCON pin function, and the Secure AHB Controller's peripheral access rules - reads back as correctly configured. However, a direct multimeter measurement of the physical SDA/SCL lines, taken while execution is frozen mid-hang inside the driver, shows both lines sitting at a clean, unchanged idle ~3.3V - meaning the I2C master's internal state machine never actually asserts a START condition onto the physical bus at all, despite software believing it did.
Reading fsl_i2c.c directly: I2C_MasterTransferBlocking() calls I2C_MasterStart() then I2C_MasterCheckStartResponse(). The latter calls I2C_PendingStatusWait(), which polls kI2C_MasterPendingFlag in a loop with no timeout at all when I2C_RETRY_TIMES is 0/undefined (confirmed this is the active code path in this project - the #else branch with an unconditional while(...) and no exit condition). Execution never returns from this call, confirmed by a shared-memory diagnostic checkpoint written immediately before the call, which is reached, and one written immediately after, which is never reached, even after 5+ seconds of continuous execution.
Register Address Value Meaning
| I2C4->STAT | 0x5008A000 | 0x00000000 | Idle. MSTPENDING=0, MSTSTATE=0, no error flags |
| I2C4->CFG | 0x5008A800 | 0x00000001 | MSTEN=1 - master mode enabled |
| FLEXCOMM4->PSELID | 0x5008AFF8 | 0x1020F3 | PERSEL=3 (I2C personality genuinely selected), I2CPRESENT=1 |
| I2C4->CLKDIV | 0x5008A814 | 74 | Non-zero, plausible divider |
| I2C4->MSTTIME | 0x5008A824 | 102 | Non-zero, plausible SCL timing |
| IOCON->PIO[1][20] (P1_20/SCL) | 0x500010D0 | 0x323 | FUNC=3 (Flexcomm alt-function), pull-up + open-drain enabled |
| IOCON->PIO[1][21] (P1_21/SDA) | 0x500010D4 | 0x323 | Same - correctly configured |
| AHB_SECURE_CTRL FLEXCOMM4_RULE | 0x500AC124 | 0 | 0b00 = "Non-secure and Non-privilege user access allowed" (most permissive) |
| AHB_SECURE_CTRL IOCON_RULE | 0x500AC130 | 0 | Same - fully open, non-secure access unrestricted |
Every one of these is exactly what a correctly-configured, unrestricted, ready-to-transact I2C master should show.
With execution frozen mid-hang (confirmed via the checkpoint diagnostic, STAT still reading 0x00000000 at this exact moment):
Both lines are indistinguishable from a fully idle, untouched bus. The physical pins never moved at all, despite I2C_MasterStart() having already executed (it's called before I2C_MasterCheckStartResponse(), where we're actually hung) and despite every register above showing a fully configured, unrestricted master.
Given every software-visible register indicates a correctly configured, unrestricted I2C master, but the physical SDA/SCL pins provably never move (confirmed by direct voltage measurement while frozen inside the driver call), what remaining hardware-level step is required to get the I2C4 peripheral to actually drive its pads on this part?
Specifically:
Happy to provide the full project, a logic analyzer capture, or any additional register dump on request.
Posting the resolution in case anyone finds this thread later with a similar symptom.
IOCON_PIO_FUNC3 does not route P1_20/P1_21 to FC4 I2C on this board. The correct alternate function is FUNC5, with open-drain disabled at the IOCON level (the I2C peripheral's own CFG register handles open-drain behavior internally - enabling it again at the IOCON pad level was also wrong).
I had derived FUNC3 by reading the pin_signal ordering in a comment (PIO1_20/FC7_RTS_SCL_SSEL1/CT_INP14/FC4_TXD_SCL_MISO_WS/PLU_OUT2) and assuming 0-indexed position = function number. That assumption was incorrect for this pin.
With the wrong alternate function selected, every I2C-internal register still read back as perfectly configured:
None of that depends on the physical pin actually being connected to the I2C block - PSELID and CFG are entirely internal to the Flexcomm/I2C logic, independent of which alternate function the IOCON mux happens to route to. So the peripheral was internally fully configured and "ready," while the physical SDA/SCL pins were never actually wired to it at all. This produced a permanent hang in I2C_MasterTransferBlocking() (confirmed via SDK source: no timeout when I2C_RETRY_TIMES is 0) with zero error flags ever raised, and - the piece that finally proved it - a multimeter measurement showing the physical bus completely unchanged at idle voltage even while frozen mid-transaction, since the pins genuinely were never driven.
Created a fresh, separate single-core test project for the same board and used the MCUXpresso wizard's own auto-generated BOARD_InitACCELPins() function (this board has an onboard accelerometer wired to the same I2C4 bus, so the wizard ships a working, verified pin mux for these exact pins). Reading that function's actual generated source showed IOCON_PIO_FUNC5 with open-drain disabled - directly contradicting my own hand-derived FUNC3/open-drain-enabled configuration. Switching to match NXP's own verified values immediately resolved the permanent hang; a subsequent, unrelated address-NAK (0x77 not acknowledging) turned out to just be a loose header-pin contact, resolved by moving the connection to different header pins.
If a Flexcomm peripheral's own registers (PSELID, CFG, CLKDIV, etc.) all read back as correctly configured but the physical bus never shows any activity (confirmed via multimeter/scope) - don't trust a hand-derived alternate function number from reading the pin_signal comment ordering. Cross-check against any wizard-auto-generated pin mux function this SDK ships for the same physical pins on the same board (peripherals the eval board itself uses - accelerometers, codecs, etc. - are a good source of a genuinely verified reference), or regenerate the pin mux via the MCUXpresso Pins tool directly rather than hand-writing IOCON_PinMuxSet() calls from an assumed function index.
Thanks again to everyone who engaged with the earlier posts in this thread and the original multicore boot thread - the register-level verification work across both investigations is what eventually made this contradiction (everything configured correctly, yet nothing on the bus) visible enough to chase down.