Skip to content

Commit

Permalink
Update README.md
Browse files Browse the repository at this point in the history
  • Loading branch information
SpenceKonde committed Nov 6, 2022
1 parent 59b7ad6 commit ca4dfeb
Showing 1 changed file with 4 additions and 4 deletions.
8 changes: 4 additions & 4 deletions megaavr/libraries/Wire/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -337,11 +337,11 @@ flush();

~A `flush()` method exists on all versions of Wire.h; indeed, `Stream` which it subclasses demands that - however very rarely is it actually implemented by anything other than Serial - (where there is a specific and very common reason use case) where one must clear the buffer in a specific way (by waiting for it to empty) before doing things like going to sleep or performing a software reset. Wire has rarely implemented this. In this case, it performs the functionality that the datasheets refer to as a "TWI_FLUSH" - this resets the internal state *of the master* - and at the Wire library level, the buffers are cleared; that command is apparently intended for error handling. The hardware keeps track of activity on the bus (as required by the protocol), but misbehaving devices can confuse the master - they might do something that the specification says a device will will not do, or generate electrical conditions that the master is unable to interpret in a useful way - pins not reaching the logic level thresholds, malformed data and which may in turn leave it confused as to whether the bus is available. It might be necessary to call in an attempt to recover from adverse events, which has historically been a challenge for the Wire library.~

There are two issues that lead to this method being a stub (do-nothing method, required to subclass stream). First is the tension with between the hardware "flush" functionality, which according to the datasheet is intended to clear up detected bus errors, with the arduino API for HardwareSerial, the only stream where flush is generally implemented, where it instead waits for outgoing transmissions to finish. Secondly, either all, or all non-tinyAVR parts appear likely to be impacted by an erratum that renders the hardware mechanism less than useful: "Flush Non-Functional", which according to the errata sheets can in practice cause the very problem it was intended to solve, and advising us to disable and reenable TWI master instead.
There are two issues that lead to this method being a stub (do-nothing method, required to subclass stream). First is the tension with between the hardware "flush" functionality, which according to the datasheet is intended to clear up detected bus errors, with the arduino API for HardwareSerial, the only stream where flush is generally implemented, where it instead waits for outgoing transmissions to finish. Secondly, either all, or all non-tinyAVR parts appear likely to be impacted by an erratum that renders the hardware mechanism less than useful: "Flush Non-Functional", which according to the errata sheets can in practice cause the very problem it was intended to solve, and advising us to disable and re-enable TWI master instead.

So, using the hardware's flush function to clear the master's state it out. One can also argue that because of the precedent set by HardwareSerial, we should defer to it's well-defined behavior over the foggy definition of the arduino Stream API, and have it mimic that functionality. But in that case, for the I2C master, the methods to send data already block until the operation is completed, and on the slave side, the device is at the mercy of the master - we could be waiting literally an eternity (that is, it is equivalent to useing Serial.flush() when acting as a slave device in synchronous mode to transmit when the XCK pin has no clock signal - this is essentially the same situation with a different interface. Waiting for data to be clocked out, without control over that clock, is probably not the ideal API to provide either; the same behavior can be achieved with `while (slaveTransactionOpen()) {...}`, but this gives an opportunity to have a timeout after which the slave can decide that the master has fallen over and (for example) disable and reenable the interface, expecting that the next contact from the master will be a fresh start condition from a reinitialized master.
So, using the hardware's flush function to clear the master's state is out. One can also argue that because of the precedent set by HardwareSerial, we should defer to it's well-defined behavior over the foggy definition of the arduino Stream API, and have it mimic that functionality. But in that case, for the I2C master, the methods to send data already block until the operation is completed, and on the slave side, the device is at the mercy of the master - we could be waiting literally an eternity (that is, it is equivalent to using Serial.flush() when acting as a slave device in synchronous mode to transmit when the XCK pin has no clock signal - this is essentially the same situation with a different interface. Waiting for data to be clocked out, without control over that clock, is probably not the ideal API to provide either; the same behavior can be achieved with `while (slaveTransactionOpen()) {...}`, but this gives an opportunity to have a timeout after which the slave can decide that the master has fallen over and (for example) disable and re-enable the interface, expecting that the next contact from the master will be a fresh start condition from a reinitialized master.

It is not hard to imagine a scenario where that would be relevant: Just imagine that you are writing application code for the master, and upload the new version of your code while it happens to be in the middle of a transaction. If you're waiting on the completion of transactions before taking some action, you probably want to make sure you are handling this case. That said,the most common action of concern here is putting the part into sleep mode, in which case, refer to the section below on Wire and Sleep - a better model is likely to simply have the device attempt to sleep during every pass through loop() if it has nothing else to do (as of 2.6.2).
It is not hard to imagine a scenario where that would be relevant: Just consider the case of writing application code for the master, and uploading a new version while it happens to be in the middle of a transaction. You probably do not want to wait for the master (reset) master to attempt (and fail at, because it would get a bus arbitration error as the slave attempted to answer) communication before doing anything, as you'd likely then have to reset both devices at once to upload - sometimes. If you're waiting on the completion of transactions before taking some action, you probably want to make sure you are handling the case where the master does not finish the transaction for some reason. I2C masters rarely take long periods of time between clocking in bytes when functioning correctly. At 100kHz, a byte takes under 100 us to transfer, and the master (if it's another Arduino device) is likely blocking during that time, waiting to grab another byte immediately. Even with a 130 byte buffer, this means that after a dozen ms, it should have been able to receive an entire buffer. If you were to wait 20 ms (or somewhat longer if you specifically modify the library for a massive buffer), you could conclude that the master is unlikely to ever finish. On the other hand, when the only think you intend to do is put the part to sleep, you can attempt to sleep on every pass through loop() instead as described below. On the third hand, if the system were battery powered, and that occurred, the slave would still be in a bad state, consuming IDLE sleep mode current, instead of POWER_DOWN current, which is many times lower, so maybe this would not be so great after all. In any event, a flush that worked like hardware serial would not help that case either, but rather make it drain the battery about twice as fast by not sleeping at all. This brings discussion back to the point I mentioned near the beginning: When something goes wrong with I2C, it usually goes very wrong. Unlike SPI, where you have a pin state to watch to see if the master thinks you are in a transaction, there is nothing physical that you can monitor and notice that the master vanished in the middle of a transaction.

```c++
uint8_t endTransmission(bool sendStop);
Expand Down Expand Up @@ -415,7 +415,7 @@ All modern AVRs, since the release of the first tinyAVR 0/1-series, through the
## Known incompatible devices
For an unknown reason, the contactless IR-thermometer MLX90614 is not working out of the box with the modern AVRs. We suspect it is related to the new TWI module implementation interacting with a some aspect of the interface on the MLX90614 which does not follow the specifications. A workaround is to set the clock frequency to a value between 110-125kHz.
I would also suggest testing this with `Wire.specialConfig(0,1,3)` with the default speed (which normally doesn't work). If that fixes it, see if either of those non-zero arguments can be zero'ed out. If the third argument is the key one, see how far you can lower it before it fails (note that this may depend on the physical electronic properties of the bus that determine the rise and fall times). If you do this experiment, please report your results to me so that I can update this section!
I would also suggest testing this with `Wire.specialConfig(0,1,3)` with the default speed (which normally doesn't work). If that fixes it, see if either of those non-zero arguments can be zero'ed out. If the third argument is the key one, see how far you can lower it before it fails (note that this may depend on the physical electronic properties of the bus that determine the rise and fall times). If you do this experiment, please report your results to me so that I can update this section!
At least two of the rewrites of the clock caluculation algorithm that have been submitted, independently, by two different individuals (the second of whom didn't stop there and rewrote the whole library, slashing it's flash footprint while adding functionality) were prompted initially by users trying to get this specific part to work. So - really we're all in debt to this manufacturer, whose combination of compelling hardware saddled with a badly botched I2C interface has led to so much improvement in our Wire library.
Expand Down

0 comments on commit ca4dfeb

Please sign in to comment.