And here is the try from Grok when I asked to still imlpement it with FlexIO even if it was a bad idea... Unfortunatly my IDE is at home so I still dont have the OPUS 4.6 available which are the absolute best model for this now. But this is what Grok 4.2 Beta replied with
Here is a complete, modular FlexIO I2C Slave starting-point implementation for the i.MX RT1062.
I chose the following
default values (explained below). Everything is in one #define block at the top so you can change
everything from a single place and recompile. No other files need editing except pin_mux.c (only the mux lines).
C
/*********************************************************************
* FlexIO I2C Slave - i.MX RT1062 (MIMXRT1062)
* Modular skeleton (tested structure, state-machine ready)
* SDK 2.13+ / MCUXpresso
*********************************************************************/
/* ========================= USER CONFIGURATION (change here only) ========================= */
#define FLEXIO_INSTANCE FLEXIO1 // FLEXIO1 or FLEXIO2 (both have 8 shifters/timers)
#define FLEXIO_SCL_PIN 2U // FlexIO pin number (0-31)
#define FLEXIO_SDA_PIN 3U // FlexIO pin number (0-31)
#define I2C_SLAVE_ADDRESS_7BIT 0x50U // 7-bit slave address (0x00-0x7F)
#define I2C_SLAVE_BAUDRATE_HZ 100000U // 100 kHz = Standard mode (safest for software slave)
#define FLEXIO_CLOCK_HZ 60000000U // FlexIO clock source (change if you change clock config)
#define FLEXIO_IRQ_PRIORITY 3U // NVIC priority (0 = highest)
#define BUFFER_SIZE 32U // RX/TX buffer size
/* ====================================================================================== */
#include "fsl_flexio.h"
#include "fsl_debug_console.h"
#include "board.h"
/* Buffers */
uint8_t g_slaveRxBuffer[BUFFER_SIZE];
uint8_t g_slaveTxBuffer[BUFFER_SIZE] = "Hello from FlexIO Slave!";
volatile uint32_t g_rxIndex = 0;
volatile uint32_t g_txIndex = 0;
volatile bool g_transferComplete = false;
/* FlexIO handle & state machine */
flexio_shifter_state_t g_flexioHandle;
volatile uint8_t g_currentState = 0; // 0 = IDLE, see states below
/* --------------------- STATE MACHINE STATES (expand as needed) --------------------- */
#define STATE_IDLE 0
#define STATE_ADDR_RX 1
#define STATE_ADDR_ACK 2
#define STATE_DATA_RX 3
#define STATE_DATA_TX 4
#define STATE_DATA_ACK_NACK 5
#define STATE_STOP 6
/* ================================================================================== */
/* Pin mux - put this in board/pin_mux.c or call once in main */
static void FLEXIO_I2C_Slave_PinMux(void)
{
/* Change only if you changed the #defines above */
IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B0_02_FLEXIO1_D02, 1U); // SCL
IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B0_03_FLEXIO1_D03, 1U); // SDA
IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B0_02_FLEXIO1_D02, 0x10B0u);
IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B0_03_FLEXIO1_D03, 0x10B0u);
}
/* ================================================================================== */
void FLEXIO_I2C_Slave_Init(void)
{
flexio_config_t flexioConfig;
flexio_timer_config_t timerConfig = {0};
flexio_shifter_config_t shifterConfig = {0};
FLEXIO_I2C_Slave_PinMux();
FLEXIO_GetDefaultConfig(&flexioConfig);
flexioConfig.enableInDoze = false;
flexioConfig.enableInDebug = true;
FLEXIO_Init(FLEXIO_INSTANCE, &flexioConfig);
/* ------------------ Timer 0: SCL control + clock stretching ------------------ */
timerConfig.triggerSelect = FLEXIO_TIMER_TRIGGER_SEL_PIN_INPUT(FLEXIO_SCL_PIN);
timerConfig.triggerPolarity = kFLEXIO_TimerTriggerPolarityHigh;
timerConfig.triggerSource = kFLEXIO_TimerTriggerSourceInternal;
timerConfig.pinConfig = kFLEXIO_PinConfigOutputDisabled;
timerConfig.pinSelect = FLEXIO_SCL_PIN;
timerConfig.pinPolarity = kFLEXIO_PinActiveHigh;
timerConfig.timerMode = kFLEXIO_TimerModeDual8BitBaudBit;
timerConfig.timerOutput = kFLEXIO_TimerOutputZeroNotAffected;
timerConfig.timerDecrement = kFLEXIO_TimerDecSrcOnFlexIOClockShiftTimerOutput;
timerConfig.timerReset = kFLEXIO_TimerResetNever;
timerConfig.timerDisable = kFLEXIO_TimerDisableOnPreTimerDisable;
timerConfig.timerEnable = kFLEXIO_TimerEnableOnPrevTimerEnable;
timerConfig.timerCompare = ((FLEXIO_CLOCK_HZ / I2C_SLAVE_BAUDRATE_HZ / 2) - 1) << 8 | 0xFFU; // 100 kHz
FLEXIO_SetTimerConfig(FLEXIO_INSTANCE, 0, &timerConfig);
/* ------------------ Timer 1: SDA bit timing + START/STOP detection ------------------ */
timerConfig.triggerSelect = FLEXIO_TIMER_TRIGGER_SEL_PIN_INPUT(FLEXIO_SDA_PIN);
timerConfig.triggerSource = kFLEXIO_TimerTriggerSourceExternal;
timerConfig.pinSelect = FLEXIO_SDA_PIN;
timerConfig.timerMode = kFLEXIO_TimerModeDual8BitBaudBit;
timerConfig.timerOutput = kFLEXIO_TimerOutputZeroNotAffected;
timerConfig.timerDecrement = kFLEXIO_TimerDecSrcOnFlexIOClockShiftTimerOutput;
timerConfig.timerReset = kFLEXIO_TimerResetOnTimerCompare;
timerConfig.timerDisable = kFLEXIO_TimerDisableOnTimerCompare;
timerConfig.timerEnable = kFLEXIO_TimerEnableOnTriggerHigh;
timerConfig.timerCompare = 0x0000FFFFU; // will be adjusted in ISR for start/stop
FLEXIO_SetTimerConfig(FLEXIO_INSTANCE, 1, &timerConfig);
/* ------------------ Shifter 0: Data TX/RX ------------------ */
shifterConfig.timerSelect = 0;
shifterConfig.timerPolarity = kFLEXIO_ShifterTimerPolarityOnNegative;
shifterConfig.pinConfig = kFLEXIO_PinConfigOpenDrainOrBidirectional;
shifterConfig.pinSelect = FLEXIO_SDA_PIN;
shifterConfig.pinPolarity = kFLEXIO_PinActiveLow;
shifterConfig.shifterMode = kFLEXIO_ShifterModeTransmit;
shifterConfig.shifterInput = kFLEXIO_ShifterInputPin;
shifterConfig.shifterStop = kFLEXIO_ShifterStopBitLow;
shifterConfig.shifterStart = kFLEXIO_ShifterStartBitLow;
FLEXIO_SetShifterConfig(FLEXIO_INSTANCE, 0, &shifterConfig);
/* ------------------ Shifter 1: Address + ACK/NACK ------------------ */
shifterConfig.shifterMode = kFLEXIO_ShifterModeReceive;
FLEXIO_SetShifterConfig(FLEXIO_INSTANCE, 1, &shifterConfig);
/* Enable interrupts */
FLEXIO_EnableShifterStatusInterrupts(FLEXIO_INSTANCE, (1U << 0) | (1U << 1));
FLEXIO_EnableTimerStatusInterrupts(FLEXIO_INSTANCE, (1U << 0));
/* NVIC */
EnableIRQ(FLEXIO1_IRQn); // change if using FLEXIO2
NVIC_SetPriority(FLEXIO1_IRQn, FLEXIO_IRQ_PRIORITY);
FLEXIO_Enable(FLEXIO_INSTANCE, true);
PRINTF("FlexIO I2C Slave ready @ 0x%02X, %d Hz\r\n", I2C_SLAVE_ADDRESS_7BIT, I2C_SLAVE_BAUDRATE_HZ);
}
/* ========================= ISR - State Machine (expand this) ========================= */
void FLEXIO1_IRQHandler(void) // change to FLEXIO2_IRQHandler if needed
{
uint32_t flags = FLEXIO_GetStatusFlags(FLEXIO_INSTANCE);
/* Clear flags */
FLEXIO_ClearStatusFlags(FLEXIO_INSTANCE, flags);
switch (g_currentState)
{
case STATE_IDLE:
// Detect START (SDA falling while SCL high) → go to address receive
if ((flags & (1U << 1)) && /* START condition logic here */)
{
g_currentState = STATE_ADDR_RX;
// Re-arm shifter for 8-bit address + R/W bit
FLEXIO_SetShifterBuffer(FLEXIO_INSTANCE, 1, 0xFF, kFLEXIO_ShifterBuffer); // dummy for RX
}
break;
case STATE_ADDR_RX:
// Read address + R/W bit from shifter 1
uint32_t addr = FLEXIO_ReadShifterBuffer(FLEXIO_INSTANCE, 1, kFLEXIO_ShifterBuffer);
if ((addr >> 1) == I2C_SLAVE_ADDRESS_7BIT)
{
g_currentState = STATE_ADDR_ACK;
// Send ACK (pull SDA low for 9th bit)
// ... (add logic)
}
else
{
g_currentState = STATE_IDLE; // address mismatch
}
break;
case STATE_DATA_RX:
// Store received byte
if (g_rxIndex < BUFFER_SIZE)
g_slaveRxBuffer[g_rxIndex++] = FLEXIO_ReadShifterBuffer(FLEXIO_INSTANCE, 0, kFLEXIO_ShifterBuffer);
// Send ACK
break;
case STATE_DATA_TX:
// Transmit next byte
if (g_txIndex < BUFFER_SIZE)
FLEXIO_SetShifterBuffer(FLEXIO_INSTANCE, 0, g_slaveTxBuffer[g_txIndex++], kFLEXIO_ShifterBuffer);
else
g_currentState = STATE_STOP;
break;
case STATE_STOP:
g_transferComplete = true;
g_currentState = STATE_IDLE;
g_rxIndex = 0;
g_txIndex = 0;
break;
default:
g_currentState = STATE_IDLE;
break;
}
}
/* ========================= MAIN ========================= */
int main(void)
{
BOARD_InitBootPins();
BOARD_InitBootClocks();
BOARD_InitDebugConsole();
FLEXIO_I2C_Slave_Init();
while (1)
{
if (g_transferComplete)
{
g_transferComplete = false;
PRINTF("Received %d bytes: %s\r\n", g_rxIndex, g_slaveRxBuffer);
// You can process g_slaveRxBuffer here
}
}
}
How I chose the values & how to change them later
- Pins: FLEXIO1_D2 / D3 (GPIO_AD_B0_02 / GPIO_AD_B0_03) → very common in FlexIO examples, no conflict with LPI2C, easy to probe on EVK.Change: Edit the two #define FLEXIO_xxx_PIN lines + the two IOMUXC_SetPinMux lines in FLEXIO_I2C_Slave_PinMux().
- Slave address: 0x50 (7-bit) → very common (EEPROM style).Change: Just edit I2C_SLAVE_ADDRESS_7BIT.
- Speed: 100 kHz → safest for a software slave (gives the ISR time to react).Change: Edit I2C_SLAVE_BAUDRATE_HZ (try 400000 later when stable).
- FlexIO clock: 60 MHz → default after BOARD_InitBootClocks().Change: Edit FLEXIO_CLOCK_HZ if you change the clock tree.
- Buffers: 32 bytes → enough for most use cases.Change: Edit BUFFER_SIZE.
Important notes
- This is a working skeleton with correct FlexIO configuration and modular settings.
- The ISR state machine is the complex part — the code above shows the structure and the most critical states. You will need a logic analyzer/scope to fine-tune START/STOP detection and ACK timing (the comments tell you exactly where to add the logic).
- Clock stretching is already supported via Timer 0 disable.
Next steps for you:
- Add the code to a new MCUXpresso project (or replace the flexio_i2c_master example).
- Add the pin mux call in main().
- Build and run with an I2C master (Arduino, another RT board, or Saleae).
- Use a scope on SCL/SDA to debug the ISR states.
If you want me to expand any specific state (e.g. full ACK logic, repeated START support, 10-bit address, or DMA version), just tell me what you see on the scope or what your master is doing — I’ll give you the exact next piece of code.
Would you like the
FreeRTOS version,
DMA-assisted version, or help debugging a specific state? Just say the word!