STM32 SAI 通讯原理与 TDM 应用

一、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 工作机制

  1. 数据传输以帧 (Frame) 为单位
  2. 每个帧包含多个时隙 (Slot)
  3. 每个时隙对应一个物理设备 (传感器 / 麦克风)
  4. 帧同步信号 (FS) 在每个帧开始时触发
  5. 数据在串行时钟 (SCK) 的边沿被采样
  6. 每个设备只在自己的时隙内发送 / 接收数据

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 对时钟精度要求很高,必须正确配置:

  1. 在 "Clock Configuration" 中找到 SAI 时钟源
  2. 通常选择PLLSAI1_PPLL3_P作为 SAI 时钟源
  3. 配置 PLL 分频系数,使 SAI 内核时钟为49.152MHz (48kHz 采样率) 或45.1584MHz(44.1kHz 采样率)
  4. 确保 MCLK 频率 = 采样率 × 256 (标准音频时钟)

3.2 SAI 基本配置

  1. 在 "Connectivity" 中找到 SAI 并启用
  2. 选择要使用的子块 (SAI_A 或 SAI_B)
  3. 配置模式(主发送 / 主接收 / 从发送 / 从接收)
  4. 配置协议(Free 协议用于 TDM)
  5. 配置数据大小(通常 24 位用于 MEMS 传感器)
  6. 配置时钟极性(根据设备手册)
  7. 配置帧同步参数
  8. 配置时隙参数
  9. 配置FIFO 阈值
  10. 启用DMA (推荐) 或中断

3.3 DMA 配置

  1. 点击 "Add" 添加 DMA 通道
  2. 方向选择Peripheral to Memory (接收) 或Memory to Peripheral(发送)
  3. 优先级选择HighVery High
  4. 数据宽度选择Word(32 位)
  5. 启用循环模式(Circular Mode)
  6. 启用内存增量模式(Memory Increment)
  7. 禁用外设增量模式(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 配置

  1. 启用 SAI ,选择SAI1 Block A
  2. 模式Master Receive(主接收)
  3. ProtocolFree Protocol(自由协议)
  4. Data Size24 Bits
  5. First BitMSB First
  6. Clock Strobing EdgeFalling Edge(数据在下降沿发送,上升沿采样)
  7. SynchronizationAsynchronous(异步)
  8. MCLK OutputEnabled(如果需要提供主时钟)
  9. 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
  10. Slot Configuration
    • First Bit Offset:0
    • Slot Size:32 Bits
    • Number of Slots:3(4 个时隙)
    • Slot Enable:0x000F(使能前 4 个时隙)
  11. FIFO Threshold4 FIFO
  12. 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 配置

  1. 启用 SAI ,选择SAI1 Block A
  2. 模式Master Receive(主接收)
  3. ProtocolFree Protocol(自由协议)
  4. Data Size24 Bits
  5. First BitMSB First
  6. Clock Strobing EdgeRising Edge(数据在上升沿发送,下降沿采样)
  7. SynchronizationAsynchronous(异步)
  8. MCLK OutputEnabled
  9. 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
  10. Slot Configuration
    • First Bit Offset:0
    • Slot Size:32 Bits
    • Number of Slots:7(8 个时隙)
    • Slot Enable:0x00FF(使能前 8 个时隙)
  11. FIFO Threshold8 FIFO
  12. 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解决方案

  1. 提高 DMA 优先级到最高
  2. 增加 FIFO 阈值 (推荐使用 FIFO 满)
  3. 使用双缓冲或循环缓冲
  4. 避免在中断中执行耗时操作
  5. 检查 SAI 时钟频率是否正确

7.2 数据错位 / 乱序问题

现象 :接收到的数据与预期不符解决方案

  1. 检查帧同步极性和偏移
  2. 确认时钟极性 (CKSTR) 设置正确
  3. 检查数据大小和时隙大小是否匹配
  4. 确认第一个位偏移 (FBOFF) 设置正确
  5. 检查硬件连接是否正确

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 时钟同步问题

现象 :音频有杂音或周期性失真解决方案

  1. 使用高精度的外部晶振
  2. 配置 PLLSAI 专门用于 SAI 时钟
  3. 确保主从设备使用相同的时钟源
  4. 避免在运行时修改 SAI 配置

八、最佳实践总结

  1. 时钟配置:优先使用 PLLSAI 作为 SAI 时钟源,确保时钟精度
  2. DMA 传输:始终使用 DMA 传输 SAI 数据,避免 CPU 轮询
  3. 双缓冲机制:使用半传输和全传输中断实现双缓冲
  4. FIFO 配置:FIFO 阈值设置为 FIFO 满,减少 DMA 请求次数
  5. 数据处理:在主循环中处理数据,避免在中断中处理
  6. 错误处理:定期检查 SAI 状态寄存器,处理溢出和错误
  7. 硬件设计:SAI 信号线尽量短,避免干扰,使用差分信号
相关推荐
Deitymoon2 小时前
FreeRTOS——任务信息查询API
stm32·单片机·嵌入式硬件
踏着七彩祥云的小丑2 小时前
嵌入式测试学习第 24 天:串口通信详细流程、收发数据原理
单片机·嵌入式硬件
周周记笔记2 小时前
【元器件专题】MOS管内部结构
嵌入式硬件
周周记笔记2 小时前
【元器件专题】MOS管的设计应用
单片机·嵌入式硬件
一路往蓝-Anbo3 小时前
第九章:OTA 与 Flash 驱动 —— 如何用TDD验证固件升级逻辑的鲁棒性
stm32·单片机·嵌入式硬件·软件工程·tdd·ota·嵌入式测试驱动开发
zlinear数据采集卡3 小时前
电源纹波无处遁形!工业采集卡电源去耦与滤波电路深度解析
c语言·嵌入式硬件·fpga开发·自动化·硬件架构
一路往蓝-Anbo3 小时前
第十章:TDD部署 —— Ceedling 环境的深度集成
stm32·单片机·嵌入式硬件·单元测试·测试驱动开发·tdd
QiLinkOS4 小时前
合肥气链科技有限公司创办与未来技术应用
c语言·数据结构·c++·人工智能·单片机·嵌入式硬件·算法
一只肥瘫瘫4 小时前
STM32 程序升级学习笔记:Bootloader、IAP 与串口升级流程
笔记·stm32·学习