, , , , , , ,

This article will discuss instrumenting the PDM to PCM conversion running in the LPC812, and report on the performance achieved.

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 and links to the source code.


Previous articles have explored the process of using a MEMS digital microphone to measure sound pressure level (SPL). Our latest demonstrations have been running in an NXP LPC812 CPU, clocked at 24 MHz from the chip’s internal 12 MHz oscillator. The chip can be configured to run as fast as 30 MHz.

A natural question to ask is how much of the available CPU capacity are we using now.


Before implementing some additional capability on top of our basic PCM audio samples, we might want to know how much compute time is actually available.

Also, we are running the LPC812 at 24 MHz because that is how the sample code configured it. In theory, it can be run at 30 MHz. (The earlier test in the 8-pin LPC810 was running at 30 MHz.) So it is interesting to know if we need to turn up the clock.


We’ll use a digital output that can be probed with a logic analyzer or oscilloscope. The output will have edges at the start and end of the block of code that we wish to measure.

Note that to simultaneously measure performance in both the foreground at at interrupt level, we must use separate outputs for each thread of execution, and must be careful that the technique used to set or clear the output pin is atomic in each thread of execution.

For this article, we’ll use a single output to measure one thing at a time.

Pin set and clear macros

I’ve created a header file (splear.h) that defines symbolic names for all the GPIO pins. The ones that have dedicated uses have more specific names, and the others are named after their GPIO bit number.

In the LPC812 LPCExpresso board, there are three LEDs connected to GPIO port pins. Red is on pin 7, blue on pin 16, and green on pin 17. Using the LEDs for sonar has the nice property that you can see some changes in behavior by noticing flashes or changes in brightness even without any instrumentation attached. These pins are also convenient because they are already configured as outputs, so no further fuss and bother is required.

We define two macros, SONARSET and SONARCLR, designed to be used anywhere a C statement is syntactically legal. Since the LEDs are wired from the pin to VCC, the set macro turns the LED on, and the clear macro turns it off. This implies that we will treat the define sonar pin as active low when probing. The macros are defined to do nothing (which the optimizer should remove completely from generated code) when SONARPIN is not defined. So to use the sonar, we define it to the LED that is convenient to probe, in this case Green.

#define SONARSET do{LPC_GPIO_PORT->CLR[0] = 1 << SONARPIN;}while(0)
#define SONARCLR do{LPC_GPIO_PORT->SET[0] = 1 << SONARPIN;}while(0)
#define SONARSET do{}while(0)
#define SONARCLR do{}while(0)

Localized blocks of code can now be bracketed by matched pairs of SONARSET and SONARCLR to cause the designated pin to pulse low during execution of the block.

To extend this to probe multiple blocks, we would simply define similar macros for each distinct thread.

The set and clear operations because they use a hardware feature of the GPIO port to set or clear individual pins atomically. Writing to LPC_GPIO_PORT-&gt;CLR[0] atomically clears pins corresponding to each 1 bit in the value written, leaving all the other bits untouched. The GPIO peripheral provides SETP0, CLRP0 and NOTP0 registers that atomically set, clear, and toggle bits with a single write operation.

Probe the SPI received IRQ

Our current implementation does the entire conversion from PDM to PCM in the SPI’s receive interrupt handler, SPI0_IRQHandler() in pdmspi.c. It uses a ring buffer to move samples to the foreground loop in main().

        if (status & SPI_STAT_RXRDY) {
            uint16_t pdm = LPC_SPI0->RXDAT;
            // ....
            // process 16 bits from pdm here, and occasionally put a finished
            // PCM sample in the ring buffer for delivery to the foreground. 
            // ....


I used a USBee SX logic analyzer to probe the PDM clock, data, and the sonar. This is a simple PC-based logic analyzer probe that provide for 8 logic level inputs sampled at any rate within the bandwidth limits of USB 2.0 Hi Speed.

Here’s a screenshot showing about 18 interrupts.

Sonar screenshot

The top trace is the PDM clock, the middle is the PDM data, and the bottom is our sonar signal as configured above. As expected, the PDM clock measures 1 MHz, and each sixteenth interrupt takes longer to handle as it decimates and finishes the CIC filter to produce a finished PCM sample. The time between interrupts (measured falling edge to falling edge) is exactly 16 µs to within the precision of the 24 MHz sampling of the USBee SX.

Fifteen receive interrupts take about 3 µs to handle. The sixteenth takes 9.125 µs, or about 57.3% of the time available before the next interrupt.

This second capture has added the UART Tx line, and used it to trigger when the firmware notices a loud sound. I honked my bike horn at it fairly close to the mic, which caused the firmware to print, triggering the capture. The brown PDM data trace shows the varying pulse density, and the time between the two white patches is consistent with a 1 kHz ballpark pitch.

Honked at the microphone

Here’s a third capture after adding a second sonar trace, where the trace is low during the entire main loop process, and high while waiting for a new sample to be delivered from the interrupt level. Triggered by the start bit of the “EEK” message printed on the UART, we can see that the yellow trace stays low for more than five PCM samples while it prepares the message to be printed. Once the message is handed off to the UART driver for printing on its interrupt thread, you can see that the several samples that had been missed during the calculation are collected rapidly from the ring buffer.

Watching the SPL calculation too.


All of the code supporting this demonstration is in a public fossil repository. This post documents work that was checked in as [1fe01e0ab5].

Most posts related to handling PDM are found in the PDM Microphone Toys category.

Previous installments in the series include:

(Written with StackEdit.)