第四十五章:音频环回实验

本文对应原书第1189页起,第五篇「实战篇」。

本章在讲什么?(先看这个)

全书最后的技术实验,我们把 FPGA 的能力扩展到声音世界:驱动板载的 ES8388 音频编解码芯片(Audio CODEC),实现音频环回——用音频线把手机/电脑播放的音乐送进开发板的 LINE_IN 接口,ES8388 的 ADC 把模拟声音变成数字信号交给 FPGA,FPGA 原样转回给 ES8388 的 DAC,再从耳机口和喇叭播放出来。

这一章涉及三种接口的协同:IIC(配置芯片寄存器,复用 EEPROM 实验的驱动)、I2S(传输音频数据,本章新学)、PLL 时钟(产生音频主时钟)。它是后续做音频播放器、录音机、语音处理的必经之路。

核心概念拆解

为什么需要专用音频芯片

FPGA 的 IO 只能输出高低电平,无法直接输出保真的模拟声音。Audio CODEC = ADC + DAC + DSP 处理 + 统一接口,专用于把模拟音频和数字音频互相转换。ES8388 是一颗低功耗高品质立体声 CODEC:

  • 采样率支持 8kHz~96kHz(本章用 48kHz);
  • 采样位数支持 16/18/20/24 位(本章用 16 位);
  • 两路立体声输入(LIN1/RIN1、LIN2/RIN2)、两路立体声输出(LOUT1/ROUT1、LOUT2/ROUT2);
  • 支持 EQ 均衡器、3D 音效、ALC 自动电平控制(Automatic Level Control);
  • 工作电压低至 1.42V,音量低时自动降压省电。

图45.1.1 ES8388框图

四组接口一览

  1. 输入接口:LIN1/RIN1 与 LIN2/RIN2 两路立体声输入。本开发板上麦克风接 LIN1/RIN1,音频线输入接 LIN2/RIN2
  2. 输出接口:LOUT1/ROUT1 接耳机(PHONE 口),LOUT2/ROUT2 经 MD8002A 功放芯片(最大 3W)驱动板载 8Ω/2W 喇叭。ES8388 内部不带功放,直接接喇叭带不动;
  3. 数字音频接口(I2S,5 根线)——驱动开发者的重点关注对象:
  4. MCLK:主时钟,由 FPGA 提供,通常 12.288MHz,最高不超过 51.2MHz;
  5. SCLK/BCLK:位时钟,数据同步用;
  6. LRCK:左右声道对齐时钟(帧时钟),频率就是采样率
  7. ASDOUT:ADC 数据输出(芯片→FPGA);
  8. DSDIN:DAC 数据输入(FPGA→芯片);
  9. 配置接口:SPI 或 IIC。开发板把 CE 引脚固定接地,因此只能用 IIC 模式,器件地址为 7'b0010000(7'h10)

图45.1.2 IIC器件地址的描述

I2S 总线:音频数据的"交通规则"

I2S(Inter-IC Sound) 是飞利浦制定的电影级音频总线标准。三条关键规则:

  1. LRCK 电平选声道:低电平传左声道,高电平传右声道,LRCK 频率=采样率;
  2. BCLK 频率公式:BCLK = 2 × 采样率 × 采样位数。例:48kHz × 32 位立体声 → BCLK = 2×48000×32 = 3.072MHz。本章实现方式是 BCLK = 64 × LRCK(48k × 64 = 3.072MHz);
  3. 数据延迟 1 个 BCLK最高位(MSB)总是出现在 LRCK 变化后的第 2 个 BCLK 上升沿,高位在前。收发双方都按这个约定对齐,这正是 audio_receive/audio_send 两个模块所有"打一拍"操作的依据。

图45.1.3 I2S总线音频传输格式

主从模式:主模式下 ES8388 自己产生 SCLK/LRCK;从模式下由 FPGA 提供这两个时钟。本章采用从模式,因为从模式下采样率配置最灵活。从模式下的采样率约束:Single Speed 模式支持 8k~50kHz,MCLK/LRCK 比值可取 256/384/512/768/1024。本章取:

  • MCLK = 12.288MHz(PLL 输出)
  • LRCK = MCLK / 256 = 48kHz(即采样率)
  • BCLK = LRCK × 64 = 3.072MHz

ES8388 寄存器配置要点

ES8388 有 53 个用户可配置寄存器,通过 IIC 写入。本章共配置 29 个寄存器,关键的几步:

  1. R0:先写 0x80 软复位,再写 0x00 结束复位;
  2. R1:bit3=0 打开模拟电源(=1 则模拟部分掉电,听不到声音);
  3. R2:全部位写 0,让 ADC 和 DAC 都正常工作;
  4. R3/R4:分别使能 ADC、DAC 电源和输出通道;
  5. R8:bit7=0 设置从模式
  6. R9:MIC 增益(3dB 一档,最大 24dB);
  7. R12/R23:ADC/DAC 的数据格式——wl=011(16 位)+ I2S 格式
  8. R13/R24:Single Speed + MCLK/Fs=256;
  9. R26/R27:DAC 音量衰减(调音量就改这里);
  10. R46~R49:耳机音量(PHONE_VOLUME)和喇叭音量(SPEAK_VOLUME),范围 0~33。

图45.1.6 从机模式下的采样率

程序架构:七模块协同

图45.4.1 ES8388音频环回实验系统架构图

  • 时钟 IP 核(u_clk_wiz_0):PLL 输出 12.288MHz 作为 MCLK;
  • 从机模式信号产生模块(slave_signal_gen):由 MCLK 分频出 BCLK 和 LRCK:
parameter FREQ_DIV = 11'd256;    // MCLK/LRCK = 256 → 采样率48kHz

// LRCK:MCLK 256 分频(计满 FREQ_DIV/2-1 翻转)
always @(posedge clk or negedge rst_n) begin
    if (lrc_div_cnt == FREQ_DIV[10:1] - 1'b1) begin   // 移位代替除以2
        lrc_div_cnt <= 1'b0;
        aud_lrc <= ~aud_lrc;
    end
    else
        lrc_div_cnt <= lrc_div_cnt + 1'b1;
end

// BCLK:频率是 LRCK 的 64 倍,即 MCLK 4 分频(FREQ_DIV/64)
always @(posedge clk or negedge rst_n) begin
    if (bclk_div_cnt == FREQ_DIV[10:7] - 1'b1) begin  // 256/64=4, 移位代替除法
        bclk_div_cnt <= 1'b0;
        aud_bclk <= ~aud_bclk;
    end
    else
        bclk_div_cnt <= bclk_div_cnt + 1'b1;
end

想改采样率只需改 FREQ_DIV 或 MCLK 频率,非常灵活。

  • 音频接收模块(audio_receive):I2S 串并转换。核心思路:对 LRCK 打一拍取边沿(lrc_edge = aud_lrc ^ aud_lrc_d0),边沿到来时 rx_cnt 归零;每个 BCLK 上升沿把 aud_adcdat 存入 adc_data_t[WL-1-rx_cnt](MSB 先入);计到 WL 时把整个寄存器传给 adc_data 并脉冲 rx_done:
assign lrc_edge = aud_lrc ^ aud_lrc_d0;                 // 边沿检测

always @(posedge aud_bclk or negedge rst_n) begin
    if (rx_cnt < WL)
        adc_data_t[WL - 1'd1 - rx_cnt] <= aud_adcdat;   // 串并转换,MSB在前
end
  • 音频发送模块(audio_send):并串转换。与接收模块对称,唯一区别是数据在 BCLK 下降沿输出——这样 ES8388 才能在"LRCK 变化后第 2 个 BCLK 上升沿"正确采样到稳定数据:
always @(negedge aud_bclk or negedge rst_n) begin    // 注意:下降沿!
    if (tx_cnt < WL)
        aud_dacdat <= dac_data_t[WL - 1'd1 - tx_cnt];
    else
        aud_dacdat <= 1'b0;
end
  • IIC 配置模块(i2c_reg_cfg):按顺序把 29 个寄存器值经 i2c_data 发出。三个关键计数器:start_init_cnt(上电延时等待电源稳定)、cnt_delay(复位后延时 800ms 再配其他寄存器——不做这个延时会配置失败!)、init_reg_cnt(寄存器索引)。IIC 时钟设为 250kHz(不超过 400kHz)。
  • 音频配置模块(audio_config):封装 i2c_dri(复用 EEPROM 实验)+ i2c_reg_cfg。器件地址 SLAVE_ADDR=7'h10,BIT_CTRL=0(8 位寄存器地址)。
  • 音频控制模块(audio_ctrl):把配置/接收/发送三个模块再封装,并把接收的 adc_data 直接接到发送端 dac_data——环回就在这一根连线
  • 顶层(audio_loopback):例化 PLL、slave_signal_gen、audio_ctrl。

下载验证

管脚:aud_bclk=M18、aud_lrc=L20、aud_adcdat=M17、aud_scl=E18、aud_sda=F17、aud_dacdat=G17、aud_mclk=E19。连接:音频线一端接播放设备、另一端接开发板 LINE_IN;耳机接 PHONE 口。下载后用手机播歌,喇叭和耳机应同步播放音乐——环回成功。

图45.5.2 实物连接图

图片对照

  • 芯片原理:图 45.1.1(ES8388 框图)、图 45.1.2(IIC 器件地址)、图 45.1.3(I2S 传输格式)、图 45.1.4/45.1.5(主/从模式示意)、图 45.1.6(从模式采样率表)。
  • 硬件:图 45.2.1(硬件实物图)、图 45.2.2(ES8388 接口原理图,注意 MD8002A 功放)。
  • 架构:图 45.4.1(系统架构图)、图 45.4.2(整体模块框图)。
  • 时钟与信号:图 45.4.3/45.4.4(时钟 IP 及配置)、图 45.4.5(从机信号产生模块)。
  • 收发模块:图 45.4.6/45.4.7(接收模块框图与波形)、图 45.4.8(ILA 实测接收波形)、图 45.4.9(串并转换过程)、图 45.4.10/45.4.11(发送模块框图与波形)、图 45.4.12(ILA 实测发送波形)、图 45.4.13(并串转换示意)。
  • 配置:图 45.4.14/45.4.15(IIC 配置模块框图与波形)、图 45.4.16/45.4.17(配置开始/结束的实测波形)。
  • 封装与顶层:图 45.4.18(音频配置模块)、图 45.4.19(音频控制模块)、图 45.4.20(顶层框图)。
  • 上板:图 45.5.1(音频线外观)、图 45.5.2(实物连接图)。

初学者容易踩的坑

  1. 跳过复位后的 800ms 延时:ES8388 结束软复位后必须延时(cnt_delay 计到 800_000)再配置其余寄存器,否则部分寄存器写入失败,表现为"无声"或杂音。
  2. 发送模块用 BCLK 上升沿输出数据:会导致 ES8388 在第 2 个 BCLK 上升沿采到正在变化的数据。必须用下降沿更新 aud_dacdat。
  3. 忘了数据延迟 1 个 BCLK 的约定:收发代码里所有打一拍、取边沿的操作都是为这个约定服务的,删除任何一个边沿检测,声道就会错位或数据错位半个字节。
  4. 音量参数超出 0~33 范围:PHONE_VOLUME/SPEAK_VOLUME 越大音量越大,但超范围会写入无效值,音量表现异常。
  5. 喇叭不响就以为芯片坏了:先确认喇叭是经 MD8002A 功放驱动(LOUT2/ROUT2),检查 R4 寄存器是否使能了 LOUT2/ROUT2 输出通道。
  6. IIC 与 EEPROM 的引脚复用冲突:音频的 IIC 接口与其他 IIC 设备(如 EEPROM)引脚是复用的,调试时要留意是否有其他设备抢总线。
  7. 采样率与 MCLK 比值不匹配:改 FREQ_DIV 时必须保证 MCLK/LRCK 的比值落在数据手册允许的集合里(Single Speed 是 256/384/512/768/1024),乱设比值芯片无法锁定时钟。

小结 & 下一步

本章集齐了音频系统的三块拼图:IIC 配置(让芯片按预期工作)、I2S 时序(让数据正确流动)、时钟体系(MCLK/LRCK/BCLK 的分频关系)。核心代码其实只有两个对称的模块——audio_receive 串并转换、audio_send 并串转换,其余都是复用前面实验的积累。"打一拍取边沿 + MSB 先行 + 下降沿发送"这三板斧,适用于一切 I2S 设备驱动。

至此,全书的 45 个实验讲解完毕。回望这一路:从点亮一个 LED 到驱动高速 ADC/DAC、从单字节串口到自定义协议、从拨码开关到音频编解码芯片,你已经拥有了完整的 FPGA 实战技能图谱。下一步可以尝试书中的拓展训练(如把音频字长改成 24 位),或者选一个自己感兴趣的方向深入:图像处理、通信调制、SoC 开发(ARM+FPGA 协同),每一扇门都已经为你打开了。