The issue is that any time an I2C transaction is 'interrupted', the slave that was being addressed will be in some part of ITS bus-access sequence, and awaits clocking by the master to proceed. It can be in a condition of driving SDA low (driving 'ack' for a write-byte, driving '0 data' for a read-byte), and as long as that is true the master in UNABLE to 'normally' access the bus -- it is 'busy'. It is necessary to manually supply clocks (up to a full byte's worth, of course!) until the slave 'gives up' the data line, so that at that point you can force a 'stop' condition which will 'reset' ALL slave bus-access state machines.
The details are pretty well mired in your particulars -- which I/O, and what you would use for time-pacing. But the general point is to restore the two pins (SCL and SDA) to I/O mode (open drain operation, of course!) and set the former SDA 'open' (not driven: input). Then every 5us drive the pin formerly called SCL low, wait 5 more, then release it, and do that up to eight times until you sense (after the 5us delay just before the next attempt to drive low) SDA 'high'. Then drive SDA low yourself, wait 5us, and release it back to high -- that last edge is the I2C 'stop' (SDA rising edge while SCL high). See:
I2C device dead-lock recovery
An example for a K20 has completely different GPIO controls:
uint32_t fault_cnt = 0;
| GPIOB_PDDR &= ~(uint32_t)(3<<2); //Both as 'high', but inputs (OC) |
GPIOB_PSOR = 3<<2;
PORTB_PCR2 = PORT_PCR_MUX(1); //I2C0 SCL to I/O
PORTB_PCR3 = PORT_PCR_MUX(1); //I2C0 SDA to I/O
time_delay(2);
while( (GPIOB_PDIR & (3<<2)) != (3<<2) ) //NOT both pulled 'high'???
{ //We need to try 'clocking' the interface to get the slave to 'let go' of the data line
// technically, this can't exceed 8 '0' data bits, at which point the slave might finally expect the ACK/NACK
// BUT it is valid enough for us to just 'get control' of the data line and issue STOP to re-align ALL slaves
GPIOB_PCOR = 1<<2; //When we drive, make it a 'zero'
GPIOB_PDDR |= 1<<2; //Output now!
time_delay(2);
GPIOB_PDDR &= ~(uint32_t)(1<<2); //Float back high!
time_delay(1);
if( ++fault_cnt > 10 ) //Too many???
{
Os_LogDebug(OS_LOG_DEBUG_ERROR, "I2C0 failed to clear.\n");
break;
}
}
//Make a 'stop' condition -- a rising-edge on SDA while SCL is 'high'
GPIOB_PCOR = 1<<2;
GPIOB_PDDR |= 1<<2; //Clock low
time_delay(2);
GPIOB_PCOR = 1<<3;
GPIOB_PDDR |= 1<<3; //Data low
time_delay(1);
GPIOB_PDDR &= ~(uint32_t)(1<<2); //Float clock back high!
time_delay(1);
GPIOB_PDDR &= ~(uint32_t)(1<<3); //Float data back high!
time_delay(1);