Setup for level shifting i2c

Status
Not open for further replies.

MichaelMeissner

Senior Member+
I'm trying to setup i2c on my Teensy 3.0 that uses a TXS-0102 level shifter I got from DSS circuits (http://dsscircuits.com/index.php/i2c-level-converter). I'm using a 16x2 LCD as my test bed. The LCD works great, when I hook it up to my Teensy (which has 4.7k pull-up resistors on A4/A5), as long as I feed the VIN power to the LCD instead of the normal 3.3v that I use for other i2c devices. I've used both the standard Wire library in the 1.19 Teensy release, as well as version 6b of the Teensy 3.x improved library (i2c_t3, announced here: http://forum.pjrc.com/threads/21680-New-I2C-library-for-Teensy3)

So, I solder my first i2c level shifter and hook up the wires, and it works for about 5 minutes until I get garbage on the screen. I've tried both i2c libraries with the same result. I then figured, ok, maybe I have a solder bridge or similar, and I take my other i2c level converter I bought from DSS at the same time, and wired it up using solid core wire twisted to make contact. Again it works for a few minutes until the screen becomes garbage.

The DSS page says you don't need external pull-ups (but this is probably in reference to the Uno class processors), but as I said, I have 4.7K pull-ups on my i2c configuration. Do I need pull-ups on the 5v side of the level shifter? Or should I remove the resistors on the Teensy? Do I need extra capacitors?

The wiring is pretty simple:
  • Connect 3.3v to VccL and OE;
  • Connect SDA to 1L;
  • Connect SCL to 2L;
  • Connect VIN to VCCH;
  • Connect output SDA to 1H;
  • Connect output SCL to 1H;
  • Connect ground to GND.

I have a different i2c level shifter on order from ebay to try, but perhaps I'm missing som extra hardware to use in setting up the shfiter I have. I originally bought this level shifter for neopixels because it was claimed to be fast enough for the ws2812 leds, but if it can't keep up with 100Khz i2c, I'm wondering how it would keep up with 800Khz neopixels.

Alternatively, I've been wanting to move to Teensy 3.1's. Perhaps given it is 5v tolerant, I should just move there using 5v instead of 3.3v for the main power for my i2c devices. I imagine there are probably devices out there that might not work with 3.3v signalling and 5v power (much like the newer ws2812 leds).

My code is fairly vanilla, main code:

Code:
// Simple test of the i2c 16x2 lcd that originally came from digispark

#include <stddef.h>
#include <EEPROM.h>
#include <Meissner_Config.h>
#include <Meissner_Eeprom.h>

#if defined(PROCESSOR_ATTINY85)
#include <TinyWireM.h>

#else
#include <Wire.h>
#endif

#include <LiquidCrystal_I2C.h>

#ifndef PROCESSOR_ATTINY85
#define Serial_begin(BAUD)                Serial.begin (BAUD)
#define Serial_print(VALUE)                Serial.print (VALUE)
#define Serial_print2(VALUE, TYPE)   Serial.print (VALUE, TYPE)
#define Serial_println(VALUE)             Serial.println (VALUE)
#define Serial_flush()                          Serial.flush ()

#else
#define Serial_begin(BAUD)
#define Serial_print(VALUE)
#define Serial_print2(VALUE, TYPE)
#define Serial_println(VALUE)
#define Serial_flush()
#endif

LiquidCrystal_I2C lcd_16_2_display (I2C_LCD_CHAR_16_2, 16, 2);		// set address & 16 chars / 2 lines

const unsigned long lcd_16_2_delay_time = 3000UL;
const unsigned long lcd_16_2_delay_init   = 6000UL;
static unsigned int   lcd_16_2_count         = 1;
static int                 led_status                 = HIGH;

void
setup (void)
{
  Serial_begin (9600);
  Serial_println ("LCD 16x2 setup start");
  Serial_flush ();

  pinMode (PIN_LED, OUTPUT);
  digitalWrite (PIN_LED, HIGH);

  lcd_16_2_display.init ();
  lcd_16_2_display.backlight ();
  lcd_16_2_display.clear ();

  lcd_16_2_display.setCursor (0, 0);
  lcd_16_2_display.print ("Init. ");
  lcd_16_2_display.print (lcd_16_2_delay_init / 1000UL);
  lcd_16_2_display.print (".");
  lcd_16_2_display.print ((lcd_16_2_delay_init / 100UL) % 10UL);
  lcd_16_2_display.print (" secs");

  lcd_16_2_display.setCursor (0, 1);
  lcd_16_2_display.print ("Delay ");
  lcd_16_2_display.print (lcd_16_2_delay_time / 1000UL);
  lcd_16_2_display.print (".");
  lcd_16_2_display.print ((lcd_16_2_delay_time / 100UL) % 10UL);
  lcd_16_2_display.print (" secs");

  delay (lcd_16_2_delay_init);

  Serial_println ("LCD 16x2 setup end");
  Serial_flush ();
}

void
loop (void)
{
  Serial_print ("LCD 16x2 ");
  if (lcd_16_2_count < 10)
    Serial_print (" ");

  Serial_print ("#");
  Serial_println (lcd_16_2_count);
  Serial_flush ();

  led_status = (led_status == HIGH) ? LOW : HIGH;
  digitalWrite (PIN_LED, led_status);

  lcd_16_2_display.clear ();
  lcd_16_2_display.setCursor (0, 0);

  switch (lcd_16_2_count++ % 4)
    {
    default:
    case 0: lcd_16_2_display.print ("!@#$%^&*()[]{}=-");	break;
    case 1: lcd_16_2_display.print ("abcdefghijklmnop");	break;
    case 2: lcd_16_2_display.print ("ABCDEFGHIJKLMNOP");	break;
    case 3: lcd_16_2_display.print ("================");	break;
    }

  lcd_16_2_display.setCursor (0, 1);
  lcd_16_2_display.print ("0123456789012345");

  delay (lcd_16_2_delay_time);
}

My config header file:

Code:
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#ifndef MEISSNER_CONFIG_H
#define MEISSNER_CONFIG_H	1

#include <stddef.h>
#include <stdlib.h>
#include <inttypes.h>

#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"

#else
#include "WProgram.h"
#include "WConstants.h"
#endif


// Target machine conditionals:
#if defined(__arm__) && defined(CORE_TEENSY)
#define PROCESSOR_TEENSY_3_X	1		// run on Paul Stoffregen's ARM Cortex M4 based teensy 3.0/teensy 3.1

#ifdef __MK20DX128__
#define PROCESSOR_TEENSY_3_0	1
#endif

#ifdef __MK20DX256__
#define PROCESSOR_TEENSY_3_1	1
#endif

#elif defined(__AVR_ATmega328P__)
#define PROCESSOR_UNO_R3	1		// run on an Arduino Uno R3

#elif defined(__SAM3X8E__)
#define PROCESSOR_DUE		1		// run on Digistump's digiX (or Arduino Due)
#define PROCESSOR_DIGIX		1

#elif defined(__AVR_ATtiny85__)
#define PROCESSOR_ATTINY85	1		// run on ATtiny85 devices (trinket, gemma, digispark)
#define PROCESSOR_TRINKET	1
#define PROCESSOR_GEMMA		1
#define PROCESSOR_DIGISPARK	1
#define NO_SERIAL		1
#define NO_SERVO_H		1
#define NO_WIRE_H		1

#else
#define PROCESSOR_UNKNOWN	1		// something else
#endif


// Pins used on the different processors

// On board LED
#if defined(PROCESSOR_ATTINY85)
const uint8_t PIN_LED			= 1;
#else
const uint8_t PIN_LED			= LED_BUILTIN;
#endif

// I2C pins
#if defined(PROCESSOR_ATTINY85)
const uint8_t PIN_SDA			= 0;
const uint8_t PIN_SCL			= 2;

#else
const uint8_t PIN_SDA			= SDA;
const uint8_t PIN_SCL			= SCL;
#endif

// Pin for programming the NeoPixel
// Pin 1 has the bulitin LED on Trinket/Gemma/Digispark, and
// is the only pin that does not interfere i2c on the Gemma
#if defined(PROCESSOR_ATTINY85)
const uint8_t PIN_NEOPIXEL		= 1;

#else
const uint8_t PIN_NEOPIXEL		= 4;
#endif

// Possibly unused analog pin for setting random numbers
#if defined(PROCESSOR_TEENSY_3_X)
const uint8_t PIN_RANDOM		= A13;

#elif defined(PROCESSOR_ATTINY85)
const uint8_t PIN_RANDOM		= 2;

#else
const uint8_t PIN_RANDOM		= A3;
#endif

// Unused pin for the tindie 128x64 OLED display
#if defined(PROCESSOR_TEENSY_3_X)
const uint8_t PIN_UNUSED		= 29;

#else
const uint8_t PIN_UNUSED		= PIN_RANDOM;
#endif

// Whether a pin can be used for digital output
#if defined(PROCESSOR_TEENSY_3_X)
#define DIGITAL_OUTPUT_OK(PIN)		((PIN) < A10 || (PIN) > A14)
#define DIGITAL_INPUT_OK(PIN)		DIGITAL_OUTPUT_OK (PIN)
#define ANALOG_INPUT_OK(PIN)		((PIN) >= A0 && (PIN) <= A14)

#elif defined(PROCESSOR_UNO_R3)
#define DIGITAL_OUTPUT_OK(PIN)		((PIN) != A6 && (PIN) != A7)
#define DIGITAL_INPUT_OK(PIN)	       	DIGITAL_OUTPUT_OK (PIN)
#define ANALOG_INPUT_OK(PIN)		((PIN) >= A0 && (PIN) <= A7)

#elif defined(PROCESSOR_ATTINY85)
#define DIGITAL_OUTPUT_OK(PIN)		1
#define DIGITAL_INPUT_OK(PIN)		1
#define ANALOG_INPUT_OK(PIN)		((PIN) >= 2 && (PIN) <= 5)

#else
#define DIGITAL_OUTPUT_OK(PIN)		1
#define DIGITAL_INPUT_OK(PIN)		1
#define ANALOG_INPUT_OK(PIN)		1
#endif


// I2C addresses
const uint8_t I2C_MCP23008_BASE		= 0x20;		// MCP 23008 base (8 pin i2c expander)
const uint8_t I2C_MCP23017_BASE		= 0x20;		// MCP 23017 base (16 pin i2c expander)
const uint8_t I2C_LCD_CHAR_16_2		= 0x27;		// 16x2 character lcd in digispark lcd shield kit
const uint8_t I2C_DIGIOLE_128_64	= 0x27;		// Digiole 128x64 LCD
const uint8_t I2C_CAP1188_BASE		= 0x28;		// Adafruit Cap1188 capacitivie touch breakout board
const uint8_t I2C_OLED_128_64		= 0x3C;		// 128x64 OLED from tindie.com
const uint8_t I2C_PCF8591_BASE		= 0x48;		// PCF 8591 4 analog->digital, 1 digital->analog
const uint8_t I2C_CHRONODOT		= 0x68;		// Adafruit chronodot real time clock
const uint8_t I2C_LED_MATRIX_BASE	= 0x70;		// Adafruit 8x8 LED matrix backpack base
const uint8_t I2C_7SEGMENT		= 0x71;		// Sparkfun 7 segment display


// Reserved areas in EEPROM
// Once a field is allocated EEPROM space, never change the location
// Leave EEPROM 0..255 available to the program

// Record the max EEPROM available for each processor
#if defined(PROCESSOR_TEENSY_3_X)
const int16_t EEPROM_MAX		= 2048;

#elif defined(PROCESSOR_UNO_R3)
const int16_t EEPROM_MAX		= 1024;

#elif defined(__SAM3X8E__)
// Note, the Due does not have EEPROM, but the DigiX does, since I own DigiX, and not Due's use that
const int16_t EEPROM_MAX		= 4096;

#elif defined (PROCESSOR_ATTINY85)
const int16_t EEPROM_MAX		= 512;

#else
const int16_t EEPROM_MAX		= -1;
#endif

const int16_t EEPROM_FIRST_RESERVED	= 256;

// Saved elapsed time
struct elapsed_time {
  uint16_t	num_weeks;
  uint8_t	num_days;
  uint8_t	num_hours;
  uint8_t	num_minutes;
  uint8_t	num_seconds;
  uint16_t	num_micro;
};

const int16_t EEPROM_IDENT_LENGTH	= 16;				// # ident characters to store
const int16_t EEPROM_DATE_LENGTH	= sizeof ("mmm dd yyyy");	// hold __DATE__
const int16_t EEPROM_TIME_LENGTH	= sizeof ("hh:mm:ss");		// hold __TIME__

enum eeprom_fields {
  // Current version/length of the eeprom information
  EEPROM_CUR_VERSION			= EEPROM_FIRST_RESERVED,
  EEPROM_CUR_LENGTH,

  // Save processor type/sub-type
  EEPROM_PROCESSOR,
  EEPROM_SUB_PROCESSOR,

  // unsigned 32-bit integer to hold 32 dip-switches
  EEPROM_DIP_SWITCH_0_7,
  EEPROM_DIP_SWITCH_8_15,
  EEPROM_DIP_SWITCH_16_23,
  EEPROM_DIP_SWITCH_24_31,

  // 8 unsigned 16-bit integers to hold saved analog values
  EEPROM_UINT16_0_LO,
  EEPROM_UINT16_0_HI,
  EEPROM_UINT16_1_LO,
  EEPROM_UINT16_1_HI,
  EEPROM_UINT16_2_LO,
  EEPROM_UINT16_2_HI,
  EEPROM_UINT16_3_LO,
  EEPROM_UINT16_3_HI,
  EEPROM_UINT16_4_LO,
  EEPROM_UINT16_4_HI,
  EEPROM_UINT16_5_LO,
  EEPROM_UINT16_5_HI,
  EEPROM_UINT16_6_LO,
  EEPROM_UINT16_6_HI,
  EEPROM_UINT16_7_LO,
  EEPROM_UINT16_7_HI,

  // 1 unsigned 16-bit integer to hold the random state between runs
  EEPROM_RANDOM_LO,
  EEPROM_RANDOM_HI,

  // 16 overrides to default pin locations
  EEPROM_PIN_LED,					// pin to use for led, default to PIN_LED
  EEPROM_PIN_RANDOM,					// pin to use for seeing random, default to PIN_RANDOM
  EEPROM_PIN_NEOPIXEL,					// pin to use for neopixels, default to PIN_NEOPIXEL
  EEPROM_PIN_UNUSED,					// unused pin for tindie OLED display
  EEPROM_PIN_RESERVED4,					// reserve space for more pins
  EEPROM_PIN_RESERVED5,
  EEPROM_PIN_RESERVED6,
  EEPROM_PIN_RESERVED7,
  EEPROM_PIN_RESERVED8,
  EEPROM_PIN_RESERVED9,
  EEPROM_PIN_RESERVED10,
  EEPROM_PIN_RESERVED11,
  EEPROM_PIN_RESERVED12,
  EEPROM_PIN_RESERVED13,
  EEPROM_PIN_RESERVED14,
  EEPROM_PIN_RESERVED15,
  EEPROM_PIN_RESERVED16,

  // 8 bytes to hold elapsed time
  EEPROM_ELAPSED,

  // 16 bytes to hold a null terminated ID string
  EEPROM_IDENT				= EEPROM_ELAPSED + sizeof (struct elapsed_time),

  // Hold __DATE__ from build to allow sketches to check if this is the first run after being programmed
  EEPROM_DATE				= EEPROM_IDENT + EEPROM_IDENT_LENGTH,

  // Hold __TIME__ from build to allow sketches to check if this is the first run after being programmed
  EEPROM_TIME				= EEPROM_DATE + EEPROM_DATE_LENGTH,

  // last byte
  EEPROM_LAST_RESERVED			= EEPROM_TIME + EEPROM_TIME_LENGTH
};

// Return value if the pin is not set (eeprom default value)
const uint8_t NO_PIN			= 0xff;

// Change version # if we ever add/subtract fields in the middle
const uint8_t EEPROM_VERSION		= 1;
const uint8_t EEPROM_LENGTH		= (((int)EEPROM_LAST_RESERVED) - (int)EEPROM_FIRST_RESERVED);


#endif	/* MEISSNER_CONFIG_H */
 
Last edited:
Do I need pull-ups on the 5v side of the level shifter?

I would say yes. I don't have much evidence for you, but the few times I've used level translation to go from a Uno at 5 V to a sensor at 3.3 V I had pull-ups on the sensor breakout board and everything worked as expected. Sorry I can't be of more help.
 
Page 10 of the spec sheet for the Chip used on the DSS-Cirquits level shifter states this:

Pullup or Pulldown Resistors on I/O Lines
Each A-port I/O has an internal 10-kΩ pullup resistor to VCCA, and each B-port I/O has an internal 10-kΩ pullup resistor to VCCB. If a smaller value of pullup resistor is required, an external resistor must be added from the I/O to VCCA or VCCB (in parallel with the internal 10-kΩ resistors). Adding lower value pull-up resistors will effect VOL levels, however. The internal pull-ups of the TXS0102 are disabled when the OE pin is low.

So the 4.7k eternal pull-ups are not needed on either the 3.3V or 5V side. Not 100KHz anyway.
I don't see a schematic on the DSS-Circuits website but I believe there are small caps (0.1uF) on each voltage rail on the board.

If you have an oscilloscope, now would be the time to check out the signal.
 
Last edited:
I don't know what to say now. I rewired the DSS shifter that I had set aside, and put in 2 4.7K pull-up resistors (this was on a breadboard, not soldered), and it worked, displaying the output for about 2 hours. I knocked the Teensy and lcd off the desk, and the pull-up resistors fell out, and the lcd continued going. Now perhaps where I had the Teensy before had more electrical noise (it was next to a ethernet hub), and it affected the DSS shifter somewhat. I dunno, it now seems to work.

I don't have an oscilloscope.

I do know the Teensy does need the 4.7K pull-ups for normal i2c operation.
 
Status
Not open for further replies.
Back
Top