i2c_t3 basic example returns FAIL

JBrownlow

Member
Trying to run the basic_master_callback (on Teensy 3.6) and basic_slave (on Teensy LC) examples from the Teensy i2c_t3 library, in an attempt to help myself understand several other i2c communication problems I am having, just seems to lead to more problems!

My setup is shown in the picture, and the code used is mentioned above. The Serial monitor returns the following on repeat, forever increasing the same error counter and data message:

Sending to Slave: 'Data Message #0' Reading from Slave:
FAIL - Cannot acquire bus, possible stuck SDA/SCL

I2C_ERRCNT_RESET_BUS: 0
I2C_ERRCNT_TIMEOUT: 0
I2C_ERRCNT_ADDR_NAK: 0
I2C_ERRCNT_DATA_NAK: 0
I2C_ERRCNT_ARBL: 0
I2C_ERRCNT_NOT_ACQ: 1
I2C_ERRCNT_DMA_ERR: 0
Sending to Slave: 'Data Message #1' Reading from Slave:
FAIL - Cannot acquire bus, possible stuck SDA/SCL

I2C_ERRCNT_RESET_BUS: 0
I2C_ERRCNT_TIMEOUT: 0
I2C_ERRCNT_ADDR_NAK: 0
I2C_ERRCNT_DATA_NAK: 0
I2C_ERRCNT_ARBL: 0
I2C_ERRCNT_NOT_ACQ: 2
I2C_ERRCNT_DMA_ERR: 0

Any ideas what I might be doing wrong?
Any help is much appreciated!

Additional information:
I successfully found a current sensor using the basic_scanner example, but could not detect the Teensy LC in the same situation with basic_slave code on the device. Is it possible I was also doing something else wrong here?
 

Attachments

  • i2c_t3 Setup.jpg
    i2c_t3 Setup.jpg
    46.2 KB · Views: 70
From your picture, it looks like the pull-up resistors are on the wrong pins (it looks like you have attached the resistors to pins 24, 25, an 26). They should be on the same pins as SDA/SCL. Even though you have two teensys on the breadboard, you only need one set of pullup-resistors (i.e. you need the pull-up resistors somewhere on the bus).

In addition to the resistors being on the wrong pins, it looks like you have wired them as pull-down pins (pull-up pins go between the data pin and 3.3v, pull-down pins go between the data pin and ground).

Finally from the marking on the resistors, they are probably not strong enough. In the past, Paul has said 2.2K resistors are what a 3.3v system needs, and it looks like you only have 2K resistors. You can go to higher resistors, and it will work, though much higher, and it will degrade the max speed that the i2c bus can run at. The 2.2K resistor is more of a guideline for simple 3.3v i2c buses. If you have a longer or more complicated i2c bus, you will likely need to calculate what level of resistors to use.
 
The pull-down resistors are on pins 10, 11 and 12, I placed them there as the software below requires, to receive different parts of information. (This actually took me ages to figure out as I haven't done much to do with pull-up/down resistor stuff!)

I would have thought 2k ohms was close enough to 2.2k ohms to do the job, particularly as the pull down resistors in this case are only to pull down the data line for the purposes of the software, but as you say I just need to pass the boundary of 2.2k ohms? (but I'll swap them for some 2.2k ones anyway)

I wondered if I could get away with using the internal pull ups for the Teensy 3.6, but I read somewhere that there may be a hardware bug? So will I need a pull up resistor on each of the SDA and SCL lines at a value of 2.2k?
If there really is a hardware bug then for the Teensy 3.6, should I always use 2.2k pull-ups on the I2C lines?

Thanks for your help!


This is the code from the examples:

Basic_Master_Callback.ino

Code:
// -------------------------------------------------------------------------------------------
// Basic Master Callback
// -------------------------------------------------------------------------------------------
//
// This creates a simple I2C Master device which when triggered will send/receive a text
// string to/from a Slave device.  It is intended to pair with a Slave device running the
// basic_slave sketch.  
//
// Functionally this sketch is similar to the basic_master sketch, except for three things:
// 1) It uses callbacks to handle the results and errors, instead of explicitly coding such
//    routines after each send/receive call.
// 2) It can optionally demonstrate background transfers by setting the NONBLOCKING flag (from
//    external observation these transfers will appear similar, but the LED duration will
//    be shorter as they exit immediately after initiating the transfer).
// 3) There is an added test to dump error diagnostics from the error counting system.
//
// Pull pin12 input low to send.
// Pull pin11 input low to receive.
// Pull pin10 input low to dump error diagnostics.
//
// This example code is in the public domain.
//
// -------------------------------------------------------------------------------------------

#include <i2c_t3.h>

// Memory
#define MEM_LEN 256
char databuf[MEM_LEN];
int count;

#define NONBLOCKING

void setup()
{
    pinMode(LED_BUILTIN,OUTPUT);    // LED
    digitalWrite(LED_BUILTIN,LOW);  // LED off
    pinMode(12,INPUT_PULLUP);       // Control for Send
    pinMode(11,INPUT_PULLUP);       // Control for Receive
    pinMode(10,INPUT_PULLUP);       // Control for Diagnostics

    // Setup for Master mode, pins 18/19, external pullups, 400kHz, 50ms default timeout
    Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, 400000);
    Wire.setDefaultTimeout(50000); // 50ms

    // Data init
    memset(databuf, 0, sizeof(databuf));
    count = 0;

    Serial.begin(115200);

    Wire.onTransmitDone(transmitDone);
    Wire.onReqFromDone(requestDone);
    Wire.onError(errorEvent);
}

void loop()
{
    uint8_t target = 0x66; // target Slave address

    // Send string to Slave
    //
    if(digitalRead(12) == LOW)
    {
        digitalWrite(LED_BUILTIN,HIGH);   // LED on

        // Construct data message
        sprintf(databuf, "Data Message #%d", count++);

        // Print message
        Serial.printf("Sending to Slave: '%s' ", databuf);

        // Transmit to Slave
        Wire.beginTransmission(target);   // Slave address
        Wire.write(databuf,strlen(databuf)+1); // Write string to I2C Tx buffer (incl. string null at end)
        #if defined(NONBLOCKING)
            Wire.sendTransmission();          // Transmit to Slave, non-blocking
        #else
            Wire.endTransmission();           // Transmit to Slave, blocking
        #endif

        // After send complete, callback will print result

        digitalWrite(LED_BUILTIN,LOW);    // LED off
        delay(100);                       // 100ms delay
    }

    // Read string from Slave
    //
    if(digitalRead(11) == LOW)
    {
        digitalWrite(LED_BUILTIN,HIGH);   // LED on

        // Print message
        Serial.print("Reading from Slave: ");

        // Read from Slave
        #if defined(NONBLOCKING)
            Wire.sendRequest(target, (size_t)MEM_LEN); // Read from Slave (string len unknown, request full buffer), non-blocking
        #else
            Wire.requestFrom(target, (size_t)MEM_LEN); // Read from Slave (string len unknown, request full buffer), blocking
        #endif

        // After request complete, callback will print result

        digitalWrite(LED_BUILTIN,LOW);    // LED off
        delay(100);                       // 100ms delay
    }

    // Diagnostics - print error count summary
    //
    if(digitalRead(10) == LOW)
    {
        digitalWrite(LED_BUILTIN,HIGH);   // LED on

        // Print errors
        Serial.print("\n");
        Serial.printf("I2C_ERRCNT_RESET_BUS: %d\n", Wire.getErrorCount(I2C_ERRCNT_RESET_BUS));
        Serial.printf("I2C_ERRCNT_TIMEOUT:   %d\n", Wire.getErrorCount(I2C_ERRCNT_TIMEOUT));
        Serial.printf("I2C_ERRCNT_ADDR_NAK:  %d\n", Wire.getErrorCount(I2C_ERRCNT_ADDR_NAK));
        Serial.printf("I2C_ERRCNT_DATA_NAK:  %d\n", Wire.getErrorCount(I2C_ERRCNT_DATA_NAK));
        Serial.printf("I2C_ERRCNT_ARBL:      %d\n", Wire.getErrorCount(I2C_ERRCNT_ARBL));
        Serial.printf("I2C_ERRCNT_NOT_ACQ:   %d\n", Wire.getErrorCount(I2C_ERRCNT_NOT_ACQ));
        Serial.printf("I2C_ERRCNT_DMA_ERR:   %d\n", Wire.getErrorCount(I2C_ERRCNT_DMA_ERR));

        // uncomment to zero all errors after dumping
        //for(uint8_t i=0; i < I2C_ERRCNT_DMA_ERR; i++) Wire.zeroErrorCount((i2c_err_count)i);

        digitalWrite(LED_BUILTIN,LOW);    // LED off
        delay(100);                       // 100ms delay
    }
}

//
// Trigger after Tx complete (outgoing I2C data)
//
void transmitDone(void)
{
    Serial.print("OK\n");
}

//
// Trigger after Rx complete (incoming I2C data)
//
void requestDone(void)
{
    Wire.read(databuf, Wire.available());
    Serial.printf("'%s' OK\n",databuf);
}

//
// Trigger on I2C Error
//
void errorEvent(void)
{
    Serial.print("FAIL - ");
    switch(Wire.status())
    {
    case I2C_TIMEOUT:  Serial.print("I2C timeout\n"); Wire.resetBus(); break;
    case I2C_ADDR_NAK: Serial.print("Slave addr not acknowledged\n"); break;
    case I2C_DATA_NAK: Serial.print("Slave data not acknowledged\n"); break;
    case I2C_ARB_LOST: Serial.print("Arbitration Lost, possible pullup problem\n"); Wire.resetBus(); break;
    case I2C_BUF_OVF:  Serial.print("I2C buffer overflow\n"); break;
    case I2C_NOT_ACQ:  Serial.print("Cannot acquire bus, possible stuck SDA/SCL\n"); Wire.resetBus(); break;
    case I2C_DMA_ERR:  Serial.print("DMA Error\n"); break;
    default:           break;
    }
}

Basic_Slave.ino

Code:
// -------------------------------------------------------------------------------------------
// Basic Slave
// -------------------------------------------------------------------------------------------
//
// This creates a simple I2C Slave device which will print whatever text string is sent to it.
// It will retain the text string in memory and will send it back to a Master device if 
// requested.  It is intended to pair with a Master device running the basic_master sketch.
//
// This example code is in the public domain.
//
// -------------------------------------------------------------------------------------------

#include <i2c_t3.h>

// Function prototypes
void receiveEvent(size_t count);
void requestEvent(void);

// Memory
#define MEM_LEN 256
char databuf[MEM_LEN];
volatile uint8_t received;

//
// Setup
//
void setup()
{
    pinMode(LED_BUILTIN,OUTPUT); // LED

    // Setup for Slave mode, address 0x66, pins 18/19, external pullups, 400kHz
    Wire.begin(I2C_SLAVE, 0x66, I2C_PINS_18_19, I2C_PULLUP_EXT, 400000);

    // Data init
    received = 0;
    memset(databuf, 0, sizeof(databuf));

    // register events
    Wire.onReceive(receiveEvent);
    Wire.onRequest(requestEvent);

    Serial.begin(115200);
}

void loop()
{
    // print received data - this is done in main loop to keep time spent in I2C ISR to minimum
    if(received)
    {
        digitalWrite(LED_BUILTIN,HIGH);
        Serial.printf("Slave received: '%s'\n", databuf);
        received = 0;
        digitalWrite(LED_BUILTIN,LOW);
    }
}

//
// handle Rx Event (incoming I2C data)
//
void receiveEvent(size_t count)
{
    Wire.read(databuf, count);  // copy Rx data to databuf
    received = count;           // set received flag to count, this triggers print in main loop
}

//
// handle Tx Event (outgoing I2C data)
//
void requestEvent(void)
{
    Wire.write(databuf, MEM_LEN); // fill Tx buffer (send full mem)
}
 
I would have thought 2k ohms was close enough to 2.2k ohms to do the job, particularly as the pull down resistors in this case are only to pull down the data line for the purposes of the software, but as you say I just need to pass the boundary of 2.2k ohms? (but I'll swap them for some 2.2k ones anyway)

I wondered if I could get away with using the internal pull ups for the Teensy 3.6, but I read somewhere that there may be a hardware bug? So will I need a pull up resistor on each of the SDA and SCL lines at a value of 2.2k?
If there really is a hardware bug then for the Teensy 3.6, should I always use 2.2k pull-ups on the I2C lines?

As I understand it, ARM processors internal pull-ups are just not strong enough, so you need a set of pull-ups on the SCL/SDA lines. I.e., it is not a 'bug', but a 'feature'. I was looking through some Adafruit Feather machines for their M0, M4, and ESP32 feathers and they have similar wording that you may need pullups:
 
On this page scoll down to the Pullups section on the readme file, this is where I read it, but HW bug did sound like the incorrect terminology to me! https://github.com/nox771/i2c_t3

Adding the pull ups seems to have fixed my issue! Thanks a lot.

The reason I thought I could get it working without pull-ups was because in the readme file linked above it said this should still work for a single device on i2c when I2C_PULLUP_INT is used in the line below, instead of I2C_PULLUP_EXT:

Code:
Wire.begin(I2C_SLAVE, 0x66, I2C_PINS_18_19, I2C_PULLUP_EXT, 400000);
 
Interestingly I can only make it work using external pull-ups and I only get errors using any other method with internal pull-ups.

I'm happy enough that this is resolved!
 
On this page scoll down to the Pullups section on the readme file, this is where I read it, but HW bug did sound like the incorrect terminology to me! https://github.com/nox771/i2c_t3

Adding the pull ups seems to have fixed my issue! Thanks a lot.

The reason I thought I could get it working without pull-ups was because in the readme file linked above it said this should still work for a single device on i2c when I2C_PULLUP_INT is used in the line below, instead of I2C_PULLUP_EXT:

Code:
Wire.begin(I2C_SLAVE, 0x66, I2C_PINS_18_19, I2C_PULLUP_EXT, 400000);
I would imagine that README file was written when the author was using a AVR processor (like an Arduino Uno) where internal pull-ups do work. I wish there was a solder jumper on the ARM machines that you could enable a pull-up resistor without having to wire it up.
 
FWIW, I just tried this same experiment with the 'basic_slave' sketch on a T3.2 and the 'basic_master' on a T3.5, and it works fine with or without 2.2K pullups with:

Code:
Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_INT, 400000);

used in the 'master' sketch, and


Code:
 Wire.begin(I2C_SLAVE, 0x66, I2C_PINS_18_19, I2C_PULLUP_INT, 400000);

used in the 'slave' sketch.

Here's a photo of the setup:

IMG_7280.jpg

And scope traces for the 'INT_PULUP' and 'EXT_PULLUP'

I2CMasterToSlaveWithExtPullups.jpg
I2C_PULLUP_EXT

I2CMasterToSlaveWithIntPullups.jpg
I2C_PULLUP_INT

As can be seen from the scope plots, there doesn't seem to be any difference between the 'INT' and 'EXT' (with 2.2K pullups) cases. So, at least for this simple setup the 'I2C_PULLUP_INT' setting seems to work fine. YMMV ;-)
 
Last edited:
Back
Top