Posted March 13, 2013 by nox771: ------------------------------------------------------ Hello all, Recently I needed to utilitze the I2C but I found the default library somewhat inadequate for my needs. So I've gone ahead and rewritten it with the following changes. This was primarily done for the needs of my project, but I figured I would upload the code in case anyone else finds it useful. The library is called i2c_t3 and is a replacement for the Wire library (changing #include to #include should be all that is needed). Note this is only for the Teensy3, I have removed the AVR code. I'm not a fan of many things in the Arduino API, but I did maintain compatibility with Wire as far as I know (the one exception I ran into when compiling the example sketches is the onReceive callback which I changed from onReceive(int len) to onReceive(size_t len) - reason is I changed all the buffer indicies to size_t). In the attached Zip I've included two example sketches in addition to the libraries - one for a Master device and one for a Slave device. They were made to talk to each other (I used two T3's back-to-back on a breadboard to debug). Here are the changes (also summarized at the top of the library .cpp and .h files): Reworked the begin() function. The default Wire begin() functions work, but I added an expanded function which allows setting the following options (refer to the enums in the i2c_t3.h file for the arguments): The pins to use for I2C can be specified as pins 18/19 or 16/17. The pullups can be specified as internal or external. As mentioned in the previous code the pullups are strong (I measured them as approx. 190 ohms) but they can be useful if your slave pulldowns are strong enough. The T3 has pretty strong pulldowns, so I had no problem running my debug setup of two T3's back-to-back using just the internal pullups. Note with such a low R pullup, there is an associated current drain hit if that is important to your project. Of course external pullups can also be used as well. The communication rate can be specified, supported rates are: 100k, 400k, 600k, 800k, 1M, 1.2M, 1.5M, 2.0M, and 2.4M. Rates above 1.2M are only supported if the bus freq is 48MHz. Note that for high-freq rates, despite what the datasheet says, the actual rates are not that high. As an example I was able to run my back-to-back T3's at the highest rate setting (2.4M), however my scope put the actual toggling of the clock line at about ~1.8MHz. There was also a significant gap between bytes, about 3us (the gap varies, but I saw many about that long). Overall I saw about 8us (from beginning of one byte to beginning of the next) for it to transfer 8bits+Ack, so 8bits in 8us = 1bit/us = 1Mbit/s throughput. So, not bad, but also not 2.4Mbit/s either. I changed the Master Tx/Rx modes to interrupt-driven. This allows backgrounding of large transfers. This is especially useful on big transfers on a slow bus. With the change to interrupt driven, I added non-blocking Tx/Rx routines, and routines for working with background transfers (status/done/finish). They are: sendTransmission() - non-blocking transmit sendRequest() - non-blocking receive status() - reports current I2C status done() - indicates Tx/Rx complete (used for main loop polling of background I2C) finish() - blocks until Tx/Rx complete or bus error Added readByte/peekByte for uint8_t return values (I'm not a fan of int returns on a byte based bus). Added a fix for Slave Rx mode involving RepSTARTs. For those not aware, Slave Rx mode on this part is completely fubar'd, it has no method of detecting STOP or RepSTART conditions in that mode. The very clever SDA-rising ISR that Paul added is a neat way to detect STOPs, but unfortunately it can't detect RepSTARTs. To fix that I check for IAAS occuring while that part is already in Slave Rx mode, which implies RepSTART occured. Just an FYI, while studying this problem I ran into an interesting indirect admission of it - the ref manual for this part is marked Feb12, but if you look at the KL25 Sub-Family ref manual (Cortex-M0 parts), dated Sep12 then take a look at the I2C0_FLT register definition you will find some interesting changes regarding the STOP function. It appears they patched the problem on those parts. Separated the Tx/Rx buffer sizes for configuring asymmetric devices (adjustible in the i2c_t3.h header file). Changed all Tx/Rx buffer indicies to size_t to allow for large (>256byte) buffers. I left my debug routines in place. They are controlled via header defines and default to OFF (meaning they compile to nothing). Debug without JTAG is painful on ISRs, so the method I came up with is to buffer up the messages and feed them out over Serial. FYI - debug has a dependency on the ring buffer class (aka circular FIFO) that I also included. Basically the I2C ISR dumps its messages into a large ring buffer, then a PIT based interrupt will periodically dump those in small chunks to Serial. There are varying levels of verbosity (also controlled by the header defines). If someone wants to see ISR diagnositcs, just uncomment those, and if it overruns and gets mangled on a large transfer, just increase the size of the ringbuf (large transfers can easily generate several kB of diag data). From the testing I've done the library seems to work, but of course if anyone tries it out and runs into a problem let me know (or better yet, debug it, then let me know)