STM32 SAI 基础与实战
在 STM32 中被广泛用于连接外部音频设备。通过 SAI,MCU 可以与放大器、ADC、DAC、音频处理器等外部音频芯片进行高效的数据交互。本文将介绍 STM32 F7 系列 SAI 的主要功能及其典型使用方法。
1. SAI 简介
SAI(Serial Audio Interface,串行音频接口) 在 STM32 中被广泛用于连接外部音频设备。通过 SAI,MCU 可以与放大器、ADC、DAC、音频处理器等外部音频芯片进行高效的数据交互。
本文将介绍 STM32 F7 系列 SAI 的主要功能及其典型使用方法。
1.1 SAI 内部结构

- 双子模块架构: SAI 由两个相互独立的音频子模块组成,每个子模块都带有专用的时钟发生器。
- 移位寄存器: 每个子模块包含一个32位移位寄存器,用于音频数据的串行输入与输出。
- FIFO 缓存: 每个子模块内置 FIFO,既可由 CPU 直接访问,也可通过 DMA 实现高效传输,降低 CPU 负担。
- I/O 管理: 每个子模块可控制4根专用信号线:
- FS_x:帧同步
- SCK_x:位时钟
- SD_x:串行数据
- MCLK_x:主时钟
1.2 工作模式:
每个子模块既可作为发送器(Transmitter),也可作为接收器(Receiver),并支持主模式(Master) 和从模式(Slave):
- 主模式:子模块自行生成 SCK_x 和 FS_x 信号。
- 从模式:子模块使用来自外部或另一子模块的 SCK_x 和 FS_x 信号。
⚠️ 特殊情况:在 AC’97 协议模式下,即使子模块作为从机使用外部时钟,FS 信号仍然由 SAI 输出。
1.3 支持的协议模式
- Free Protocol Mode(可配置 I²S、PCM、TDM 等协议)
-
I2S
I²S 是一种用于在集成电路组件间传输数字音频信号的串行接口协议,通常用于 MCU、DAC、ADC 或音频处理器之间。其特点是将音频信号以脉冲编码调制(PCM)形式进行传输,支持立体声(左右声道)数据。
信号线:
- 串行时钟 (SCK),又称 位时钟 (BCLK)
- 字选择 (WS),又称左右声道时钟 (LRCLK) 或帧同步 (FS)。
- WS = 0:左声道
- WS = 1:右声道
- 串行数据 (SD)
-
TDM
- 通过时间划分时隙,将多路信号复用到同一物理链路上。
- 每帧划分为多个 Slot(时隙)。
- 每个通道(声道)分配固定 Slot,数据在对应 Slot 中发送。
-
- S/PDIF 接口模式(符合 IEC 60958 标准)
- AC’97 模式(兼容 Audio Codec '97 标准)
1.4 帧同步信号
- 用于标识一帧音频数据的起始位置
- I²S 协议:表示左右声道切换(L/R Clock)
- TDM/PCM 协议:标记帧开始,帮助接收端正确区分 Slot
1.5 Slot配置
- Slot:音频帧的基本单元,承载实际音频数据
- 每帧由若干 Slot 构成,STM32 SAI 最多支持 16 个 Slot
- Slot 大小可独立配置(16bit、24bit、32bit 等)
- TDM 模式:不同通道数据依次放入不同 Slot
- I²S/PCM 模式:常用两个 Slot(左/右声道)
2. 实验
2.1 实验准备
必备开发软件:STM32CubeIDE、STM32CubeMX
实验目标:掌握SAI基本操作,通过BSP软件包实现 Audio Record & Play。
硬件需求:STM32 开发板(如 STM32F746G-DISCO)
2.2 实验步骤
-
打开 STM32CubeMX,选择目标芯片或开发板,创建新工程。
-
配置 PLLM 为 25,以产生 1 MHz 时钟用于 SAI 时钟源。
-
生成代码并导入 STM32CubeIDE.
2.3 实验代码
-
从下载的板子软件包里找到需要的BSP文件,添加到工程里:
-
设置工程属性,将 BSP 头文件目录添加到包含路径中。
-
从下载的板子软件包里找到以下hal driver文件,添加到工程的hal driver目录下:
stm32fxx_hal_i2s.h
stm32fxx_hal_sai.h
stm32fxx_hal_sai_ex.h
stm32fxx_hal_sdram.h
stm32fxx_hal_tim.h
stm32fxx_hal_tim_ex.h
stm32fxx_hal_uart.h
stm32fxx_hal_uart_ex.h
stm32fxx_hal_ll_fmc.h
stm32fxx_hal_i2s.c
stm32fxx_hal_sai.c
stm32fxx_hal_sai_ex.c
stm32fxx_hal_sdram.c
stm32fxx_hal_tim.c
stm32fxx_hal_tim_ex.c
stm32fxx_hal_uart.c
stm32fxx_hal_uart_ex.c
stm32fxx_hal_ll_fmc.c
- 在
stm32f7xx_hall_conf.h
中启用以下宏定义
#define HAL_SDRAM_MODULE_ENABLED
#define HAL_I2S_MODULE_ENABLED
#define HAL_SAI_MODULE_ENABLED
#define HAL_TIM_MODULE_ENABLED
#define HAL_UART_MODULE_ENABLED
- 修改Flash LD文件配置SDRAM:
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 320K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 1024K
SD_RAM (xrw) : ORIGIN = 0xC0000000, LENGTH = 8192K
}
.sd_ram :
{
*(.SD_RAM);
} >SD_RAM
- 包含头文件与定义全局变量:
#include "stm32746g_discovery_audio.h"
#include "stm32746g_discovery.h"
#define PCM_BUFFER_SIZE (1024)
#define RECORD_BUFFER_SIZE (409600)
#define VOLUME_LEVEL (70)
static uint16_t pcm_buffer[PCM_BUFFER_SIZE];
__attribute__((section(".SD_RAM"))) uint16_t record_buffer[RECORD_BUFFER_SIZE]; // 把录音buffer放在sdram
static uint32_t record_buffer_size = 0;
uint32_t play_size = 0;
- 实现回调函数:
// 录音 buffer 完整回调
void BSP_AUDIO_IN_TransferComplete_CallBack(void) {
memcpy(record_buffer + record_buffer_size, pcm_buffer + PCM_BUFFER_SIZE / 2, PCM_BUFFER_SIZE);
record_buffer_size += PCM_BUFFER_SIZE / 2;
if(record_buffer_size >= RECORD_BUFFER_SIZE)
{
BSP_AUDIO_IN_Stop(CODEC_PDWN_SW);
}
}
// 录音 buffer 半满回调
void BSP_AUDIO_IN_HalfTransfer_CallBack(void) {
memcpy(record_buffer + record_buffer_size, pcm_buffer, PCM_BUFFER_SIZE);
record_buffer_size += PCM_BUFFER_SIZE / 2;
if(record_buffer_size >= RECORD_BUFFER_SIZE)
{
BSP_AUDIO_IN_Stop(CODEC_PDWN_SW);
}
}
// 播放 buffer 完整回调
void BSP_AUDIO_OUT_TransferComplete_CallBack(void)
{
memcpy(pcm_buffer + PCM_BUFFER_SIZE/2, record_buffer + play_size, PCM_BUFFER_SIZE);
play_size+=PCM_BUFFER_SIZE/2;
if(play_size >= RECORD_BUFFER_SIZE)
{
BSP_AUDIO_OUT_Stop(CODEC_PDWN_SW);
}
}
// 播放 buffer 半满回调
void BSP_AUDIO_OUT_HalfTransfer_CallBack(void)
{
memcpy(pcm_buffer, record_buffer + play_size, PCM_BUFFER_SIZE);
play_size+=PCM_BUFFER_SIZE/2;
if(play_size >= RECORD_BUFFER_SIZE)
{
BSP_AUDIO_OUT_Stop(CODEC_PDWN_SW);
}
}
- 初始化与录放音:
// 初始化sdram
BSP_SDRAM_Init();
// 初始化音频输入
if(BSP_AUDIO_IN_Init(DEFAULT_AUDIO_IN_FREQ, // 音频采样频率
DEFAULT_AUDIO_IN_BIT_RESOLUTION, // 每个采样点的数据位数
DEFAULT_AUDIO_IN_CHANNEL_NBR // 声道数
) == AUDIO_OK)
{
// 开始录音
BSP_AUDIO_IN_Record((uint16_t *) (pcm_buffer), PCM_BUFFER_SIZE);
}
// 等待录音完成
while(record_buffer_size < RECORD_BUFFER_SIZE){};
// 初始化音频输出
if(BSP_AUDIO_OUT_Init(OUTPUT_DEVICE_BOTH, VOLUME_LEVEL , DEFAULT_AUDIO_IN_FREQ) == AUDIO_OK)
{
// 初始化播放buffer
memcpy(pcm_buffer, record_buffer, PCM_BUFFER_SIZE*2);
play_size += PCM_BUFFER_SIZE;
// 配置两个slot
BSP_AUDIO_OUT_SetAudioFrameSlot(CODEC_AUDIOFRAME_SLOT_02);
// 开始播放
BSP_AUDIO_OUT_Play((uint16_t*)pcm_buffer, PCM_BUFFER_SIZE*2);
}
更多推荐
所有评论(0)