I2S n00b woes.. #430
-
Hi.. Trying to get "Simpletone" working with a UDA1334A board. I had to change the frequency up to 16000, as that is the minimum for this DAC, apart from that, no other changes. But which outputs on the Pico are the defaults for this sketch, and how to change them? Would it be like " I2S.setBCLK();" and " I2S.setDOUT();" with LRCLK being BCLK+1? That has not worked for me (so far).. I know my DAC is fine as it works with the raspi4 very well, pretty much out of the box. Also /i would like/ to get this running 24 bit/48KSS (or 96K if possible..) at some point. Can anyone share some clue? Oh I'm an old programmer, but new (Day4 has just started) with the Pico, and impatient I guess. Love that it works with Arduino IDE, as Vscode and/or Micropython are two things I absolutely hate! |
Beta Was this translation helpful? Give feedback.
Replies: 12 comments 36 replies
-
It might be best to edit the title of your post to something that describes the topic better - for example " "Simpletone" example with UDA1334A I2S DAC". The discussions get to stick around for a while, so it will help people know whether they want to read it, or answer it. I've been meaning to try one of these, but I haven't been in the mood to solder the header pins on, yet :) I think I'd start by using the default pins for I2C and SPI, to control and send data to the DAC, then there shouldn't be anything to set up. Otherwise, you'll need to be specifying the pins for SDA and SCL, rather than by giving names that refer to pins on the DAC. I've attached a pinout diagram. You'll see there are two possible I2C ports, with pins numbered 0 and 1. You can use either, provided you're not using other peripherals already. Each one can have those pins in a few different places. The way it works is that there are only two peripheral devices, but they can each do several different things. If you're only using the default Serial, that's sent over USB, so that doesn't count as one of them, but say you wanted to use UART0, or SPI0, you'd have to use the pins for I2C1, not for I2C0... at least that's my take on it. It would be best to check. We can check which I2C and SPI pins are in use by default, or ask Earle :) but we should probably find out for ourselves, by reading I2C.h and I2S.h, and maybe SPI.h, under libraries, off the Code page. I haven't looked, yet. If the SPI0 pins are in use, we need to use the I2C1 pins (SDA1 & SCL1 labelled here as I2C1 SDA & I2C1 SCL), if I'm understanding it correctly. Assuming that Simpletone example has been tested, we only need to know what the default pins are, then use those. I hope that helps you to make a start on it. I may get the soldering done and join in, at some point. Since people have sometimes made mistakes, drawing up this sort of pinout diagram, it's best to refer to the RP2040 datasheet, to make sure. |
Beta Was this translation helpful? Give feedback.
-
I think there might be a problem in that the loop isn't timed in any way, so I'd expect the sound to waver a bit as interrupts happen. In more serious applications there's usually a buffer containing a sample, or being filled by something calculating it on the fly, and a timer triggering an interrupt at precise intervals, to go and read a value from the buffer and send it to the DAC. I haven't looked into what interrupts are going on in the background, but I expect there are some. I started work on a PWM audio signal generator, which just refers to the microseconds timer, micros(), to decide when to flip the output - so, not much different to this example except it was outputting to a GPIO bit, not a DAC, but it was trying to time it in some way. I also tried it with the CPU clock counter, which counts at 125MHz, by default - not much point, for audio frequencies, but that's another option. Even though that code is timed, I expect interrupts are throwing out the precise timing of when the bit flips, every now and then. To add to the problems, I was reading values from the ADC inputs, and not doing any smoothing. It sounds entertainingly noisy / jittery. Most of that is no doubt due to the ADCs, which are VERY noisy, and have a fault, vaguely documented in the RP2040 datasheet as an erratum, but with not nearly enough detail. I could just set the value as a constant, of course, to eliminate that, but I suspect there'd still be some jitter, from the interrupts. In commercial applications that use audio output, there won't be any interrupts running that can cause that sort of jitter, I guess. My first attempt at PWM audio on the Pi Pico is in discussion #184 "Pi pico control PWM with arduino IDE?". I got a bit further with it, and I don't mind sharing my later code, if you want it, but that covers the general idea, and covers how to compare against micros() or millis(), to trigger something at a particular time or interval. That approach is at least better than just the untimed endless loop in the example, if not perfect. |
Beta Was this translation helpful? Give feedback.
-
Intriguing. I'll have a go at it soon. I'm never very energetic, at this time of year :) Yes, 24 bit and more bit rate would be interesting. Interrupts and there being no others happening seems like the way to go. While I remember, here's some links about the ADC problem. The official erratum concluded that it's good for something like 8.5 bits, from a 12 bit ADC, but there's more to it than that. This first link gives a frequency analysis, and it shows that four values happen far more often than others - the others aren't so bad, just noisy: https://forums.raspberrypi.com/viewtopic.php?f=144&t=299904 I'm thinking it needs some processing, maybe watching the last so many values and ignoring the extremes, then averaging what's left. If it's watching a signal that can be expected to change relatively smoothly, like turning a pot, that should work. If the number of samples that go into the average is a power of two, then the division to calculate the average from the sum is just a shift, and we could keep a running total, subtracting an older value and adding a newer one - that way, there'd be no need to keep re-calculating the total by adding all the recent samples. Here's a couple more related links: I guess we could also just exclude those four values, and wait for one that isn't one of those, then maybe average the last four, eight or sixteen accepted values, to smooth it out. Here's an analysis of the problems (which links to the previous link, above): |
Beta Was this translation helpful? Give feedback.
-
I'm wondering if the initialisation is missing the part where it sets the output volume. In the Adafruit example, they don't set it, so it's presumably on full, but that may be because it's part of the creator for the I2S object, or gets set in i2s.begin(). Perhaps the library used here (from Pico extras?) requires you to do that explicitly. Maybe it would be better to try Adafruit's library, since their board is likely to be very similar, then we can try their example, and look for forum posts about that board. Here's the relevant parts of setup() from their example:
The rest of it is here, with details of how to install their i2s library: https://learn.adafruit.com/adafruit-i2s-stereo-decoder-uda1334a/arduino-wiring-test EDIT: I guess we can't use it directly, because the Pico doesn't have a physical I2S interface, but we can still look at the code: https://github.com/adafruit/Adafruit_ZeroI2S The UDA1334A doesn't seem to have a way to set the output volume, but maybe it's some other initialisation problem. |
Beta Was this translation helpful? Give feedback.
-
Another possible problem - is the PLL pin (PLL0) pulled low on your board, or have you grounded it, to enable audio mode? This is from the UDA1334ATS datasheet:
I don't know if that's the exact chip used, of course. There are others in the series, but probably with a lot in common. |
Beta Was this translation helpful? Give feedback.
-
I had my logic analyzer hooked up for something else so I wired it up to record the output from the |
Beta Was this translation helpful? Give feedback.
-
@RISC-E-Biscuit your GP27 line is definitely not correct. As Andy2No said, that's supposed to be a square wave at 2x the sample freq (i.e. 16khz) that is used to select left or right channel. To me, your image looks like noise-induced transients and not a real pin value. The falling edge of DOUT seems to cause a pulse. I'd check/swap the analyzer cables. It if's consistently weird, move to another Pico. |
Beta Was this translation helpful? Give feedback.
-
@earlephilhower. Thinking, with an I2S DAC output being analog. It might be nice to show an analog type signal in a demo. While i use github, I rarely use git, and no idea how to offer this up, so just posting here for you to add/modify whatever as you see fit. o/C_U_T__H_E_R_E__________ /* Make a reasonably clean sine wave via an I2S DAC. Default rate is 96000KS/S - easily changed. Uses Earle F. Philhower's I2S library for Arduino IDE from https://github.com/earlephilhower/arduino-pico, and Connections (currently default for the I2S lib?) You probably need to provide power to the DAC too. Hints: Change the count step (or a multiplier) - change the frequency. I2S pins are re-assigned from default as detailed above, to free up the non-reassignable analog inputs. #include <I2S.h> const unsigned int samplerate=96000; // Wavetable - Big because somebody might want to make a very pure 20Hz sine at 96KS/S, and This Is The Way. const unsigned int increment = 25; //1=20Hz,5=100Hz,10=200Hz,25-500Hz, 50=1KHz,100=2KHz,500=10KHz,1000=20KHz - all @96KS/S. const unsigned int WaveTableLength = 4800; const int WaveTable[4800] PROGMEM = { //16bit x 4800 entry wavetable. unsigned count = 0; // a raw counter void setup() { void loop() o/C_U_T__H_E_R_E__________ |
Beta Was this translation helpful? Give feedback.
-
@RISC-E-Biscuit I haven't tried assigning different pins, but this is what I've concluded from trying to understand the library code, in the form of a comment block to add.
The main thing I've learned is that you can't just assign each of the three pins wherever you want - there are limits on the pin number, and the WS signal has to be next to the BCLK signal - on the pin numbered one higher than that one. |
Beta Was this translation helpful? Give feedback.
-
Yeh, I saw that before I started.The way the internal switching works.. Pins should be considered to be in "groups"or so. Hence " in the dump I posted above.. It's working a little better than expected now, and I use GP22 to do some "metrics" - timing of the code.. The writes seem quite quick, but then have little holidays.. I will be diving down into the i2s code to figure out why, but first trying to find a decent (dumbass-like-me-understandable) howto for programming the piosm at register level. I really wanna do 24/32 bit and to go duplex to a full codec. Also fighting the urge to spend far too much money on a book about programming the pico in assembly language. (which is where I come from.. I find C etc. hard work due to the abstractions and excessive handholding rules) Certainly things are started now though. What I have in mind (a MIDI or CV/S-Trig (compatible with both) sound synthesizer) is probably possible. |
Beta Was this translation helpful? Give feedback.
-
Here's an example of using DMA to feed an SPI DAC on a Pico, which is really well documented. It would need some small changes to work with arduino-pico, then you'd have to figure out it it's possible to do the same thing with PIO based I2S: https://vanhunteradams.com/Pico/DAC/DMA_DAC.html There are some good details about manipulating the hardware timers. |
Beta Was this translation helpful? Give feedback.
-
The author of that Pi Pico DMA example (V. Hunter Adams) also has some examples of PIO assembly language programming, listed on his home page: |
Beta Was this translation helpful? Give feedback.
@RISC-E-Biscuit your GP27 line is definitely not correct. As Andy2No said, that's supposed to be a square wave at 2x the sample freq (i.e. 16khz) that is used to select left or right channel.
To me, your image looks like noise-induced transients and not a real pin value. The falling edge of DOUT seems to cause a pulse. I'd check/swap the analyzer cables. It if's consistently weird, move to another Pico.