Read two IMUs with one Teensy 3.2

Status
Not open for further replies.

Bita

Member
hello all,
For my project, I need to read data from 2 or more IMUs and although still I am not sure if it is possible, I want to read them with just one Teensy 3.2. I am using the code bellow to read one IMU and I use I2C. I think I should define the address for another IMU in functions "void Gyro_Init()" and "void Accel_Init()". But really don't know how (I am very new in programming Arduino and Teensy). Actually, I want to modify this code for reading two IMUs. Also, have no idea for two IMUs connections to Teensy (I can read one IMU and can connect it to Teensy).
Thank you so much.

#include <Wire.h>
#include <SoftwareSerial.h>
//12/20/2016
//Wire.setSDA(A3) and Wire.setSCL(A2)
#include <Wire.h>
#include <L3G.h>
#include <LSM303.h>

L3G gyro;
LSM303 compass;

double aRoll;
double aPitch;
double aYaw;
double ax;
double ay;
double az;
double vx;
double vy;
double vz;

int SENSOR_SIGN[9] = {1,1,1,-1,-1,-1,1,1,1};
double TIME;

#define GRAVITY 256
#define ToRad(x) ((x)*0.01745329252)
#define ToDeg(x) ((x)*57.2957795131)
#define Gyro_Gain_X 0.07
#define Gyro_Gain_Y 0.07
#define Gyro_Gain_Z 0.07
#define Gyro_Scaled_X(x) ((x)*ToRad(Gyro_Gain_X))
#define Gyro_Scaled_Y(x) ((x)*ToRad(Gyro_Gain_Y))
#define Gyro_Scaled_Z(x) ((x)*ToRad(Gyro_Gain_Z))
#define M_X_MIN -3506
#define M_Y_MIN -3473
#define M_Z_MIN -3764
#define M_X_MAX 3162
#define M_Y_MAX 3011
#define M_Z_MAX 2552
#define Kp_ROLLPITCH 0.02
#define Ki_ROLLPITCH 0.00002
#define Kp_YAW 1.2
#define Ki_YAW 0.00002
#define OUTPUTMODE 1
#define PRINT_ANALOGS 1
#define PRINT_EULER 0
#define STATUS_LED 13

float G_Dt=0.02;
long timer=0;
long timer_old;
long timer24=0;
int AN[6];
int AN_OFFSET[6]={0,0,0,0,0,0};
int gyro_x;
int gyro_y;
int gyro_z;
int accel_x;
int accel_y;
int accel_z;
int magnetom_x;
int magnetom_y;
int magnetom_z;
float c_magnetom_x;
float c_magnetom_y;
float c_magnetom_z;
float MAG_Heading;
float Accel_Vector[3]= {0,0,0};
float Gyro_Vector[3]= {0,0,0};
float Omega_Vector[3]= {0,0,0};
float Omega_P[3]= {0,0,0};
float Omega_I[3]= {0,0,0};
float Omega[3]= {0,0,0};
float roll;
float pitch;
float yaw;
float errorRollPitch[3]= {0,0,0};
float errorYaw[3]= {0,0,0};
unsigned int counter=0;
byte gyro_sat=0;
float DCM_Matrix[3][3]= {
{
1,0,0 }
,{
0,1,0 }
,{
0,0,1 }
};
float Update_Matrix[3][3]={{0,1,2},{3,4,5},{6,7,8}};
float Temporary_Matrix[3][3]={
{
0,0,0 }
,{
0,0,0 }
,{
0,0,0 }
};

float TIMENEW;
int i;
int y;

float MAG_X;
float MAG_Y;
float cos_roll;
float sin_roll;
float cos_pitch;
float sin_pitch;
float error=0;
float temporary[3][3];
float renorm=0;
float mag_heading_x;
float mag_heading_y;
float errorCourse;
static float Scaled_Omega_P[3];
static float Scaled_Omega_I[3];
float Accel_magnitude;
float Accel_weight;
int ic=0;
double Pmkf[4][4];
double xhat_old[4][1];
float Time;

void setup()
{
Serial.begin(115200);
Wire.setSDA(A3);
Wire.setSCL(A2);

I2C_Init();
//Wire.begin();
delay(150);
Accel_Init();
Compass_Init();
Gyro_Init();
delay(20);
for(i=0;i<32;i++)
{
Read_Gyro();
Read_Accel();
for(y=0; y<6; y++)
AN_OFFSET[y] += AN[y];
delay(20);
}
for(y=0; y<6; y++)
AN_OFFSET[y] = AN_OFFSET[y]/32;
AN_OFFSET[5]-=GRAVITY*SENSOR_SIGN[5];
for(y=0; y<6; y++)
delay(200);
timer=millis();
delay(20);
counter=0;

}

void loop()
{
ImuReadings();
Serial.println(aRoll);

Time=(float)millis()/1000;
}



//*******************************************************************
//EXTRA FUNCTIONS OUT OF LOOP
//*******************************************************************

void I2C_Init()
{
Wire.begin();
}

void Compass_Heading()
{
cos_roll = cos(roll);
sin_roll = sin(roll);
cos_pitch = cos(pitch);
sin_pitch = sin(pitch);
c_magnetom_x = (float)(magnetom_x - SENSOR_SIGN[6]*M_X_MIN) / (M_X_MAX - M_X_MIN) - SENSOR_SIGN[6]*0.5;
c_magnetom_y = (float)(magnetom_y - SENSOR_SIGN[7]*M_Y_MIN) / (M_Y_MAX - M_Y_MIN) - SENSOR_SIGN[7]*0.5;
c_magnetom_z = (float)(magnetom_z - SENSOR_SIGN[8]*M_Z_MIN) / (M_Z_MAX - M_Z_MIN) - SENSOR_SIGN[8]*0.5;
MAG_X = c_magnetom_x*cos_pitch+c_magnetom_y*sin_roll*sin_pitch+c_magnetom_z*cos_roll*sin_pitch;
MAG_Y = c_magnetom_y*cos_roll-c_magnetom_z*sin_roll;
MAG_Heading = atan2(-MAG_Y,MAG_X);
}

void Gyro_Init()
{
gyro.init();
gyro.writeReg(L3G::CTRL_REG4, 0x20); // 2000 dps full scale L3G_CTRL_REG4
gyro.writeReg(L3G::CTRL_REG1, 0x0F); // normal power mode, all axes enabled, 100 Hz L3G_CTRL_REG1
gyro.writeReg(L3G::CTRL_REG1, 0xFF);
}

void Read_Gyro()
{
gyro.read();

AN[0] = gyro.g.x;
AN[1] = gyro.g.y;
AN[2] = gyro.g.z;
gyro_x = SENSOR_SIGN[0] * (AN[0] - AN_OFFSET[0]);
gyro_y = SENSOR_SIGN[1] * (AN[1] - AN_OFFSET[1]);
gyro_z = SENSOR_SIGN[2] * (AN[2] - AN_OFFSET[2]);
}


void Accel_Init()
{
compass.init();
compass.enableDefault();
switch (compass.getDeviceType())
{
case LSM303::device_D:
compass.writeReg(LSM303::CTRL2, 0x18);
break;
case LSM303::device_DLHC:
compass.writeReg(LSM303::CTRL_REG4_A, 0x28);
break;
default:
compass.writeReg(LSM303::CTRL_REG4_A, 0x30);
}
}

void Read_Accel()
{
compass.readAcc();
AN[3] = compass.a.x >> 4;
AN[4] = compass.a.y >> 4;
AN[5] = compass.a.z >> 4;
accel_x = SENSOR_SIGN[3] * (AN[3] - AN_OFFSET[3]);
accel_y = SENSOR_SIGN[4] * (AN[4] - AN_OFFSET[4]);
accel_z = SENSOR_SIGN[5] * (AN[5] - AN_OFFSET[5]);
}

void Compass_Init()
{

}

void Read_Compass()
{
compass.readMag();
magnetom_x = SENSOR_SIGN[6] * compass.m.x;
magnetom_y = SENSOR_SIGN[7] * compass.m.y;
magnetom_z = SENSOR_SIGN[8] * compass.m.z;
}

void Normalize(void)
{
error= -Vector_Dot_Product(&DCM_Matrix[0][0],&DCM_Matrix[1][0])*.5;
Vector_Scale(&temporary[0][0], &DCM_Matrix[1][0], error);
Vector_Scale(&temporary[1][0], &DCM_Matrix[0][0], error);
Vector_Add(&temporary[0][0], &temporary[0][0], &DCM_Matrix[0][0]);
Vector_Add(&temporary[1][0], &temporary[1][0], &DCM_Matrix[1][0]);
Vector_Cross_Product(&temporary[2][0],&temporary[0][0],&temporary[1][0]);
renorm= .5 *(3 - Vector_Dot_Product(&temporary[0][0],&temporary[0][0]));
Vector_Scale(&DCM_Matrix[0][0], &temporary[0][0], renorm);
renorm= .5 *(3 - Vector_Dot_Product(&temporary[1][0],&temporary[1][0]));
Vector_Scale(&DCM_Matrix[1][0], &temporary[1][0], renorm);
renorm= .5 *(3 - Vector_Dot_Product(&temporary[2][0],&temporary[2][0]));
Vector_Scale(&DCM_Matrix[2][0], &temporary[2][0], renorm);
}

void Drift_correction(void)
{
Accel_magnitude = sqrt(Accel_Vector[0]*Accel_Vector[0] + Accel_Vector[1]*Accel_Vector[1] + Accel_Vector[2]*Accel_Vector[2]);
Accel_magnitude = Accel_magnitude / GRAVITY;
Accel_weight = constrain(1 - 2*abs(1 - Accel_magnitude),0,1);
Vector_Cross_Product(&errorRollPitch[0],&Accel_Vector[0],&DCM_Matrix[2][0]);
Vector_Scale(&Omega_P[0],&errorRollPitch[0],Kp_ROLLPITCH*Accel_weight);
Vector_Scale(&Scaled_Omega_I[0],&errorRollPitch[0],Ki_ROLLPITCH*Accel_weight);
Vector_Add(Omega_I,Omega_I,Scaled_Omega_I);
mag_heading_x = cos(MAG_Heading);
mag_heading_y = sin(MAG_Heading);
errorCourse=(DCM_Matrix[0][0]*mag_heading_y) - (DCM_Matrix[1][0]*mag_heading_x);
Vector_Scale(errorYaw,&DCM_Matrix[2][0],errorCourse);
Vector_Scale(&Scaled_Omega_P[0],&errorYaw[0],Kp_YAW);
Vector_Add(Omega_P,Omega_P,Scaled_Omega_P);
Vector_Scale(&Scaled_Omega_I[0],&errorYaw[0],Ki_YAW);
Vector_Add(Omega_I,Omega_I,Scaled_Omega_I);
}
void Matrix_update(void)
{
Gyro_Vector[0]=Gyro_Scaled_X(gyro_x);
Gyro_Vector[1]=Gyro_Scaled_Y(gyro_y);
Gyro_Vector[2]=Gyro_Scaled_Z(gyro_z);
Accel_Vector[0]=accel_x;
Accel_Vector[1]=accel_y;
Accel_Vector[2]=accel_z;
Vector_Add(&Omega[0], &Gyro_Vector[0], &Omega_I[0]);
Vector_Add(&Omega_Vector[0], &Omega[0], &Omega_P[0]);
#if OUTPUTMODE==1
Update_Matrix[0][0]=0;
Update_Matrix[0][1]=-G_Dt*Omega_Vector[2];
Update_Matrix[0][2]=G_Dt*Omega_Vector[1];
Update_Matrix[1][0]=G_Dt*Omega_Vector[2];
Update_Matrix[1][1]=0;
Update_Matrix[1][2]=-G_Dt*Omega_Vector[0];
Update_Matrix[2][0]=-G_Dt*Omega_Vector[1];
Update_Matrix[2][1]=G_Dt*Omega_Vector[0];
Update_Matrix[2][2]=0;
#else
Update_Matrix[0][0]=0;
Update_Matrix[0][1]=-G_Dt*Gyro_Vector[2];
Update_Matrix[0][2]=G_Dt*Gyro_Vector[1];
Update_Matrix[1][0]=G_Dt*Gyro_Vector[2];
Update_Matrix[1][1]=0;
Update_Matrix[1][2]=-G_Dt*Gyro_Vector[0];
Update_Matrix[2][0]=-G_Dt*Gyro_Vector[1];
Update_Matrix[2][1]=G_Dt*Gyro_Vector[0];
Update_Matrix[2][2]=0;
#endif
Matrix_Multiply(DCM_Matrix,Update_Matrix,Temporary_Matrix);

for(int x=0; x<3; x++)
{
for(int y=0; y<3; y++)
{
DCM_Matrix[x][y]+=Temporary_Matrix[x][y];
}
}
}

void Euler_angles(void)
{
pitch = -asin(DCM_Matrix[2][0]);
roll = atan2(DCM_Matrix[2][1],DCM_Matrix[2][2]);
yaw = atan2(DCM_Matrix[1][0],DCM_Matrix[0][0]);
}

float Vector_Dot_Product(float vector1[3],float vector2[3])
{
float op=0;

for(int c=0; c<3; c++)
{
op+=vector1[c]*vector2[c];
}

return op;
}

void Vector_Cross_Product(float vectorOut[3], float v1[3],float v2[3])
{
vectorOut[0]= (v1[1]*v2[2]) - (v1[2]*v2[1]);
vectorOut[1]= (v1[2]*v2[0]) - (v1[0]*v2[2]);
vectorOut[2]= (v1[0]*v2[1]) - (v1[1]*v2[0]);
}

void Vector_Scale(float vectorOut[3],float vectorIn[3], float scale2)
{
for(int c=0; c<3; c++)
{
vectorOut[c]=vectorIn[c]*scale2;
}
}

void Vector_Add(float vectorOut[3],float vectorIn1[3], float vectorIn2[3])
{
for(int c=0; c<3; c++)
{
vectorOut[c]=vectorIn1[c]+vectorIn2[c];
}
}
void Matrix_Multiply(float a[3][3], float b[3][3],float mat[3][3])
{
float op[3];
for(int x=0; x<3; x++)
{
for(int y=0; y<3; y++)
{
for(int w=0; w<3; w++)
{
op[w]=a[x][w]*b[w][y];
}
mat[x][y]=0;
mat[x][y]=op[0]+op[1]+op[2];

float test=mat[x][y];
}
}
}


void ImuReadings()
{
if((millis()-timer)>=5)
{
counter++;
timer_old = timer;
timer=millis();
if (timer>timer_old)
G_Dt = (timer-timer_old)/1000.0;
else
G_Dt = 0;
Read_Gyro();
Read_Accel();
if (counter > 5)
{
counter=0;
Read_Compass();
Compass_Heading();
}
Matrix_update();
Normalize();
Drift_correction();
Euler_angles();
#if PRINT_ANALOGS==1
//TIME = micros();
//TIMENEW = TIME/1000000;
aRoll=ToDeg(roll);
aPitch=ToDeg(pitch);
aYaw=ToDeg(yaw);
vx=(Gyro_Scaled_X(gyro_x));
vy=(Gyro_Scaled_Y(gyro_y));
vz=(Gyro_Scaled_Z(gyro_z));
ax=Accel_Vector[0]*9.81/256;
ay=Accel_Vector[1]*9.81/256;
az=Accel_Vector[2]*9.81/256;
//Serial.println(aRoll);
#endif


}
}
 
First question here, are you using two of the same IMU? And if so does the data sheet show anyway to change the device address? It certainly should be possible to have a Teensy talk to multiple addresses but doesn't help if the devices are locked to a single address.
 
Dear GremlinWrangler
Thank you for your reply. I am using two AltIMU-10 v4. you can see all the information and data sheet from here:
https://www.pololu.com/product/2470

I am sure from data sheet that two addresses for two IMUs are possible using SA0 pin. In data sheet it says:

"The slave address (SAD) associated to the LSM303D is 00111xxb, whereas the xx bits are modified by the SDO/SA0 pin in order to modify the device address. If the SDO/SA0 pin is connected to the voltage supply, the address is 0011101b, otherwise, if the SDO/SA0 pin is connected to ground, the address is 0011110b. This solution permits the connection and addressing of two different accelerometers to the same I2C lines."

So, what I have done is to solder the SA0 pin of one of the IMUs and it is connected to ground. The SA0 pin of the other IMU is free. I have connected both IMUs to the same I2C. but with that code, I am able to reed data from just one of them (the one with free SA0 pin) so I do not know how to modify the code to read the two. You know, I think it is a programming problem.
Thank you
 
Well you can at least have two IMUs, on the same bus which is a good start and if you couldn't was going to be a major problem.

Since you can, have a look in
https://github.com/pololu/lsm303-arduino/blob/master/LSM303.cpp
and
https://github.com/pololu/l3g-arduino/blob/master/L3G.cpp
for the line:
bool LSM303::init(deviceType device, sa0State sa0)
and
bool L3G::init(deviceType device, sa0State sa0)

If I'm working out which device and libraries you have correctly you can add the device type and device address as you initially said in the init comments. Looks like the examples don't help but it looks like enter the values to match:
https://github.com/pololu/l3g-arduino/blob/master/L3G.h
Which has a list of device and address options of:
enum deviceType { device_4200D, device_D20, device_D20H, device_auto };
enum sa0State { sa0_low, sa0_high, sa0_auto };

So best guess would be to try swapping gyro.init to gyro.init(device_D20H,sa0_low); and see if it still works, then change it to sa0_high and see if that operates your other device (leave first one connected but should be not being activated).

That confirmed you then need to define two gyro objects and init them seperatly and confirm nothing goes wrong with two instances running together.
 
Last edited:
data sheet it says:

... If the SDO/SA0 pin is connected to the voltage supply, the address is 0011101b, otherwise, if the SDO/SA0 pin is connected to ground

... solder the SA0 pin of one of the IMUs and it is connected to ground. The SA0 pin of the other IMU is free.

That 'free' pin should be wired to 3.3V supply. A note by onehorse with his unit says floating can give unreliable results.

May not be the primary problem . . .
 
Trick with a multiplexor is that you then need custom code on the Teensy side to do the sensor fusion side of things, since you have a single interface instance getting data from two different sensors. If you just want the raw sensor data you can just manually toggle back and forth, but gets interesting when you want a stream of data into some form of algorithm.
 
Dear all:
Thank you all of you for your help. I finally can read Two IMUs using the same I2C bus. Now, I would like to use the second I2C bus on Teensy, and read from 4 IMUs. 4 IMUs are the maximum number of IMUs that I need for my project. So, I want to use Wire1 for the second bus. Based on information of this topic:
https://forum.pjrc.com/threads/21680-New-I2C-library-for-Teensy3
I added the i2c_t3 library in Arduino library and changed the #include <Wire.h> to #include <i2c_t3.h>. But I received compiler error:
collect2.exe: error: ld returned 1 exit status
I played a lot to remove this error. finally I gave up and tried to use Wire.h library. when I use for example Wire1.begin(), the error is Wire1 has not been declared in this scope.
Now I am using this piece of code:
#include <Wire.h>
extern TwoWire Wire1;
to somehow declare Wire1 but again I get the same error:
collect2.exe: error: ld returned 1 exit status
have no idea how to get rid of this! Any ideas?
Thanks
 
Again it is sometimes hard to help debug problems without seeing code and the like.

An issue that many have run into is the Wire library only works on Wire (0) object, it does not have support for Wire1, Wire2, Wire3, depending on which boards you have. In your case I think Teensy3.2 so only Wire.

I2C_t3 library is great, but it creates the same Wire object as the wire library and uses the same processor resources. So if a program uses both, typically it dies in the link (ld) phase with duplicate symbols or the like. And the problem is that if you are including libraries that work with I2C, they typically have wire.h included and you run into these issues.

Currently I have a version of Wire library, that I created that creates the other objects. I am still playing with it so I have not issued a PULL reuest yet. Things like if I automatically create Wire1 and you don't use it, it eats up memory... So how best to minimize it.

If you are interested, the code is up in my fork of Wire library in a new branch which is up at: https://github.com/KurtE/Wire/tree/Support-Wire1/2/3
 
Dear KurtE:
Thank you very much for your help. Actually the code I am working with is the code in my first post. Because it is long, I don't copy it to my other posts but always I am playing with that. I could modify that code to read two IMUs using same bus. Now I am trying to modify that code again to read IMUs using two separate bus. This way, finally I will read 4 IMUs simultaneously!
What I did, I downloaded your Wire library version and substituted the Wire library in directory bellow with it:

C:/Program Files(86)/Arduino/hardware/arduino/avr/libraries

still error keeps popping up!
 
Why do so many i2c devices have fixed addresses, or allow you to only offset by 1? Is it that difficult to allow changing one register for altering the address?
I see these i2c devices with tons of configuration ability but not for the address. And it's not the like the address space is large enough to have a unique address for every sensor.
 
Dear KurtE:
Thank you very much for your help. Actually the code I am working with is the code in my first post. Because it is long, I don't copy it to my other posts but always I am playing with that. I could modify that code to read two IMUs using same bus. Now I am trying to modify that code again to read IMUs using two separate bus. This way, finally I will read 4 IMUs simultaneously!
What I did, I downloaded your Wire library version and substituted the Wire library in directory bellow with it:

C:/Program Files(86)/Arduino/hardware/arduino/avr/libraries

still error keeps popping up!
Actually the place to update the Wire library would be at:
C:/Program Files(86)/Arduino/hardware/teensy/avr/libraries/SPI
 
Why do so many i2c devices have fixed addresses, or allow you to only offset by 1? Is it that difficult to allow changing one register for altering the address?
I see these i2c devices with tons of configuration ability but not for the address. And it's not the like the address space is large enough to have a unique address for every sensor.

I saw this the other day:
Limitations
The assignment of slave addresses is one weakness of I²C. Seven bits is too few to prevent address collisions between the many thousands of available devices, and manufacturers rarely dedicate enough pins to configure the full slave address used on a given board. Three pins is typical, giving only eight choices of slave address. ...

Then there is licensing for many hundreds of devices - some 13+ year old details here in this PDF
 
Hello again!
I am still struggling to make the second I2C bus work. I am still working with the code in my first post. Now I have two devices with the same addresses and want to change my code in order that Teensy can recognize the second device on the second I2C bus. Note, I am not able to use i2c_t3 library. I am using KurtE modified Wire library. The thing is, I want to use L3G and LSM303 libraries too. Do I have to make changes in these libraries? How can I define addresses for Teensy to recognize my second device on second I2C bus?

Another question! I never have worked with SPI. My IMU can use SPI too. Do you think using SPI is easier? I am not sure if I can read two IMUs with the same SPI.
Thank you in advance.
 
Probably.

The unfortunate thing about I2C (and SPI), is most of the libraries have hard coded WIRE. or SPI. which is specific to one buss.

So yes you probably need to update those libraries as well. But again there is the rub on how to do it such that you have code for both busses.

As for SPI. One advantage, is you don't have to deal with I2C address issue. You simply have to have different CS pins, so it can be easier.

However similar issues of mostly hard coded SPI. in libraries and the default SPI library objects do not have a common base class, so hard to setup classes to work with multiple of these buses. I have my own SPIN library that I created that creates a set of wrapper classes for each of the SPI objects that all derive from a base class, such that I can define other classes where I can pass in a pointer to which SPI object to use for that instance. I then create a version of the ILI9341_t3 library (ili9341_t3n), that allows me to pass in which SPI buss to use... But these are the exception...
 
I suggest this add-on to do this:
https://www.tindie.com/products/onehorse/tca9548a-i2c-multiplexer/

2015-02-02T04:36:39.606Z-TCA9548A.front.jpg

I have one of these for this purpose but haven't gotten around to using it yet. But onehorse is a user on here, and his products are always top-notch, and the code to use it is always available.
Each of these gives you 8 i2c devices to control with the same address, and you can expand to 64 devices.
 
LinuxGeek - Looks like it could be a good solution.

But thought I would mention that I updated my testing version of the Adafruit_SSD1306 library, to allow me to pass in a reference to which Wire object to use for that display. So I should be able to host multiple of these devices (but I only own 1).

While making this change I updated my Wire libraries base class to be TwoWire instead of TwoWireB which I had as I think this will work with the only other library I saw that had a reference (or pointer) to Wire object was EasyTransfer.

Again this is currently only in my github... Will check a few more things, maybe make the allocation of buffers optionally use malloc/free...
 
Thanks a lot KurtE and linuxgeek for your useful information.
I think working with a multiplexer for this case is a good idea. I must wait until the holidays will be over then ask my professor to buy one of these for me.
But maybe another option is to connect two Teensys and let them to communicate. I am thinking if I read two IMUs with one Teensy and can transfer data to another Teensy, then I can read the other two IMUs with another Teensy and problem will get resolved. I have seen some available example codes for data transferring but all of them transfer just one data byte when I must transfer two groups of data from two IMUs which are decimal numbers. Do you think it is possible? Also I found two method of communications. One method use serial communication and Two devices must be connect by TX an RX. Another method is to connect them with I2C again and use Wire library. The later needs device address ID (Teensy 3.2) that I could not find. I don't know the advantage of one method over the other one. please advise.
Thanks a lot
 
The other option is the teensy-LC has x2 i2c busses. I do this regularly and put the same i2c sensor on different pins.

If you change the address by 1 for your sensor (which is common), you get could get 4 of the same i2c sensor on one teensy.

I think the teensy 3.6 might have 3 i2c busses.

The teensy-LC (48MHz, less RAM, and no floating point unit) is a bit slower so not sure if you need a lot of processing on board.
 
Thanks a lot KurtE and linuxgeek for your useful information.
I think working with a multiplexer for this case is a good idea. I must wait until the holidays will be over then ask my professor to buy one of these for me.
But maybe another option is to connect two Teensys and let them to communicate. I am thinking if I read two IMUs with one Teensy and can transfer data to another Teensy, then I can read the other two IMUs with another Teensy and problem will get resolved. I have seen some available example codes for data transferring but all of them transfer just one data byte when I must transfer two groups of data from two IMUs which are decimal numbers. Do you think it is possible? Also I found two method of communications. One method use serial communication and Two devices must be connect by TX an RX. Another method is to connect them with I2C again and use Wire library. The later needs device address ID (Teensy 3.2) that I could not find. I don't know the advantage of one method over the other one. please advise.
Thanks a lot

You should not give up the option to use both I2C's to read two identical I2C devices.
After all, it is only SW that must be adapter.
IMO this is easier than to establish robust Teensy to Teensy I2C synchronisation.
 
Couple of things here.

Still fixing my multiple Wire object version of wire. Ran into interesting timing problem, that I have a fix (hack?) for... More on different thread.

So believe with my version of library it would not take too much work to update a couple of libraries to allow them to work with different I2C buss...

But now to your new questions. Yes you can use Serial to do this, simply setup the TX of one to the RX of the other.... You can then decide on how/when they report. One way is to have the slave one output as fast as possible a packet of information about the IMUS, that the other side receives. Other method is to have Master ask for information. Could be with Serial data sent from Master, could be by IO pins connected between the boards...

Could use SPI as well...

Or as you mentioned could use I2C. What address? Any you want. You simply need for the Master and Slave to both use the same address...

On the Slave side, you setup I2C by typically setting up a couple of call back functions, and call begin with the slave address: From my Master/Slave code I have:
Code:
  SLAVE_I2C.onReceive(receiveEvent);  // register a slave event handler
  SLAVE_I2C.onRequest(requestEvent);
  SLAVE_I2C.begin(SLAVE_ADDR);
The receive Event handler is called when the Master Sends data to the slave. The onRequest is called when the Master queries data from the slave. In my test case I have my Master/Slave emulate setting/Query registers. So my handlers look like:

Code:
void receiveEvent(int count_bytes)
{
  read_write = SLAVE_I2C.read();  // first byte is command
  if (count_bytes > 1) start_reg = SLAVE_I2C.read();
  if (read_write) {
    while (SLAVE_I2C.available()) {
      registers[start_reg] = SLAVE_I2C.read();
      start_reg++;
    }

  } else {
    // read mode
    if (SLAVE_I2C.available()) {
      cnt_reg = SLAVE_I2C.read();
    }
  }
}

void requestEvent()
{
  // Host is asking for data.
  for (uint8_t i = 0; i < cnt_reg; i++) {
    SLAVE_I2C.write(registers[start_reg++]);
  }
}

On the master side, the Host does the normal host things, and when it wants to talk to my slave it starts a transfer to the specified slave. A couple of my helper functions in this test case show this:
Code:
//==========================================================================================
// Some master request or set register functions
uint8_t ReadReg(uint8_t reg) {
  MASTER_I2C.beginTransmission(SLAVE_ADDR); // transmit to device #8
  MASTER_I2C.write(0);              // Do a read
  MASTER_I2C.write(reg);
  MASTER_I2C.write(1);              // one byte
  MASTER_I2C.endTransmission();    // stop transmitting

  // Then request data from other side
  if (MASTER_I2C.requestFrom(SLAVE_ADDR, 1)) { // Try to get the data back from the other side
    return MASTER_I2C.read(); // read in the result and return it.
  }
  return 0xff;
}

void WriteReg(uint8_t reg, uint8_t val) {
  //Serial.printf("Write Reg %d = %d\n", reg, val);
  MASTER_I2C.beginTransmission(SLAVE_ADDR); // transmit to device #8
  MASTER_I2C.write(1);              // Do a read
  MASTER_I2C.write(reg);
  MASTER_I2C.write(val);              // one byte
  MASTER_I2C.endTransmission();    // stop transmitting
}
FYI in this code case I currently have:
Code:
#define SLAVE_I2C  Wire3
#define MASTER_I2C  Wire
#define SLAVE_ADDR 8
As again I am testing out multiple Wire objects on the same T3.6 processor
 
Dear KurtE:
As always, your reply was very useful and complete. I still have not connected the two Teensy but got the idea how to do it.

Couple of things here.

Still fixing my multiple Wire object version of wire. Ran into interesting timing problem, that I have a fix (hack?) for... More on different thread.
So believe with my version of library it would not take too much work to update a couple of libraries to allow them to work with different I2C buss...

I am happy I hear this from you. You know, your wire library is really what I need. Because when I can do everything with just one Teensy it is ridiculous to use extra device like Teensy or multiplexer and if I use, surely, this is not something that my professor enjoys. But the thing is I am not a professional like you. For example, so far I have learnt how to use L3G and LSM303 libraries but if I want to update them, honesty, I don't think I know how! I checked your information in another thread. For example you have updated Adafruit_SSD1306 library. I compared this with the original one. Yes, few lines have been changed but you can do it. If I wanted to do it, I couldn't. Sorry for myself :(
 
Couple of things here:
1) I updated my Wire library branch (name with malloc in it) to one commit and verified I could run one of the SSD-1306 displays on Wire1 on TLC, T3.2 and T3.6. Verified it builds for T3.0 and T2.0... So issued Pull request - more later on other thread.

I also did a quick hack up of the L3G library to allow me to use this version of my Wire library and pass in which buss to use for an object.
I put my changes back up to GitHub in new fork/branch: https://github.com/KurtE/l3g-arduino/tree/Wire0123

Did not run it but did verify their example app still builds and it still builds, when I change their object to say use Wire1, like:
L3G gyro(Wire1);

Again I have not tried it.

Likewise hacked up lsm303 library with new branch that did the same. https://github.com/KurtE/lsm303-arduino/tree/Wire0123,
with it I tried compiling their Serial example with: LSM303 compass(Wire2);
And it built... Again I have not tested it...

Kurt
 
Dear Kurt:

Thanks a lot for your updates. I really got surprised when I saw you had modified L3G and lsm303 libraries.:p Thank you.:eek:
I feel now I am close to my goal although still I am failing. First, I am afraid I have connection problem. because to use second I2C buss, I need pins 29 and 30 which are on back side of Teensy. So what I did, I have soldered two narrow wires to these two pins and still I can put Teensy on the small breadboard and have access to these two pins with those wires. Maybe this kind of soldering was not good idea. I searched through internet. No one uses those pins like that. I have attached a picture showing that bellow. Maybe problem is this because when I upload the program to my Teensy, compiler doesn't give me any error but the serial monitor doesn't print any number.

TeensyPin2930.jpg

So in summary, what I did I downloaded your final version of wire library and two other libraries. Then updated L3G and lsm303 libraries in this folder:
C:/Documents/Arduino/Libraries

Then I just changed the two lines of my code in my first post like this:

L3G gyro (Wire1);
LSM303 compass (Wire1);

I tried my other codes. They work well with new libraries and serial monitor prints numbers. But when I change the buss, then I face the problem.
Thanks again.
 
Status
Not open for further replies.
Back
Top