PLN | EUR | USD

Audio Processing with STM32

STMicroelectronics (ST) has just released a new entry in their high-performance ARM Cortex-M7 line, the STM32H735, available exclusively through Digi-Key for the first 30 days of its release. When new releases like this come across my desk, I like to make a small project with it that incorporates multiple features. For this blog, I chose to investigate the various audio peripherals and display the results using the STM32H735G-DK Discovery kit. For a brief overview of the kit and an out of box demo, see the video from one of my colleagues below.

Sound Capture

The STM32H735G has two dedicated Serial Audio Interfaces (SAI) that offer a lot of flexibility. Each SAI has two sub-blocks that can be configured independently as a master or slave and either transmitter or receiver for I2S type communication. Alternatively, they can provide a clock for PDM microphones and convert the output to PCM.

Other peripherals of the device may be repurposed for audio as well. Four of the device’s SPI interfaces can be configured for I2S. The embedded sigma-delta filter (DFSDM) can also handle the conversion for PDM microphones in place of the SAIs.

On the Discovery kit, both SAI configurations mentioned above can be seen in action. One of the SAI interfaces is connected via I2S to a Cirrus Logic WM8994 codec. With the codec, audio is received through the Line In port and played back through the headphone jack or the amplified speaker outputs. The second SAI operates in PDM mode and is connected directly to the on-board microphone.

ST provides a BSP sample project in the STM32CubeMX framework to quickly get started developing with these audio peripherals. The project contains low level drivers for the peripherals and higher-level drivers for Discovery kit features like the audio codec. For example, configuring audio input from the Line In connector using the BSP drivers is very straightforward:

Copy
#define BUFFER_SIZE    2048

int16_t audio_buffer[BUFFER_SIZE]
BSP_AUDIO_Init_t  AudioInInit;

/* Initialize audio interface to use line in port for input */
AudioInInit.Device = AUDIO_IN_DEVICE_ANALOG_LINE1;
AudioInInit.ChannelsNbr = 2;
AudioInInit.SampleRate = 44100;
AudioInInit.BitsPerSample = AUDIO_RESOLUTION_16B;
AudioInInit.Volume = 80;

/* Initialize audio in on Instance 0 (SAI I2S) */
BSP_AUDIO_IN_Init(0, &AudioInInit);

/* Start DMA recording into buffer */
BSP_AUDIO_IN_Record(0, (uint8_t *) audio_buffer, 2* BUFFER_SIZE);

Utilizing ARM CMSIS-DSP

If you have developed with ARM before, you will most likely be aware of the CMSIS (Cortex Microcontroller Software Interface Standard). If not, it’s a hardware abstraction layer with code that is consistent across different ARM Cortex implementations. In this case, I’m interested in the CMSIS-DSP library which contains common signal processing functions that are optimized for the ARM architecture.

For me, the simplest way to incorporate the CMSIS libraries was directly from the source. The entire CMSIS repo can be cloned from ARM’s GitHub, but only the CMSIS/DSP/Include and CMSIS/DSP/Include directories need to be added to your ST project to get started. Generally, these should be copied to the Drivers/CMSIS directory in the default project structure.

There are two quick project tweaks to make before compiling. First, add the new CMSIS/DSP/Include directory in the project’s include paths. The arm_math.h header located there is what you will include in your application to access the DSP functions.

Second, there are two #defines that the DSP library uses for information about the hardware architecture: __FPU_PRESENT and ARM_MATH_CM7. I found the easiest way to make sure they’re found is to add them to the global project symbols as shown below.

Figure 1. Project symbol definitions.

After that calculating an FFT takes only a few lines:

Copy
#include “arm_math.h”

#define FFT_SIZE    256

arm_rfft_fast_instance_f32 fft_inst;
float32_t fft_in[FFT_SIZE], fft_out[FFT_SIZE];
float32_t fft_mag[FFT_SIZE>>1];

/* Initialize ARM FFT instance with num points */
arm_rfft_fast_init_f32(&fft_inst, FFT_SIZE);

/* Fill the fft_in buffer from Line In or Microphone input */

/* Perform forward direction 32-bit FFT */
arm_rfft_fast_f32(&fft_inst, fft_in, fft_out, 0);

/* Calculate magnitude (buffer size is half because real + imag parts are merged) */
arm_cmplx_mag_f32(fft_out, fft_mag, FFT_SIZE >> 1);

Bringing It All Together

With the FFT calculated, it’s nice to view the results graphically. The function below combines functions from the ST BSP drivers and the CMSIS-DSP library to draw a minimalist visualizer-style graph on the Discovery kit’s display.

Figure 2. Visualizer displayed on Discovery kit.

Copy
static void Display_FFT(float32_t *fft_mag)
{
	uint8_t i;
	float32_t max_val;
	uint32_t max_idx;
	uint32_t box_height;
	uint8_t box_width = 2;

	/* Draw horizontal axis */
	UTIL_LCD_FillRect(10, 135, 460, 2, UTIL_LCD_COLOR_WHITE);

	/* Use max value in the results for scale */
	arm_max_f32(fft_mag, FFT_SIZE >> 1, &max_val, &max_idx);

	/* Draw frequency bins */
	for(i = 0; i < 230; i++)
	{
		box_height = 100 * (fft_mag[i] / max_val);
		UTIL_LCD_FillRect(10 + box_width*i, 135, box_width, box_height, UTIL_LCD_COLOR_WHITE);
	}
}

Conclusion

The STM32H735 is a powerful microcontroller with a ton of features on-board especially for multimedia and signal processing applications. Digi-Key is the exclusive distributor for the product until the end of the month so check it out on our website if it sounds like a good fit for your next project.

Informacje o autorze

Image of Taylor Roorda Taylor Roorda, Associate Applications Engineer at Digi-Key Electronics, joined the organization in 2015 with primary areas of interest around embedded systems, programmable logic and signal processing. He holds a Bachelor degree in Electrical Engineering from North Dakota State University and spends his free time playing guitar and writing music.
More posts by Taylor Roorda