Firstly, the two pages in particular that inspired this project are:
Before reading these, I didn't really know what i2s was, so it would never have occurred to me that there was anything resembling a standard digital audio interface in there.
Anyway, after some research, it seems the IC to use for this task is a WM8805 (or WM8804); and before potentially breaking an Echo Dot, testing this on an RPi as a I2S source first felt like a good idea.
Things I've learned (which are no doubt obvious to many);
- i2s essentially comes in 3 and 4 wire varieties. The MCLK line for i2s is required by some devices, but not by others. Also, different devices can have timing constants for the relationship between MCLK and the BCLK. Specifically, the WM8805 requires MCLK, the Raspberry Pi doesn't supply it, and the WM8805 doesn't like the MLCK from an Echo Dot.
- i2s devices can operate in master or slave mode, which is independent of which way the audio is going; the master device generates LRCLK/BCLK/MCLK
- The HIFI DiGi+ boards (S/PDIF hat for the Pi) - which are based on the WM8804 - looked very promising, as they take the 3-wire i2s from the Pi and output S/PDIF - exactly what I want. Except... the WM8804 is operating in master mode, generating LRCLK/BCLK/MCLK (with MCLK going nowhere as the Pi doesn't need it). The Echo is generating LRCLK/BCLK/MCLK, so the WM8805 needs to operate in slave mode, and so will require MCLK meaning the DiGi+ board can't be used. How annoying.
- MCLK needs to be synchronised to BCLK/LRCLK. Generating a clock that's the right frequency but not synchronised kinda works (frustratingly close to ok), but results in crackling audio. The WM8805 has an inbuilt PLL which can be configured to generate the right frequency, but the input must come from either the S/PDIF input (not used) or a crystal, so it was never synchronised to BCLK.
So, the solution I've gone for is to use a CDCE906 PLL to generate MCLK from BCLK, and an atmega328p (no prizes for guessing why) to configure it and the WM8805. There is almost certainly a simpler/cheaper way to multiply the frequency, but this was easy.
This approach seems to work well with both the Raspberry Pi i2s output, and the Echo Dot (with a minor code change when moving from RPi to Echo).
Schematics, PCB layout and code on gitlab: https://gitlab.com/dswann/3wire-i2s-to-spdif
The atmega328p is programmed using the Arduino IDE, naturally, but I've not managed to get the serial programming working (don't think it's auto re-setting and I didn't add a reset button). Not sure if that's a design or assembly issue yet. Programming it using the ISP header works, as does serial output of debugging info, so whatever.
The PCB has a selectable MCLK input - either from the i2s header, or generated by the CDCE906 from BCLK.
As the CDCE906 allows for two clock inputs, I've fed one BCLK and the other CLKOUT from the WM8805. The second is not used and pointless; using it to generate MCLK results in noisy/crackling audio as it won't be synchronised to BCLK. I added it mostly so I could confirm that MCLK really does need to be in sync with BCLK - my earlier test with it unsynchronised was close enough to working that I did wonder if the crackling audio was down to my especially poor PCB layout. As the sketch never selects that clock input, it should be harmless though. With hindsight, using the MCLK on the i2s header would probably have been a smarter choice for the second clock input.
Whilst the digital audio output works well, it's physically a mess. The board is designed to fit in the Echo, but I've not figured out where/how to mount it yet.
Also, there's still the issue where the only audio output is now via S/PDIF - which isn't so great when the amplifier it's connected to is off....