[应用相关] STM32 SAI 基础与实战

[复制链接]
517|1
观海 发表于 2025-10-11 15:38 | 显示全部楼层 |阅读模式
1. SAI 简介
SAI(Serial Audio Interface,串行音频接口) 在 STM32 中被广泛用于连接外部音频设备。通过 SAI,MCU 可以与放大器、ADC、DAC、音频处理器等外部音频芯片进行高效的数据交互。

本文将介绍 STM32 F7 系列 SAI 的主要功能及其典型使用方法。

1.1 SAI 内部结构

8139268ea095f3102f.png

图片来源: STM 32 手册
双子模块架构: 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

8222968ea09530d550.png

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 时钟源。

5885768ea094883374.png

生成代码并导入 STM32CubeIDE.

2.3 实验代码
从下载的板子软件包里找到需要的BSP文件,添加到工程里:

3170168ea094018d32.png

设置工程属性,将 BSP 头文件目录添加到包含路径中。

2035468ea093856954.png

从下载的板子软件包里找到以下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);
    }


————————————————
版权声明:本文为CSDN博主「ting_zh」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ting_zh/article/details/151711619

jf101 发表于 2025-10-12 22:20 | 显示全部楼层
STM32 中被广泛用于连接外部音频设备
您需要登录后才可以回帖 登录 | 注册

本版积分规则

158

主题

4410

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部