Sometimes a fault can cause an I2C bus to get stuck. This happens when a slave device is driving a low/logic-0 state on the bus while waiting for another clock pulse, but the master has already aborted a transaction, rebooted, become confused by bus noise, or some other fault.
A bus stuck in this way can be cleared by cycling 9 clock pulses on the bus, causing any device holding the bus to release its low state drive.
The Aardvark I2C/SPI Host Adapter and Promira Serial Platform can be used to clear the bus by putting the appropriate pins into GPIO mode. Pullups should then be enabled and the pins set to inputs. The output buffers should be set to low/0. Then toggle between input/output mode to do the bus cycling.
This process must be initiated by the host software. With systems where this problem occurs frequently, it may be recommended to include this sequence as part of a normal initialization process, or it may be part of an error recovery process.
On the other hand, when the bus is entirely powered by the Aardvark adapter or Promira Serial Platform and all devices are certainly reset by the controlling software, this type of recovery is generally not necessary.
Screenshot of the recovery process on the bus:
Channel 1: SCL (Yellow)
Channel 2: SDA (Blue)
Below are sample codes using Python that allow the host software to perform the actions needed to clear the stuck I2C bus:
def i2c_clear_bus (handle):old_config = aa_configure(handle, AA_CONFIG_QUERY)
assert old_config in (AA_CONFIG_GPIO_ONLY, AA_CONFIG_SPI_GPIO,
AA_CONFIG_GPIO_I2C, AA_CONFIG_SPI_I2C), \
aa_status_string(ret)
ret = aa_configure(handle, AA_CONFIG_GPIO_ONLY)
assert ret == AA_CONFIG_GPIO_ONLY, aa_status_string(ret)
ret = aa_gpio_pullup(handle, AA_GPIO_SCL | AA_GPIO_SDA)
assert ret == AA_OK, aa_status_string(ret)
ret = aa_gpio_direction(handle, 0) # inputs
assert ret == AA_OK, aa_status_string(ret)
ret = aa_gpio_set(handle, 0)
assert ret == AA_OK, aa_status_string(ret)
for i in range(9):
ret = aa_gpio_direction(handle, AA_GPIO_SCL)
assert ret == AA_OK, aa_status_string(ret)
ret = aa_gpio_direction(handle, 0)
assert ret == AA_OK, aa_status_string(ret)
ret = aa_configure(handle, old_config)
assert ret == old_config, aa_status_string(ret)
def i2c_clear_bus (channel):old_config = ps_app_configure(channel, PS_APP_CONFIG_QUERY)
assert old_config in (PS_APP_CONFIG_GPIO, PS_APP_CONFIG_I2C,
PS_APP_CONFIG_SPI,
PS_APP_CONFIG_SPI | PS_APP_CONFIG_I2C), \
ps_app_status_string(ret)
ret = ps_app_configure(channel, PS_APP_CONFIG_GPIO)
assert ret == PS_APP_CONFIG_GPIO, ps_app_status_string(ret)
ret = ps_gpio_direction(channel, 0) # inputs
assert ret == PS_APP_OK, ps_app_status_string(ret)
ret = ps_gpio_set(channel, 0)
assert ret == PS_APP_OK, ps_app_status_string(ret)
for i in range(9):
ret = ps_gpio_direction(channel, 1)
assert ret == PS_APP_OK, ps_app_status_string(ret)
ret = ps_gpio_direction(channel, 0)
assert ret == PS_APP_OK, ps_app_status_string(ret)
ret = ps_app_configure(channel, old_config)
assert ret == old_config, ps_app_status_string(ret)
For any questions on using the Aardvark I2C/SPI Host Adapter or Promira Serial Platform for this use case, please email us sales@totalphase.com.