[sdiy] PRNG for PDM, was Re: Quantizer with Arduino, ADS1115 and MCP4725
mskala at northcoastsynthesis.com
mskala at northcoastsynthesis.com
Thu Jun 20 15:09:32 CEST 2024
On Thu, 20 Jun 2024, Mike Bryant wrote:
> Only if you're not using an MCU with PWM built in, I'd suggest using PDM
> (just reverse the bits from the counter to the comparator). Moves any noise
> as you change the output voltage higher in frequency.
That reminds me of something interesting, though not closely related, that
came up in one of my recent projects:
https://northcoastsynthesis.com/news/phase-meter/
I wanted to implement a PDM DAC using a couple of GPIO pins followed by a
filter, and I had a fair bit of spare capacity for software on the
microcontroller, so I implemented something like this:
a hardware timer is in the background constantly keeping track of where we
are in the output waveform
look up timer value in a table in ROM to get desired output voltage
the table entry contains two output values for the GPIO pins, and a
16-bit threshold
generate a 16-bit pseudorandom number
compare pseudorandom number against threshold
depending on result, send one or the other value to the GPIO pins
repeat this whenever not doing other work (in practice, about 1MHz)
Basically it's dithering the 16-bit sample values down to 2 bits, in a
generalization of common 1-bit PDM.
The interesting question for me was how to do that "generate a 16-bit
pseudorandom number" step, bearing in mind that I wanted to do it in a way
that would be cheap in software.
I tried using an LFSR, at basically three instructions per update: shift
a bit off the end into a status flag, skip next instruction conditional on
the flag, XOR the polynomial into the register. I also tried a linear
sequence based on the Golden Ratio: one instruction per update, add
0x9E37 to the register every time, letting it overflow. The theoretical
basis is that 0x9E37 approximates 0.618... times 2^16, and in some sense
is a maximally irrational number (not well-approximated by any rational
with small numerator and denominator), so I thought there should be as
little reinforcement among the alias frequencies as possible.
I didn't try bit-reversal because I didn't see a way to do it cheaply
in the PIC24 instruction set.
What I found was that the Golden Ratio thing gave more satisfactory
results than the LFSR. Less noise from the PDM made it through the filter
downstream of the GPIO pins. It wasn't a huge difference and they were
both acceptable, but I don't think it was my imagination either. I didn't
take very careful notes but I think it was about a 4dB difference in the
noise floor at the output jack, downstream of the filter.
I think what was going on was that the linear sequence would put a lot of
energy into relatively few frequency spikes, but they were at frequencies
high enough to be killed by the filter, bearing in mind the significant
gap between sample rate of about 1MHz and filter cutoff of about 800Hz.
And then that meant less noise energy available to make it through the
filter. The LFSR on the other hand made the noise look like a good
approximation of white noise, and that meant more of it was in the filter
passband.
It's also possible that just by having a simpler update procedure, the
linear sequence generator meant I could afford to do more updates, so the
effective sample rate was higher. I doubt that was really significant,
though, because even though it's a 3:1 difference, the microcontroller was
doing plenty of other work (the table lookup alone being several
instructions, and identical for either technique), and so the real
difference in effective sample rate wasn't a lot.
The linear-sequence technique would be more expensive than an LFSR or bit
reversal in a hardware implementation, but in software, on the chip I was
using, it was cheaper than those.
--
Matthew Skala
North Coast Synthesis Ltd.
More information about the Synth-diy
mailing list