Richard-Gemmell / teensy4_i2c

An I2C library for the Teensy 4. Provides slave and master mode.
MIT License
94 stars 22 forks source link

Timed out waiting for transfer to finish. Wire1 Problem #31

Open 01-BlackHawk-11 opened 2 years ago

01-BlackHawk-11 commented 2 years ago

I have an IMU module that works with the default I2C bus (Wire). However, whenever I use the alternate Buses Wire1 Or Wire2 by for example changing Wire.begin() to Wire1.begin() or Wire2.begin() I get this error message : Timed out waiting for transfer to finish. How can I solve this issue?

image

Richard-Gemmell commented 2 years ago

Hi, It could be a wiring problem. You need to use pins 17 and 16 for Wire1. Note that the wires are the other way round to port 0 which you use for Wire. i.e. for Wire, pin 18 is for SDA and pin 19 is for SCL but for Wire1 pin 17 is SDA and pin 16 is SCL.

If you're using the right pins then it may be another wiring issue. e.g. a short or dodgy connection somewhere.

It's also possible that there's an issue with the gyro library you're using. It might be hard coded to use Wire rather than Wire 1. You might need to tell it to use Wire 1 in some way. If you post your code then I might be able to tell.

Are you using a Teensy 4 or Teensy 4.1? I haven't tested Wire2 on the Teensy 4 but I believe it works Ok on the Teensy 4.1.

I apologise in advance if I'm telling you stuff you already know.

cheers, Richard

01-BlackHawk-11 commented 2 years ago

I am using a Teensy 4.1. Here are the Project files. I2C.ino is the one responsible for I2C communication. https://cardmaillouisville-my.sharepoint.com/:f:/g/personal/anabde01_louisville_edu/Ejiq4f_P1eRMnkj6Nxmzzv8BsdJ_-WK7L9gCy1BtimSsyQ?e=xPMHoN

Richard-Gemmell commented 2 years ago

I'm not sure which driver you're using for your IMU but I guess it might be this one or one of the other drivers that Pololu supply.

They all seem to be hard coded to use Wire which is the first I2C port. See https://github.com/pololu/l3g-arduino/blob/master/L3G.cpp#L177 for example. You'll need to change all the lines beginning Wire. to use Wire1. if you want to use the second I2C port.

Alternatively you ask Pololu to change the driver to support different ports.

good luck, Richard

01-BlackHawk-11 commented 2 years ago

https://cardmaillouisville-my.sharepoint.com/:f:/g/personal/anabde01_louisville_edu/EpizqYWtWvlAqwhK8lhiUp0BTxP5XPYdAYci5F0FFc9LhQ?e=z9dL0w These are the libraries I'm using.

Richard-Gemmell commented 2 years ago

I just had a look. They both have the same problem. You'll need to modify LIS3MDL.cpp and LSM6.cpp as I said above. Just change Wire. to Wire1. in your local copy. It should work. (Likewise for Wire2.)

I don't think there's anything more I can do for you at this point.

cheers, Richard

01-BlackHawk-11 commented 2 years ago

Thank you so much It worked Modified the libraries to work with the Wire 1 & Wire 2.

01-BlackHawk-11 commented 2 years ago

However I want to ask you. Can I use all of the I2C buses simultaneously? And if so how can I take readings from all the 3 modules given that each I2C bus has a module connected?

Richard-Gemmell commented 2 years ago

You can definitely use all 3 I2C buses at the same time.

You can also read multiple devices with the same I2C bus as long as they have different I2C slave addresses. You can often configure a breakout board to switch between different addresses. For example this board explains how you can configure it to use 1 of 2 different addresses. It looks like your device has a similar setup. You tell it which address you've configured it to use with the call to init(). See LI3MDL.cpp line 46 for example. The sa1 determines which adddress to use.

Your biggest problem is that the drivers are hard coded to use Wire, Wire1 etc. If you need to use 3 identical modules then you either need 3 different copies of each driver or you need to modify each driver so that they use a member variable to hold the I2C device. You can then pass Wire or Wire2 as an argument to the init() function. So line 46 might look like this

bool LIS3MDL::init(deviceType device, sa1State sa1, I2CDriverWire& wire)

If this is all new to you, you'll need to find out about classes in C++. That'll explain member variables to you.

But in short, it's definitely possible but you'll need to do more work that you were expecting.

FWIW I've often found that the drivers provided by suppliers are a bit basic like this. They work well as "get you going" examples but are a bit limited.

If you have the time, it's a good idea to check out the datasheet for each device you're using. The driver is just a convenient way of using the device. The datasheet tells you what it can really do. The person who wrote the driver will have made choices about what features to include and what to leave out.

cheers, Richard

01-BlackHawk-11 commented 2 years ago

Okay. Thank you so much Richard! I think I know what to do now.

Richard-Gemmell commented 2 years ago

Cool. Good luck.

01-BlackHawk-11 commented 2 years ago

So I did what you suggested and I got this error. Arduino: 1.8.19 (Windows 10), TD: 1.56, Board: "Teensy 4.1, Raw HID, 600 MHz, Faster, US English"

I2C: In function 'void Accel_Init()':

I2C:93: error: no matching function for call to 'LSM6::init(I2CDriverWire&)'

gyro_acc.init(Wire1);

                  ^

In file included from C:\Program Files (x86)\Arduino\MiniIMU9AHRS\MinIMU9AHRS\I2C.ino:32:0:

C:\Program Files (x86)\Arduino\libraries\LSM6/LSM6.h:97:10: note: candidate: bool LSM6::init(LSM6::deviceType, LSM6::sa0State, I2CDriverWire&)

 bool init(deviceType device = device_auto, sa0State sa0 = sa0_auto, I2CDriverWire& wire = Wire);

      ^

C:\Program Files (x86)\Arduino\libraries\LSM6/LSM6.h:97:10: note: no known conversion for argument 1 from 'I2CDriverWire' to 'LSM6::deviceType'

I2C: In function 'void Compass_Init()':

I2C:138: error: no matching function for call to 'LIS3MDL::init(I2CDriverWire&)'

mag.init(Wire1);

             ^

In file included from C:\Program Files (x86)\Arduino\MiniIMU9AHRS\MinIMU9AHRS\I2C.ino:33:0:

C:\Program Files (x86)\Arduino\libraries\LIS3MDL/LIS3MDL.h:49:10: note: candidate: bool LIS3MDL::init(LIS3MDL::deviceType, LIS3MDL::sa1State, I2CDriverWire&)

 bool init(deviceType device = device_auto, sa1State sa1 = sa1_auto, I2CDriverWire& wire = Wire);

      ^

C:\Program Files (x86)\Arduino\libraries\LIS3MDL/LIS3MDL.h:49:10: note: no known conversion for argument 1 from 'I2CDriverWire' to 'LIS3MDL::deviceType'

no matching function for call to 'LSM6::init(I2CDriverWire&)'

This report would have more information with "Show verbose output during compilation" option enabled in File -> Preferences.

Richard-Gemmell commented 2 years ago

When you call mag.init(Wire1); the compiler thinks you're trying to set device to Wire1. This doesn't make sense so it fails.

IIRC When you have default arguments you must specify all the arguments to the left of the one you are setting explicitly as well as the one you care about.

e.g. mag.init(device_auto, sa1_auto, Wire1

So you can also fix it by making the wire argument the first one. Your call to init will then work. So this function:

bool init(I2CDriverWire& wire = Wire, deviceType device = device_auto, sa0State sa0 = sa0_auto);

Can be called the way you want.

mag.init(Wire1);

cheers, Richard

01-BlackHawk-11 commented 2 years ago

Update. Tried them both in LSM6 & LIS3MDL. This is what i got: Arduino: 1.8.19 (Windows 10), TD: 1.56, Board: "Teensy 4.1, Raw HID, 600 MHz, Faster, US English"

In file included from C:\Program Files (x86)\Arduino\libraries\LSM6\LSM6.cpp:1:0:

C:\Program Files (x86)\Arduino\libraries\LSM6/LSM6.h:99:12: error: 'I2CDriverWire' has not been declared

bool init(I2CDriverWire& wire = Wire, deviceType device = device_auto, sa0State sa0 = sa0_auto);

        ^

C:\Program Files (x86)\Arduino\libraries\LSM6/LSM6.h:99:34: error: 'Wire' was not declared in this scope

bool init(I2CDriverWire& wire = Wire, deviceType device = device_auto, sa0State sa0 = sa0_auto);

                              ^

C:\Program Files (x86)\Arduino\libraries\LSM6\LSM6.cpp:46:6: error: prototype for 'bool LSM6::init(I2CDriverWire&, LSM6::deviceType, LSM6::sa0State)' does not match any in class 'LSM6'

bool LSM6::init(I2CDriverWire& wire, deviceType device, sa0State sa0)

  ^

In file included from C:\Program Files (x86)\Arduino\libraries\LSM6\LSM6.cpp:1:0:

C:\Program Files (x86)\Arduino\libraries\LSM6/LSM6.h:99:7: error: candidate is: bool LSM6::init(int&, LSM6::deviceType, LSM6::sa0State)

bool init(I2CDriverWire& wire = Wire, deviceType device = device_auto, sa0State sa0 = sa0_auto);

   ^

Error compiling for board Teensy 4.1.

This report would have more information with "Show verbose output during compilation" option enabled in File -> Preferences.

Richard-Gemmell commented 2 years ago

Hi, I think you've made mistakes with header files and includes. If you're not familiar with those terms then have a look at some C++ tutorials that explain them.

In short, you need the function declarations in the LSM .h files to match the ones in the .cpp files. You need to add #include <i2c_driver_wire.h> to each .h file that refers to I2CDriverWire.

If there's anyone nearby who understands C++ or Arduino programming then they will be able to help you with these things.

cheers, Richard