1
$\begingroup$

The Broadcom BCM2711 GPIO controller used by the Raspberry Pi has a hardware PWM implementation option using an algorithm that provides an average value defined by a rational fraction $N/M$ defined as follows in section 8.3. PWM Implementation starting on page 156:

  1. Set context = 0
  2. context = context + N
  3. if (context >= M) context = context - M send 1 else send 0
  4. Repeat from step 2

where context is a register which stores the result of the additions/subtractions.

I've implemented this in a Python script to try to start to get an idea how I'll need to filter it in order to get a low-noise, stable voltage. Here with $M=200$ I've incremented through $0 \le N \le M$ for over 1000 time points each, then applied a gaussian smoothing with a $\sigma$ of 10 time points just to get a quick look.

I took the Fourier transform of the middle 1024 points, ignoring a $3 \sigma$ buffer on each side.

I was surprised to see that some values of $N$ produce signals that are orders of magnitude more noisy than other nearby values of $N$.

Questions:

  1. This kind of analysis is new territory for me, if I've made any major errors in the analysis I would appreciate knowing about it.
  2. If I need near-DC voltages I can just run the BCM2711 at a moderately high frequency and filter the heck out of the output with a simple RC filter. But if I wanted to work closer to the frequency that the algorithm was running, what additional analysis should I do on this algorithm before deciding on a more aggressive low-pass filter and simulating it's performance?

filtered waveforms for different values of N

power spectra of filtered waveforms

stdev of filtered waveforms

Here's the Python script for the plots and analysis:

import numpy as np
import matplotlib.pyplot as plt
from scipy import ndimage
from numpy.fft import rfft, rfftfreq

sigma = 10
ignore = 3 * sigma
M = 200
big = []
for N in range(M+1):
    context = 0
    bob = []
    for i in range(1024 + 2 * ignore):
        context += N
        if (context >= M):
            context -= M
            bob.append(1)
        else:
            bob.append(0)
    big.append(bob)

big = np.array(big, dtype=float)
fbig = ndimage.gaussian_filter1d(big, sigma=sigma, mode='mirror', order=0, axis=-1)
fbig_trunc = fbig[:, ignore:-ignore]
print('fbig_trunc.shape: ', fbig_trunc.shape)
ft = rfft(fbig_trunc, axis=-1)
power = np.abs(ft)**2
power /= power.max()
dB_rel = 10 * np.log10(power)
print('power.shape: ', power.shape)
print('power.max(): ', power.max())
print('dB_rel.shape: ', dB_rel.shape)
print('dB_rel.max(): ', dB_rel.max())

if True:
    plt.figure()
    for thing in fbig:
        plt.plot(thing, linewidth=0.8)
    plt.ylim(-0.02, 1.02)
    plt.title('sigma={:2d}, M={:3d}'.format(sigma, M), fontsize=16)
    plt.xlabel('time steps', fontsize=16)
    plt.show()

if True:
    plt.figure()
    maxi = dB_rel[:, 1:201].max()
    plt.imshow(dB_rel[:, :201], vmax=maxi, vmin=-100)
    plt.colorbar()
    plt.gca().set_aspect(2.0)
    plt.title('FT relative power (dB), sigma={:2d}, M={:3d}'.format(sigma, M), fontsize=16)
    plt.xlabel('frequency', fontsize=16)
    plt.ylabel('N', fontsize=16)
    plt.show()

if True:
    plt.figure()
    std = fbig_trunc.std(axis=-1)
    plt.plot(10*np.log10(std**2))
    plt.title('stdev relative power, sigma={:2d}, M={:3d}'.format(sigma, M), fontsize=16)
    plt.xlabel('N', fontsize=16)
    plt.ylabel('stdev (dB rel)', fontsize=16)
    plt.show()
$\endgroup$
5
  • $\begingroup$ @Glorfindel Why edit other people's posts and undo their use of in-line links? by now I probably have thousands of posts with inline links. They make it much much much easier to copy/paste short quotes or images along with their links without messing up the target post. I understand some folks view SE posts as if they were programs and so they feel readability of the "source code" is important, but I find that reusability of my material when I move bits to new posts is more important to me. $\endgroup$
    – uhoh
    Commented Jul 17, 2022 at 6:40
  • 1
    $\begingroup$ it used to be the standard, and some links don't work inline (e.g. when they contain parentheses). That's why I chose to write my bot like that. I can make an exception for your posts, no problem. $\endgroup$
    – Glorfindel
    Commented Jul 17, 2022 at 12:59
  • 1
    $\begingroup$ @Glorfindel Oh I'm quite fond of your bots and don't want to get on their bad side; my theory is that when the singularity happens AI will eat all of Stack Exchange and remember what we said :-) Thanks very much for the exception - it's much better to be granted exceptions than to throw them! $\endgroup$
    – uhoh
    Commented Jul 17, 2022 at 13:33
  • $\begingroup$ @Glorfindel I just happened to notice that this prolific user also seems to use inline links extensively (I did a manual spot check in Travel SE) and there could be more users who like them as well. I think they provide a useful functionality to those of us who make use of them. $\endgroup$
    – uhoh
    Commented Jul 17, 2022 at 20:58
  • $\begingroup$ @Glorfindel unrelated here but I didn't see your user ID autocomplete in Tavern on the Meta so not sure if you'd have seen it there: CelesTrak is now a 501(c)(3) non-profit and redirects from .com to .org, should old posts be edited "à la bot"? $\endgroup$
    – uhoh
    Commented Jul 19, 2022 at 21:48

1 Answer 1

2
$\begingroup$

The pulse generation algorithm sounds like it is more like Pulse Density Modulation (PDM), not Pulse Width Modulation (PWM), because PWM would pack the ones into the beginning of counting, and leave the zeroes in the end.

Indeed some values of N and M cause the output pattern to have a certain repetitive bit stream, for example the 100/200 means the output is 50% 0 and 50% 1, so the output is a bitstream of 1,0,1,0... which is easy to filter. If you set N and M to be exactly ratio of 1/3, the output pattern will also be repetitive, 0,0,1,0,0,1... So, in light of this this also explains the spectrogram of N=50 M=200, which is 25%, 0,0,0,1,0,0,0,1.

These patterns that have a short repetition rate show up as pure tone peaks in the spectrogram. Choosing the values so that there is no obvious short repeating patterns basically dithers the bitstream and widens the spectrum to have multiple lower peaks.

Depending on what is the usage of the output will determine how to filter it. For many cases a simple RC filter is enough, as long as the PDM bitstream output rate is high enough for the RC filter cutoff frequency.

$\endgroup$
2
  • $\begingroup$ Ah! This is extremely helpful; searching for Pulse Density Modulation opens up a whole body of literature for me to read, thank you! $\endgroup$
    – uhoh
    Commented May 17, 2020 at 9:09
  • 1
    $\begingroup$ It looks like I lost my way and forgot to stop by and accept. This is the answer that I needed to move forward. Thanks! $\endgroup$
    – uhoh
    Commented Feb 3, 2022 at 14:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.