一、SAI 通讯原理基础
1.1 SAI 概述
SAI(Serial Audio Interface) 串行音频接口是 STM32 系列单片机中专门为音频和高精度传感器应用设计的高性能外设。它提供了两个完全独立的音频子块 (SAI_A 和 SAI_B),每个子块都可以独立配置为主机或从机,支持全双工通信。
SAI 相比传统 I2S 接口的核心优势:
- 支持TDM 时分复用模式,最多可连接 16 个设备
- 更灵活的帧和时隙配置
- 支持 8/10/16/20/24/32 位数据宽度
- 内置 FIFO 缓冲区 (8 个 32 位字)
- 支持 DMA 传输,减轻 CPU 负担
1.2 SAI 核心架构
每个 SAI 子块包含以下关键组件:
- 时钟生成单元:产生串行时钟 (SCK) 和帧同步信号 (FS)
- 帧同步单元:生成和检测帧同步信号
- 数据串行化 / 反串行化单元:并行数据与串行数据转换
- FIFO 缓冲区:8 个 32 位深度,用于数据缓存
- DMA 接口:支持高速数据传输
- 中断控制器:提供多种中断源
1.3 SAI 工作模式详解
SAI 支持五种主要工作模式:
| 模式 | 描述 | 典型应用 |
|---|---|---|
| Free 协议模式 | 最灵活的模式,可配置为任何自定义协议 | TDM 接口传感器、自定义音频协议 |
| I2S 模式 | 标准 I2S 协议,支持 Philips、MSB、LSB 对齐 | 立体声麦克风、音频编解码器 |
| PCM 模式 | 脉冲编码调制模式,支持短帧和长帧 | 单声道麦克风、电话系统 |
| AC'97 模式 | 音频编解码器接口 | 传统音频 Codec |
| SPDIF 模式 | 索尼 / 飞利浦数字接口 | 数字音频传输 |
1.4 TDM 模式核心原理
TDM(Time Division Multiplexing) 时分复用模式是 SAI 最强大的功能,它允许在同一根串行数据线上传输多个通道的数据。
TDM 工作机制:
- 数据传输以帧 (Frame) 为单位
- 每个帧包含多个时隙 (Slot)
- 每个时隙对应一个物理设备 (传感器 / 麦克风)
- 帧同步信号 (FS) 在每个帧开始时触发
- 数据在串行时钟 (SCK) 的边沿被采样
- 每个设备只在自己的时隙内发送 / 接收数据
TDM 关键参数:
- 帧长度 (FRL):一个帧包含的总位数
- 时隙数量 (NBSLOT):一个帧包含的时隙数 (最多 16 个)
- 时隙大小 (SLOTSZ):每个时隙的位数 (8/16/32 位)
- 帧同步活动长度 (FSALL):FS 信号有效持续的位数
- 帧同步偏移 (FSOFF):FS 信号与第一个数据位的偏移量
二、SAI 关键寄存器详解
2.1 全局配置寄存器 (SAI_GCR)
cpp
地址偏移: 0x00
复位值: 0x00000000
- SYNCOUT1:0 (位 1-0): 同步输出选择
- 00: 无同步输出
- 01: SAI_A 作为同步主
- 10: SAI_B 作为同步主
- SYNCIN1:0 (位 3-2): 同步输入选择
- 00: 无同步输入
- 01: 同步于 SAI_A
- 10: 同步于 SAI_B
2.2 子块配置寄存器 1 (SAI_xCR1)
cpp
地址偏移: 0x04 + 0x0400 * x (x=A/B)
复位值: 0x00000040
- MODE1:0 (位 1-0): 模式选择
- 00: 主发送器 (Master Transmitter)
- 01: 主接收器 (Master Receiver)
- 10: 从发送器 (Slave Transmitter)
- 11: 从接收器 (Slave Receiver)
- PRTCFG1:0 (位 3-2): 协议配置
- 00: Free 协议 (用于 TDM)
- 01: SPDIF 协议
- 10: AC'97 协议
- 11: 保留
- DS3:0 (位 7-4): 数据大小
- 0000: 8 位
- 0001: 10 位
- 0010: 16 位
- 0011: 20 位
- 0100: 24 位
- 0101: 32 位
- LSBFIRST (位 8): 最低位优先
- 0: MSB 优先 (默认)
- 1: LSB 优先
- CKSTR (位 9): 时钟极性
- 0: 数据在 SCK 下降沿发送,上升沿采样
- 1: 数据在 SCK 上升沿发送,下降沿采样
- SYNCEN1:0 (位 11-10): 同步使能
- 00: 异步模式
- 01: 内部同步
- 10: 外部同步
- MONO (位 12): 单声道模式
- 0: 立体声
- 1: 单声道
- SAICLK2:0 (位 18-16): SAI 时钟源选择
- 000: PLLSAI1_P
- 001: PLLSAI2_P
- 010: PLL3_P
- 011: I2S_CKIN
- 100: HSI
- MCKDIV5:0 (位 23-19): 主时钟分频系数
- 0-63: 分频值
2.3 子块配置寄存器 2 (SAI_xCR2)
cpp
地址偏移: 0x08 + 0x0400 * x (x=A/B)
复位值: 0x00000000
- FTH2:0 (位 2-0): FIFO 阈值
- 000: FIFO 空
- 001: 1/4 FIFO
- 010: 1/2 FIFO
- 011: 3/4 FIFO
- 100: FIFO 满
- FFLUS (位 3): FIFO 刷新
- 0: 无操作
- 1: 刷新 FIFO
- TRIS (位 4): 三态管理
- 0: 数据引脚在非活动时隙保持驱动
- 1: 数据引脚在非活动时隙进入高阻
- MUTE (位 5): 静音使能
- 0: 正常操作
- 1: 发送静音值
2.4 帧配置寄存器 (SAI_xFRCR)
cpp
地址偏移: 0x0C + 0x0400 * x (x=A/B)
复位值: 0x00000007
- FRL7:0 (位 7-0): 帧长度
- 0-255: 帧长度 = FRL + 1 位
- FSALL6:0 (位 14-8): 帧同步活动长度
- 0-127: FS 有效长度 = FSALL + 1 位
- FSDEF (位 16): 帧同步定义
- 0: FS 是开始信号
- 1: FS 是通道识别信号
- FSPOL (位 17): 帧同步极性
- 0: FS 低电平有效
- 1: FS 高电平有效
- FSOFF (位 18): 帧同步偏移
- 0: FS 与第一个数据位同时开始
- 1: FS 比第一个数据位提前一个 SCK 周期
2.5 时隙配置寄存器 (SAI_xSLOTR)
cpp
地址偏移: 0x10 + 0x0400 * x (x=A/B)
复位值: 0x00000000
- FBOFF4:0 (位 4-0): 第一个位偏移
- 0-31: 第一个数据位的偏移量
- SLOTSZ1:0 (位 7-6): 时隙大小
- 00: 与数据大小 (DS) 相同
- 01: 16 位
- 10: 32 位
- 11: 保留
- NBSLOT3:0 (位 11-8): 时隙数量
- 0-15: 时隙数量 = NBSLOT + 1
- SLOTEN15:0 (位 31-16): 时隙使能
- 每一位对应一个时隙,1 表示使能
2.6 数据寄存器 (SAI_xDR)
cpp
地址偏移: 0x14 + 0x0400 * x (x=A/B)
复位值: 0x00000000
- 32 位读写寄存器,用于发送和接收数据
- 写入数据会被放入发送 FIFO
- 读取数据会从接收 FIFO 中取出
2.7 状态寄存器 (SAI_xSR)
cpp
地址偏移: 0x18 + 0x0400 * x (x=A/B)
复位值: 0x00000008
- OVRUDR (位 0): 溢出 / 下溢标志
- 接收模式: 1 表示 FIFO 满时又收到数据 (溢出)
- 发送模式: 1 表示 FIFO 空时需要发送数据 (下溢)
- FREQ (位 3): FIFO 请求标志
- 1 表示 FIFO 达到阈值
- FLVL2:0 (位 9-7): FIFO 级别
- 000: FIFO 空
- 001: FIFO 中有 1-2 个字
- 010: FIFO 中有 3-4 个字
- 011: FIFO 中有 5-6 个字
- 100: FIFO 中有 7-8 个字
三、STM32CubeMX SAI 配置步骤
3.1 系统时钟配置
SAI 对时钟精度要求很高,必须正确配置:
- 在 "Clock Configuration" 中找到 SAI 时钟源
- 通常选择PLLSAI1_P 或PLL3_P作为 SAI 时钟源
- 配置 PLL 分频系数,使 SAI 内核时钟为49.152MHz (48kHz 采样率) 或45.1584MHz(44.1kHz 采样率)
- 确保 MCLK 频率 = 采样率 × 256 (标准音频时钟)
3.2 SAI 基本配置
- 在 "Connectivity" 中找到 SAI 并启用
- 选择要使用的子块 (SAI_A 或 SAI_B)
- 配置模式(主发送 / 主接收 / 从发送 / 从接收)
- 配置协议(Free 协议用于 TDM)
- 配置数据大小(通常 24 位用于 MEMS 传感器)
- 配置时钟极性(根据设备手册)
- 配置帧同步参数
- 配置时隙参数
- 配置FIFO 阈值
- 启用DMA (推荐) 或中断
3.3 DMA 配置
- 点击 "Add" 添加 DMA 通道
- 方向选择Peripheral to Memory (接收) 或Memory to Peripheral(发送)
- 优先级选择High 或Very High
- 数据宽度选择Word(32 位)
- 启用循环模式(Circular Mode)
- 启用内存增量模式(Memory Increment)
- 禁用外设增量模式(Peripheral Increment)
四、TDM 接口 MEMS 加速度计读写实现
4.1 硬件连接
以ADXL355-TDM三轴加速度计为例:
- SAI_SCK → ADXL355_SCLK
- SAI_FS → ADXL355_FSYNC
- SAI_SD → ADXL355_SDO
- SAI_MCLK → ADXL355_MCLK (可选)
- VCC → 3.3V
- GND → GND
注意:ADXL355-TDM 支持最多 4 个传感器共享同一总线,每个传感器占用一个时隙。
4.2 STM32CubeMX 配置
- 启用 SAI ,选择SAI1 Block A
- 模式 :Master Receive(主接收)
- Protocol :Free Protocol(自由协议)
- Data Size :24 Bits
- First Bit :MSB First
- Clock Strobing Edge :Falling Edge(数据在下降沿发送,上升沿采样)
- Synchronization :Asynchronous(异步)
- MCLK Output :Enabled(如果需要提供主时钟)
- Frame Configuration :
- Frame Length:31(32 位 / 帧)
- Frame Sync Active Length:0(1 位有效)
- Frame Sync Definition:Start of Frame
- Frame Sync Polarity:Active High
- Frame Sync Offset:No Offset
- Slot Configuration :
- First Bit Offset:0
- Slot Size:32 Bits
- Number of Slots:3(4 个时隙)
- Slot Enable:0x000F(使能前 4 个时隙)
- FIFO Threshold :4 FIFO
- DMA 配置:添加 DMA 通道,循环模式,32 位数据宽度
4.3 完整代码实现
cpp
#include "stm32h7xx_hal.h"
#include <stdio.h>
// 全局变量
SAI_HandleTypeDef hsai_BlockA1;
DMA_HandleTypeDef hdma_sai1_a;
#define ACCEL_CHANNELS 4
#define ACCEL_BUFFER_SIZE ACCEL_CHANNELS * 2 // 双缓冲
uint32_t accel_tdm_buffer[ACCEL_BUFFER_SIZE];
int32_t accel_data[ACCEL_CHANNELS][3]; // [通道][X/Y/Z]
uint8_t accel_data_ready = 0;
// SAI接收完成回调函数
void HAL_SAI_RxCpltCallback(SAI_HandleTypeDef *hsai)
{
if(hsai->Instance == SAI1_Block_A)
{
accel_data_ready = 1;
}
}
// 初始化SAI TDM加速度计
void SAI_TDM_Accel_Init(void)
{
// 启动DMA循环接收
HAL_SAI_Receive_DMA(&hsai_BlockA1, (uint8_t*)accel_tdm_buffer, ACCEL_BUFFER_SIZE);
}
// 解析加速度数据
void Parse_Accel_Data(void)
{
if(!accel_data_ready) return;
accel_data_ready = 0;
// 处理每个通道
for(int ch=0; ch<ACCEL_CHANNELS; ch++)
{
// ADXL355输出20位有符号数,左对齐在32位时隙中
// 数据格式: [20位数据][12位0]
int32_t raw_data = (int32_t)(accel_tdm_buffer[ch] << 4) >> 12;
// 分解X/Y/Z轴数据(ADXL355在一个时隙内连续发送X/Y/Z)
// 注意:不同传感器的数据格式可能不同,请参考手册
accel_data[ch][0] = raw_data & 0xFFFFF; // X轴
accel_data[ch][1] = (raw_data >> 20) & 0xFFFFF; // Y轴
accel_data[ch][2] = (raw_data >> 40) & 0xFFFFF; // Z轴
// 符号扩展
if(accel_data[ch][0] & 0x80000) accel_data[ch][0] |= 0xFFF00000;
if(accel_data[ch][1] & 0x80000) accel_data[ch][1] |= 0xFFF00000;
if(accel_data[ch][2] & 0x80000) accel_data[ch][2] |= 0xFFF00000;
// 转换为g值(ADXL355 ±8g范围: 4μg/LSB)
float x_g = accel_data[ch][0] * 0.000004f;
float y_g = accel_data[ch][1] * 0.000004f;
float z_g = accel_data[ch][2] * 0.000004f;
printf("Channel %d: X=%.3fg, Y=%.3fg, Z=%.3fg\r\n", ch, x_g, y_g, z_g);
}
}
// 主函数
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_SAI1_Init();
SAI_TDM_Accel_Init();
while(1)
{
Parse_Accel_Data();
HAL_Delay(10);
}
}
五、TDM 接口 MEMS 麦克风拾音实现
5.1 硬件连接
以INMP441-TDM数字 MEMS 麦克风为例:
- SAI_SCK → INMP441_SCK
- SAI_FS → INMP441_WS
- SAI_SD → INMP441_SD
- SAI_MCLK → INMP441_MCLK (可选)
- VCC → 3.3V
- GND → GND
- L/R → GND (左声道) 或 VCC (右声道)
注意:INMP441-TDM 支持最多 8 个麦克风共享同一总线,每个麦克风占用一个时隙。
5.2 STM32CubeMX 配置
- 启用 SAI ,选择SAI1 Block A
- 模式 :Master Receive(主接收)
- Protocol :Free Protocol(自由协议)
- Data Size :24 Bits
- First Bit :MSB First
- Clock Strobing Edge :Rising Edge(数据在上升沿发送,下降沿采样)
- Synchronization :Asynchronous(异步)
- MCLK Output :Enabled
- Frame Configuration :
- Frame Length:255(256 位 / 帧)
- Frame Sync Active Length:0(1 位有效)
- Frame Sync Definition:Start of Frame
- Frame Sync Polarity:Active High
- Frame Sync Offset:No Offset
- Slot Configuration :
- First Bit Offset:0
- Slot Size:32 Bits
- Number of Slots:7(8 个时隙)
- Slot Enable:0x00FF(使能前 8 个时隙)
- FIFO Threshold :8 FIFO
- DMA 配置:添加 DMA 通道,循环模式,32 位数据宽度
5.3 完整代码实现
cpp
#include "stm32h7xx_hal.h"
#include <stdio.h>
#include <string.h>
// 全局变量
SAI_HandleTypeDef hsai_BlockA1;
DMA_HandleTypeDef hdma_sai1_a;
#define MIC_CHANNELS 8
#define AUDIO_SAMPLE_RATE 48000
#define AUDIO_BUFFER_SIZE 1024 // 每个通道的采样点数
#define TOTAL_BUFFER_SIZE MIC_CHANNELS * AUDIO_BUFFER_SIZE
uint32_t mic_tdm_buffer[TOTAL_BUFFER_SIZE];
int32_t audio_buffer[MIC_CHANNELS][AUDIO_BUFFER_SIZE];
uint8_t audio_buffer_ready = 0;
uint8_t audio_buffer_half = 0; // 1: 前半部分, 2: 后半部分
// SAI半接收完成回调函数
void HAL_SAI_RxHalfCpltCallback(SAI_HandleTypeDef *hsai)
{
if(hsai->Instance == SAI1_Block_A)
{
audio_buffer_half = 1;
audio_buffer_ready = 1;
}
}
// SAI接收完成回调函数
void HAL_SAI_RxCpltCallback(SAI_HandleTypeDef *hsai)
{
if(hsai->Instance == SAI1_Block_A)
{
audio_buffer_half = 2;
audio_buffer_ready = 1;
}
}
// 初始化SAI TDM麦克风
void SAI_TDM_Mic_Init(void)
{
// 启动DMA循环接收
HAL_SAI_Receive_DMA(&hsai_BlockA1, (uint8_t*)mic_tdm_buffer, TOTAL_BUFFER_SIZE);
}
// 处理音频数据
void Process_Audio_Data(void)
{
if(!audio_buffer_ready) return;
audio_buffer_ready = 0;
uint32_t *src_ptr;
uint32_t start_idx;
uint32_t end_idx;
// 确定处理哪一部分缓冲区
if(audio_buffer_half == 1)
{
src_ptr = mic_tdm_buffer;
start_idx = 0;
end_idx = AUDIO_BUFFER_SIZE / 2;
}
else
{
src_ptr = mic_tdm_buffer + TOTAL_BUFFER_SIZE / 2;
start_idx = AUDIO_BUFFER_SIZE / 2;
end_idx = AUDIO_BUFFER_SIZE;
}
// 解析TDM数据
for(int sample=0; sample < AUDIO_BUFFER_SIZE/2; sample++)
{
for(int ch=0; ch<MIC_CHANNELS; ch++)
{
// INMP441输出24位有符号数,左对齐在32位时隙中
// 数据格式: [24位数据][8位0]
int32_t raw_sample = (int32_t)(src_ptr[sample * MIC_CHANNELS + ch] << 8) >> 8;
// 存储到音频缓冲区
audio_buffer[ch][start_idx + sample] = raw_sample;
}
}
// 音频处理示例:计算每个通道的RMS值
for(int ch=0; ch<MIC_CHANNELS; ch++)
{
int64_t sum_sq = 0;
for(int i=start_idx; i<end_idx; i++)
{
sum_sq += (int64_t)audio_buffer[ch][i] * audio_buffer[ch][i];
}
float rms = sqrtf((float)sum_sq / (AUDIO_BUFFER_SIZE / 2));
// 转换为dBFS(满量程分贝)
float dbfs = 20.0f * log10f(rms / 8388608.0f);
printf("Channel %d RMS: %.1f dBFS\r\n", ch, dbfs);
}
}
// 主函数
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_SAI1_Init();
SAI_TDM_Mic_Init();
while(1)
{
Process_Audio_Data();
}
}
六、SAI 各种模式完整应用示例
6.1 I2S 模式 (立体声麦克风)
CubeMX 配置:
- 模式: Master Receive
- 协议: I2S Philips
- 数据大小: 16 Bits
- 标准: I2S Philips
- 采样率: 44100 Hz
- 时钟极性: Low
代码实现:
cpp
#define I2S_BUFFER_SIZE 1024
uint16_t i2s_stereo_buffer[I2S_BUFFER_SIZE];
int16_t left_channel[I2S_BUFFER_SIZE/2];
int16_t right_channel[I2S_BUFFER_SIZE/2];
void I2S_Stereo_Init(void)
{
HAL_SAI_Receive_DMA(&hsai_BlockA1, (uint8_t*)i2s_stereo_buffer, I2S_BUFFER_SIZE);
}
void HAL_SAI_RxCpltCallback(SAI_HandleTypeDef *hsai)
{
// 分离左右声道
for(int i=0; i<I2S_BUFFER_SIZE/2; i++)
{
left_channel[i] = i2s_stereo_buffer[2*i];
right_channel[i] = i2s_stereo_buffer[2*i+1];
}
}
6.2 PCM 模式 (单声道麦克风)
CubeMX 配置:
- 模式: Master Receive
- 协议: PCM
- 数据大小: 16 Bits
- 标准: PCM Short
- 采样率: 16000 Hz
- 时钟极性: High
代码实现:
cpp
#define PCM_BUFFER_SIZE 512
uint16_t pcm_mono_buffer[PCM_BUFFER_SIZE];
void PCM_Mono_Init(void)
{
HAL_SAI_Receive_DMA(&hsai_BlockA1, (uint8_t*)pcm_mono_buffer, PCM_BUFFER_SIZE);
}
void HAL_SAI_RxCpltCallback(SAI_HandleTypeDef *hsai)
{
// 处理PCM数据
for(int i=0; i<PCM_BUFFER_SIZE; i++)
{
int16_t sample = (int16_t)pcm_mono_buffer[i];
// 处理采样点...
}
}
6.3 全双工模式 (音频编解码器)
CubeMX 配置:
- SAI_A: Master Transmit
- SAI_B: Master Receive
- 协议: I2S Philips
- 数据大小: 16 Bits
- 采样率: 44100 Hz
- 同步: SAI_B 同步到 SAI_A
代码实现:
cpp
#define AUDIO_BUFFER_SIZE 1024
uint16_t tx_buffer[AUDIO_BUFFER_SIZE];
uint16_t rx_buffer[AUDIO_BUFFER_SIZE];
void SAI_FullDuplex_Init(void)
{
// 先启动发送,再启动接收
HAL_SAI_Transmit_DMA(&hsai_BlockA1, (uint8_t*)tx_buffer, AUDIO_BUFFER_SIZE);
HAL_SAI_Receive_DMA(&hsai_BlockB1, (uint8_t*)rx_buffer, AUDIO_BUFFER_SIZE);
}
void HAL_SAI_TxCpltCallback(SAI_HandleTypeDef *hsai)
{
// 填充发送缓冲区
for(int i=0; i<AUDIO_BUFFER_SIZE; i++)
{
tx_buffer[i] = 0; // 静音
}
}
void HAL_SAI_RxCpltCallback(SAI_HandleTypeDef *hsai)
{
// 处理接收数据
memcpy(tx_buffer, rx_buffer, AUDIO_BUFFER_SIZE * 2); // 回环
}
6.4 主从同步模式 (多 SAI 同步)
CubeMX 配置:
- SAI1_A: Master Transmit (同步主)
- SAI2_A: Slave Receive (同步从)
- 全局配置: SAI1 SYNCOUT=01, SAI2 SYNCIN=01
代码实现:
cpp
void SAI_Sync_Init(void)
{
// 先启动从设备,再启动主设备
HAL_SAI_Receive_DMA(&hsai2_BlockA1, (uint8_t*)rx_buffer, BUFFER_SIZE);
HAL_SAI_Transmit_DMA(&hsai1_BlockA1, (uint8_t*)tx_buffer, BUFFER_SIZE);
}
七、常见问题与解决方案
7.1 数据丢失 / 溢出问题
现象 :SAI_SR 寄存器的 OVRUDR 位被置 1解决方案:
- 提高 DMA 优先级到最高
- 增加 FIFO 阈值 (推荐使用 FIFO 满)
- 使用双缓冲或循环缓冲
- 避免在中断中执行耗时操作
- 检查 SAI 时钟频率是否正确
7.2 数据错位 / 乱序问题
现象 :接收到的数据与预期不符解决方案:
- 检查帧同步极性和偏移
- 确认时钟极性 (CKSTR) 设置正确
- 检查数据大小和时隙大小是否匹配
- 确认第一个位偏移 (FBOFF) 设置正确
- 检查硬件连接是否正确
7.3 符号扩展问题
现象 :负数显示为很大的正数解决方案:
cpp
// 24位数据符号扩展
int32_t sample_24 = (int32_t)(raw_data << 8) >> 8;
// 20位数据符号扩展
int32_t sample_20 = (int32_t)(raw_data << 12) >> 12;
// 16位数据符号扩展
int16_t sample_16 = (int16_t)raw_data;
7.4 时钟同步问题
现象 :音频有杂音或周期性失真解决方案:
- 使用高精度的外部晶振
- 配置 PLLSAI 专门用于 SAI 时钟
- 确保主从设备使用相同的时钟源
- 避免在运行时修改 SAI 配置
八、最佳实践总结
- 时钟配置:优先使用 PLLSAI 作为 SAI 时钟源,确保时钟精度
- DMA 传输:始终使用 DMA 传输 SAI 数据,避免 CPU 轮询
- 双缓冲机制:使用半传输和全传输中断实现双缓冲
- FIFO 配置:FIFO 阈值设置为 FIFO 满,减少 DMA 请求次数
- 数据处理:在主循环中处理数据,避免在中断中处理
- 错误处理:定期检查 SAI 状态寄存器,处理溢出和错误
- 硬件设计:SAI 信号线尽量短,避免干扰,使用差分信号