Cool, I like the addition of Wire or Wire1 access. I assume it works if you use both Wire & Wire1 concurrently, each with their own sensor. Have you tried it with 2?
For those interested, I have modified Adafruit's BNO055 library to be compatible with I2c_t3.
Read more about it here.
I have a strange problem, which may not be related to the i2c_t3 library, but I hope someone reading this thread can help me.
The problem is that I get failed calls to endTransmission() and requestFrom() - but only after my code has been running for minutes or even hours. The typical pattern is that I get acknowledge fails periodically, but with increasing frequency. After some hours I finally get timeout on all endTransmission calls.
I am using a Teensy 3.2 and eight MCP23017 port expanders.
Any ideas how I2C can become increasingly unstable?
It could be that some of the port expander slaves are dropping clocks and getting out-of-sync or stuck (they may not be getting stuck in a way that completely blocks the bus). Perhaps try pulling some slaves off the bus to see if it is a particular device, or try reducing the clock rate to see if you can improve the data integrity. If it is a noisy environment (bad supply/ground connections, no bypass caps, etc), that could also contribute to traffic errors.
If basic tweaks don't work then you'll need more detailed diagnosis and error codes to try and figure out why it's failing, eg:
- always same device fails?
- same error code?
- MCP23017 has a reset line. If a fail occurs and you toggle the reset line, does it start working again?
I would start with the basic stuff, trying to clean up the signaling on the line and reducing speed if necessary. If a scope shows the signals are clean, then maybe check for bad connections or bad slave devices.
In theory START/STOP signals on the line are supposed to reset Slaves regardless of their state, but having seen the verilog that some people write for these interfaces, and having written interfaces myself I can tell you theory isn't always reality.
Is there a way to use a rate less than 100? I have a situation where the capacitance is high enough to disrupt the i2c even at 100. Unfortunately there's no way for me to reduce the capacitance so I just need to find a way to get this working. Thanks
Okay, here is what I found out so far:
1) Unplugging my slave-device seems to lock up the I2C bus in such a way that it cannot recover when I re-connect it.
2) endTransmission() returns I2C_TIMEOUT because i2c->currentStatus is still I2C_SENDING when finish_() is called.
3) Looking at my scope, the SCL and SDA pins are stuck in high state.
4) Calling resetBus() or begin() does not get I2C running again.
Any ideas what might cause this?
Is there a way to use a rate less than 100? I have a situation where the capacitance is high enough to disrupt the i2c even at 100. Unfortunately there's no way for me to reduce the capacitance so I just need to find a way to get this working. Thanks
case I2C_RATE_100: *(i2c->F) = 0x2C; break; // 100k 576 (actual 104k)
case I2C_RATE_200: *(i2c->F) = 0x24; break; // 200k 288 (actual 208k)
I2C0_F = 0xYY;
1) Unplugging my slave-device seems to lock up the I2C bus in such a way that it cannot recover when I re-connect it.
2) endTransmission() returns I2C_TIMEOUT because i2c->currentStatus is still I2C_SENDING when finish_() is called.
3) Looking at my scope, the SCL and SDA pins are stuck in high state.
4) Calling resetBus() or begin() does not get I2C running again.
nox771 said:Have you tried swapping in a different T3?
nox771 said:One possibility might be something interfering with the interrupt being serviced. Have you tried running in immediate mode (non-interrupt), eg. using I2C_OP_MODE_IMM in the begin() call or by calling Wire.setOpMode(I2C_OP_MODE_IMM); (can add that line right after begin() when bus is idle). Check and see if that changes anything.
I've been noticing a very similar behavior with a project I'm working on. I've noticed it during both un/re-plugging slave devices (occasionally, not always) as well as randomly occurring during normal operations (usually between 2 seconds to an hour after power-on), but only when I have more than about 4 slaves connected. Both SDA and SCL lines stay high, no output from the Teensy. The functions resetBus() and begin() have no effect, and don't transmit even when called multiple times in a error catching function. The only thing besides power-cycling that I've found to clear this situation is to manually pull SCL low briefly (manually with a jumper).
I've seen this with 3 different T3.2 units
I've tried running using I2C_OP_MODE_IMM and the entire Teensy then locks up.
Hello TeensyLC571
Hello TeensyLC5711
Hello TeensyLC57111
Hello TeensyLC571111
Hello TeensyLC5711111
Hello TeensyLC57111111
Hello TeensyLC833
Hello TeensyLC8333
Hello TeensyLC83333
Hello TeensyLC833333
Hello TeensyLC8333333
Hello TeensyLC83333333
Hello TeensyLC474
Hello TeensyLC4744
Hello TeensyLC47444
Hello TeensyLC474444
Hello TeensyLC4744444
Hello TeensyLC986
Hello TeensyLC9866
Hello TeensyLC98666
Hello TeensyLC986666
Hello TeensyLC9866666
Hello TeensyLC98666666
#include <i2c_t3.h>
// Function prototypes
void receiveEvent(size_t len);
//
// Setup
//
void setup()
{
pinMode(LED_BUILTIN,OUTPUT); // LED
// Setup for Slave mode, address 0x66, pins 18/19, external pullups, 400kHz
Wire.begin(I2C_SLAVE, 0x66, I2C_PINS_18_19, I2C_PULLUP_EXT, I2C_RATE_400);
// register event
Wire.onReceive(receiveEvent);
Serial.begin(9600);
}
void loop()
{
digitalWrite(LED_BUILTIN,HIGH); // double pulse LED while waiting for I2C requests
delay(10); // if the LED stops the slave is probably stuck in an ISR
digitalWrite(LED_BUILTIN,LOW);
delay(100);
digitalWrite(LED_BUILTIN,HIGH);
delay(10);
digitalWrite(LED_BUILTIN,LOW);
delay(880);
}
//
// handle Rx Event (incoming I2C request/data)
//
void receiveEvent(size_t len)
{
while(Wire.available())
{
char c = Wire.read(); // receive byte as a character
}
Serial.println(); // print newline
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Windows.Devices.Enumeration;
using Rinsen.IoT.OneWire;
using Windows.Devices.I2c;
using System.Diagnostics;
using System.Threading;
namespace App1
{
public sealed partial class MainPage : Page
{
// teensy vars
private I2cDevice _teensyLC;
private Timer _teensyLCTimer;
private I2cDevice _teensy3;
private Timer _teensy3Timer;
public MainPage()
{
this.InitializeComponent();
initTeensy();
}
private async void initTeensy()
{
var settings = new I2cConnectionSettings(0x66); // teensylc address
settings.BusSpeed = I2cBusSpeed.FastMode;
string aqs = I2cDevice.GetDeviceSelector("I2C1");
var dis = await DeviceInformation.FindAllAsync(aqs);
_teensyLC = await I2cDevice.FromIdAsync(dis[0].Id, settings);
_teensyLCTimer = new Timer(this.TeensyLCTimerCallback, null, 0, 1000);
// teensy 3
settings = new I2cConnectionSettings(0x44); // teensy3 address
settings.BusSpeed = I2cBusSpeed.FastMode;
aqs = I2cDevice.GetDeviceSelector("I2C1");
dis = await DeviceInformation.FindAllAsync(aqs);
_teensy3 = await I2cDevice.FromIdAsync(dis[0].Id, settings);
_teensy3Timer = new Timer(this.Teensy3TimerCallback, null, 0, 200);
}
private void TeensyLCTimerCallback(object state)
{
byte[] writeBuff = new byte[6];
try
{
string msg = "Hello TeensyLC" + new Random().Next(999).ToString();
byte[] sendBuff = new byte[msg.Length];
sendBuff = System.Text.Encoding.UTF8.GetBytes(msg);
_teensyLC.Write(sendBuff);
Debug.Write("TeensyLC message sent [" + msg + "]\r\n");
}
catch (Exception f)
{
Debug.WriteLine(f.Message);
}
}
private void Teensy3TimerCallback(object state)
{
byte[] writeBuff = new byte[6];
try
{
string msg = "Hello Teensy3" + new Random().Next(999).ToString();
byte[] sendBuff = new byte[msg.Length];
sendBuff = System.Text.Encoding.UTF8.GetBytes(msg);
_teensy3.Write(sendBuff);
Debug.Write("Teensy3 message sent [" + msg + "]\r\n");
}
catch (Exception f)
{
Debug.WriteLine(f.Message);
}
}
}
}
The Teesny3 is running the slave example code and the TeensyLC is running the slightly modified code below ...
Code:// // handle Rx Event (incoming I2C request/data) // void receiveEvent(size_t len) { while(Wire.available()) { [COLOR=#ff0000]char c = Wire.read();[/COLOR] // receive byte as a character } Serial.println(); // print newline }
You must have some other kind of receive function. This doesn't seem to do anything at all. It reads a byte into the same local-scope "c" var, over and over, then dumps it when it exits scope and then prints a newline.
The original Slave example is only designed to work with the original Master example - it is using a protocol layer on top of base I2C communication (it has a command and address byte as part of the data payload). Your PI master is not going to be talking to it the right way.
I would start by taking a closer look at the T3 and LC receiveEvent() functions. I would not run two Slaves until you get one working exactly right. If you have trouble with that, post just that one with details of the problem.
void receiveEvent(size_t len)
{
while(Wire.available())
{
char c = Wire.read(); // receive byte as a character
Serial.print(c); // print the character
}
Serial.println(); // print newline
}
[/CODE]
I realise the original slave code was designed to work with the master which is why I modified it. (The PI code is just sending a string for this simple test and works fine with a single TeensyLC device) The point is that this code works perfectly when I swap out the TeensyLC with a Teensy3. Why? (something to do with the STOP signal?)
The original project is using a DS2482-100 with a DS18B20 thermal sensor and the Rinsen OneWire library, when I added the TeensyLC, I ran into the problems described above, where every time the sensor was read I would receive data on the TeensyLC. To help troubleshoot the issue I created a new project without the DS2482, substituting it with a Teensy3 and still I have the same issues. However the same code running on a Teensy3, in the original project works fine with the DS2482.
_teensyLCTimer = new Timer(this.TeensyLCTimerCallback, null, 0, 1000);
_teensy3Timer = new Timer(this.Teensy3TimerCallback, null, 0, 200);
Overall I'm still not getting a complete picture of what's going on here.
As I understand it, talking to either the T3 or LC slaves individually if only one is connected to the bus works fine, is that right? These are running a basic Slave that reads the data and prints it, that's all right? No other blocking interrupts or other wierdness in parallel?
Hello TeensyLC551
Hello TeensyLC97
Hello TeensyLC881
Hello TeensyLC307
Hello TeensyLC568
Hello TeensyLC993
Hello TeensyLC777
Hello TeensyLC562
Hello TeensyLC986
Hello TeensyLC412
Hello TeensyLC837
Hello TeensyLC263
Hello TeensyLC525
Hello TeensyLC5255
Hello TeensyLC52555
Hello TeensyLC525555
Hello TeensyLC5255555
Hello TeensyLC52555555
Hello TeensyLC525555555
Hello TeensyLC5255555555
Hello TeensyLC52555555555
Hello TeensyLC525555555555
Hello TeensyLC5255555555555
Hello TeensyLC52555555555555
Hello TeensyLC525555555555555
Hello TeensyLC5255555555555555
Hello TeensyLC52555555555555555
Hello TeensyLC525555555555555555
Hello TeensyLC5255555555555555555
Hello TeensyLC52555555555555555555
Hello TeensyLC525555555555555555555
Hello TeensyLC5255555555555555555555
Hello TeensyLC52555555555555555555555
Hello TeensyLC525555555555555555555555
Hello TeensyLC5255555555555555555555555
Hello TeensyLC52555555555555555555555555
Hello TeensyLC525555555555555555555555555
Hello TeensyLC5255555555555555555555555555
Hello TeensyLC52555555555555555555555555555
Hello TeensyLC525555555555555555555555555555
Hello TeensyLC5255555555555555555555555555555
Hello TeensyLC52555555555555555555555555555555
I don't know if they step on each other, or if it's right or wrong (I'm unfamiliar with this code), but it seems something could be colliding if these are sharing an interface. Is it possible to use one timer and ping-pong the messages between Slaves? That would eliminate some concerns if things aren't thread safe and they collide.
void receiveEvent(size_t len)
{
for ( size_t ii=0; ii<len; ii++)
{
char c = Wire.read(); // receive byte as a character
Serial.print(c); // print the character
}
Serial.println(); // print newline
}
When the ReceiveEvent is called :: void receiveEvent(size_t len)
The number of known bytes available is provided. There may be a problem - and this may just hide the symptom, but what do you get with:
Code:void receiveEvent(size_t len) { for ( size_t ii=0; ii<len; ii++) { char c = Wire.read(); // receive byte as a character Serial.print(c); // print the character } Serial.println(); // print newline }
If this works then this message all the next messages should arrive and be handled properly. If the chatter persists that may help clarify the problem.
Actually I'm wondering if this is due to interaction with Serial. For Serial to function there is implicit ISR traffic due to USB (USB runs at a higher priority). receiveEvent() is a callback from inside the I2C ISR, so it's really part of the ISR. Stacking ISRs works due to the NVIC, but one thing you can try is to pull the Serial stuff out of receiveEvent(). The way the code is written compounds this effect due to the way it is written - printing one byte at a time, versus printing a whole buffer. Since I2C is slow this creates a lot of dragged out USB traffic.
To fix that here is something to try - use receiveEvent() to stuff the data in a buffer, and then set a flag. Mark the flag as volatile. In your main loop check for the flag, and if it occurs print the buffer then clear the flag.
Hello TeensyLC [0]
Hello TeensyLC [1]
Hello TeensyLC [2]
Hello TeensyLC [3]
Hello TeensyLC [4]
Hello TeensyLC [5]
Hello TeensyLC [6]
Hello TeensyLC [7]
Hello TeensyLC [8]
Hello TeensyLC [9]
Hello TeensyLC [10]
Hello TeensyLC [0]
Hello TeensyLC [0]]
Hello TeensyLC [0]]]
Hello TeensyLC [0]]]]
Hello TeensyLC [0]]]]]
Hello TeensyLC [1]
Hello TeensyLC [1]]
Hello TeensyLC [1]]]
Hello TeensyLC [1]]]]
Hello TeensyLC [1]]]]]
Hello TeensyLC [1]]]]]]
Hello TeensyLC [2]
Hello TeensyLC [2]]
Hello TeensyLC [2]]]
Hello TeensyLC [2]]]]
Hello TeensyLC [2]]]]]
Hello TeensyLC [2]]]]]]
Hello TeensyLC [3]
Hello TeensyLC [3]]
Hello TeensyLC [3]]]
Hello TeensyLC [3]]]]
Hello TeensyLC [3]]]]]
Hello TeensyLC [3]]]]]]
#include <i2c_t3.h>
// Function prototypes
void receiveEvent(size_t len);
volatile bool msgRecieved = false;
char msg[31];
//
// Setup
//
void setup()
{
pinMode(LED_BUILTIN,OUTPUT); // LED
// Setup for Slave mode, address 0x66, pins 18/19, external pullups, 400kHz
Wire.begin(I2C_SLAVE, 0x66, I2C_PINS_18_19, I2C_PULLUP_EXT, I2C_RATE_400);
// register event
Wire.onReceive(receiveEvent);
Serial.begin(9600);
}
void loop()
{
if (msgRecieved)
{
Serial.println(msg);
msgRecieved = false;
}
}
//
// handle Rx Event (incoming I2C request/data)
//
void receiveEvent(size_t len)
{
memset(msg, 0, sizeof(msg));
for ( size_t ii=0; ii<len; ii++)
{
char c = Wire.read(); // receive byte as a character
msg[ii] = c; // add the character
}
msgRecieved = true;
}