What causes an I2C bus lockup, and how do you recover from it?
An I2C bus lockup occurs when a slave device holds the SDA line low indefinitely, preventing the master from generating a START or STOP condition (both require SDA to transition while SCL is high, which is impossible if SDA is stuck low). This typically happens when: (1) the master resets mid-transaction (e.g., due to a watchdog or debugger) and the slave is still driving a data bit; (2) a noise glitch causes the slave's state machine to desynchronize, leaving it in the middle of a byte; (3) a software bug in a slave (especially bit-banged slaves) causes it to hang in a data-drive state.
The standard recovery procedure is the 9-clock-pulse technique: the master drives SCL through up to 9 clock cycles while leaving SDA released (high-impedance). Each clock pulse gives the stuck slave a chance to advance through its state machine and release SDA. If the slave is stuck mid-byte, it will eventually reach the ACK/NACK bit where it releases SDA. Once SDA goes high during a clock high phase, the master can assert a proper STOP condition to reset the bus.
void i2c_bus_recovery(void) {// Configure SDA and SCL as GPIOs (not I2C alternate function)gpio_set_mode(SCL_PIN, GPIO_OUTPUT_OPEN_DRAIN);gpio_set_mode(SDA_PIN, GPIO_INPUT); // Release SDAfor (int i = 0; i < 9; i++) {gpio_write(SCL_PIN, 0); // SCL lowdelay_us(5);gpio_write(SCL_PIN, 1); // SCL high (via pull-up)delay_us(5);if (gpio_read(SDA_PIN)) break; // SDA released, done}// Generate STOP: SDA low-to-high while SCL is highgpio_set_mode(SDA_PIN, GPIO_OUTPUT_OPEN_DRAIN);gpio_write(SDA_PIN, 0);delay_us(5);gpio_write(SCL_PIN, 1);delay_us(5);gpio_write(SDA_PIN, 1); // STOP condition// Reconfigure pins for I2C alternate function}
This recovery must be done using GPIO bit-banging, not the I2C peripheral — because the peripheral itself may be in a stuck state. After recovery, reinitialize the I2C peripheral. Many production designs run this recovery at boot and after any I2C timeout.
Source: I2C Q&A
