Some I2C observations on T3.2 vs T.36 with Wire.h and i2c_t3.h

Status
Not open for further replies.

Blackaddr

Well-known member
I've been working on improving my BAGuitar library, and doing additional testing. I had some odd results I didn't expect. Perhaps someone might have some insight.

I'm using teensyduinio 1.40.

FIRST ISSUE:

I use the default I2C configuration to configure my codec. I noticed that on my T3.2, I can send transactions to the I2C and never see a failure. If I switch to my T3.6, I see random failures where Wire.endTransmission() returns errors. The errors on the T3.6 occur even at 10Khz. As a work around, I modified my write command to loop and keep retrying until it succeeds.

It's almost like the library when operating on the T3.6 is not checking a done flag correctly on the peripheral and corrupts a transfer that is in progress. This happens both on the default Wire.h library and the i2c_t3.h library.

Code:
// Without this loop, failures occur on T3.6, but never on T3.2
bool BAAudioControlWM8731::write(unsigned int reg, unsigned int val)
{
	bool done = false;

	while (!done) {
		Wire.beginTransmission(WM8731_I2C_ADDR);
		Wire.write((reg << 1) | ((val >> 8) & 1));
		Wire.write(val & 0xFF);
		if (byte error = Wire.endTransmission() ) {
			Serial.println(String("Wire::Error: ") + error + String(" retrying..."));
		} else {
			done = true;
			Serial.println("Wire::SUCCESS!");
		}
	}

	return true;
}

SECOND ISSUE:

I was calling Wire.begin() in a class constructor that was being declared as a global, so the class would be constructed before the sketch setup() gets called. With the default Wire.h library, things generally work fine. However, when I switch to the i2c_t3.h, it would hang the system before ever getting to setup(). I had to move the call to Wire.begin() out of the constructor and into a member function I call in setup().

I presume there is something about the i2c_t3 library that when setting up the peripheral, it is not yet ready and a hang occurs. Is calling begin() on the peripherals in a global instance constructor generally a bad idea?

Code:
#include "i2c_t3.h"
MyClass object;  // if Wire.begin() is called inside MyClass::MyClass() constructor, this will hang

setup() {
  object.begin(); // call Wire.begin() here instead and it works fine
}
 
for issue number #1, does this happen if you set t3.6 to 24mhz?

Good call! I'm so used to running my Teensy at full speed I forget it has a gas pedal. At 96 MHz, the problem still happens occasionally (say 1 in 10 transfers). At 72 Mhz and below it seems to have no issues, al least over sample of say 50 transfers.
 
I'm trying to reproduce this problem. Since I don't have your hardware, I'm running a Teensy 3.6 with the audio shield. I have a 25K pot added to the shield. The audio shield has a SD card with the 4 test WAV files. I've got all setting at defaults, clock is 180 MHz.

Here's the code I'm running, adapted from the WavFilePlayer example. It repetitively reads the pot and updates the SGTL5000 volume. If the Wire lib give an error, it will print the error code.

Code:
// https://forum.pjrc.com/threads/47931
#include <Audio.h>
#include <Wire.h>
#include <SD.h>

AudioPlaySdWav           playWav1;
AudioOutputI2S           audioOutput;
AudioConnection          patchCord1(playWav1, 0, audioOutput, 0);
AudioConnection          patchCord2(playWav1, 1, audioOutput, 1);
AudioControlSGTL5000     sgtl5000_1;

// Use these with the Teensy Audio Shield
#define SDCARD_CS_PIN    10
#define SDCARD_MOSI_PIN  7
#define SDCARD_SCK_PIN   14

unsigned int writeCount=0;

// set the volume, and print info if Wire lib gives any error
void setVolume(float vol) {
  int n = vol * 127.0 + 0.4999;
  if (n < 0) n = 0;
  if (n > 127) n = 127;
  n = 127 - n;
  Wire.beginTransmission(0x0A);
  Wire.write(0);
  Wire.write(0x22); // CHIP_ANA_HP_CTRL
  Wire.write(n);
  Wire.write(n);
  int r = Wire.endTransmission();
  if (r != 0) {
    Serial.println("Wire write error, r=");
    Serial.println(r);
  }
  if ((++writeCount % 50000) == 0) {
    Serial.print(writeCount);
    Serial.println(" writes performed");
  }
}

void setup() {
  Serial.begin(9600);
  AudioMemory(8);
  sgtl5000_1.enable();
  sgtl5000_1.volume(0.5);
  SPI.setMOSI(SDCARD_MOSI_PIN);
  SPI.setSCK(SDCARD_SCK_PIN);
  if (!(SD.begin(SDCARD_CS_PIN))) {
    // stop here, but print a message repetitively
    while (1) {
      Serial.println("Unable to access the SD card");
      delay(500);
    }
  }
}

void playFile(const char *filename)
{
  Serial.print("Playing file: ");
  Serial.println(filename);
  playWav1.play(filename);
  delay(5);
  while (playWav1.isPlaying()) {
    float vol = analogRead(15);
    vol = vol / 1024;
    //sgtl5000_1.volume(vol);
    setVolume(vol);
  }
}

void loop() {
  playFile("SDTEST1.WAV");  // filenames are always uppercase 8.3 format
  delay(500);
  playFile("SDTEST2.WAV");
  delay(500);
  playFile("SDTEST3.WAV");
  delay(500);
  playFile("SDTEST4.WAV");
  delay(1500);
}

Here's the result I see in the serial monitor:

sc.png

After more than one million I2C writes without any error, I'm going to say I can't reproduce the problem. Well, at least not with the audio shield and this code. I intentionally chose the WAV file player example to have the audio lib and SPI running during this test. Not sure what else I should try.
 
Hi Paul,

I have recently picked up an Audio Shield to compare against my own board for issues just like this. I'll try to reproduce the problem with that. If I cannot, it must be either in my board or in my library. I appreciate you taking some time to investigate.

If I can reproduce on the Audio Shield board, I'll post some code.
 
Status
Not open for further replies.
Back
Top