Playback is problemtic on STM and ESP32
peterhinch opened this issue · 10 comments
As a first step I am attempting blocking playback on these platforms, using your demo scripts and samples. I have two playback devices, both of which seem functional. Both are from Adafruit, a UDA1334A and a MAX98357.
On STM (a Pyboard 1.1) I cannot get the MAX to produce any output. The UDA produces a tone correctly, but music is garbled and barely recognisable.
On the ESP32 reference board I tested side-to-side-8k-16bits-stereo.wav and music-16k-16bits-stereo.wav. On the MAX both worked (as far as can be established in mono). On the UDA, side to side worked, but the tones differed between channels, one sounding a little distorted. Music output was white noise.
Thanks for taking the time to report your tests. I have some general ideas on getting your hardware to work.
I haven't tried a UDA1334 device but it should work. The datasheet indicates I2S support up to 24-bits (refer to section General Description
) If you're using a 32-bit audio file that could be a problem. Maybe, maybe not. Often, devices work past the stated spec. If you're using a 32-bit audio file I suggest switching to a 16-bit audio file and changing the associated I2S configuration inside the example file (SAMPLE_SIZE_IN_BITS = 16
)
For the Pyboard v1.1 and MAX98357: double-check your wiring, and pin assignments. It works. If in doubt use an oscilloscope at the I2S inputs to the breakout board to verify that the digital signals are making it to the device. I2S signals (SCK, WS, SD) have an obvious signature that are simple to verify.
Overall: when I2S yields poor quality sound it's almost always a wiring problem.
- I assume you are using some sort of breadboard. Is it relatively new with tight contacts? Well used breadboards have the potential to have compromised contacts which increases the contact resistance.
- Are quality wire jumpers being used? 22 AWG wire is best, with minimum wire length. It's always best to make your own custom jumpers with 22 AWG wire, versus using the often poor quality premade jumpers.
Hopefully something here will get your further along. To go deeper you'll need to report how you've connected the hardware and what I2S configuration settings you've selected in the example files.
It turns out I had bought an Adafruit UDA1334A breakout board but never used it. I tried it with an ESP32 SPIRAM device using the latest unstable download. Both the 16-bit and 32-bit (surprisingly) blocking examples play cleanly through iPhone earbuds.
As a first step, let's get this example and hardware working at your end, then move on to any other playback issues.
Here are the connections and example configuration I'd like you to try:
UDA pin | ESP32 pin |
---|---|
VIN | 3.3v output |
GND | GND |
WSEL | 25 |
DIN | 32 |
BCLK | 33 |
# ======= AUDIO CONFIGURATION =======
WAV_FILE = "music-16k-16bits-stereo.wav"
WAV_SAMPLE_SIZE_IN_BITS = 16
FORMAT = I2S.STEREO
SAMPLE_RATE_IN_HZ = 16000
# ======= AUDIO CONFIGURATION =======
# ======= I2S CONFIGURATION =======
SCK_PIN = 33
WS_PIN = 25
SD_PIN = 32
I2S_ID = 1
BUFFER_LENGTH_IN_BYTES = 40000
# ======= I2S CONFIGURATION =======
Does this work
Thanks for the pointer - that works! The only difference compared to my prior test with the UDA1344A is that previously I had Vin connected to 5V.
That seems surprising: Adafruit state supply can be between 3-5V. But it's repeatable: 5V gives random noise, 3.3V works. I've not managed to find a schematic but Adafruit do claim there's a low dropout regulator.
Do we have a rare example of an Adafruit product that doesn't work to spec?
I'll repeat my test on STM using 3.3V and report back.
Not so good on STM (Pyboard 1.1). I'm now powering the UDA1344A from 3.3V and using this config:
# ======= AUDIO CONFIGURATION =======
WAV_FILE = "music-16k-16bits-stereo.wav"
WAV_SAMPLE_SIZE_IN_BITS = 16
FORMAT = I2S.STEREO
SAMPLE_RATE_IN_HZ = 16000
# ======= AUDIO CONFIGURATION =======
# ======= I2S CONFIGURATION =======
SCK_PIN = 'Y9'
WS_PIN = 'Y4'
SD_PIN = 'X22'
I2S_ID = 2
BUFFER_LENGTH_IN_BYTES = 40000
As before, the music is recognisable but quiet (compared to the above good ESP32 result) and distorted.
This has the feel of something marginal but short of designing a PCB I'm not sure how to proceed. I've used similar connection techniques to drive displays with SPI clocks of up to 30MHz with no issues. And I find the 3.3V thing surprising too - any thoughts are welcome. FWIW my headphones are 250Ω Beyerdynamics so there are no heavy currents running around.
Overall: when I2S yields poor quality sound it's almost always a wiring problem.
I'll see if I can improve connections to a Pyboard over the weekend.
That seems surprising: Adafruit state supply can be between 3-5V. But it's repeatable: 5V gives random noise, 3.3V works. I've not managed to find a schematic but Adafruit do claim there's a low dropout regulator.
It's a mystery to me as well why it doesn't work with a 5V supply. After some digging I found a schematic at the end of the tutorial.
https://learn.adafruit.com/adafruit-i2s-stereo-decoder-uda1334a/downloads
This has the feel of something marginal but short of designing a PCB I'm not sure how to proceed.
With short wires (10 cm) and female-female jumpers + headers I've been able to get good results with both the Pyboard V1.1 and PyBoard D. But, I've had some problems as well -- when I've tried to make connections with long (20cm) male-male jumpers I sometimes get poor results. e.g. static, audio that cuts in and out. I added a new section which includes photos of the board connections that so far have yielded reliable audio results.
I find this puzzling. On the one hand I fully accept your I2S expertise. I also accept that the symptoms point to layout problems. It's an open loop system - the I2S firmware and demo scripts "know" nothing about what hardware is attached.
I'm prepared to design a PCB to fix this. But to do so really requires me to know the nature of the problem I'm trying to fix, and this is where I come adrift. Is it a problem with the digital interface between the host and the I2S board, or an analog audio problem?
The fact that failure can be complete (as in some of my tests with MAX98357) indicates a digital problem yet I've used SPI where it runs continuously at much higher speeds. These tests involved running various displays connected with similar wiring. I guess I need to study the I2S timing to see if there is a particular lack of timing tolerance.
Analog problems, especially with power and grounding, can always be an issue with audio. Yet I'm testing with headphones which measure 233Ω. Even with the MAX98357 the maximum peak current is 21mA. The UDA1344A is single ended and AC coupled (thanks for the pointer to the schematic). It runs from 3.3V so peak currents are even lower (~5mA/channel). Grounding problems might cause background noise but surely wouldn't stop it dead or transform music to random noise.
And the UDA1344A PSU observation has me beat.
I guess it has to be a digital issue. I could design a PCB using "best practice" on the digital, audio and PSU side. I feel confident that a PCB powered from a bench supply would work perfectly. If so, I'd be left with the uneasy feeling that I'd fixed a problem whose nature I don't actually understand, and that I'd learned nothing.
I'll have a poke around with the scope and see if I can figure out what's going on. I'd be grateful for any thoughts. Thanks for the great work on I2S, by the way!
This will come as no surprise but the waveforms look fine on my scope. The phase relationships are correct and the ws
signal has a 50% duty ratio (as close as my scope can measure).
I've studied the UDA1344A datasheet. As you probably know, the ws
signal drives a PLL which produces the system clocks. The datasheet has these caveats:
- The WS edge MUST fall on the negative edge of the BCK at all times for proper operation of the digital I/O data interface
- For LSB-justified formats it is important to have a WS signal with a duty factor of 50%.
These constraints are met. Their existence and the reference to a PLL makes me wonder if the use of ws
to generate internal clocks might be a cause of wiring sensitivity in the digital signals. Perhaps the PLL fails if the ws
edge suffers from ringing? If the internal clocks lose sync that might explain the drastic nature of some of the failure modes.
Does this make sense to you?
I'm prepared to design a PCB to fix this.
That might be the best next step, even if you can't figure out the root cause of the audio issues using your current hardware setup. For I2S testing I built up a test fixture using an Adafruit permaproto breadboard, female headers (to plug in the dev and breakout boards), and 22 AWG wires. These breadboards seem to be the next best thing to a custom PCB. All the point-to-point connections are soldered and short. I add quite a few test points (many Gnds, SCK, WS, SD) to make it easy to connect scope probes. I didn't detect any audio problems using this approach. Any problems always came from the C code I was writing. Some photos of the test fixture are here:
https://github.com/miketeachman/micropython-esp32-i2s-examples#hardware-test-setup
If you have a Pyboard V1.1 with female headers you might be able to design a permaproto plug-in board, where male pins are used to connect the Pyboard headers. The permaproto boards have plated vias which make this possible. The I2S devices would be on the top side.
Do you have a Pyboard D? It might be interesting to see if any different results come from that board. For me, the Pyboard D is easier to work with compared the Pyboard v1.1 mainly because I can use a breadboard with it. I find the Pyboard V1.1 somewhat challenging to connect to breakout boards. I bought the female header version which was likely a mistake.
These constraints are met. Their existence and the reference to a PLL makes me wonder if the use of
ws
to generate internal clocks might be a cause of wiring sensitivity in the digital signals. Perhaps the PLL fails if thews
edge suffers from ringing? If the internal clocks lose sync that might explain the drastic nature of some of the failure modes.Does this make sense to you?
It does. Something could be affecting the stability of the PLL. But what? Noise on the power supply rails?
I wish that I could offer better suggestions. This type of hardware debugging isn't a strong point for me.
Do you have a Pyboard D?
I have several. My idea for a PCB was to use a WBUS-DIP28 as this would enable me to power it from a Li-ion cell. Designing PCB's and getting them made is so easy and cheap these days. I remember paying a draughtsman to do the layout, getting the tape master photographed, getting the boards made (double sided but not PTH). Expensive, time consuming and crap compared to today's PTH boards with solder resist and silk screens. My last set of 3 cost under £10 delivered from Germany.
I find the Pyboard V1.1 somewhat challenging to connect to breakout boards.
It's not ideal, however this is the first time (in 7 years) that I've had problems using standard jumpers and breadboards. This with a wide variety of sensors and displays, which is why I was so surprised in this case.
This type of hardware debugging isn't a strong point for me.
It's tricky when the possible cause is on-chip. But the wiring sensitivity occurs on two different chips. It has the "flavour" of a marginal timing problem: I've seen similar sensitivity on SPI systems where the phase is wrong - it will work on a Sunday if there's an 'R' in the month. Fix the phase and all is well. So my guess remains that both chips derive timing from ws
and that long wires inject sufficient timing uncertainty to disrupt the internal clocks. This phrase from the UDA134A data seems pertinent:
Operates as an asynchronous DAC, regenerating the internal clock from the WS signal
PSU noise is a possibility but I'd expect powering from 5V to improve things with the UDA1344A because of the linear regulator on the board, whereas it made it fail. Your experience with using very short leads also seems to hint at timing issues - I'm still guessing at ringing which is a feature of long leads. Proving this would be difficult.
I think we've taken this as far as we can. I'll get a board made which I'm highly confident will work. My real interest is running and characterising this with uasyncio but I want reliable hardware as a starting point. I'll rattle your cage in the unlikely event of problems. Thanks again for your help.
Closing this as it works fine on a Pyboard D with a PCB.