I2C Slave on Teensy 4

Status
Not open for further replies.
Hi Richard,

I' m converting a system for noise measurement from Teensy 3.6 to Teensy 4.0. The system uses the Teensy to read the input from a microphone and calculate the SPL and some more values and than transmits these values over I²C to a master (in my case a NodeMCU - ESP8266 system). The Teensy 3.6 system uses the Wire lib and all works well.

For Teensy 4.0 I found your lib supporting slave mode and tried it. Well I have the problem that there are sporadically errors in the I²C transmission. To catch the problem I used your examples which worked fine. Than I changed the number of transmitted bytes from master to slave and vice versa to 31 bytes. Based on your recommendation not to use the Wire API I switched to the i2c_driver.h API but still getting sporadically errors in the transmission from slave to master.

Any hint what is going wrong is apprtiated.

Code for slave and master follows as well as some pictures showing the errors in transmisson.

Slave:
Code:
// Wire Slave Receiver
// by Nicholas Zambetti <http://www.zambetti.com>
// Modified by Richard Gemmell Oct 2019

// Demonstrates use of the Wire library
// Receives data as an I2C/TWI slave device
// Refer to the "Wire Master Writer" example for use with this
// To use it, connect a master to the Teensy on pins 18 and 19.
//
// Consider using the I2CRegisterSlave class instead of Wire to
// create an I2C slave.

// Created 29 March 2006

// This example code is in the public domain.

#include <Arduino.h>
#include <i2c_driver.h>
#include "imx_rt1060/imx_rt1060_i2c_driver.h"


uint8_t number_bytes;
uint8_t i;
uint8_t ii;

const uint16_t slave_address = 0x0055;
I2CSlave& slave = Slave;
void after_receive(int size);
void before_transmit_isr();
void after_transmit();

const size_t slave_rx_buffer_size = 64;
uint8_t slave_rx_buffer[slave_rx_buffer_size] = {};
volatile size_t slave_bytes_received = 0;

// Set up transmit buffer
const size_t slave_tx_buffer_size = 64;
uint8_t slave_tx_buffer[slave_tx_buffer_size] = {};

// Flags for handling callbacks
volatile bool before_transmit_received = false;
volatile bool after_transmit_received = false;
volatile bool buffer_underflow_detected = false;


int led = LED_BUILTIN;

void setup()
{
  pinMode(led, OUTPUT);
  digitalWrite(led, LOW);

  Serial.begin(115200);           // start serial for output
  delay(500);

  Serial.println(" "); Serial.println("I²C slave mode communication test on Teensy 4.0");
  
  // Configure I2C Slave and Start It
  slave.after_receive(after_receive);
  slave.set_receive_buffer(slave_rx_buffer, slave_rx_buffer_size);
  
  slave.before_transmit(before_transmit_isr);
  slave.after_transmit(after_transmit);
  slave.set_transmit_buffer(slave_tx_buffer, slave_tx_buffer_size);
  slave.listen(slave_address);

}

void loop()
{
  if (slave_bytes_received) {
    // now copy to transmit buffer
    memcpy(slave_tx_buffer, slave_rx_buffer, slave_bytes_received);    

//    Serial.print("loop: slave_bytes_received "); Serial.print(slave_bytes_received);
//    Serial.print(", last byte transmitted: "); 
    Serial.println(slave_rx_buffer[30]);
    
    // Clear slave_bytes_received to signal that we're ready for another message
    slave_bytes_received = 0;
  }
  if (before_transmit_received) {

  }
}


// Called by the I2C interrupt service routine.
// This method must be as fast as possible.
// Do not perform IO in it.
void after_receive(int size) {
  // This is the only time we can guarantee that the
  // receive buffer is not changing.
  // Copy the content so we can handle it in the main loop.
  if (!slave_bytes_received) {
    slave_bytes_received = size;
    digitalWrite(led, LOW);       // briefly flash the LED
  }
  // else ignore this message because the main loop hasn't
  // handled the previous one yet.
}


// Called by an interrupt service routine.
// This function must be _very_ fast. Avoid IO.
void before_transmit_isr() {
    before_transmit_received = true;
}


// Called by an interrupt service routine.
// This function must be _very_ fast. Avoid IO.
void after_transmit() {
    digitalWrite(led, HIGH);       // briefly flash the LED
    after_transmit_received = true;
    if (slave.has_error()) {
        I2CError error = slave.error();
        if (error == I2CError::buffer_underflow) {
            buffer_underflow_detected = true;
        } else {
            Serial.println("Unexpected error");
        }
    }
}

Master:
Code:
#include <Wire.h>

// define pins for I2C for NodeMCU
#define I2C_PIN_SCL D4
#define I2C_PIN_SDA D3

uint8_t i = 0;
uint8_t ii = 0;
byte buf [64] = {0x78, 0x20, 0x69, 0x73, 0x20, 0x78, 0x20, 0x69, 0x73, 0x20, 0x78, 0x20, 0x69, 0x73, 0x20, 0x78, 0x20, 0x69, 0x73, 0x20, 0x78, 0x20, 0x69, 0x73, 0x20, 0x78, 0x20, 0x69, 0x73, 0x20};
byte *buf_zeiger = &buf[0];
uint32_t error_counter = 0;
uint32_t loop_counter = 0;


void setup() {
  Wire.begin(I2C_PIN_SDA, I2C_PIN_SCL); // join i2c bus (address optional for master)
  Serial.begin(115200);  // start serial for output
  delay(500);

  Serial.println(" "); Serial.println(" "); Serial.println(" ");
  Serial.println("I²C communication test runnig");

}


void loop() {
  Serial.println(" ");
  Serial.print("tx: x is "); Serial.print(i);

  Wire.beginTransmission(0x55); // transmit to device 0x55
  buf[30] = i;
  ii = Wire.write(buf_zeiger, 31);        // sends 31 bytes
  if (ii == 31) {
    // all bytes send
    ii = Wire.endTransmission();    // stop transmitting
    if (ii != 0) error_counter++;
    //  Serial.print(", return value of endTransmission: "); Serial.print(ii);

    i++;

    ii = 0;
    Wire.requestFrom(0x55, 31);    // request 31 bytes from slave device 0x55
    Serial.print(", rx: ");
    while (Wire.available()) { // slave may send less than requested
      char c = Wire.read(); // receive a byte as character
      if (ii < 30) {
        Serial.print(c);         // print the character
        if (c != buf[ii]) error_counter++;
      } else {
        Serial.print(int(c));
        if (c != buf[ii]) error_counter++;
      }
      ii++;
    }
    Serial.print(", error_counter: "); Serial.print(error_counter);
    if (error_counter != 0) {
      Serial.println(" ");
      Serial.print("error_counter: "); Serial.print(error_counter);
      Serial.print(", loop_counter: "); Serial.print(loop_counter);
      while (1) {
        delay(100);
      }
    }
    loop_counter++;
    //  Serial.print(", loop_counter: "); Serial.print(loop_counter);

    delay(150);
  } else {
    // something went wrong
    Wire.endTransmission(); // stop transmission an start again
    Serial.print(" error, # bytes send: "); Serial.print(ii);
  }

}

Some pictures:

p20.jpgp21.jpgp41.jpg

Regards,

Helmut
 
I couldn't see how to perform asynchonous writes as master, so I added a few extra functions to i2c_driver_wire.cpp to enable me to do it:

Code:
/*
 * Starts a transaction with the slave at the given address and blocking status.
 * Setting blocking true forces polling until the previous command completes.
 * Setting non-blocking, returns false when the previous command not yet completed.
 * This allows the whole transaction to be skipped for non-vital data updates.
 * @param addr the IIC slave address to use.
 * @param blocking whether to use a blocking or non-blocking call.
 * @return whether to continue the transaction or not.
 */
bool I2CDriverWire::beginTransmission(const uint8_t addr, [B]bool blocking[/B]) {
    if (master.finished()) {
        write_address = (uint16_t)addr;
        tx_next_byte_to_write = 0;
        return true;
    } else if (blocking) {
        [B]complete(); // Wait until complete //[/B]
        write_address = (uint16_t)addr;
        tx_next_byte_to_write = 0;
        return true;
    } else {
        return false;
    }
}

/*
 * Waits for transaction completion or timeout (blocking).
 */
void I2CDriverWire::complete() {
    while ((int)millis() < [B]final_time[/B]) { // 200 ms timeout //
        if (master.finished()) {
            return;
        }
        [B]yield(); // Good idea? //[/B]
    }
    //Serial.println("Timed out waiting for transfer to finish.");
}

/*
 * Asynchronous write operation.
 */
void I2CDriverWire::sendTransmission() {
    master.write_async(write_address, tx_buffer, tx_next_byte_to_write, true);
    [B]final_time = (int)millis() + timeout_millis;[/B] // Record start time for timeout check //
}

// Added to allow existing code to be ported more readily //
void I2CDriverWire::setSDA(uint8_t sda) {
    // Conforms to Teensy standards //
}
void I2CDriverWire::setSCL(uint8_t scl) {
    // Conforms to Teensy standards //
}
// ---------- Header ---------- //
void setSDA(uint8_t);
void setSCL(uint8_t);
bool beginTransmission(const uint8_t addr, bool blocking);
void complete();
void sendTransmission();

When using asynchronous transfer, all data transmissions must use the overloaded beginTransmission(address, blocking). The blocking parameter determines how to respond if the previous operation has not yet completed:
- blocking (true): wait until complete then continue the transmission.
- blocking (false): skip this transmission (useful for non-essential display updates etc).

Code:
if (wptr->[B]beginTransmission(0x70, true)[/B]) {
    wptr->write((uint8_t)0x00); // Write data here //
    ...
    wptr->[B]sendTransmission()[/B]; // Asynchonous transmission //
}

Does that seem like a good approach?

Also, just wondering whether the polling for completeness could call yield() to help other processes while waiting?

Wish List: Ultra Fast Mode (UFm) would be great!

Thanks,

microderm
 
I apologise for not responding to the latest posts. It turns out I hadn't subscribed to the thread. I'll get back to both of you soon.

To anyone posting in the future, please note that I don't monitor the PJRC forums. If you ask for help with my library and I don't reply in a day or two then I probably haven't noticed that you want help. Please send me an email via this forum or GitHub to get my attention.

cheers,
Richard
 
Hi hbit,

The output you've posted suggests that individual bits are being corrupted during transmission. This is probably a hardware problem of some kind.

The most likely hardware problem when changing from a Teensy 3.6 to a Teensy 4 implementation is the pull up resistors. I think people often used 4.7k resistors with Teensy 3s but that's too high for a Teensy 4. You need to use 2.2 kOhm or 1 kOhm resistors instead. Other than that, make sure the 2 devices have a good common ground and that the wiring is sound. I expect you've already done that though. :)

If you're still having hardware problems then you may have some subtle electrical or electronic issue. I don't know enough about the hardware side to offer you any advice about that.

The most common software problem is a race condition involving the main loop and event handlers. The problem is that the Teensy will interrupt almost any code in loop() in order to handle I2C. Note that I2C transmissions aren't atomic. The Teensy may start a function call in loop(), then receive a byte or two, then continue with the same function call in loop(), then receive another byte etc.

I think there's a bug like that in the slave code you posted. It's possible that the contents of slave_rx_buffer are changing in the middle of the call to memcpy() on line 73. Likewise, the slave could be transmitting during the call to memcpy(). If this happens then the master will receive part of one message and part of the next one.

I think the best solution would be to move the call to memcpy from loop() into before_transmit_isr. This works because the before_transmit event handler is called just before the slave starts transmitting to the master. At this point, we know that the master is no longer transmitting to the slave so slave_rx_buffer should not be changing. e.g.

Code:
void before_transmit_isr() {
  if (slave_bytes_received) {
    // now copy to transmit buffer
    memcpy(slave_tx_buffer, slave_rx_buffer, slave_bytes_received);    

//    Serial.print("loop: slave_bytes_received "); Serial.print(slave_bytes_received);
//    Serial.print(", last byte transmitted: "); 
    Serial.println(slave_rx_buffer[30]);
    
    // Clear slave_bytes_received to signal that we're ready for another message
    slave_bytes_received = 0;
  }
  before_transmit_received = true;
}

It's not ideal to call Serial.println() in an event handler but you'll probably get away with it. Don't forget to remove the memcpy code from loop().

For anyone else reading this please check out the list of common problems.

I hope this helps.
Richard.
 
Hi microderm,

The "real" API for my library is the one in i2c_driver.h. This is inherently asynchronous and should lend itself well to this sort of problem. See I2CMaster::write_async() and I2CMaster::finished(). You might not even need the equivalent of complete() if you're really interested in whether you can start a transaction rather than deliberately waiting for one to finish.

I created I2CDriverWire so that people who prefer the existing Wire library would have a drop in replacement. It's also handy for people porting existing code. Wire doesn't support the sort of behaviour you want which is why you had to create these new methods. I'm reluctant to modify of extend the Wire implementation as I would prefer people to use i2c_driver.h when Wire isn't sufficient. I also want to avoid creating an implementation of Wire which has a different flavour to the reference implementation.

Having said all that, your approach seems like a perfectly reasonable way to get the behaviour you want. The example code you give is neat and tidy which is always a good sign.

I believe your code contains a bug or two. The problem is that millis() will sometimes return a value close to the max_int. When that happens this line
Code:
final_time = (int)millis() + timeout_millis;
can produce a number less than the current value of mills() causing complete() to return immediately without calling master.finished(). This is why I2CDriverWire::finish() uses elapsedMillis().

I wasn't aware of the scheduler library and yield() so I don't have any experience with using it. Your suggested use seems like a good place to call yield(). Of course, it won't matter unless you have additional processes to yield to. :)

To loop round to where I started, I'd suggest having a go at writing your extension with I2CMaster on it's own without I2CDriverWire. You may well be able to come up with an implementation which avoids blocking at all.

I shied away from implementing Ultra Fast Mode because it looks fairly complicated and I didn't need it. If your needs ever change from "nice to have" to "must have" then please raise an issue on github. I might be inspired to have a go particularly if other people want it too. :)

cheers,
Richard
 
Hi Richard,

one of your suggestions made me a bit curious, changing the pull up to 1k. Just for intrest I tried it and it got even worse. So I looked into the pin configuration and found that the internal pull ups are enabled (PUS(3) - 22k). If it's enabled on both sides that gives 11k what should be o.k. for standard I2C speed of 100 kHz and a short distance. After playing a bit with DSE, SRE I tried the hysteresis bit (HYS) and that changed the situation. With HYS sat the testprogram was running for some hours transferring 31 bytes back anf forth without any error (something I expect for a standard I²C connection).

imx_rt1060_i2c_driver.cpp looks now
Code:
// Copyright © 2019 Richard Gemmell
// Released under the MIT License. See license.txt. (https://opensource.org/licenses/MIT)
//
// Fragments of this code copied from WireIMXRT.cpp © Paul Stoffregen.
// Please support the Teensy project at pjrc.com.

//#define DEBUG_I2C // Uncomment to enable debug tools
#ifdef DEBUG_I2C
#pragma clang diagnostic push
#pragma ide diagnostic ignored "hicpp-signed-bitwise"
#include <Arduino.h>
#endif

#include <imxrt.h>
#include <pins_arduino.h>
#include "imx_rt1060_i2c_driver.h"

#define DUMMY_BYTE 0x00 // Used when there's no real data to write.
#define NUM_FIFOS 4     // Number of Rx and Tx FIFOs available to master
#define MASTER_READ 1   // Makes the address a read request
#define MASTER_WRITE 0  // Makes the address a write request
#define MAX_MASTER_READ_LENGTH 256  // Maximum number of bytes that can be read by a single Master read


#define PINCONFIG (IOMUXC_PAD_HYS | IOMUXC_PAD_ODE | IOMUXC_PAD_SRE | IOMUXC_PAD_DSE(4) | IOMUXC_PAD_SPEED(1) | IOMUXC_PAD_PKE | IOMUXC_PAD_PUE | IOMUXC_PAD_PUS(3))

// Debug tools
#ifdef DEBUG_I2C
static void log_slave_status_register(uint32_t ssr);
static void log_master_status_register(uint32_t msr);
static void log_master_control_register(const char* message, uint32_t mcr);
#endif

static uint8_t empty_buffer[0];

I2CBuffer::I2CBuffer() : buffer(empty_buffer) {
}

static void initialise_pin(IMX_RT1060_I2CBase::PinInfo pin) {
//    *(portControlRegister(pin.pin)) |= IOMUXC_PAD_PKE | IOMUXC_PAD_PUE | IOMUXC_PAD_PUS(3);
    *(portControlRegister(pin.pin)) = PINCONFIG;
    *(portConfigRegister(pin.pin)) = pin.mux_val;
    if (pin.select_input_register) {
        *(pin.select_input_register) = pin.select_val;
    }
}
.
.
.


Would be great if you could recheck it.

Here are my testprograms for teensy4.0 for Master and Slave side

Master:
Code:
//#include <Wire.h>
#include <i2c_driver.h>
#include <i2c_driver_wire.h>


uint8_t i = 0;
uint8_t ii = 0;
byte buf [64] = {0x78, 0x20, 0x69, 0x73, 0x20, 0x78, 0x20, 0x69, 0x73, 0x20, 0x78, 0x20, 0x69, 0x73, 0x20, 0x78, 0x20, 0x69, 0x73, 0x20, 0x78, 0x20, 0x69, 0x73, 0x20, 0x78, 0x20, 0x69, 0x73, 0x20};
byte *buf_zeiger = &buf[0];
uint32_t error_counter = 0;
uint32_t loop_counter = 0;
String input_string = "";
int number_bytes_tx;
bool string_complete;
char ch;

void setup() {
  Wire.begin(); // join i2c bus (address optional for master)
  Serial.begin(115200);  // start serial for output
  delay(500);

  pinMode(13, OUTPUT);

  Serial.println(" "); Serial.println(" "); Serial.println(" ");
  Serial.println("I²C communication test runnig");
  Serial.print("input number of bytes for each transmission: ");

  string_complete = false;
  while (!string_complete) {
    while (Serial.available()) {
      // get the new byte:
      char in_char = (char)Serial.read();
      // add it to the inputString:
      input_string += in_char;
      // if the incoming character is a newline, set a flag so the main loop can
      // do something about it:
      if (in_char == '\n') {
        string_complete = true;
      }
    }
  }

  number_bytes_tx = input_string.toInt();
  Serial.println(number_bytes_tx); Serial.println(" ");

}


void loop() {
  Serial.println(" ");
  Serial.print("tx: x is "); Serial.print(i);

  Wire.beginTransmission(0x55); // transmit to device 0x55
  buf[number_bytes_tx - 1] = i;
  ii = Wire.write(buf_zeiger, number_bytes_tx);        // sends x bytes
  if (ii == number_bytes_tx) {
    // all bytes send
    ii = Wire.endTransmission();    // stop transmitting
    if (ii != 0) error_counter++;
    //  Serial.print(", return value of endTransmission: "); Serial.print(ii);

    i++;
    delay(1);
    Wire.requestFrom(0x55, number_bytes_tx);    // request x bytes from slave device 0x55
    Serial.print(", rx: ");
    ii = 0;
    while (Wire.available()) { // slave may send less than requested
      char c = Wire.read(); // receive a byte as character
      if (ii < number_bytes_tx - 1) {
        Serial.print(c);         // print the character
        if (c != buf[ii]) error_counter++;
      } else {
        Serial.print(int(c));
        if (c != buf[ii]) error_counter++;
      }
      ii++;
    }
    //    Serial.print(", # errors: "); Serial.print(error_counter);
    if (error_counter != 0) {
      Serial.println(" ");
      Serial.print("error_counter: "); Serial.print(error_counter);
      Serial.print(", loop_counter: "); Serial.print(loop_counter);
      while (1) {
        if ( Serial.available()) SCB_AIRCR = 0x05FA0004;
        digitalWriteFast( 13, !digitalReadFast(13));
        delay(100);
      }
    }
  } else {
    // something went wrong
    Wire.endTransmission(); // stop transmission and start again
    Serial.print(" error, wrong # bytes send: "); Serial.print(ii);
  }

  loop_counter++;
  //  Serial.print(", loop_counter: "); Serial.print(loop_counter);
  if ( Serial.available()) SCB_AIRCR = 0x05FA0004;
  
  delay(100);
}


Slave:
Code:
// Wire Slave Receiver
// by Nicholas Zambetti <http://www.zambetti.com>
// Modified by Richard Gemmell Oct 2019

// Demonstrates use of the Wire library
// Receives data as an I2C/TWI slave device
// Refer to the "Wire Master Writer" example for use with this
// To use it, connect a master to the Teensy on pins 18 and 19.
//
// Consider using the I2CRegisterSlave class instead of Wire to
// create an I2C slave.

// Created 29 March 2006

// This example code is in the public domain.

#include <Arduino.h>
#include <i2c_driver.h>
#include "imx_rt1060/imx_rt1060_i2c_driver.h"


uint8_t number_bytes;
uint8_t i;
uint8_t ii;

const uint16_t slave_address = 0x0055;
I2CSlave& slave = Slave;
void after_receive(int size);
void before_transmit_isr();
void after_transmit();

const size_t slave_rx_buffer_size = 64;
uint8_t slave_rx_buffer[slave_rx_buffer_size] = {};
volatile size_t slave_bytes_received = 0;

// Set up transmit buffer
const size_t slave_tx_buffer_size = 64;
uint8_t slave_tx_buffer[slave_tx_buffer_size] = {};

// Flags for handling callbacks
volatile bool before_transmit_received = false;
volatile bool after_transmit_received = false;
volatile bool buffer_underflow_detected = false;



void setup()
{

  Serial.begin(115200);           // start serial for output
  delay(500);

  Serial.println(" "); Serial.println("I²C Slave Mode Teensy4.0 with lib imx_rt1060_i2c_driver");

  // Configure I2C Slave and Start It
  slave.set_receive_buffer(slave_rx_buffer, slave_rx_buffer_size);
  slave.after_receive(after_receive);

  slave.before_transmit(before_transmit_isr);
  slave.after_transmit(after_transmit);
  slave.set_transmit_buffer(slave_tx_buffer, slave_tx_buffer_size);
  slave.listen(slave_address);

}

void loop()
{
  if (slave_bytes_received) {

    //    Serial.print("loop: slave_bytes_received "); Serial.print(slave_bytes_received);
    //    Serial.print(", last byte transmitted: ");
    Serial.print("# rx: "); Serial.print(slave_bytes_received); Serial.print(" ");
    Serial.print(slave_rx_buffer[slave_bytes_received - 1]); Serial.print(" ");

    // Clear slave_bytes_received to signal that we're ready for another message
    slave_bytes_received = 0;
  }
  if (before_transmit_received) {
    before_transmit_received = false;
  }
  if (after_transmit_received) {
    after_transmit_received = false;
    if (buffer_underflow_detected) {
      Serial.println("App: Buffer Underflow. (Master asked for too many bytes.)");
      buffer_underflow_detected = false;
    } else {
      Serial.println(" tx ok");
    }
  }
}


// Called by the I2C interrupt service routine.
// This method must be as fast as possible.
// Do not perform IO in it.
void after_receive(int size) {
  // This is the only time we can guarantee that the
  // receive buffer is not changing.
  // Copy the content so we can handle it in the main loop.
  if (!slave_bytes_received) {
    slave_bytes_received = size;
    // now copy to transmit buffer
    memcpy(slave_tx_buffer, slave_rx_buffer, slave_bytes_received);
  }
  // else ignore this message because the main loop hasn't
  // handled the previous one yet.
}


// Called by an interrupt service routine.
// This function must be _very_ fast. Avoid IO.
void before_transmit_isr() {
  before_transmit_received = true;
}


// Called by an interrupt service routine.
// This function must be _very_ fast. Avoid IO.
void after_transmit() {
  //    digitalWrite(led, HIGH);       // briefly flash the LED
  after_transmit_received = true;
  if (slave.has_error()) {
    I2CError error = slave.error();
    if (error == I2CError::buffer_underflow) {
      buffer_underflow_detected = true;
    } else {
      Serial.println("Unexpected error");
    }
  }
}

Regards,

Helmut
 
Would be great if you could recheck it.

Thanks for that. The pin configuration is the one part of the driver that I don't really understand. I wouldn't be at all surprised to learn that I got something wrong. I'll definitely check it out.
 
Hi Helmut,

I've spent the last few days playing around with the pin configuration. I found NXPs documentation which wasn't in the datasheet and I also looked at changes made to the official Teensy Wire implementation.

The practical upshot of this, is that I've changed the driver to use the config you suggested except that I omitted IOMUXC_PAD_SRE. I've also added API methods so that users can override the default config if necessary without having to modify the driver files. See I2CDriverWire::setPadControlConfiguration(). I've updated the README to explain this as well.

The long story is that I tested various configurations on my project. This has 2 I2C connections. One to an INA260 current sensor and one to a Raspberry Pi. The Teensy is the master for the first connection and a slave for the second. I've never been able to make the connection to the Pi reliable. I was pretty sure that the problem was down to noise and I finally got the error rate down to about 1 failure in 10,000 messages. To achieve this, I had to add external pullups and rearranged the board to keep noisy lines away from the I2C wires.

I've looked at each part of the pad config to see what effect it has. I removed the external pullups before testing as I knew that increased the error rate significantly. The only settings that made any noticeable difference were:-
* the link fails completely if DSE is set to 0
* enabling the 22k internal resistor reduced the error rate by 15%
* HYS completely eliminated all errors

So I believe I've confirmed your finding that enabling IOMUXC_PAD_HYS seems to eliminate errors caused by noise. I decided to copy the other settings from the official Teensy Wire implementation as they work fine with my project and fix other problems. The only exception is that I didn't removed SRE as NXP's documentation says you shouldn't use it unless necessary as it can cause EMC problems.

Thanks very much for your tip. It's helped enormously.
Richard
 
Hi Richard,

good to hear that it helped to make your communication between Pi and Teensy4 more reliable in a noisy environment. The application note from NXP (AN5078) you found, is definitly information that should have been in the reference manual.

Regards,
Helmut
 
Hello! All was looking good until I tried to include the audio lib or the lcd lib, it then throws lots of error on compile. here is the code... T4 on 1.52. What can I do? It compiles fine if I remove the includes for the audio or lcd but I need them ; )

Code:
// Wire Slave Sender
// by Nicholas Zambetti <http://www.zambetti.com>
// Modified by Richard Gemmell Oct 2019

// Demonstrates use of the Wire library
// Sends data as an I2C/TWI slave device
// Refer to the "Wire Master Reader" example for use with this
// To use it, connect a master to the Teensy on pins 18 and 19.
//
// Consider using the I2CRegisterSlave class instead of Wire to
// create an I2C slave.

// Created 29 March 2006

// This example code is in the public domain.

#include <Audio.h>


//#include <SerialFlash.h>
// Screen
//#include <ILI9341_t3.h>

//#include "font_Arial.h"
//#define TFT_DC  5
//#define TFT_CS 10

//ILI9341_t3 tft = ILI9341_t3(TFT_CS, TFT_DC);



// alternative wire lib
#include <Arduino.h>
#include <i2c_driver.h>
#include <i2c_driver_wire.h>

void requestEvent();

int led = LED_BUILTIN;

void setup()
{
  pinMode(led, OUTPUT);
  Wire.begin(2);        // join i2c bus with address #8
  //Wire.onReceive(receiveEvent); // Receive event
  Wire.onRequest(requestEvent); // Request event
  Serial.begin(115200);
}

void loop()
{
  delay(100);
}

// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void requestEvent()
{
  digitalWrite(led, HIGH);      // briefly flash the LED
  Wire.write("hello ");     // respond with message of 6 bytes
                                // as expected by master
  digitalWrite(led, LOW);
}

// from slave receiver //
// function that executes whenever data is received from master
// this function is registered as an event, see setup()

errors...

Code:
/var/folders/lm/jj9j_4s516l88sg7hbq9blb00000gp/T/arduino_build_127074/libraries/Wire/WireIMXRT.cpp.o:(.data.Wire2+0x0): multiple definition of `Wire2'
/var/folders/lm/jj9j_4s516l88sg7hbq9blb00000gp/T/arduino_build_127074/libraries/teensy4_i2c/i2c_driver_wire.cpp.o:(.bss.Wire2+0x0): first defined here
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/bin/ld: Disabling relaxation: it will not work with multiple definitions
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/bin/ld: Warning: size of symbol `Wire2' changed from 116 in /var/folders/lm/jj9j_4s516l88sg7hbq9blb00000gp/T/arduino_build_127074/libraries/teensy4_i2c/i2c_driver_wire.cpp.o to 112 in /var/folders/lm/jj9j_4s516l88sg7hbq9blb00000gp/T/arduino_build_127074/libraries/Wire/WireIMXRT.cpp.o
/var/folders/lm/jj9j_4s516l88sg7hbq9blb00000gp/T/arduino_build_127074/libraries/Wire/WireIMXRT.cpp.o: In function `Print::availableForWrite()':
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp:235: multiple definition of `Wire1'
/var/folders/lm/jj9j_4s516l88sg7hbq9blb00000gp/T/arduino_build_127074/libraries/teensy4_i2c/i2c_driver_wire.cpp.o:/Users/Alex/Documents/Arduino/libraries/teensy4_i2c/src/i2c_driver_wire.cpp:123: first defined here
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/bin/ld: Warning: size of symbol `Wire1' changed from 116 in /var/folders/lm/jj9j_4s516l88sg7hbq9blb00000gp/T/arduino_build_127074/libraries/teensy4_i2c/i2c_driver_wire.cpp.o to 112 in /var/folders/lm/jj9j_4s516l88sg7hbq9blb00000gp/T/arduino_build_127074/libraries/Wire/WireIMXRT.cpp.o
Multiple libraries were found for "SD.h"
/var/folders/lm/jj9j_4s516l88sg7hbq9blb00000gp/T/arduino_build_127074/libraries/Wire/WireIMXRT.cpp.o: In function `Print::availableForWrite()':
 Used: /Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/SD
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp:235: multiple definition of `Wire'
 Not used: /Applications/Ard Teensyduino 1.52.app/Contents/Java/libraries/SD
/var/folders/lm/jj9j_4s516l88sg7hbq9blb00000gp/T/arduino_build_127074/libraries/teensy4_i2c/i2c_driver_wire.cpp.o:/Users/Alex/Documents/Arduino/libraries/teensy4_i2c/src/i2c_driver_wire.cpp:123: first defined here
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/bin/ld: Warning: size of symbol `Wire' changed from 116 in /var/folders/lm/jj9j_4s516l88sg7hbq9blb00000gp/T/arduino_build_127074/libraries/teensy4_i2c/i2c_driver_wire.cpp.o to 112 in /var/folders/lm/jj9j_4s516l88sg7hbq9blb00000gp/T/arduino_build_127074/libraries/Wire/WireIMXRT.cpp.o
collect2: error: ld returned 1 exit status
Error compiling for board Teensy 4.0.

edit: I have done some reading and I see that I need to change all the includes for Wire.h. is there a way to do this so that it isn't going to break when I next update teensyduino? Also how can i find where it needs changing for example if I include Audio.h it breaks, but there is no mention of Wire.h in Audio.h


Thanks for any input. A
 
Last edited:
Hi alrj,

have you changed all occurences of "#include Wire.h" in the Audio lib and in the lcd lib to "#include <i2c_driver.h> #include <i2c_driver_wire.h>"? You can not use both librarys at the same time.
Also use the latest version of Richards teensy4_i2c lib.

Regards,
Helmut
 
Thanks I have realised that is what I need to do but searching Audio.h for wire provided no results.

edit: i see it shows where the issue is in the readout.

Also if I go down this route is there a way to avoid loosing all the changes when I update teensyduino? I have also been looking at RichGs alternative i2c implementation in the same lib but I don't really understand it!
 
If I do this in WireIMXRT.cpp which is where the error is caused when I add Audio.h

Code:
//#include "Wire.h"
#include <i2c_driver.h>
#include <i2c_driver_wire.h>


I get a whole heap of trouble...

Code:
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp: In member function 'void I2CDriverWire::begin()':
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp:18:2: error: 'hardware' was not declared in this scope
  hardware.clock_gate_register |= hardware.clock_gate_mask;
  ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp:19:2: error: 'port' was not declared in this scope
  port->MCR = LPI2C_MCR_RST;
  ^
In file included from /Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/cores/teensy4/core_pins.h:33:0,
                 from /Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/cores/teensy4/wiring.h:39,
                 from /Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/cores/teensy4/WProgram.h:45,
                 from /var/folders/lm/jj9j_4s516l88sg7hbq9blb00000gp/T/arduino_build_655445/pch/Arduino.h:6,
                 from /Users/Alex/Documents/Arduino/libraries/teensy4_i2c/src/i2c_driver_wire.h:7,
                 from /Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp:4:
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp:23:42: error: 'sda_pin_index_' was not declared in this scope
  *(portControlRegister(hardware.sda_pins[sda_pin_index_].pin)) = PINCONFIG;
                                          ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/cores/teensy4/pins_arduino.h:141:61: note: in definition of macro 'portControlRegister'
 #define portControlRegister(pin) ((digital_pin_to_info_PGM[(pin)].pad))
                                                             ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp:30:42: error: 'scl_pin_index_' was not declared in this scope
  *(portControlRegister(hardware.scl_pins[scl_pin_index_].pin)) = PINCONFIG;
                                          ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/cores/teensy4/pins_arduino.h:141:61: note: in definition of macro 'portControlRegister'
 #define portControlRegister(pin) ((digital_pin_to_info_PGM[(pin)].pad))
                                                             ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp: At global scope:
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp:37:6: error: redefinition of 'void I2CDriverWire::begin(uint8_t)'
 void TwoWire::begin(uint8_t address)
      ^
In file included from /Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp:4:0:
/Users/Alex/Documents/Arduino/libraries/teensy4_i2c/src/i2c_driver_wire.h:98:17: note: 'void I2CDriverWire::begin(uint8_t)' previously defined here
     inline void begin(uint8_t address) { begin((int)address); }

                 ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp:46:33: error: no 'void I2CDriverWire::setSDA(uint8_t)' member function declared in class 'I2CDriverWire'
 void TwoWire::setSDA(uint8_t pin) {
                                 ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp:68:33: error: no 'void I2CDriverWire::setSCL(uint8_t)' member function declared in class 'I2CDriverWire'
 void TwoWire::setSCL(uint8_t pin) {
                                 ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp:90:27: error: no 'bool I2CDriverWire::force_clock()' member function declared in class 'I2CDriverWire'
 bool TwoWire::force_clock()
                           ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp: In member function 'virtual size_t I2CDriverWire::write(uint8_t)':
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp:125:6: error: 'transmitting' was not declared in this scope
  if (transmitting || slave_mode) {
      ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp:125:22: error: 'slave_mode' was not declared in this scope
  if (transmitting || slave_mode) {
                      ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp:126:7: error: 'txBufferLength' was not declared in this scope
   if (txBufferLength >= BUFFER_LENGTH+1) {
       ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp:126:25: error: 'BUFFER_LENGTH' was not declared in this scope
   if (txBufferLength >= BUFFER_LENGTH+1) {
                         ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp:130:3: error: 'txBuffer' was not declared in this scope
   txBuffer[txBufferLength++] = data;
   ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp:130:12: error: 'txBufferLength' was not declared in this scope
   txBuffer[txBufferLength++] = data;
            ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp: In member function 'virtual size_t I2CDriverWire::write(const uint8_t*, size_t)':
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp:138:6: error: 'transmitting' was not declared in this scope
  if (transmitting || slave_mode) {
      ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp:138:22: error: 'slave_mode' was not declared in this scope
  if (transmitting || slave_mode) {
                      ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp:139:18: error: 'BUFFER_LENGTH' was not declared in this scope
   size_t avail = BUFFER_LENGTH+1 - txBufferLength;
                  ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp:139:36: error: 'txBufferLength' was not declared in this scope
   size_t avail = BUFFER_LENGTH+1 - txBufferLength;
                                    ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp:144:10: error: 'txBuffer' was not declared in this scope
   memcpy(txBuffer + txBufferLength, data, quantity);
          ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp: At global scope:
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp:166:25: error: no 'bool I2CDriverWire::wait_idle()' member function declared in class 'I2CDriverWire'
 bool TwoWire::wait_idle()
                         ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp:185:9: error: prototype for 'uint8_t I2CDriverWire::endTransmission(uint8_t)' does not match any in class 'I2CDriverWire'
 uint8_t TwoWire::endTransmission(uint8_t sendStop)
         ^
In file included from /Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp:4:0:
/Users/Alex/Documents/Arduino/libraries/teensy4_i2c/src/i2c_driver_wire.h:62:13: error: candidate is: uint8_t I2CDriverWire::endTransmission(int)
     uint8_t endTransmission(int stop = true);

             ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp:237:9: error: prototype for 'uint8_t I2CDriverWire::requestFrom(uint8_t, uint8_t, uint8_t)' does not match any in class 'I2CDriverWire'
 uint8_t TwoWire::requestFrom(uint8_t address, uint8_t length, uint8_t sendStop)
         ^
In file included from /Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp:4:0:
/Users/Alex/Documents/Arduino/libraries/teensy4_i2c/src/i2c_driver_wire.h:68:13: error: candidate is: uint8_t I2CDriverWire::requestFrom(int, int, int)
     uint8_t requestFrom(int address, int quantity, int stop = true);

             ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp:296:9: error: prototype for 'uint8_t I2CDriverWire::requestFrom(uint8_t, uint8_t, uint32_t, uint8_t, uint8_t)' does not match any in class 'I2CDriverWire'
 uint8_t TwoWire::requestFrom(uint8_t addr, uint8_t qty, uint32_t iaddr, uint8_t n, uint8_t stop)
         ^
In file included from /Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp:4:0:
/Users/Alex/Documents/Arduino/libraries/teensy4_i2c/src/i2c_driver_wire.h:68:13: error: candidate is: uint8_t I2CDriverWire::requestFrom(int, int, int)
     uint8_t requestFrom(int address, int quantity, int stop = true);

             ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp:316:20: error: 'I2C_Hardware_t' in 'using TwoWire = class I2CDriverWire {aka class I2CDriverWire}' does not name a type
 constexpr TwoWire::I2C_Hardware_t TwoWire::i2c1_hardware = {
                    ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp:322:29: error: 'i2c1_hardware' is not a member of 'TwoWire {aka I2CDriverWire}'
 TwoWire Wire(&IMXRT_LPI2C1, TwoWire::i2c1_hardware);
                             ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp:325:20: error: 'I2C_Hardware_t' in 'using TwoWire = class I2CDriverWire {aka class I2CDriverWire}' does not name a type
 constexpr TwoWire::I2C_Hardware_t TwoWire::i2c3_hardware = {
                    ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp:331:30: error: 'i2c3_hardware' is not a member of 'TwoWire {aka I2CDriverWire}'
 TwoWire Wire1(&IMXRT_LPI2C3, TwoWire::i2c3_hardware);
                              ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp:334:20: error: 'I2C_Hardware_t' in 'using TwoWire = class I2CDriverWire {aka class I2CDriverWire}' does not name a type
 constexpr TwoWire::I2C_Hardware_t TwoWire::i2c4_hardware = {
                    ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp:340:30: error: 'i2c4_hardware' is not a member of 'TwoWire {aka I2CDriverWire}'
 TwoWire Wire2(&IMXRT_LPI2C4, TwoWire::i2c4_hardware);
                              ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp: In member function 'void I2CDriverWire::setClock(uint32_t)':
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/Wire/WireIMXRT.cpp:351:2: error: 'port' was not declared in this scope
  port->MCR = 0;
  ^
Multiple libraries were found for "SD.h"
 Used: /Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/SD
 Not used: /Applications/Ard Teensyduino 1.52.app/Contents/Java/libraries/SD
Error compiling for board Teensy 4.0.
 
alrj said:
Thanks I have realised that is what I need to do but searching Audio.h for wire provided no results.

You need to replace all usages of Wire.h in any library dependencies and not just in the headers that you include. In your case, this means replacing the #include "Wire.h" in each of the control_xxx.cpp files in your hardware\teensy\avr\libraries\Audio directory.

alrj said:
Also if I go down this route is there a way to avoid loosing all the changes when I update teensyduino?
I don't know anything about how teensyduino does updates but I suspect you'll have to re-apply your changes. You could use copies of the libraries you need but then you wouldn't gain the benefit of any updates.

alrj said:
I have also been looking at RichGs alternative i2c implementation in the same lib but I don't really understand it!
If you haven't already found them you might like to have a look at the examples.

Whichever teensy4_i2c API you use you'll still have to change audio etc to use i2c_driver_wire.h.

If you're already familiar with the Arduino Wire API and you know that it'll do what you want, then you might be better of sticking with it.

If you let me know what you're trying to achieve with the I2C part of your project then I'll be happy to suggest which API to use and to provide examples etc.

If you've never encountered I2C before then the critical thing to understand is that it's a master/slave setup. The master initiates every transfer whether it's sending or receiving. The slave can only react to the master's demands.

cheers,
Richard
 
Thanks for the reply. I am pretty familiar with the Arduino i2c library but I have never used an arduino/teensy as a slave before. I just want to send trigger messages of a few bytes from one to another.

I tried to replace "Wire.h" as per the instructions but I got the errors I posted in post #39. what have I done wrong?

regards, Al.
 
Okay So! I have audio working via updating the control elements : ) thank you for your explanation.

I have now added "#include <ILI9341_t3.h>" for the screen I am using. Here is the code and the errors...

Code:
// Wire Slave Sender
// by Nicholas Zambetti <http://www.zambetti.com>
// Modified by Richard Gemmell Oct 2019

// Demonstrates use of the Wire library
// Sends data as an I2C/TWI slave device
// Refer to the "Wire Master Reader" example for use with this
// To use it, connect a master to the Teensy on pins 18 and 19.
//
// Consider using the I2CRegisterSlave class instead of Wire to
// create an I2C slave.

// Created 29 March 2006

// This example code is in the public domain.

#include <Audio.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

// Screen
#include <ILI9341_t3.h>
//#define TFT_DC  5
//#define TFT_CS 10
// MOSI=11, MISO=12, SCK=13
//ILI9341_t3 tft = ILI9341_t3(TFT_CS, TFT_DC);

#include <i2c_driver.h>
#include <i2c_driver_wire.h>

void requestEvent();

int led = LED_BUILTIN;

void setup()
{
  pinMode(led, OUTPUT);
  Wire.begin(2);        // join i2c bus with address #8
  //Wire.onReceive(receiveEvent); // Receive event
  Wire.onRequest(requestEvent); // Request event
  Serial.begin(115200);
}

void loop()
{
  delay(100);
}

// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void requestEvent()
{
  digitalWrite(led, HIGH);      // briefly flash the LED
  Wire.write("ACK ");     // respond with message of 6 bytes
                                // as expected by master
  digitalWrite(led, LOW);
}

// from slave receiver //
// function that executes whenever data is received from master
// this function is registered as an event, see setup()
/*void receiveEvent(int howMany)
{
  digitalWrite(led, HIGH);       // briefly flash the LED
  while(Wire.available() > 1) {  // loop through all but the last
    char c = Wire.read();        // receive byte as a character
    Serial.print(c);             // print the character
  }
  Serial.println();
  int x = Wire.read();           // receive byte as an integer
  Serial.println(x);             // print the integer
  digitalWrite(led, LOW);
} */

Errors...

Code:
In file included from /Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/exception:162:0,
                 from /Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/typeinfo:34,
                 from /Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/functional:53,
                 from /Users/Alex/Documents/Arduino/libraries/teensy4_i2c/src/i2c_driver.h:9,
                 from /Users/Alex/Documents/Arduino/slave_sender_working/slave_sender_working.ino:29:
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/bits/exception_ptr.h:118:68: error: macro "swap" requires 2 arguments, but only 1 given
         exception_ptr(static_cast<exception_ptr&&>(__o)).swap(*this);
                                                                    ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/bits/exception_ptr.h:126:26: error: macro "swap" requires 2 arguments, but only 1 given
       swap(exception_ptr&) _GLIBCXX_USE_NOEXCEPT;
                          ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/bits/exception_ptr.h:161:23: error: macro "swap" requires 2 arguments, but only 1 given
     { __lhs.swap(__rhs); }
                       ^
In file included from /Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/string:52:0,
                 from /Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/stdexcept:39,
                 from /Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/array:38,
                 from /Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/tuple:39,
                 from /Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/functional:55,
                 from /Users/Alex/Documents/Arduino/libraries/teensy4_i2c/src/i2c_driver.h:9,
                 from /Users/Alex/Documents/Arduino/slave_sender_working/slave_sender_working.ino:29:
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/bits/basic_string.h:1953:29: error: macro "swap" requires 2 arguments, but only 1 given
       swap(basic_string& __s) _GLIBCXX_NOEXCEPT;
                             ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/bits/basic_string.h:5218:47: error: macro "swap" passed 6 arguments, but takes just 2
   basic_string<_CharT, _Traits, _Alloc>& __rhs)
                                               ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/bits/basic_string.h:5220:39: error: macro "swap" requires 2 arguments, but only 1 given
     noexcept(noexcept(__lhs.swap(__rhs)))
                                       ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/bits/basic_string.h:5222:23: error: macro "swap" requires 2 arguments, but only 1 given
     { __lhs.swap(__rhs); }
                       ^
In file included from /Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/string:53:0,
                 from /Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/stdexcept:39,
                 from /Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/array:38,
                 from /Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/tuple:39,
                 from /Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/functional:55,
                 from /Users/Alex/Documents/Arduino/libraries/teensy4_i2c/src/i2c_driver.h:9,
                 from /Users/Alex/Documents/Arduino/slave_sender_working/slave_sender_working.ino:29:
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/bits/basic_string.tcc:59:27: error: macro "swap" requires 2 arguments, but only 1 given
     swap(basic_string& __s) _GLIBCXX_NOEXCEPT
                           ^
In file included from /Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/tuple:39:0,
                 from /Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/functional:55,
                 from /Users/Alex/Documents/Arduino/libraries/teensy4_i2c/src/i2c_driver.h:9,
                 from /Users/Alex/Documents/Arduino/slave_sender_working/slave_sender_working.ino:29:
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/array:115:26: error: macro "swap" requires 2 arguments, but only 1 given
       swap(array& __other)
                          ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/array:274:56: error: macro "swap" passed 4 arguments, but takes just 2
     swap(array<_Tp, _Nm>& __one, array<_Tp, _Nm>& __two)
                                                        ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/array:275:39: error: macro "swap" requires 2 arguments, but only 1 given
     noexcept(noexcept(__one.swap(__two)))
                                       ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/array:276:23: error: macro "swap" requires 2 arguments, but only 1 given
     { __one.swap(__two); }
                       ^
In file included from /Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/functional:55:0,
                 from /Users/Alex/Documents/Arduino/libraries/teensy4_i2c/src/i2c_driver.h:9,
                 from /Users/Alex/Documents/Arduino/slave_sender_working/slave_sender_working.ino:29:
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/tuple:580:23: error: macro "swap" requires 2 arguments, but only 1 given
       swap(tuple& __in)
                       ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/tuple:590:23: error: macro "swap" requires 2 arguments, but only 1 given
       void swap(tuple&) noexcept { /* no-op */ }
                       ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/tuple:740:23: error: macro "swap" requires 2 arguments, but only 1 given
       swap(tuple& __in)
                       ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/tuple:1133:35: error: macro "swap" requires 2 arguments, but only 1 given
     noexcept(noexcept(__x.swap(__y)))
                                   ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/tuple:1134:19: error: macro "swap" requires 2 arguments, but only 1 given
     { __x.swap(__y); }
                   ^
In file included from /Users/Alex/Documents/Arduino/libraries/teensy4_i2c/src/i2c_driver.h:9:0,
                 from /Users/Alex/Documents/Arduino/slave_sender_working/slave_sender_working.ino:29:
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/functional:2030:16: error: macro "swap" requires 2 arguments, but only 1 given
  __x.swap(*this);
                ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/functional:2071:26: error: macro "swap" requires 2 arguments, but only 1 given
  function(__x).swap(*this);
                          ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/functional:2089:37: error: macro "swap" requires 2 arguments, but only 1 given
  function(std::move(__x)).swap(*this);
                                     ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/functional:2132:52: error: macro "swap" requires 2 arguments, but only 1 given
    function(std::forward<_Functor>(__f)).swap(*this);
                                                    ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/functional:2141:28: error: macro "swap" requires 2 arguments, but only 1 given
    function(__f).swap(*this);
                            ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/functional:2154:30: error: macro "swap" requires 2 arguments, but only 1 given
       void swap(function& __x)
                              ^
/Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/tools/arm/arm-none-eabi/include/c++/5.4.1/functional:2370:19: error: macro "swap" requires 2 arguments, but only 1 given
     { __x.swap(__y); }
                   ^
Multiple libraries were found for "SD.h"
 Used: /Applications/Ard Teensyduino 1.52.app/Contents/Java/hardware/teensy/avr/libraries/SD
 Not used: /Applications/Ard Teensyduino 1.52.app/Contents/Java/libraries/SD
Error compiling for board Teensy 4.0.
No files were added to the sketch.
 
The problem is that both ILI9341_t3.h and <functional> define swap. I can get it to compile by including ILI9341_t3.h after i2c_driver_wire.h like this:-
Code:
#include <i2c_driver.h>
#include <i2c_driver_wire.h>

#include <ILI9341_t3.h>

The problem may re-appear as you add more code to your project.
 
I managed to get my code to compile using the Adafruit TFT lib but whenever I called playwav it would hang unless I commented out wire begin so I have admitted defeat and I will use UART. Thanks for your help, it is very much appreciated
 
Hi Richard,

Thanks very much for this, I was looking for a async I2C implementation and you have done all the work for me :)

I have a slight issue though, there seems to be around a 300-350us delay before things get moving:

In the following images probe 2 is high in the call, probe 3 is high from the start of the call till all data is available.

First using the normal Wire stuff:
screenshot_656.jpg

Now using master.read_async():
screenshot_657.jpg

So the async part works a dream but the actual time to get data here increases by around 320us.

Is this expected?

Many thanks

Andy
 
So I investigated a little further, so this is where we are seeing the problem:

Whenever the LPI2C is enabled, it monitors the I2C bus to detect when the I2C bus is idle (MSR[BBF]). The I2C bus is no longer considered idle if either SCL or SDA are low, and the I2C bus becomes idle if a STOP condition is detected or if a bus idle timeout is detected (as configured by MCFGR2[BUSIDLE]).

Basically it doesn't think the bus is idle, bit weird as SCL and SDA are high and it doesn't start till the bus idle timeout defined here:

LPI2C_MCFGR2_BUSIDLE(3900);

I have changed this value to check and it does effect this pause but obviously is probably something we don't want to do!

I will investigate further to see why it thinks the bus is not idle...
 
OK, I think the problem is that the BUSIDLE time is being set incorrectly.

From the datasheet page 2751 the minimum BUSIDLE is:
(CLKLO+SETHOLD+2) × 2

Also on page 2772 it states that you can disable it totally which seems to go against page 2751!

I tested both disabling and setting the minimum value and both work fine here but decided to keep it safe:


Code:
void IMX_RT1060_I2CMaster::set_clock(uint32_t frequency) {
    if (frequency < 400000) {
        // Use Standard Mode - up to 100 kHz
        port->MCCR0 = LPI2C_MCCR0_CLKHI(55) | LPI2C_MCCR0_CLKLO(59) |
                      LPI2C_MCCR0_DATAVD(25) | LPI2C_MCCR0_SETHOLD(40);
        port->MCFGR1 = LPI2C_MCFGR1_PRESCALE(1);
        port->MCFGR2 = LPI2C_MCFGR2_FILTSDA(5) | LPI2C_MCFGR2_FILTSCL(5) |
                       LPI2C_MCFGR2_BUSIDLE(2 * (59 + 40 + 2));
    } else if (frequency < 1000000) {
        // Use Fast Mode - up to 400 kHz
        port->MCCR0 = LPI2C_MCCR0_CLKHI(26) | LPI2C_MCCR0_CLKLO(28) |
                      LPI2C_MCCR0_DATAVD(12) | LPI2C_MCCR0_SETHOLD(18);
        port->MCFGR1 = LPI2C_MCFGR1_PRESCALE(0);
        port->MCFGR2 = LPI2C_MCFGR2_FILTSDA(2) | LPI2C_MCFGR2_FILTSCL(2) |
                       LPI2C_MCFGR2_BUSIDLE(2 * (28 + 18 + 2));
    } else {
        // Use Fast Mode Plus - up to 1 MHz
        port->MCCR0 = LPI2C_MCCR0_CLKHI(9) | LPI2C_MCCR0_CLKLO(10) |
                      LPI2C_MCCR0_DATAVD(4) | LPI2C_MCCR0_SETHOLD(7);
        port->MCFGR1 = LPI2C_MCFGR1_PRESCALE(0);
        port->MCFGR2 = LPI2C_MCFGR2_FILTSDA(1) | LPI2C_MCFGR2_FILTSCL(1) |
                       LPI2C_MCFGR2_BUSIDLE(2 * (10 + 7 + 2));
    }

This brings the delay down to around 20us.

I also think there are some errors maybe in the "Fast Mode Plus", I will look into that.

I will get a pull request going as well...
 
Hi Andy,

Thanks for reporting this. I have to confess that I didn't look at the timing configuration very hard. I pinched it from the Teensy Wire implementation and just did a quick sanity check on it. It might be worth checking the latest version of Wire to see if that's been improved since I copied the config.

I'll be very glad to integrate any improvements you suggest. A pull request would be brilliant.

thanks,
Richard
 
Status
Not open for further replies.
Back
Top