This post continues experimenting with the PDM conversion to PCM samples in an LPC812 CPU. From the earlier tests we are confident in the basic PDM to SPL arithmetic. For this post we will concentrate on the PDM to PCM conversion, and demonstrate it is working as expected by actually recording some audio.
This is part of a series of articles on the general subject of audio signal processing from air to information. See the section at the end for the full series.
Microphones and CPUs
I originally embarked on this project by wondering if Pulse Density Modulated (PDM) audio could be handled in a useful way in an Arduino and its ATMega CPU. Then I noticed the even smaller ATTiny85 CPU and decided to set the bar higher. The result was a stunt where I measured Sound Pressure Level (SPL) by signal processing the PDM bit stream and commanded a hobby servo motor to move proportional to the SPL.
While playing with the idea, I noticed that there doesn’t seem to be any useful breakout boards for any of the many tiny digital microphones found in bluetooth headsets and cell phones, where their small size, low power consumption, and all digital signalling are all advantages. But their small size and digital signalling also make them more difficult for the hobbyist to handle and some kind of breakout or application PCB would be welcome.
I also noticed the relatively new ARM Cortex-M0+ based LPC800 family from NXP. The model that caught my eye was the LPC810, which is available in the very old-school 8-pin plastic DIP package, and is remarkably friendly to experimenters since that is the package that solderless breadboards were invented to handle. It has an SPI interface that could be used to capture a PDM bit stream, and resources to produce an analog output proportional to SPL, as well as convenience features such as a UART missing from the ATTiny85. It can also run its 32-bit ARM CPU core at 30 MHz, but only has 4 KB of FLASH for code, and 1 KB of SRAM. So I ported the SPL meter stunt from the ATTiny85 to the LPC810, and posted another video of it operating.
Cheshire decided that there ought to be a convenient way to play with PDM microphones, and set out to design a suitably inexpensive board. We’ll post more about this effort soon. We decided to move a step up the product family and picked the LPC812 in a 16-bit TSOP package. It has four times the memory and twice as many pins for future flexibility. So I ported the demo yet again to this processor, and have been testing it with the 20-pin package flavor on an evaluation board.
Our last post exercised the PDM to SPL conversion based on that post using PDM test vectors hand-crafted to get expected SPL values.
Now, we wanted to actually hear some audio.
Capturing Audio
In the early posts, I claimed that recording audio in such a tiny CPU was impractical. While true for the ATTiny85, this was mostly because there was only one peripheral in the CPU intended to move serial data which we needed to capture the PDM input. There is also nowhere near enough RAM to capture enough audio to analyze just a fragment.
The LPC800 chips, however, have dedicated UARTs in addition to the SPI peripheral I’m using to capture the PDM bits. So it seems plausible that PDM audio could be captured, converted to PCM, and sent to a PC over a UART.
Asynchronous serial at 115200 baud has 10 bit frames delivering 8 bit bytes. So in theory it can deliver up to 11520 bytes per second or 92160 bits per second. Our finished PCM samples are signed 14 bits, delivered at 7812.5 samples per second, or 109375 bits per second. So we clearly can’t deliver the full resolution samples at the full rate. Our maximum resolution for our sample rate would be 11.8 bits per sample, which is an extremely inconvenient number of bits to pack in a byte stream.
However, a common audio format in the PC is 8-bit unsigned PCM, and that would require only 7812.5 bytes per second which clearly is within the serial line’s capacity. So we can start there, and consider options to improve fidelity later, such as sending four 10-bit samples in 8 bytes for 78125 bits per second delivered. That additional resolution would come at a cost of needing to unpack it into a familiar format on the PC, of course, which reinforces the idea of 8-bit samples.
Another approach to improving resolution within the available serial port bandwidth would be to use A-Law or µ-Law encoding. These are a non-linear encodings invented for the telephone system which pack 12 bits or more of dynamic range in an 8-bit sample.
To transfer a simple 8-bit linear sample, we merely have to reduce our 14-bit signed PCM sample to 8 bits unsigned. This is done by shifting the signed sample right by six bits, adding 128 to the result, then clipping it at 0 and 255. The resulting 8-bit sample is stored in a buffer, and periodically handed to the UART for transmission to the PC.
Tools needed to capture and play
Tera Term for serial data capture
On the PC, I generally use Tera Term for interacting with devices over serial lines. Since Tera Term can create a log file containing every byte it sees, it will be sufficient for capturing our audio recording.
Audacity for sound file conversion and editing
The result will be a file full of raw 8-bit PCM samples, so it can’t just be played directly. It needs to be converted into something that can be played, such as a WAV or even MP3.
For that, I like the open source audio editor Audacity. It provides a full-featured multitrack audio editor with lots of effects and analysis tools. Most importantly for this task, it can import 8-bit PCM raw samples and save them in any of a long list of formats.
Project source code and build tools
Get the latest source code from our repository. The version I checked in yesterday handles 14 bit to 8 bit unsigned raw PCM. Given that µ-Law is specified to take 14 bit samples as its input, it is extremely tempting to change to µ-Law encoding in a future version. Tempting enough that it is likely to happen.
You also need a LPC812 and the ability to wire up a PDM format digital microphone as described in an earlier post. Naturally, you’ll also need the tool chain and IDE to build and flash your CPU. See that post and the file readme.txt
in the project folder for all the gory details and links to all the tools.
How I did it
- Get the basic firmware built, flashed and running as described in an earlier post.
-
Note the new features;
- Add a small double-buffer for raw samples, filled with scaled and clipped 8-bit values.
- Ship each buffer to the UART as it fills, flipping to the alternate for continuous operation.
- Control what prints with the
monitor
global, set to 1 at boot for the normal peak, level, and average output monitor. Set to 2 for the EEK output on sudden increases in SPL, or 4 for the raw sample collection. It is probably not wise to combine 4 with the other flag bits.
- With the code running and the debugger active, pause the debugger then modify
monitor
to be 4. With the firmware as of check-in [5218c33e38], you will need to stop execution inmain()
in order formonitor
to be in scope and editable. A future version may relax that requirement. -
With execution still paused, set up the serial capture in Tera Term.
- Pick
File|Log...
from the menu. - Navigate to a suitable place to store the recording and give it a name
- In the Options area, check Binary and clear Append. Make sure that Timestamp is also clear.
- Click Save to begin saving all serial data to the named log file.
- Find the Tera Term Log window and restore it so you have easy access to the Close button which you will use to stop recording.
- Pick
- Resume execution in the debugger. The terminal will show lots of nonsense characters. Unfortunately Tera Term does not have a capture mode where it does not display the received serial data.
-
Speak, recite, declaim, sing, honk horns, ring bells or otherwise make noise.
-
Click Close in the Tera Term Log window to stop writing data to the file.
-
Pause execution and set monitor back to 1, 2 or 3 if desired.
-
Launch Audacity to get a new empty project.
-
Pick
File|Import|Raw data...
-
Locate your Tera Term log file, click Open.
-
In the Import Raw Data dialog, choose “Unsigned 8 bit PCM”, “No endianness”, “1 Channel (Mono)” and make the sample rate be 7812.5 Hz then click Import.
-
You can now play, edit, and analyze the track you recorded.
Context
Previous installments include:
- Introduction to MEMS Microphones
- Using a PDM Microphone
- PDM in a Tiny CPU
- Resolution of audio recovered from a PDM data stream
- PDM in ATTiny85 Source Code
- SPL is Logarithmic
- PDM in another 8-pin CPU
- PDM to SPL Demonstrated in an LPC810
- Testing the PDM to SPL arithmetic
All of the code supporting this demonstration is in a public fossil repository. This post documents work that was checked in as 5218c33e38.
Update
A fair number of words have been added to this thread of articles. You can find a handy collection of all of them at the PDM Microphone category page. We’ve demonstrated the signal processing chain in breadboards based on the ATTiny85, LPC810, LPC812, and in our LPC812 with microphone future product, which we call the SPLear™.
If you have a project involving embedded systems, micro-controllers, electronics design, audio, video, or more we can help. Check out our main site and call or email us with your needs. No project is too small!
+1 626 303-1602
Cheshire Engineering Corp.
120 W Olive Ave
Monrovia, CA 91016
(Written with StackEdit.)
I have been reading your series with interest. The Broadcom BCM2835 on the Raspberry Pi has an input (and the clock) for a pair of PDM signals. The chip includes a 4th order CIC decimation filter with a selectable decimation factor. A low pass filter with compensation for the droop in the CIC filter still needs to be implemented in software, likewise DC blocking. This would seem to be a great simplification over using a separate ARM chip. In particular, it eliminates the problem of getting the audio from one device to the other.
Unfortunately, I have been able to find nothing more about this feature than given in the BCM2835-ARM-Peripherals.pdf data sheet. Presumably, commercial users of this device would use this feature alongside the camera input and H264 encoder. However a search of the Internet has found nothing about anyone actually using the PDM input.
LikeLike
The BCM2835 may have an audio peripheral, but it appears to be really lightly documented as you’ve noticed. I can tell from a quick skim of the RPi schematic and the peripherals data sheet that it is possible the needed pins can be reached if the Broadcom’s GPIO28 through GPIO31 can be mapped to their ALT2 function. I haven’t done very much hardware hacking on the RPi yet, so I’m not certain what implications that has.
It is interesting, however, that the actual audio peripheral isn’t used in the stock RPi configuration for its built-in audio output. I’m not sure what that is hinting at exactly, but it could be a hint at some quirkiness that made it easier to use the PWM instead. Of course, the quirks could be in a linux audio component instead, I just don’t know.
I’ve noticed that a lot of devices that have digital audio interfaces have hints at poorly documented modes to support PDM. So the level of non-documentation here is perfectly consistent. It was precisely the combination of these readily available and cheap microphones with the dearth of (openly published) information that led me to start poking at them in the first place.
LikeLike