I've described PDM in
here and
here.
If you look at a PWM signal, it consists of regular periods, during which there is either zero or two transitions (so there is either one pulse, or the signal stays low (0% duty) or high (100% duty) for the entire period). To determine the value it represents, you take the average over exactly one period.
If you look at a PDM signal, it can change state every clock cycle. It contains much higher frequency components. To determine the value at any clock cycle, you average over a fixed number of clock cycles; that number is up to you.
PWM duty can change at most once per period, but PDM duty can change every clock cycle.
PWM is implemented using a simple counter. The period of the counter determines the resolution of the signal.
PDM is implemented using an addition every clock cycle, with the overflow/carry determining the output state. The addition is done using modular (wraparound) arithmetic, and the register width determines the precision, but also the lowest frequency components that can be produced. Just like in PWM, full resolution is achieved only when averaging over the wraparound number of samples. (So, if you use an n-bit register, over 2
n samples.)
Let's say we sample the sine wave at 1024 regular points using 8-bit precision in PWM, PDM, and PCM, all using 256 clock cycles per sample, and then do a discrete Fourier transform to obtain the amplitude spectrum of these three signals. In Python:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import scipy as sp
import numpy as np
from math import sin, pi
from sys import stdout, stderr
stderr.write("Generating data ... ")
stderr.flush()
pwm = np.array(range(0, 256 * 1024), dtype = np.float)
pdm = np.array(range(0, 256 * 1024), dtype = np.float)
pcm = np.array(range(0, 256 * 1024), dtype = np.float)
pdmstate = 0
for i in range(0, 1024):
sample = round(128.0 + 112.0 * sin(i * pi / 512.0))
pwm[256*i:256*i+sample] = 1.0
pwm[256*i+sample:256*i+256] = 0.0
pcm[256*i:256*i+256] = sample
for k in range(i*256, i*256+256):
pdmstate += round(128.0 + 112.0 * sin(k * pi / 131072.0))
pdm[k] = pdmstate // 256
pdmstate = pdmstate & 255
stderr.write("Done.\n")
stderr.write("Calculating FFT ... ")
stderr.flush()
pwmfft = np.fft.rfft(pwm, norm='ortho')
pdmfft = np.fft.rfft(pdm, norm='ortho')
pcmfft = np.fft.rfft(pcm, norm='ortho')
stderr.write("Done.\n")
stderr.flush()
# pwmfft = np.fft.fftshift(pwmfft)
# pdmfft = np.fft.fftshift(pdmfft)
freq = np.fft.rfftfreq(len(pwm))
pcmamp = np.abs(pcmfft) # **2 for power spectrum instead of amplitude spectrum
pwmamp = np.abs(pwmfft) # **2 for power spectrum instead of amplitude spectrum
pdmamp = np.abs(pdmfft) # **2 for power spectrum instead of amplitude spectrum
for i in range(0, 1024*128):
stdout.write("%14.12f %18.12f %18.12f %18.12f\n" % (freq[i], pcmpower[i], pwmpower[i], pdmpower[i]))
The output contains the frequency in the first column, the amplitude spectrum for the PCM signal in the second column, the amplitude spectrum for the PWM signal in the third column, and the amplitude spectrum for the PDM signal in the fourth column.
If we plot these using logarithmic axes, ignoring the very lowest frequencies (longest wavelengths), we get the following graph:
(I saved the above output in a file named
out, and then ran
set term png enhanced size 1024,384 ; set output 'plot.png' ; unset xtic ; unset ytics ; set xlabel 'Frequency, logarithmic' ; set ylabel 'Magnitude, logarithmic' ; set logscale xy ; set xrange [0.0001:0.5] ; plot 'out' u 1:3 t 'PWM' w lines lc rgb '#ff0000', 'out' u 1:4 t 'PDM' w lines lc rgb '#0000ff', 'out' u 1:2 t 'PCM' w lines lc -1 in Gnuplot.)
The PCM signal has a lot of spread-out quantization noise. (It stays constant for 256 cycles, as it has 256 different amplitudes, whereas the two others have only amplitudes 0 or 1. This causes the spectral effects.) Anyway, PCM/DAC signal only needs a lowpass filter to yield acceptable results.
The PWM signal has a HUGE spike at the 256-sample wavelength, because that's its period. This is very difficult to filter out.
The PDM signal, on the other hand, has most of its spectral content in the higher frequencies. If you look carefully, you'll see that its first spike is about one quarter from the right edge (specifically, at the 16th PWM spike counting from left), corresponding to the original sine wave frequency.
This means that there isn't much low-frequency noise, and a low-pass filter can easily remove the quantization noise since it is at those very high frequencies, and spread out even there.