I2C bus clear - Page 1
Forum section: electronics / general

author: cv007
author_rank: Frequent Contributor
timestamp: May 23, 2025, 10:14:35 pm
content: https://www.nxp.com/docs/en/user-guide/UM10204.pdfUM10204I am rewriting an i2c driver and was thinking about how to deal with a stuck sda line. The specification of course deals with it in a single line of text- 3.1.16, 'If the data line (SDA) is stuck LOW, the controller should send nine clock pulses', which is pretty poor treatment of the potential problem by a specification.It seems to be a common solution. Although there are now many that do something smarter than this, I do not see many (any?) taking into account the fact that a slave can do clock stretching on any scl low period (although most likely only done in the ack/nack bit period). Treating every scl clock as a fixed period in the end probably only means you have to attempt recovery more times than was needed had you watched for clock stretching.Ignoring the clock stretching for now, the simple 9 scl clock solution the specification proposes may work most of the time, but there is a scenario where it will fail- if the slave is 'stuck' in the master write ack bit period, simply clocking scl 9 times would simply bring the slave back to the ack/nack period where it may again ack the write. Getting into that specific 'sync' would be unlikely, and it would take very little break that sync, but still possible.Writing/using various methods to deal with this, all requiring giving the pins back to gpio and turning off the i2c peripheral then 'bit banging' a recovery procedure- they all seem somewhat ugly and not very satisfying that you have to go external to the peripheral to solve this problem. I then started looking looking for a solution that the i2c peripheral could do on its own.What I eventually came up with is this-the i2c spec device ID address is 11111XX RW, where RW will be set to 1 as we are not interested in actually getting the idno device will ack the address we will use - 1111111+R(1)we can force the peripheral to idle when needed (after arb lost)in the case of the newest generation of avr the peripheral will put out a clock (from idle) even if sda is low in its attempt to produce a start+address (so 1 scl clock at minimum is produced)we are ultimately looking for a nack of this start+address, which means sda has been released, and in contrast arbitration lost or was ack'd means sda is still lowarbitration lost means the peripheral goes to a busy state and releases scl/sda until it sees a stop (we will force to idle since will not see a stop)//codeloop (with a limit)-do a read of address 127, if arbitration lost or was ack'd (sda low in any bit), set to idle and loop againwas nack'd, which means we put out start+[address+R]+nack+stop (and all on the bus will have seen both the start and stop)We get to stay in the peripheral without having to take over/bit bang the pins via gpio, any clock stretching is handled by the hardware, and we can use existing peripheral functions to take care of the problem.Is there something I am missing?I am not sure if all i2c peripherals will generate a start even when sda is low, but in the case of the newer avr it is happy generating the first sda low (even though already low) then scl low (start) and only checks sda for arbitration at each subsequent scl high. If a peripheral does not want to produce a start because its looking for sda high first, then I guess this would not work as you then cannot get scl to clock via the peripheral.

author: nctnico
author_rank: Super Contributor
timestamp: May 23, 2025, 10:40:34 pm
content: It goes without saying that the master should not be pulling the SDA line low when sending the 9 clock pulses (while adhering to clock stretching).

author: cv007
author_rank: Frequent Contributor
timestamp: May 24, 2025, 01:43:16 am
content: QuoteHence the address is 127 and the rw bit also 1 (read).At the first point where sda is released, which should take place in clock 1-8 (which may be only 'single clocks' before arbitration fails), the next read transaction will produce a valid start (since sda was released, and the master is sinking sda low while scl is high). Once a valid start, any device is required to reset its logic so this read transaction completes along with a nack (3.1.10 Note 4).Also, in addition to the advantages stated previously, the peripheral already knows about bus timing requirements, where the bit banger has to come up with their own timing.The question of when to do this- in my i2c driver at the moment, I keep a record of the last status register. A failed transfer for any reason the app will have to address on their own (usually retry), and the driver before doing a transaction will check the last status register value- if there was previously a bus/arbitration error then the recovery procedure is performed and if the recovery is successful the transaction proceeds. If the sda line was ok and recovery not really needed, the recovery process simply does an address read which no one will ack and no harm done. There is no procedure the app needs to do to handle this problem, other than do what it already does when a transaction fails (typically try again).

author: wek
author_rank: Frequent Contributor
timestamp: May 24, 2025, 06:38:58 am
content: If SDA is held low by other device, your mcu won't be able to generate a START.The procedure is, generate *up to* 9 clocks, until the other device releases SDA, and then perform STOP.If there's no provision to do exactly this in the mcu peripheral, bitbanging is necessary. That you see this as ugly, is not a technical problem...JW

author: DavidAlfa
author_rank: Super Contributor
timestamp: May 24, 2025, 10:57:49 am
content: No, that won't work, you can't send or read anything if SDA is stuck low, the only way is to manually toggle SCL.You shouldn't just send 9 clocks, you don't know in which clock cycle the device got stuck, the idea is to manually start sending clocks and checking SDA state after each one.I wouldn't limit it to 9 clocks, keep smashing SCL until SDA releases.With SDA stuck low, you're basically generating start and stop conditions.X

author: cv007
author_rank: Frequent Contributor
timestamp: May 24, 2025, 11:12:59 am
content: QuoteAs I stated, the next gen avr (AVR16EB32 in this case) does generate a start when sda is low, and only checks sda for arbitration on the rising scl clock. So a single clock gets put on the bus at a minimum, losing arbitration each time until sda is released. How many mcu's do the same is a question, and the answer is most likely not found in a datasheet (was not found in the avr datasheet, and looking at some other cortex-m's I don't see that kind of info either).In the case of this avr, sda must only be sampled on the scl rising edge so it puts out a start condition whether sda was already pulled down or not. I have not tried other mcu's, but would suspect at least in the case of a samD10 I have it will act the same (as it appears the peripheral is very similar). I have other mcu's (all cortex-m)- stm32, renesas, silabs, nrf52, atmel, etc., so I guess I can eventually find out how many act the same.QuoteThere are some that follow the i2c specification as stated, and simply generate the 9 clocks. I have been around the internet a few laps and have looked at many solutions, so am aware of all the variations implemented and what they doing and why they work. There are also a some manufacturer code I have looked at to see what they do, and its not uncommon that the problem is simply ignored and you are on your own. I was just thinking of a way to do this while staying in the confines of the i2c peripheral, using existing functions, and end up with a more general solution. Of course it hinges on how the peripheral is designed, and in the case of my particular avr this does work ok and am happy to eliminate the bit banging which was done previously.

author: nctnico
author_rank: Super Contributor
timestamp: May 24, 2025, 12:14:18 pm
content: Quote from: cv007 on May 24, 2025, 01:43:16 amNo. SDA should go high at some point and the remaining clock pulses should reset the internal statemachines of all connected slave devices. Hence the need for 9 pulses. The 9 clock pulses are not part of a transaction.

author: cv007
author_rank: Frequent Contributor
timestamp: May 24, 2025, 03:09:41 pm
content: There is a slight change to improve what I originally stated- instead of address 127+read, 127+write is used for several reasons. One being that the avr will not do a 0 byte read (and is not really a thing when slave is in a normal state), and will always clock in 1 read byte when we really did not want more than the address+rw. The use of a write instead works a little better.There are two cases where the slave is driving the sda line (and 'stuck'), and since the peripheral is doing the recovery it will handle any slave scl clock stretching that takes place so will ignore scl and assume the peripheral can handle that. First, a master write/slave read where the slave is driving sda in the ack bit position. Second, a master read/slave write where the slave can be driving sda in any of the 8 data bit positions.First scenario below. Probably not an ideal representation since not graphical, but will have to do.Code:using the i2c peripheral for bus clearS | ADDRESS RW | A /A | Pscenario 1.slave in reading mode (master write)-the slave is 'stuck' at the ack clock position, ack'ing and waiting for scl to go low so it can release sdaARBSCL SDA MASTER SLAVE LOSTH L ? ACK slave 'stuck' at clock #9 in a master write,waiting for scl low so it can release sdarecovery started, address=127, rw=0 (write)L L S slave releases sda when scl low, master generated start (only scl though), now begins to clock out address+rwH H 1 RD first bit of address as master sees it, slave sees this as first bit of data to readL HH H 2 RD~~~~~~3-7 same~~~~~~~~~~L LH L 8 RD this is rw bit (write)L L slave takes over sda, set to ack (previous data was 254 as it sees it)H L A/A ACK slave acks, master sees ack of address 127L L master takes over sda again, prepares for stopH L P1 master releases scl, stop part1H H P2 master releases sda, stop completeda stop was seen by everyone on the bus, slave logic is resetSo essentially, we let the slave treat our start+address+rw as data (start not seen by anyone, since slave had it initially pulled low), got to the ack/nack position, let the slave ack/nack, and the master has the ability to generate a stop as the slave is again in read mode and has released sda. The stop will be seen by all devices on the bus. Without the peripheral and doing the recovery 'manually' (bit bang via gpio), this process could be shortened as you only need to get to the first slave sda release. But the point is to use the peripheral, and a single method that works for this first scenario and the second scenario will also use the same transaction. In this case the address was not important, as we did not produce a start so was not seen by devices that were in a good logic state, and what the slave read as data did not matter, we only wanted to get to the slave sda release point using only the peripheral.If the slave was stuck at any other point in the master write/slave read, there would be no need for a recovery as the slave does not have control of sda and any start would reset its logic (any normal transaction started).I will spell out the second scenario next, where slave is in a write mode.

author: nctnico
author_rank: Super Contributor
timestamp: May 24, 2025, 03:15:42 pm
content: A better approach is likely to disconnect SDA from the I2C pheripheral and making the I2C peripheral generate 9 clock cycles in order to reset the I2C bus. IF the I2C peripheral doesn't have support for doing a bus reset procedure.

author: cv007
author_rank: Frequent Contributor
timestamp: May 24, 2025, 04:18:34 pm
content: Second scenario, where slave is stuck in write mode (master read).Code:using the i2c peripheral for bus clearS | ADDRESS RW | A /A | Pscenario 2.slave in writing mode (master read)-the slave is 'stuck' at any data bit position, driving sda lowwe will say that the slave is 'stuck' putting out data at bit7 for brevity, and the data its writing is 0x00ARBSCL SDA MASTER SLAVE LOSTH L ? WR slave 'stuck' at clock #7 in a master read/slave writerecovery started, address=127, rw=0 (write)L L S slave keeps sda low for (next) bit8, master generated start (only scl though), now begins to clock out address+rwH L 1 WR Y first bit of address as master sees it (should be 1), slave sees this as bit8 of data its writingarbitration lost as sda is L, but master wanted H (1)master gets to idle (forced), and tries againH LL L S WR slave releases sda, master generated start (only scl though), now begins to clock out address+rwH H 1 master had control of sda, and set first address bit Hslave sees master ack/nack as an nack and now releases sda and its logic now waits for a startmaster continues with address+rw without arbitration lost (so no error, except was nack'd which we do care about)master produces a stop, which all devices will see and all logic resetAt any point in the slave data where the bit data is 0, we get an arbitration error and the master tries again. At any point where slave bit data is 1, the previous arbitration error which restarts the master to try again, then produces a valid start since sda was released by the slave. Once a valid start is produced, the slave resets its logic, the master completes the start+address+rw+ack and then prodiuces a stop. Now you can also see that the rw bit (we used write) does not come into play as that point is never reached, and the address we used will not get a response as its used for device id (see i2c spec in first post). The address is not even that important, as long as the first bit is 1 so an arbitration error is produced, it doesn't matter if someone on the bus does ack this transaction as we are not writing any data anyway.QuoteIn the case of the avr I am using, you get both scl/sda controlled by i2c when enabled and there is no way to disable just sda.edit-I have also checked out an EFR32, and it too will generate a start condition regardless of the sda state (up until the first scl rising edge where it first checks sda for collision). Using their provided driver library transfer function, that function will on its own force the state to idle if it sees a previous arbitration error. This basically acts as I am doing, except I use an address of 127 to force an arbitration error at the first bit (in the case of scenario 2), and loop up to 9 times to to get to where there is no arbitration error. The EFR library function will catch the arbitration error at possibly a later bit in the address (depends on address), and rather than clear the bus in the library function, the attempts the user makes after the problem will eventually clear the bus (up to 9 failed transactions before things start to work).Only a sample size of 2 manufacturers so far (and one can assume their peripherals are duplicated to many of their mcu's), and I do not know how others act yet, but it could be a design decision to allow a start condition to be generated without regard to sda as a means to always give the ability to produce a clock, even if only a single clock at a time. Simply through the process of arbitration lost and getting at least a single clock generated each time, plus the user/library normal handling of errors, means only persistence in using normal functions is needed to clear the bus. An i2c transfer failure for any mcu will typically result in repeated attempts, which in these mcu's will shortly clear the bus through normal use. If this is the case, they do not document it and are still vague about what line conditions are required before a start will be generated.If there are mcu's which do require both scl/sda to be high before generating a start, then you will be required to do something else, such as mentioned in the previous post (internally disconnect sda if possible) or bit bang a solution.

author: mikerj
author_rank: Super Contributor
timestamp: May 25, 2025, 12:24:48 pm
content: Quote from: cv007 on May 24, 2025, 11:12:59 amI fully agree that the standard is not well written in this part but basic knowledge of I2C should show what the intent is. The standard explicitly states that SDA should be released by the slave at some pointwithinthe 9 cycles, a bit of thought shows that continuing to provide additional clock cycles after this point is the wrong thing to do. If people really are just throwing 9 clocks in and hoping for the best they are clearly not very experienced or knowledgable.

author: mtwieg
author_rank: Frequent Contributor
timestamp: May 25, 2025, 04:31:43 pm
content: Quote from: mikerj on May 25, 2025, 12:24:48 pmWhen I first ran into the stuck bus issue myself, I also assumed that stopping the SCL pulses when SDA is released was the right thing to do. Once SDA is high, the master can issue START/STOP bits, and the slaves should always abort whatever transaction they were in, so long as they adhere to the I2C spec.But there have been times where vendors of specific slave devices have advised me that this will not be robust, and that a minimum of 8/9 SCL pulses must be used. Although they don't admit it, it means their device does not obey the spec. That's just the world we live in.

author: nctnico
author_rank: Super Contributor
timestamp: May 25, 2025, 04:37:00 pm
content: And you also need to think about simplicity. Testing the SDA line adds extra complexity which can go wrong. Keep mechanisms which are supposed to act as a failsafe as simple as possible. In case of I2C it doesn't matter how many SCL clock cycles you produce as long as you don't create start / stop conditions (by keeping SDA high). BTW, a common trick which exploits this is to share an I2C bus among devices with the same address by using an analog mux on the SDA lines.

author: Siwastaja
author_rank: Super Contributor
timestamp: May 26, 2025, 10:24:29 am
content: Quote from: mtwieg on May 25, 2025, 04:31:43 pm... which is why I suggest the following:Add PFET or smart power switch IC on the supply line of every I2C device (or common to all devices). Generate power-on resets when something gets stuck.Getting to initialized state takes too long? -> don't use I2C for anything timing sensitive, use something else (e.g. for IMUs, SPI).

author: wraper
author_rank: Unknown
timestamp: May 26, 2025, 10:39:31 am
content: Quote from: nctnico on May 25, 2025, 04:37:00 pmReading this thread feels like SCL clock cycles cost money and people argue how to save them.

author: Jeroen3
author_rank: Super Contributor
timestamp: May 26, 2025, 10:49:28 am
content: TI recommends:Quote from:https://www.ti.com/lit/an/scpa069/scpa069.pdfThe way I did this to recover communication to an eeprom:QuoteQuote from: Siwastaja on May 26, 2025, 10:24:29 amThis ^^^The optimal solution would be if chip vendors would include a bus recover mechanism in their peripheral your could activate. But I don't think they actually developed this IP themselves.iirc there are some vendors using a successor called I3C.Quote from:https://www.mipi.org/resources/i3c-frequently-asked-questions

author: cv007
author_rank: Frequent Contributor
timestamp: May 26, 2025, 11:34:40 am
content: It is odd, unless I have simply missed it, that I have never seen anything on the internet or in any documentation that a particular mcu may already have the means to clear a bus by use of the peripheral only, with no need to bit bang a solution. When writing an i2c driver for any particular mcu, the first thing I'm now going to find out is if it is able to generate a start (resulting in a clock) when the sda line is pulled low. If so, no need to create a bit banged bus clear solution. Although nothing inherently wrong with a bit banged solution, and may end up being the only option for the mcu, using the existing i2c peripheral that already handles timing, pin states, clock stretching, status, plus the functions are already in place, seems like a much better option.There is one subtle situation when a slave is stuck in the ack phase of a master write (with sda pulled low), and if the mcu in use will generate a start condition when sda is pulled low- the master may put out a normal write transaction and the appearance will be that the address+rw was ack'd (by the wayward slave, assuming it is happy to receive more data), and the master will get the impression that its talking to the addressed slave. Although the intended addressed slave is not listening because there was no actual start produced, and what the confused slave is doing with that data is unknown, or whether the master thinking it has successfully talked to the intended slave ends up being a problem, eventually the master will get to a point where it produces a stop and everyone will be back in sync.The first scenario I spelled out (referred to as scenario 1 in a previous post) there will be no indication there is a stuck sda (with the 2 mcu's I tested so far anyway), so one will go through a normal transaction and never realize you were talking to the wrong slave, and the previous paragraph describes what happens. Still end up with a stop and a slave back in sync, but may have missed doing something important without knowing that it did not actually happen.One method with the mcu that has the 'semi idle' start feature, whether it is actually ever put to use for this one particular rare case, would be to start every transaction with a write to address 127- no write actually takes place, and does not matter whether it was ack'd/nack'd, but it will always result in a state where a valid restart will be produced for the actual transaction that follows. There then would be no possible phantom writes taking place as described above.This same method, when always applied whether needed or not (just doing, no checking if needed), would also take care of scenario 2 at the same time. You would get an extra byte at the start of each series of transfers (or up to 9 attempts if arbitration errors due to scenario 2), and although the probabilities are quite low that they turn out to be necessary, it may be something to consider. Rather than continue the transaction, although the bus clear will have completed, it would probably be better to return an error that indicates some slave may have been confused so the user can re-init the slave(s) to make sure they are in some sane state. If the 127 address was ack'd there may have been scenario 1, if arb lost (and a single master) scenario 2, and if was nack'd the single time then all ok so continue the transaction.For the gen2 avr, although the i2c (twi) peripheral takes over the pins when enabled, the pin inputs are still accessible to gpio so in that case simply monitoring the state of sda before any transaction is started would allow for making a decision first whether the need is there. But that is mcu specific, and the pin may get routed completely away from gpio.My original goal was to see if the peripheral could handle this problem on its own, and my conclusion is it can but there remains one requirement- the mcu has to allow for a start from a 'semi idle' bus state (scl high/sda low).I just tested an stm32g031, and it too will generate a start condition when sda is low- result is a single clock, and subsequent arbitration checking of sda on the first rising scl edge. So avr gen2, silabs efr32, and stm32 that I have tested have the ability to generate a clock when sda is low, so the peripheral is able to do a bus clear on its own.QuoteThis is if you are making your own board, but you may not have made the board and have to deal with what you have so would have to come up with a plan where you do not have the ability to power cycle the device. For your own designs that are battery powered, the added benefit of controlling i2c device power is to be able to simply turn it off when not in use. Whether the device standby power is low enough to not care, or is higher than you would like, by simply turning it off when not in use you know what the power consumption is. The i2c devices I use can easily be powered by a gpio pin.

author: jeremy0
author_rank: Contributor
timestamp: May 26, 2025, 01:15:46 pm
content: Quote from: cv007 on May 26, 2025, 11:34:40 amThe RP2040 manual describes a built-in I2C bus clear feature (4.3.13) - but it turns out this is from the 3rd party IP doco and is not implemented in the RP2040. Seehttps://forums.raspberrypi.com/viewtopic.php?p=1934072#p1934072

author: cv007
author_rank: Frequent Contributor
timestamp: May 26, 2025, 10:14:07 pm
content: Although my sample size is only 3 different mcu's (and most likely includes all mcu's in the same family), I am beginning to think that all/most/many will generate at minimum a single clock for any ic2 peripheral internal idle state + start.There is no way for a master to prevent simultaneous start conditions (with multiple masters on the bus) as it would not have the capability to (always) detect the difference between its own sda state and another master doing the same even though there is a time difference when these sda states are driven. Since there is no way to detect when this happens (every time), the specification arbitration scheme is relied on to sort it out. Without the ability to always detect a simultaneous start, and the arbitration scheme in place, there is no real need for the i2c peripheral to check sda until the rising edge of scl, where arbitration takes place and all potential masters/slaves on the bus have released scl so everyone now in sync (everyone checking arbitration at the same point).I have more mcu's I can check, but kind of a pain to do as I would have to again get familiar with datasheet/tools/etc., and some of these I have not used in a long time.If you want to check if your mcu is capable of doing the 'ID bus recovery method', something simple like this can be done-setup i2c pins as neededsetup i2c peripheral as neededloop:do a start+[address(127)+WR]+stop (no data to write)wait 100ms (or whatever you want)force i2c to idle (reset peripheral, or clear arbitration error)goto loopNow get out your scope/logic analyzer out and monitor scl and sda (only scl is fine)run this test, watch scl- it should put out nine clocks, +1 for the stopconnect the sda pin to ground, if you see a single scl clock (after which arbitration is lost) your mcu can do bus recovery using only the peripheralexample for stm32 I just tested-I didn't have an i2c driver for the stm32 (all my own code, I do not use their hal code), so just added a simple class that sets up the pins, rcc clock, and has only 2 functions (I already have code for rcc/gpio/others, so new code needed was minimal). I didn't bother trying to figure out the o2c peripheral completely (its stm32, will require reading many times to fully understand), and only generate a start+address+rw (no stop). The peripheral reset will release the pins and get back to where a start once again can be generated. The code is minimal but enough to generate a start.Code://stm32g031k8#include "Boards.hpp" //ledintmain (){I2c i2c{ MCU::I2c1_B8B9 }; //I2C1/PB8/PB9while(1){i2c.reset();i2c.startWrite(127);board.led.toggle();CPU::delayMS(500);}}edit- I have an NXP MCX dev board, relatively new series of mcu's from NXP (but is from kinetis/freescale side). I am putting out an i2c byte (address 127+wr) using a modified sdk example, and their high level blocking transfer function will hang when sda is stuck low (and no scl clock is produced either). So in this case it appears the hardware is waiting for an actual idle bus before it puts out a start condition. It acts this way even from a mcu reset (with sda tied low), so I do not think its something that is being missed as far as resetting the peripheral/flags. I'm not sure if there is a provision for a timeout in this nxp blocking transfer (haven't looked deep enough in their code), but if using this function one would be needed.This probably points out where the difference is in the approach to determining bus state by various mcu's- the previous mcu's tested have an internal bus state that can be forced into an idle state and will put at least a single scl clock (no looking at bus levels), where this nxp mcx for instance will look at the bus levels to get to an idle state and if sda is stuck low it can never get to idle where it would be willing to attempt a start condition.

author: Doctorandus_P
author_rank: Super Contributor
timestamp: May 27, 2025, 11:10:36 pm
content: There is no good fix for I2C. It's an old standard and that is one of the things that was simply not imlemented. SMbus is a newer standard, mostly I2C compatible, but in addition it has added timeouts and some other things that make it more robust.https://en.wikipedia.org/wiki/System_Management_Bus

author: cv007
author_rank: Frequent Contributor
timestamp: May 28, 2025, 05:44:01 am
content: I tested the pi pico (rp2040) and it also acts like the nxp mcx- it will not attempt a start when sda is pulled low. They have blocking functions in their sdk with and without a timeout, but would be a bad idea to use the ones without a timeout as they will not return if sda is stuck low. I guess they did not consider this as a possibility. A better idea it seems to me would be to either eliminate the no timeout functions, or at least provide a default/long timeout for these versions so at least one cannot get stuck inside the sdk function (these no timeout versions already do call a lower level function that does take a timeout function/value, so a simple addition of a default timeout would fix). If a user wants to write blocking code for any reason- fine, if an sdk function blocks and the possibility exists to block forever- not fine.QuoteFor the rp2040, I also tried putting the sda pin back to gpio then trying to do a transfer, but that did not produce any clocks. Sounds like a good idea at first but these peripherals that want to actually see both scl/sda idle first will most likely not be happy with the disconnected internal pin routing, and at least in the case of the rp2040 it appears to get treated as if the pin was low.It appears to me a big advantage of the avr gen2/stm32 i2c design approach which always puts out at least 1 clock, is that even if the stuck sda problem is ignored these mcu's will soon get to a cleared bus via normal activity (you will deal with arbitration errors, but will also continue to attempt transfers therefore putting out clocks).

author: Sal Ammoniac
author_rank: Super Contributor
timestamp: May 28, 2025, 03:44:00 pm
content: I have handled this situation at the board level by using MOSFETs to be able to power-cycle all of the I2C peripherals on the board if one or more of them get hung. That's the only sure way of recovering from a stuck bus.

author: mtwieg
author_rank: Frequent Contributor
timestamp: May 30, 2025, 12:45:11 pm
content: Quote from: Siwastaja on May 26, 2025, 10:24:29 amQuote from: Sal Ammoniac on May 28, 2025, 03:44:00 pmUnfortunately this option isn't always viable. A couple anecdotal scenarios I've dealt with:1. The slave was inside the main PSU for the overall device (including the MCU acting as the master). This is a COTS PSU, the implementation of the slave isn't under our control. No way to force a reset without cold-resetting the entire device (not allowable).2. The slave was a coulomb counter, which needs to be connected to the battery being monitored. The current sense amplifier pins also provide power to the slave. Adding FETs would degrade the coulomb counter performance. Also resetting the chip would mean losing the volatile data inside, which violates requirements for the device.In both these cases, the datasheet for the slave claimed that it was SMbus compliant. However, neither implemented the SCL timeout feature mandated by the SMbus standard. So bit-banging SCL was the only option.