Have version of Wire library that supports Wire1, Wire2, Wire3...

Status
Not open for further replies.

KurtE

Senior Member+
I am not sure if this is the best approach, but after starting and stopping this a few times, I decided to create a version of the Wire library that generates Wire1 for most boards and Wire2 for T3.5 and T3.6 and Wire3 for T3.6.

Currently these are like SPI library where as each of these are individual classes. In the end it may be nice to update this to maybe make a base class that does most of the work (like I2c_t3), and have some virtual functions, such that maybe we can pass in a pointer to one of these objects to another class and have that class be able to work with different busses for different objects... May do that.

But in the mean time, I pushed up a new branch on my fork of the Wire library: https://github.com/KurtE/Wire/tree/Support-Wire1/2/3

To test this, I made a version of the Adafruit_SSD1306 library where in the header file you can define which Wire object to compile for. Again this is up at:
https://github.com/KurtE/Adafruit_SSD1306/tree/Wire!/2/3
Again not as nice as I2C_t3 in that you have to edit the library to change busses and you can't have multiple ones on different ones... I do have a branch of this library as well that supports i2c_t3 which I was using to test during the T3.6 beta.

Any way I did some rudimentary testing of this, by trying out one of the I2C displays on the default I2C pins for each buss (Wire, Wire1, Wire2, Wire3) on a T3.6 and also tried it on a T3.2 on Wire and Wire1.

Again probably needs more testing, but if this is the approach that we desire to go with the wire library can issue Pull Request.

Kurt

P.S. - When doing this, I noticed that we were hard coding doing attachInterrupt(18, ...) in the Wire object and wondered what 18 was... Turns out default SDA pin... In my copy changed to the current SDA pin... likewise on detach...
 
Today I did some more hacking on this and created a base class for the I2C objects, which does most of the work, but has some virtual methods for things like begin, end, setSDA, setSCL. I also moved the buffers into the object instead of static objects and the object has a pointer to the I2C register structure for the specific buss, such that almost all of the methods, that did not deal with IO pins was kept in the base class.

So far I have moved Wire1, Wire2 and WIre3 to be sub-classes of this class. I have not yet moved Wire over to it, but may try it.

I also added some #defines into wire.h to allow you to control which objects get created.

Updated the branch I mentioned above. So far my testing was with the OLED, which only does output. May try tomorrow with one of my I2C sensors, to see if they can talk...
 
I wasn't clear on this, could you elaborate?

multiple ? on different ?
Sorry, currently in the SSD1306 branch I mentioned, you can change which I2C Buss to use by changing a define in the libraries main header file. So you can not therefore have multiple of these devices where some on are lets says Wire1 and others on Wire2. In theory you can maybe still have two of them on the same buss, where you change the I2C address.

With the branch I made for i2ct3 library, I believe I made it such that you can pass in which wire object to use for each instance, as there is a base class.

However if I complete this version of Wire, by also making the main Wire object be a subclass of my base class, then I will be able to do the same thing this as well. Alternatively one could build a version of the display library that you either pass in the SDA/SCL pins and it figures out which buss to use or your could pass in the buss number and then the code that talks to the display would simply check which buss and do Wire. or Wire1. or ... for the appropriate buss.

Hope that makes sense.

But first off I am doing a little testing to see if they work in client mode...
 
Got it. I really like the i2c_t3 library's ability to use the multiple i2c ports on the T-LC to put the same i2c device on each port. It's pretty handy to not have to do a i2c multiplexer when you are trying to keep it small.
 
At some point hopefully the standard Wire library will support Wire1. Once I am semi happy with my version may issue Pull Request. I have not been doing any testing yet on TLC, mainly working with T3.6.

Looks like the basic Client stuff is working. I have a simiple sketch that acts like there are 8 registers that the Master can read or write from the slave. I have both the client and master in same sketch, and I tried with Wire as master talking to Wire1, then Wire2, then Wire3. I then had Wire3 as master talking to Wire and then to Wire2... So I think most combinations should work.

If anyone is interested the sketch looks like:
Code:
// Quick and dirty I2c Master/Slave combination test.

#define SLAVE_ADDR 8
#include <Wire.h>
#define SLAVE_I2C  Wire

#define MASTER_I2C  Wire3
#define REG_COUNT 8
extern void requestEvent();
void setup()
{
  while (!Serial && (millis() < 3000)) ;
  Serial.begin(115200);
  delay(250);

  // Start up both Master and save SPIs
  Serial.println("Before Master begin");
  MASTER_I2C.begin(); // join i2c bus (address optional for master)
  Serial.println("Before Slave Begin");
  SLAVE_I2C.onReceive(receiveEvent);  // register a slave even handler
  SLAVE_I2C.onRequest(requestEvent);
  SLAVE_I2C.begin(SLAVE_ADDR);
  pinMode(13, OUTPUT);
  digitalWrite(13, LOW);
}

uint8_t vals_to_write = REG_COUNT;

void loop()
{
  digitalWrite(13, HIGH);
  for (uint8_t i = 0; i < REG_COUNT; i++) {
    uint8_t reg_val = ReadReg(i);
    Serial.printf("Reg %d = %d\n", i, reg_val);
  }

  // lets try updating all of them...
  Serial.println("Update values");
  for (uint8_t i = 0; i < REG_COUNT; i++) {
    WriteReg(i, vals_to_write++);
  }
  digitalWrite(13, LOW);

  
  Serial.println("\nHit any key to repeat");
  while (Serial.read() == -1) ;
  while (Serial.read() != -1) ; // get rid of other data.
}

//==========================================================================================
// 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.
  }
}

uint8_t WriteReg(uint8_t reg, uint8_t 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
}

//===========================================================================================
// Some slave registers and state values
uint8_t registers[REG_COUNT] = {0, 1, 2, 3, 4, 5, 6, 7}; // some dummy registers to play with.
uint8_t  read_write = 0;
uint8_t  start_reg = 0;
uint8_t  cnt_reg = 1;

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();
    }

  } 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++]);
  }
}
 
Another quick update:

Converted the main object to use same base class as the others. Then tested with the above program talking both as master and as client to Wire3. I also updated the code to call setSDA and setSCL on Wire0 to alternate pins 33, 34 and again tested it both as master and client.

One thing I wonder about is the memory usage. That is by default I create the Wire objects for each of the valid busses for that processor. I am not too worried about T3.5/6 as they have a reasonable amount of memory. But for the T3.2 and TLC they have less (but only two objects).

Also in the header file I have defines:
Code:
#define WIRE_DEFINE_WIRE0 
#define WIRE_DEFINE_WIRE1
#define WIRE_DEFINE_WIRE2
#define WIRE_DEFINE_WIRE3
That if you remove them should not create the specific objects...

Currently each instance uses a reasonable amount of memory including (buffers are like 32 and 33 bytes by default):
Code:
    uint8_t rxBuffer[WIRE_RX_BUFFER_LENGTH];
    uint8_t rxBufferIndex;
    uint8_t rxBufferLength;

    uint8_t txAddress;
    uint8_t txBuffer[WIRE_TX_BUFFER_LENGTH];
    uint8_t txBufferIndex;
    uint8_t txBufferLength;
    uint8_t slave_mode;

    uint8_t transmitting;
    void (*user_onRequest)(void);
    void (*user_onReceive)(int);
    static void sda_rising_isr(void);
    uint8_t sda_pin_num;
    uint8_t scl_pin_num;
    uint8_t receiving;      // are we receiving...
    uint8_t irqcount;

    KINETIS_I2C_t * kinetisk_pi2c;
I don't know of any easy way within the Wire like usage, where the objects just exist.

One option might be to at a minimum use something like malloc to allocate the buffers, when the user calls some specific methods, like begin. Would add a slight overhead to dereference pointers. Alternately could have Wire, Wire1... be a simple forwarder class, that when first called it does a new of the actual object.... Would need to check a few of the methods to know when to create the real object (probably begin, setSDA, setSCL, onReceive, onRequest).

Thoughts?

Obviously the easyiest and maybe proper answer is to punt and use i2c_t3...
 
Do you have sample code for a SSd1306 running on teensy3.2 Wire 1 pins 29 and 30?

Probably best to not use this old thread for this... And open new thread about SSD1306 devices.

The Wire library now supports all of the Wire objects.

So depending on your needs, you could simply edit whatever library you wish to use and substitute using WIRE1 instead of WIRE...

I have a version of the Adafruit_SSD1306 driver that supports passing in which wire object to use, which is up on github in the branch: https://github.com/KurtE/Adafruit_SSD1306/tree/Wire!/2/3
I thought I did a Pull Request to Adafruit, but don't see it. I think I did not do it as there were lots of other outstanding requests for updates and nothing has changed in their library in 2 years.
 
Status
Not open for further replies.
Back
Top