rsta2 / circle

A C++ bare metal environment for Raspberry Pi with USB (32 and 64 bit)

Home Page:https://circle-rpi.readthedocs.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Digilent pmod i2s2 distorted audio

nickverlinden opened this issue · comments

I've hooked up (this) i2s module (currently testing output only), but i'm getting distorted audio. I've tested this using my own audio sample playback code where you can hear the sample but sounds like there is some distortion and a lot of noise (which plays back fine when using sndpwm), but also with sample 34-sounddevices where i hear what sounds like a modulated square wave instead of a modulated sine wave.

I hooked up the module using these pins (schema):
Pi 3.3v -> 3_3V
Pi GND -> GND
Pi GPIO18 -> ADOUT MCLK
Pi GPIO19 -> ADOUT LRCK
Pi GPIO21 -> ADOUT SDIN

Any ideas?
If i connect GPIO18 to ADOUT SCLK instead of ADOUT MCLK, i get nothing.
I also played around in sample 34-sounddevices with the SAMPLE_RATE and WRITE_FORMAT, but no significant difference.
Also just tested this with a PCM5102A, and works without problems.

Many thanks!

The standard Circle I2S driver is not compatible with this device and I think it needs the following modifications:

  • The master clock (MCLK) must be 4 times greater than the serial bit clock (SCLK), which is normally provided by the driver. That is 384 times the sample rate.

  • The width of two channels must be exactly 48 bits. Normally the driver uses two 32 bits (total 64 bits) channels with 24 bits data each. This is not supported here.

These modifications should be applied by the following patch, which is based on Circle 44.5 from master branch:

diff --git a/lib/i2ssoundbasedevice.cpp b/lib/i2ssoundbasedevice.cpp
index ab6194e..674eec7 100644
--- a/lib/i2ssoundbasedevice.cpp
+++ b/lib/i2ssoundbasedevice.cpp
@@ -35,7 +35,7 @@
 #include <assert.h>
 
 #define CHANS			2			// 2 I2S stereo channels
-#define CHANLEN			32			// width of a channel slot in bits
+#define CHANLEN			24			// width of a channel slot in bits
 
 //
 // PCM / I2S registers
@@ -114,11 +114,10 @@ CI2SSoundBaseDevice::CI2SSoundBaseDevice (CInterruptSystem *pInterrupt,
 		unsigned nClockFreq =
 			CMachineInfo::Get ()->GetGPIOClockSourceRate (GPIOClockSourcePLLD);
 		assert (nClockFreq > 0);
-		assert (8000 <= nSampleRate && nSampleRate <= 192000);
-		assert (nClockFreq % (CHANLEN*CHANS) == 0);
-		unsigned nDivI = nClockFreq / (CHANLEN*CHANS) / nSampleRate;
-		unsigned nTemp = nClockFreq / (CHANLEN*CHANS) % nSampleRate;
-		unsigned nDivF = (nTemp * 4096 + nSampleRate/2) / nSampleRate;
+		assert (32000 <= nSampleRate && nSampleRate <= 48000);
+		double fDiv = nClockFreq / 384.0 / nSampleRate;
+		unsigned nDivI = fDiv;
+		unsigned nDivF = (fDiv - nDivI) * 4096.0 + 0.5;
 		assert (nDivF <= 4096);
 		if (nDivF > 4095)
 		{
@@ -304,11 +303,11 @@ void CI2SSoundBaseDevice::RunI2S (void)
 	// enable channel 1 and 2
 	write32 (ARM_PCM_TXC_A,   TXC_A_CH1WEX
 				| TXC_A_CH1EN
-				| (1 << TXC_A_CH1POS__SHIFT)
+				| (0 << TXC_A_CH1POS__SHIFT)
 				| (0 << TXC_A_CH1WID__SHIFT)
 				| TXC_A_CH2WEX
 				| TXC_A_CH2EN
-				| ((CHANLEN+1) << TXC_A_CH2POS__SHIFT)
+				| (CHANLEN << TXC_A_CH2POS__SHIFT)
 				| (0 << TXC_A_CH2WID__SHIFT));
 
 	write32 (ARM_PCM_RXC_A,   RXC_A_CH1WEX

These modifications are for I2S output only. You can use standard sample rates of 44100 and 48000 Hz, not more because the clock generator with MASH can generate a maximum frequency of 25 MHz.

I wasn't able to test this without hardware, of course. If it doesn't work, we have to rethink this.

I tested these modifications on the hardware, but doesn't work as expected.
What i found when trying sample 34-sounddevice:

Changing CHANLEN, clock speed, and Write in RunI2S:
No sound

only changing clock speed:
440hz tone sounds much higher pitched, and distorted

only changing CHANLEN, Write in RunI2S:
No Sound

No changes:
440hz tone pitch sounds correct, but distorted

This makes me think the clock is fine. Is it perhaps the order of MSB and LSB that are different? I seem to recal having similar distortion in an old esp32 project that had the wrong i2s format settings.

According to this datasheet pg. 119 the bit order is MSB to LSB in the Raspberry Pi I2S peripheral, which is the same as noted in the Pmod I2S2 Reference Manual, you have referenced above.

Unfortunately I think, my assumptions were wrong, we cannot generate MCLK with the I2S peripheral in the Raspberry Pi, because the serial bit clock is not only presented via the GPIO pin, but is also used to shift the data in the peripheral. I think we need a second separate GPIO clock for this purpose. I do not know, if this will work, because this second clock wouldn't be synchronized with the SCLK signal and the data, coming from the I2S peripheral, but we could try. Let me think about this until tomorrow.

Thx Rene, but if it's too much trouble for the implementation then let's move on. I just need to find a reasonable high quality i2s DAC + ADC for my sampler project. If this is too difficult/exotic to get working, I'd rather put my efforts in getting the audio injector octo running. Two years ago when I started working with Circle I got it to work in 2ch mode using some existing code for a vGuitar rig made by Patrick on Hackaday. If I want to use all of the 8ch out and 6ch in, i'll have to try and implement TDM in Circle, which i'm afraid is above my capabilities but I can try. Patrick ported Paul Stofregen's Teensy Audio Library to Circle, and that had TDM support, and believe he got it to work with Circle. Maybe i can try to reverse engineer/hack it into Circle.

OK, no problem. I think continuing here would only be of interest for Circle, if there would be many potential users of the Pmod I2S2 on the Raspberry Pi, but I did a search in the forums and found only one user. Good luck for your plans with the Teensy Audio Library port, and thank you for using Circle!