基于 FPGA 的 SD 卡音频播放与电子琴系统

一、项目概述
(可做实物一套(含代码,讲解,仿真),需要实物请联系QQ:1391074994)
本项目实现了一个基于 Intel Cyclone IV E FPGA 的 SD 卡音频播放系统,同时兼具电子琴功能。核心功能包括:
- 电子琴演奏:通过 10 个按键(7 音阶 + 低/中/高音切换),使用 DDS 合成正弦波,驱动 WM8978 音频芯片实时发声
- SD 卡录音/回放:将弹奏的音频存入 SD 卡,支持从 SD 卡读取并回放已录制的音频
- 人机交互:OLED 128x32 显示屏显示当前音阶编号与音域,21 个 LED 指示当前音符频率
硬件平台
- FPGA 芯片:Intel Cyclone IV E 系列
- 音频芯片:WM8978(I2C 配置 + I2S 数据传输)
- 存储介质:SD 卡(SPI 模式)
- 显示模块:OLED 128x32(SPI 接口)
- 输入设备:10 个按键
二、工程文件结构
rtl/
├── top_audio_sd.v # 顶层模块(系统集成)
├── top_audio_sd_tb.v # 仿真测试平台
├── audio_sd_ctrl.v # SD卡音频交互控制
├── data_gen.v # 音频数据生成与FIFO中转
├── led.v # 21个LED指示灯控制
│
├── fifo/
│ └── async_fifo.v # 异步FIFO(通用参数化模块)
│
├── key/
│ ├── key_debounce.v # 按键消抖
│ ├── key_top.v # 8按键顶层处理
│ ├── dds_sin.qip # DDS正弦波ROM IP索引
│ └── pll_clk.qip # PLL时钟模块IP索引
│
├── sd/
│ ├── sd_ctrl_top.v # SD卡顶层控制
│ ├── sd_init.v # SD卡SPI初始化
│ ├── sd_read.v # SD卡SPI读数据
│ └── sd_write.v # SD卡SPI写数据
│
├── wm8978/
│ ├── wm8978_ctrl.v # WM8978总控模块
│ ├── wm8978_config.v # WM8978配置顶层
│ ├── i2c_dri.v # I2C驱动
│ ├── i2c_reg_cfg.v # WM8978寄存器配置
│ ├── audio_send.v # I2S音频发送
│ └── audio_receive.v # I2S音频接收
│
├── wave/
│ ├── wave_ctrl.v # 频率控制核心
│ ├── da_wave_send.v # DDS正弦波发生器
│ ├── time_send.v # 时序控制/回放调度
│ └── sd_wr_ctrl.v # SD卡读写测试控制
│
└── oled/
└── OLED12832.v # OLED 128x32 显示驱动
三、顶层模块架构
top_audio_sd(顶层集成模块)
参数:
| 参数 | 值 | 说明 |
|---|---|---|
sim |
0/1 | 仿真模式开关(1 = 仿真) |
START_ADDR |
8256 | SD 卡起始扇区地址 |
AUDIO_SEC |
79076 | 录音最大扇区数 |
时钟域:
| 时钟 | 频率 | 用途 |
|---|---|---|
clk |
50MHz | 系统主时钟 |
pll_clk_sd |
25MHz | SD 卡 SPI 时钟 |
pll_clk_aud |
12.288MHz | WM8978 主时钟(MCLK) |
aud_bclk |
~3.072MHz | I2S 位时钟(WM8978 MASTER 模式) |
子模块连接图:
┌─────────────────────────┐
│ top_audio_sd │
│ │
key_in[3:0] ──────────►│ key_top (功能键) │
key_in[9:4] ──────────►│ key_top (7音阶键) │
key_in[15:10] ────────►│ key_top (音域键) │
│ │
│ wave_ctrl ──► da_wave_send ──┤
│ │ │
│ ▼ ▼
│ fre_show ────► OLED12832 dac_data ──► wm8978_ctrl
│ fre_show ────► led │
│ │ │
│ time_send data_gen │
│ (回放调度) ◄──► (FIFO中转) │
│ │ │ │
│ ▼ ▼ │
│ sd_ctrl_top ──── SD卡 │
│ (SPI读写) │
└─────────────────────────┘
四、子系统详细说明
4.1 时钟系统
- 系统时钟
clk= 50MHz(由板载晶振提供) - 通过 PLL (
pll_clk模块)产生:pll_clk_sd= 25MHz:供 SD 卡 SPI 控制器使用pll_clk_aud= 12.288MHz:WM8978 的 MCLK 主时钟
- WM8978 工作在 MASTER 模式 ,产生
aud_bclk和aud_lrc信号
4.2 SD 卡子系统
模块层次 :sd_ctrl_top → sd_init / sd_write / sd_read
sd_ctrl_top --- SD 卡顶层控制器
- 根据
sd_init_done、wr_busy、rd_busy选择对应子模块的接口信号 - 优先顺序:初始化 > 写 > 读
- 输入 25MHz 时钟,SPI 接口工作频率约 125KHz(DIV_FREQ=200)
sd_init --- SD 卡 SPI 初始化
- 状态机流程:上电等待(5000 周期) → CMD0(复位) → CMD8(电压检测 2.7~3.6V) → CMD55 → ACMD41(循环等待直到就绪,超时 25000 周期)
- 初始化完成后拉高
sd_init_done
sd_write --- SD 卡 SPI 写数据
- 发送 CMD24(WRITE_BLOCK)单块写命令
- 数据格式:起始字节
0xFE+ 512 字节数据(256×16bit)+ 2 字节 CRC - 等待 BUSY 信号释放后完成
sd_read --- SD 卡 SPI 读数据
- 发送 CMD17(READ_SINGLE_BLOCK)单块读命令
- 等待响应(0x00)和起始字节(0xFE)
- 接收 512 字节数据(256×16bit),输出
rd_val_data和rd_val_en
关键扇区地址:
| 参数 | 值 | 说明 |
|---|---|---|
START_ADDR |
8256 | 录音数据起始扇区 |
AUDIO_SEC |
79076 | 录音最大扇区数 |
4.3 WM8978 音频子系统
模块层次 :wm8978_ctrl → wm8978_config + audio_send + audio_receive
wm8978_config --- 寄存器配置
- 例化
i2c_dri(I2C 驱动)和i2c_reg_cfg(寄存器配置表) - 配置完成后拉高
cfg_done
i2c_dri --- I2C 驱动
- 从机地址
0x34(7-bit) →0x34 << 1 = 0x68(8-bit) - 三段式状态机:IDLE → 发送从机地址 → 发送寄存器地址(8/16位) → 写数据 / 读数据 → STOP
- I2C 时钟频率 250KHz(50MHz / 200)
i2c_reg_cfg --- WM8978 寄存器配置表
配置 17 个寄存器:
| 寄存器 | 功能 | 关键值 |
|---|---|---|
| R0 (0x00) | 软复位 | 0x0000 |
| R1 (0x01) | 电源管理1 | 使能 BIASEN, VMIDSEL1:0 |
| R2 (0x02) | 电源管理2 | 使能 DACL, DACR, LOUT1, ROUT1 |
| R3 (0x03) | 电源管理3 | 使能 RMIX, LMIX, LOUT2, ROUT2, RMIC, LMIC |
| R4 (0x04) | 音频接口 | I2S 16bit, 格式 |
| R6 (0x06) | 时钟生成 | MCLK / 采样率分频 |
| R7 (0x07) | 附加控制 | MASTER 模式 |
| R10 (0x0A) | DAC 控制 | DAC 软静音控制 |
| R43 (0x2B) | 左输入控制 | 0x0100 |
| R44 (0x2C) | 右输入控制 | 0x0100 |
| R47 (0x2F) | 左输出混合 | 0x0100 |
| R48 (0x30) | 右输出混合 | 0x0100 |
| R49 (0x31) | 左输出音量 | 0x0139 |
| R50 (0x32) | 右输出音量 | 0x0139 |
| R51 (0x33) | 左耳机输出 | 0x0100 + PHONE_VOLUME(20) |
| R52 (0x34) | 右耳机输出 | 0x0100 + PHONE_VOLUME(20) |
| R53 (0x35) | 左喇叭混合 | 0x0100 + SPEAK_VOLUME(40) |
| R54 (0x36) | 右喇叭混合 | 0x0100 + SPEAK_VOLUME(40) |
audio_send --- I2S 音频发送
- 字长
WL = 16bit - 在
aud_lrc边沿加载dac_data,串行输出到aud_dacdat - 发送完成输出
tx_done
audio_receive --- I2S 音频接收
- 字长
WL = 32bit - 从
aud_adcdat采集音频数据,输出adc_data[31:0]
4.4 电子琴子系统(波形生成)
模块层次 :key_top → wave_ctrl → da_wave_send
key_top --- 按键顶层
- 例化 8 个
key_debounce消抖模块 - 消抖参数:20ms(500000 周期 @ 50MHz),仿真模式 100 周期
- 输出:按键按下瞬间的单周期脉冲
key_data_1~8
key_debounce --- 按键消抖
- 检测按键状态变化后启动 20ms 倒计时
- 计数器归零后采样稳定值,输出
key_flag和key_value
wave_ctrl --- 频率控制核心
- 按键映射:10 个按键 → 21 个音符频率
| 按键组 | 功能 |
|---|---|
func_key[3:0] |
功能选择(4按键) |
play_key[3:0] |
7 个音阶按键(Do/Re/Mi/Fa/Sol/La/Si) |
tone_key[3:0] |
低音 / 中音 / 高音 切换 |
- 21 个音符频率表(对应低/中/高三个音域):
| 音阶 | 低音 | 中音 | 高音 |
|---|---|---|---|
| Do | 262Hz | 523Hz | 1046Hz |
| Re | 294Hz | 587Hz | 1174Hz |
| Mi | 330Hz | 659Hz | 1319Hz |
| Fa | 349Hz | 698Hz | 1397Hz |
| Sol | 392Hz | 784Hz | 1568Hz |
| La | 440Hz | 880Hz | 1760Hz |
| Si | 494Hz | 988Hz | 1976Hz |
- 频率控制字计算 :
freq_data = fre_act * 85 - 回放模式下从
audio_data_rd获取频率信息 - 输出
fre_show用于 OLED 和 LED 显示
da_wave_send --- DDS 正弦波发生器
- 相位累加器:
phase_addr+freq_data(频率控制字) - 从
dds_sinROM(4096×8bit)中查表输出正弦波数据 send_flag控制输出使能- 输出
da_data[7:0]送至 WM8978 DAC
4.5 音频数据交互子系统
模块 :data_gen + audio_sd_ctrl
data_gen --- 数据生成与中转
- 例化两个异步 FIFO:
u_async_fifo_audio_save:弹奏音频 → FIFO → SD 卡写u_async_fifo_audio_play:SD 卡读 → FIFO → 回放播放
- 数据位宽 16bit,深度参数化(仿真模式使用较小深度)
核心逻辑:
- 录音模式 :用户弹奏时,
audio_data_wr_vld触发数据存入 save FIFO,fifo 数据计数达到阈值后向 SD 卡写一个扇区 - 回放模式 :从 SD 卡读取扇区数据存入 play FIFO,根据
read_speed控制读取速率,输出audio_data_rd
audio_sd_ctrl --- SD 卡音频交互控制
- 参数 :
START_ADDR = 8448(录音起始扇区)AUDIO_SEC = 104422(录音扇区总数)
- 状态机管理 SD 卡扇区读写调度
- 生成 DAC 数据输出,控制 FIFO 写入计数
time_send --- 时序/回放控制
- 参数
NUM_DATA = 255 - 按键触发检测 :11 级移位寄存器实现消抖,输出
touch脉冲 time_cnt倒计时管理音频播放时长- 100ms 定时生成
audio_data_rd_req信号 - 支持
read_speed加速回放机制
4.6 显示子系统
OLED12832 --- OLED 显示驱动
- 分辨率 128×32 像素
- 内置 5×8 点阵 ASCII 字库(0-9, A-Z, a-z 及特殊符号)
- 状态机:MAIN → INIT → SCAN → WRITE → DELAY
- 显示内容:
- 当前音阶编号(0~20)
- 音域状态(低/中/高音,用 "LOW"/"MED"/"HIG" 标识)
- SPI 4 线接口(DC, SDA, SCL, RES, CS)
led --- LED 指示灯
- 21 个 LED,对应 21 个音符频率
- 当
fre_show匹配某个预定义的频率值时,对应 LED 通过send_flag闪烁 - 支持频率值:262, 294, 330, 349, 392, 440, 494, 523, 587, 659, 698, 784, 880, 988, 1046, 1174, 1319, 1397, 1568, 1760, 1976
4.7 测试/仿真
top_audio_sd_tb --- 仿真测试平台
- 提供 50MHz 主时钟(周期 20ns)
- 复位信号持续 2000ns 后释放
- 仿真按键操作:
key_play_10按键(中音 Do):按 8ms + 松 20ms + 按 4ms + 松 20mskey_play_7按键(Si):按 4ms + 松 30ms + 按 4ms
- 总仿真时间 100ms(5000000 个时钟周期)
sd_wr_ctrl --- SD 卡读写测试
- 在
sd_init_done上升沿触发 - 写操作:向扇区 2000 写入递增数据
- 读操作:读回扇区 2000 数据并校验
- 数据校验失败时拉高
error_flag - 仅在仿真模式下可通过
sim参数选择内部模块连接到此控制器
五、时钟域与接口信号汇总
时钟域分布
| 模块 | 时钟信号 | 频率 |
|---|---|---|
top_audio_sd |
clk |
50MHz |
sd_ctrl_top / SD 子系统 |
pll_clk_sd |
25MHz |
wm8978_ctrl / 音频子系统 |
clk + aud_bclk |
50MHz + ~3.072MHz |
data_gen FIFO(写端) |
clk |
50MHz |
data_gen FIFO(读端) |
pll_clk_sd |
25MHz |
顶层端口列表
| 端口名 | 方向 | 位宽 | 功能 |
|---|---|---|---|
clk |
input | 1 | 系统主时钟 50MHz |
rst_n |
input | 1 | 异步复位(低有效) |
key_in |
input | 16 | 16 个按键输入 |
sd_mosi |
output | 1 | SD 卡 SPI MOSI |
sd_miso |
input | 1 | SD 卡 SPI MISO |
sd_clk |
output | 1 | SD 卡 SPI 时钟 |
sd_cs |
output | 1 | SD 卡 SPI 片选 |
aud_bclk |
input | 1 | I2S 位时钟 |
aud_lrc |
input | 1 | I2S 左右声道 |
aud_dacdat |
output | 1 | I2S DAC 数据 |
aud_adcdat |
input | 1 | I2S ADC 数据 |
i2c_sda |
inout | 1 | I2C 数据线 |
i2c_scl |
output | 1 | I2C 时钟线 |
olcd_cs |
output | 1 | OLED 片选 |
olcd_dc |
output | 1 | OLED 数据/命令 |
olcd_scl |
output | 1 | OLED SPI 时钟 |
olcd_sda |
output | 1 | OLED SPI 数据 |
olcd_res |
output | 1 | OLED 复位 |
led |
output | 4 | LED 指示输出 |
六、系统工作流程
6.1 上电初始化
上电
├─ PLL 锁定 → 产生 sd_clk / aud_clk
├─ WM8978 初始化:I2C 配置 17 个寄存器
├─ SD 卡初始化:SPI 模式 CMD0→CMD8→CMD55→ACMD41
└─ OLED 初始化:写入配置命令 → 显示开机画面
6.2 电子琴演奏模式
按键按下 → key_top 消抖 → 单脉冲输出
└─ wave_ctrl:按键组合 → 查表得频率 → 计算 freq_data
└─ da_wave_send:DDS 累加 → ROM 查表 → da_data
└─ wm8978_ctrl:audio_send → I2S 发送 → WM8978 DAC → 发声
└─ fre_show → OLED 显示音阶 / LED 指示音符
6.3 录音流程(弹奏 → SD 卡)
弹奏 → audio_data_wr_vld → 数据存入 save FIFO
└─ data_gen:FIFO 数据量 ≥ 256 → wr_start_en
└─ sd_write:CMD24 → SPI 写入 512 字节 → wr_busy 释放
└─ wr_sec_addr++
6.4 回放流程(SD 卡 → 播放)
time_send:检测回放按键 → touch 触发 → audio_data_rd_req
└─ data_gen:FIFO 空 → rd_start_en
└─ sd_read:CMD17 → SPI 读取 512 字节 → 存入 play FIFO
└─ FIFO → audio_data_rd → wave_ctrl → da_wave_send → 发声
七、关键参数汇总
| 模块 | 参数 | 值 | 说明 |
|---|---|---|---|
top_audio_sd |
START_ADDR |
8256 | 录音起始扇区 |
top_audio_sd |
AUDIO_SEC |
79076 | 最大录音扇区数 |
audio_sd_ctrl |
START_ADDR |
8448 | 音频控制起始扇区 |
audio_sd_ctrl |
AUDIO_SEC |
104422 | 音频控制扇区总数 |
sd_init |
DIV_FREQ |
200 | SPI 时钟分频(250KHz) |
sd_init |
POWER_ON_NUM |
5000 | 上电等待周期 |
sd_init |
OVER_TIME_NUM |
25000 | ACMD41 超时周期 |
i2c_dri |
SLAVE_ADDR |
0x34 | WM8978 I2C 地址 |
i2c_dri |
CLK_FREQ |
50MHz | 驱动时钟频率 |
i2c_dri |
I2C_FREQ |
250KHz | I2C 总线频率 |
i2c_reg_cfg |
REG_NUM |
17 | 配置寄存器数量 |
i2c_reg_cfg |
PHONE_VOLUME |
20 | 耳机音量 |
i2c_reg_cfg |
SPEAK_VOLUME |
40 | 喇叭音量 |
audio_send |
WL |
16 | I2S 发送字长 |
audio_receive |
WL |
32 | I2S 接收字长 |
time_send |
NUM_DATA |
255 | 回放数据计数 |
key_debounce |
消抖时间 | 20ms | @50MHz = 1000000 ticks |
da_wave_send |
ROM | 4096×8bit | 正弦波查找表 |
async_fifo |
DATA_WIDTH |
16 | 数据位宽 |
async_fifo |
FIFO_DEPTH |
256/自定义 | FIFO 深度 |
八、外部 IP 核依赖
| IP 核 | 文件 | 功能 |
|---|---|---|
| PLL 锁相环 | pll_clk |
50MHz → 25MHz + 12.288MHz |
| DDS ROM | dds_sin |
4096×8bit 正弦波查找表,初始化文件 dds_4096x8b_wave_sin.mif |
| FIFO | async_fifo_ip |
异步 FIFO,数据中转缓冲 |
九、开发环境
- FPGA 器件:Intel Cyclone IV E(EP4CE 系列)
- 开发工具:Quartus Prime / Quartus II
- 仿真工具:ModelSim(仿真文件中使用 $display 打印调试信息)
- 硬件平台:正点原子 FPGA 开发板
十、注意事项
- 跨时钟域处理 :
data_gen中 FIFO 写端使用 50MHz,读端使用 25MHz(SD 卡时钟),通过异步 FIFO 实现可靠跨时钟域传输 - SD 卡格式:需使用 FAT32 格式化,音频数据存储在指定扇区偏移位置(起始扇区 8256)
- 仿真模式 :
top_audio_sd带sim参数 = 1 时,sd_wr_ctrl替换audio_sd_ctrl进行 SD 卡读写功能测试 - 参数一致性 :
top_audio_sd与audio_sd_ctrl中的扇区参数不同,请在整合同一个系统时确认正确的参数值 - WM8978 主从模式:当前配置 WM8978 为 MASTER 模式,由 WM8978 提供 BCLK 和 LRC。如需改为 SLAVE 模式,需修改 R7 寄存器配置