I2C problems with Teensy 3.6 and WM8731 codec

Status
Not open for further replies.

DerekR

Well-known member
For the past week or so I have been trying unsuccessfully to use a WM8731 codec with a T3.6. Others are having the same problem and it is actively being discussed in another thread. I have started this new thread because I believe the issue might be more general than just communication with this particular chip.

I have built a AudioBoard compatible plugin board with a WM8731 mounted on an AdaFruit 28 pin TSSOP breakout board. The circuit is basically the same as the Mikroe WM8731 board, but uses only LINEIN and LINEOUT. Here's the thing, it works great with a T3.2 but fails absolutely on a T3.6. The problem is obviously in the I2C communication to load the 8731 registers. The I2C code reports a NACK error on every write, regardless of I2C rate, CPU frequency, or value of the SDA and SCLK pullup resistors (from 1k to 10k). Here are a couple of sample programs that work with the 3.2 but not the 3,6. First, the Hardware example code from Teensy WM8731PassThroughStereo.ino:
Code:
/*
 * A simple hardware test which receives audio from the audio shield
 * Line-In pins and send it to the Line-Out pins and headphone jack.
 *
 * This example code is in the public domain.
 */
//*** Works on T3.2 but not 3.6 ***

#include <Audio.h>

AudioInputI2S       I2Sin;
AudioOutputI2S      I2Sout;
AudioConnection     c1(I2Sin, 0, I2Sout, 0);
AudioConnection     c2(I2Sin, 1, I2Sout, 1);
AudioControlWM8731  codec;

void setup(){
  AudioMemory(12);
  codec.enable();
  codec.inputSelect(AUDIO_INPUT_LINEIN);
  codec.inputLevel(1.0);
}

void loop(){}
It works just great on the T3.2, the output reflects the input with a 12dB gain. A nice, clean output. This example fails completely with the 3.6.

So I went back to basics with the Wire library, and wrote the smallest code I could that would illustrate what is going on. Here it is, it simply loops withe a beginTransmission()- endTransmission() loop,
Code:
#include <Wire.h>

void setup() {
  Wire.begin();
}

void loop() {
 Wire.beginTransmission(0x1A);
 Wire.endTransmission(true);
 delayMicroseconds(200);
}

Note 0x1A is the address of the WM8731. I then looked at the SDA and SCL lines on my mixed-mode 'scope. Here's what I got on the T3.2,
Teensy_3.2.jpg
If you decode the packet, the addres byte is 0011|0100 = 0x34 which is 0x1A<<1 which is correct, and then the 9th bit (R/W) is 0 which indicates a write from the master, which is also correct. The short blip at the end is the ACK from the WM8731. So all is good in T3.2 land.

If we simply transfer the codec (and code) to a T3.6 board, we get a different story.

Teensy_3.6.jpg
Here I stored the T3,2 SDA waveform from above as a reference, and it is shown in blue for comparison. The 3.6's SDA is yellow, and the SCL is again pink. If you decode the SDA it is 0x34 as it should be, BUT note that the 9th bit is now a 1, and there is no ACK blip from the codec! Where did that high R/W bit suddenly appear from? Something is wrong here, and it is no wonder we are not getting ACKs, indicates the WM8731 is not awake and listening for data. Sure enough, if I include data bytes as part of the packet I don't see ACKs for any of them.

I have tested this on three T3.6's with the same result.

In addition I tried running the T3.6 with a bit-banged I2C library and it worked. I don't have screenshots to show this, but the R/W bit was low.
My conjecture is that the inverted R/W bit in the Wire.beginTransmission() is probably causing the problem.

Could it be that the 3.6 is using WireKinetis.h and WireKinetis.cpp and the 3.2 is using Wire.cpp and Wire.h? What makes me really puzzled is that I use the Wire library to talk to other I2C devices on T3.6's, and surely such a potentially serious bug would have shown up by now??? Is the WM8731 a particularly "picky" device?
 
DerekR,

Just to confirm, even if you use the I2C bus with no I2S elements in the code, you still see errors? I found I could eliminate the I2C errors by removing I2S entirely from the build.

Just for reference, I first posted about I2C issues I experienced with the WM8731 here. I only saw the issue on the T3.6, I was never able to produce it on the T3.2 with the WM8731. I tried very hard to reproduce the issue with the Teensy Audio Board (featuring SGTL5000) and could not. I did not have a T3.5 to test.

I thoroughly analyzed the failed transactions with a scope and there appeared to be no reason for the WM8731 to NACK. In the end, I suspected that the WM8731 is very sensitive to something like the slew rate of the SCL signal, or was getting an internal setup or hold time violation, possiblly some internal cross talk. As a result, the combination of Teensy 3.6 with WM8731 had issues that simply would not appear when the Teensy was paired with a SGTL5000 under similar test conditions.

My final solution was to add BAAudioControlWM8731 to the BALibrary which included retransmission on error which at least ensured the coded could be controlled effectively. Not ideal, but has been effective.

For me, since the problem went away if the I2S was disabled (or not included), this might explain why you don't see I2C issues with other I2C non-codec devices. In those situations you woudn't be running the I2S unless it's a codec. Either way, it's still very likely the WM8731 is just very picky as DerekR suggested. But the I2C/I2S issue could also be a problem on the WM7831 rather than having anything to do with the Teensy or it's libraries.
 
But the little Wire sketch has no I2S... By the way I think you did a nice job with BALibrary. I will certainly use it when I get this issue resolved.
 
I remember a long time ago there where problems with the Teensy booting too fast for some I2C chips. I don't think that this is the problem here, and it is unlikely.., but who knows.. maybe try to add a delay(500) as first line in setup()?
 
I'm off to add 220R resistors right now, more later... Incidentally, I noticed the other day that the T3.6 worked for a few hours IF (and only IF) I had the 'scope probes connected to the SCL and SDA lines! Then that just stopped working... Semms that the chip is real flaky.
 
Yes I can't look now, too - maybe the slow slew rate is already enabled on the pins - but even when - it is still much too fast for i2c and perhaps this is part of the problem. If you try the resistors: they need to be close to the teensy.
 
Fantastic! Adding the two 220R resistors on the SDA and SCL lines fixed the problem, and the WM8731 is now working on both the T3.6 and T3.2s without errors. It is working with the Audio library AudioControlWM8731 as well. The only concern I have is a suspicion that it may be interfering with other devices on the I2C bus, and I'll be looking into that tomorrow.

Here is a screenshot of the I2C lines with a beginTransmission() packet as I showed in the original post, but this time with the resistors installed. The T3.2 and T3.6 are now identical.

T3.6_with_resistors.jpg

Note that the ACK bit is now being brought low by the slave (WM8731). The 1.2k and 220R resistors form a voltage divider that prevent I2C lines being taken to zero. This effect could be significantly reduced by using a higher value pull-up. For now however, it's working.

Here's a photo of the prototype Audio Board plug-in compatible WMt8731 board with the chip on it's breakout board. The chip plugs in to the female headers on the main board, which in turn plugs into a female header on the Teensy. The ribbon cable is for the LINEIN and LINEOUT connections.

codec_board.jpg
 
Paul please correct me - the series resistors work as termination-resistors (I hope this translation is right?) and are needed because of the fast slew rate? There seem to be reflections. (and the WM-chip can not handle this)
The I2C specs say 1microsecond (for 100KHz), the Teensy has a factor 10 - 100 faster rate.
Beeing a analog-noob - I got that right?
If so, would it be a good idea to add them for other "slow" i2c-devices , too - as a general rule?
 
Frank, I think, it's easier to see and to understand that the series resistors form a kind of RC low pass filter together with the following bus and slave input capacitance which limits the slew rate.
 
My best guess at this point (before those 2 extra MikroE-506s arrive) is the low-pass filter, pretty much what Theremingenieur said.

Not planning to actually do anything on this until I have that hardware here for testing.
 
Thanks :) Is there any way to calculate this "lowpass" ? or a rule-of-thumb? Isn't the capacitance very low?
 
AKAIK, there is no rule of thumb. Too many variables like parasitic inductance which can but might not compensate parasitic capacitance, etc, etc.

The proceeding would be, using a 1:10 oscilloscope probe because it has lower capacitance than a 1:1, Decreasing the pull-up resistors first, until the seen slew rate is at its maximum and won't increase further (optimal termination). Afterwards, if the slew rate is too high or if there is even ringing on the signal, increase the series resistors until you obtain the desired slew rate.
 
An interesting thread, but aren't you decoding the serial bits incorrectly?

Code:
If you decode the packet, the addres byte is 0011|0100 = 0x34 which is 0x1A<<1 which is correct,
and then the 9th bit (R/W) is 0 which indicates a write from the master, which is also correct.
The short blip at the end is the ACK from the WM8731

There is a 7 bit address which is shifted left one bit, the 8th bit is the R/W bit. The 9th bit is the ACK/NACK bit. And the blip is just a blip that happens when the slave released the ACK and the master then drives the data line low to implement the stop condition on the bus.
 
You are absolutely correct. Apologies to all - I can't count...:D The Wire library data packets sent from the master (Teensy) were correct all along, and it was the codec's ACK/NACK bit that was not being set.
Unfortunately I don't seem to be able to edit the original post.
 
I thought we were out of the woods, but as I hinted last night there are problems still with the added resistors when another device is added to the I2C bus!

In my case when I added an Adafruit SI5131 board (SI5131A clock generator breakout) to the bus, the NACK errors were back and observable on the 'scope, and the system freezes again. Unplug the SI5131 and we're good to go again... This is on hardware that I've run for 2+ years with the SGTL5000 without any problems, and indeed when I replaced the WM8731 with the Teensy Audio Board today everything ran fine once again.

I thought this was going to be a quick investigation :( I'm debating whether it's worth trying the 12c_t3 library and running the two devices on separate I2C buses or just call it quits at this point....
 
I guess that's the point were the "low pass" view does not fit anymore, a real low pass would work with two devices..
I think this type of termination works with single-ended connections only. But there are other ways for termination.

But I think the most reliable way would be to use a I2C buffer-chip, in this case.
 
Something subtle must be happening. Looking at the two first scope pictures, I see a little bit of undershoot on the SDA line that looks just a little bit worse with the Teensy 3.6. I would try two things.

Expand the time base of the scope to the 100ns range and take a good look at the rising and falling edges of the Data and Clock lines.
Remove the series resistors and hang a 100pf cap to ground on both the Data and Clock lines and see if that makes things better or worse.
 
It seems normal to me that, if you add a device to the bus, the bus impedance (be it capacitive, inductive, or resistive) changes and the termination and source impedance have naturally to be adapted. That’s pretty much the daily bread of every RF/radio engineer. Unfortunately, in our more and more digital and plug-n-play world, this way of thinking and the knowledge are about getting lost
 
I've read that a simple series resistor does only work for point-to-point connections. If that's correct an other type of termination is needed. There are several, all with different pros/cons.. so adding a buffer might be the most reliabel/flexible solution - and you can use i2c in the intended way - as a bus ( with the option to add more devices later without fiddling with resistors, capacitors and a scope)
 
I've read that a simple series resistor does only work for point-to-point connections.
That's wrong. It's all the time about impedance matching between the source, the transmission line, and the destination. That can work with series resistors under certain conditions.
If that's correct an other type of termination is needed. There are several, all with different pros/cons.. so adding a buffer might be the most reliabel/flexible solution - and you can use i2c in the intended way - as a bus ( with the option to add more devices later without fiddling with resistors, capacitors and a scope)
In fact, if you do not want to make the effort to look at such things with your analog RF eyes, minimizing the source impedance by using a buffer might be the easiest solution. But where is the academic challenge, then? ;)
 
lol, of course it's fun. but it requires fiddling as soon you change a little detail..
Maybe I play with my cs42448 and use longer wires to see if I can reproduce the problem. Have to read the datasheet first, if it is a "slow" I2C device, too... It would be a chance to use my new o'scope :)
 
Status
Not open for further replies.
Back
Top