用 STM32 进行音频处理

STMicroelectronics (ST) 的高性能 ARM Cortex-M7 产品线刚刚发布一款全新产品 STM32H735,发布后前 30 天由 DigiKey 独家供货。像这样的新产品拿到我手上时,我一般会用它搞个小项目,以深入了解其各项特性。在这篇博客中,我选择使用 STM32H735G-DK Discovery 套件来研究各种音频外设并显示结果。我的一个同事提供了下面这个关于套件简介和开箱演示的视频,请先观看一下。

声音捕捉

STM32H735G 具有两个专用的串行音频接口 (SAI),提供了很大的灵活性。每个 SAI 都有两个子块,可以分别配置为主机或从机, 并包含用于 I2S 类型通信的发射器或接收器。此外,它们还可以为 PDM 麦克风提供时钟,并将输出转换为 PCM。

该器件的其他外设也可以重新改换用途而用于音频。其四个 SPI 接口均可配置用于 I2S。嵌入式三角积分滤波器 (DFSDM) 还可代替 SAI 来处理 PDM 麦克风的转换。

在这个 Discovery 套件中,上面提到的两个 SAI 配置均可以看到起作用。SAI 接口之一通过 I2S 连接到 Cirrus Logic WM8994 编解码器。有编解码器,音频就可以通过 Line In 端口接收,并通过耳机插孔或放大的扬声器输出播放出来。第二个 SAI 工作在 PDM 模式下,并直接连接至板载麦克风。

ST 在 STM32CubeMX 架构中提供了一个 BSP 示例项目,可快速开始使用这些音频外设进行开发。该项目包含用于这些外设的低层驱动程序以及用于 Discovery 套件功能(如音频编解码器)的高层驱动程序。例如,使用 BSP 驱动程序配置来自 Line In 连接器的音频输入就非常简单:

副本
#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);

利用 ARM CMSIS-DSP

如果您以前用 ARM 开发过,那么很可能已经了解 CMSIS(Cortex 微控制器软件接口标准)。如果没用过,那么知道它是一个硬件抽象层就行,其代码在不同的 ARM Cortex 实现中是一致的。就目前这种情况,我对 CMSIS-DSP 库感兴趣,该库包含针对 ARM 架构进行了优化的常见信号处理功能。

对我而言,合并 CMSIS 库的最简单方法就是直接从源代码上进行。整个 CMSIS 存储库可以从 ARM 的 GitHub 上克隆,但是只需将 CMSIS/DSP/IncludeCMSIS/DSP/Include 目录添加到您的 ST 项目即可开始。通常,这些文件应复制到默认项目结构中的 Drivers/CMSIS 目录。

在编译之前,先执行两个快速项目调整。首先,在项目的 include 路径中添加新的 CMSIS/DSP/Include 目录。位于该目录的 arm_math.h 标头将包含在您的应用程序中,以访问 DSP 功能。

其次, DSP 库使用了两个 #define 来定义有关硬件架构的信息:__FPU_PRESENT 和 ARM_MATH_CM7。我发现确保找到它们的最简单方法是将它们添加到全局项目符号中,如下所示。

图 1:项目符号定义。

后面是 FFT 计算,仅需几行代码:

副本
#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);

全部汇集在一起

计算完 FFT 后,就可以用图形方式查看结果了。下面的函数结合了来自 ST BSP 驱动程序和 CMSIS-DSP 库的函数,可以在 Discovery 套件的显示屏上绘制出一个简约的观察仪样式的图形。

图 2:Discovery 套件上显示的观察仪。

副本
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);
	}
}

总结

STM32H735 是一款功能强大的微控制器,具有大量板载功能,尤其适合多媒体和信号处理应用。在本月月底前,DigiKey 是该产品的独家分销商,因此如果您认为这个对您的下一个项目很有用,那么请到我们的网站进一步了解。

关于此作者

Image of Taylor Roorda Taylor Roorda 是 DigiKey 的助理应用工程师,于 2015 年加入公司,主要负责嵌入式系统、可编程逻辑和信号处理领域的工作。Taylor 拥有北达科他州立大学电子工程学士学位,业余时间喜欢弹吉他和搞音乐创作。
More posts by Taylor Roorda
 TechForum

Have questions or comments? Continue the conversation on TechForum, Digi-Key's online community and technical resource.

Visit TechForum