2026寒假练 - 基于FPGA的麦克风实时音量表与频谱显示
该项目使用了小脚丫FPGA电赛训练平台,实现了实时音量表与频谱显示的设计,它的主要功能为:使用ADC采样麦克风音频信号,实时显示音量与频谱信息。
标签
FPGA
小脚丫
频谱分析
滤波器
FFT
数字信号处理
2026寒假练
电赛训练平台
音量表
IIR
枫雪天
更新2026-03-24
33

任务介绍

本项目实现了2026寒假练活动小脚丫FPGA开发板的自命题任务,基于小脚丫FPGA核心板(MAX10M02SCM153)与配套训练板,实现了一套完整的基于麦克风的实时音量表与频谱显示系统。系统从麦克风模拟信号的采集出发,经过 ADC 数字化、直流去除、增益控制、包络跟踪和 FFT 频谱分析,最终在 LED 条形音量表、RGB LED、OLED 屏幕以及七段数码管等多种显示设备上实时呈现音量和频谱信息。

硬件平台

本次使用小脚丫 FPGA 核心板,是一款面向数字逻辑与嵌入式信号处理学习的紧凑型开发平台。该核心板搭载了 Intel(Altera)MAX 10 系列 FPGA 芯片 10M02SCM153C8G,具备 2,304 个逻辑单元、1 个 PLL、32 个嵌入式 9-bit 乘法器以及 110,592 bits 的片上存储器,非常适合用于中小规模的数字信号处理与外设控制应用。核心板板载 12 MHz 晶振、8 个用户 LED(active low)、2 组 RGB LED、两位七段数码管、4 个用户按键及 4 个拨码开关,便于快速搭建原型系统。

配套训练板提供了 10-bit 并行 ADC(3PA1030)、10-bit DAC、SSD1306 OLED 显示屏(128×64,SPI 接口)、旋转编码器以及麦克风放大模块接口等丰富外设资源,为音频信号采集与处理提供了完整的硬件支撑。

在软件方面,本项目采用 SystemVerilog 进行全部 RTL 设计,使用 Quartus 作为综合与布局布线工具,整个系统以纯硬件逻辑方式实现,无需软核处理器参与。

电赛+麦克风放大模块-白底-16-9.png

主控设备:小脚丫 FPGA 核心板

  • 搭载 Intel MAX 10 FPGA(10M02SCM153C8G)
  • 板载 12 MHz 晶振,通过 PLL 倍频至 50 MHz 系统时钟
  • 8 个用户可编程 LED 指示灯与 2 组 RGB LED
  • 4 个用户按键与 4 个拨码开关
  • 两位七段数码管

训练板扩展外设

  • 10-bit 并行 ADC(3PA1030),采样时钟由 FPGA 产生
  • 128×64 SSD1306 OLED 显示屏(SPI 接口)
  • 旋转编码器(带按键)
  • ADC输入接口

扩展板

  • 驻极体麦克风放大模块

调试设备

  • ADALM2000 口袋仪器
  • USB转TTL模块

任务分析与实现

本系统实现了基于 FPGA 的完整音频处理管线,主要功能包括:

五通道数据交互:

  • 音频信号采集与预处理
    • ADC 采样时钟:12.5 MHz,经 625 倍抽取后有效采样率为 20 kHz
    • IIR 一阶高通滤波器去除直流分量,截止频率约 3 Hz
  • 音量包络跟踪与显示
    • 包络跟踪器实时提取音量信息
    • 8 个 LED 构成条形音量表
    • RGB LED 根据音量等级显示不同颜色
  • 频谱分析
    • 8 点 Cooley-Tukey FFT,输出 4 个频段能量值
    • 旋转因子采用移位近似(无乘法器),误差不超过 2.8%
  • OLED 显示控制
    • 支持音量条形图与频谱柱状图两种显示模式
    • 按键切换模式,编码器调节增益与衰减参数
  • 调试串口输出
    • 921600 bps UART,Ping-Pong 缓冲帧格式传输采样数据

方案框图:

数据处理软件流程图:

系统设计与实现过程

本项目基于小脚丫 FPGA 开发板完成了从音频采集到多种显示输出的全链路数字信号处理系统设计,以下详细描述各功能模块的设计要点和关键实现。

一、设计前的准备

1. 硬件环境

  • FPGA 芯片:Intel MAX 10 10M02SCM153C8G
  • 开发板:小脚丫 FPGA 核心板 + 训练板
  • 外设资源
    • 10-bit 并行 ADC 用于麦克风信号采集
    • SPI 接口驱动 SSD1306 OLED 显示屏
    • 旋转编码器用于参数调节
    • 8 个 LED 与 2 组 RGB LED 用于音量指示

2. 软件环境

  • EDA 工具:Quartus
  • HDL 语言:SystemVerilog
  • 仿真调试:UART 串口输出 + Python 脚本可视化

二、时钟与复位模块(clk_rst)

整个系统的时钟基础由 clk_rst 模块提供。核心板上的 12 MHz 晶振信号通过 PLL IP 核倍频至 50 MHz 作为系统主时钟,同时 PLL 输出的 locked 信号用于产生可靠的全局异步复位。复位逻辑采用了 20-bit 计数器延时释放方式,确保上电后 PLL 稳定锁定之后系统才退出复位状态。

systemverilogreg [19:0] rst_cnt;
reg rst_n_int;
always @(posedge clk_50m or negedge pll_locked) begin
if (!pll_locked) begin
rst_cnt <= 20'd0;
rst_n_int <= 1'b0;
end else if (rst_cnt < 20'hFFFFF) begin
rst_cnt <= rst_cnt + 20'd1;
rst_n_int <= 1'b0;
end else begin
rst_n_int <= 1'b1;
end
end

三、ADC 接口与采样抽取(adc_interface)

ADC 接口模块是整个音频采集链的起点。模块将 50 MHz 系统时钟四分频产生 12.5 MHz 的 ADC 采样时钟,并检测其下降沿作为数据锁存时机。为了将高速 ADC 数据降至适合音频处理的速率,模块内部实现了 625 倍抽取平均滤波器,最终输出有效采样率为 20 kHz 的 10-bit 音频样本。

ADC 数据经过两级触发器同步后累加入 20-bit 累加器中,每累积 625 个样本后取高 10 位作为平均值输出,同时产生 sample_valid 脉冲通知下游模块。

systemverilog// 抽取 + 平均:12.5MHz / 625 = 20kHz
if (decim_cnt == 10'd624) begin
sample_data <= (accum + {10'd0, sync2}) >> 10;
sample_valid <= 1'b1;
accum <= 20'd0;
decim_cnt <= 10'd0;
end else begin
accum <= accum + {10'd0, sync2};
decim_cnt <= decim_cnt + 10'd1;
end

四、直流去除模块(dc_remove)

麦克风放大模块输出的模拟信号通常带有直流偏置(约 VCC/2),若不去除将严重影响后续包络提取与频谱分析的准确性。本项目采用 IIR 一阶高通滤波器实现直流去除,差分方程为:

y[n] = y[n-1] - (y[n-1] >>> 10) + x[n] - x[n-1]

为保证精度,输入信号 x 左移 10 位进行定点对齐,累加器 y_acc 总位宽为 21 bit(1 位符号 + 10 位整数 + 10 位小数)。输出取 y_acc[19:9] 作为 11-bit 有符号结果。该滤波器的截止频率约为 fs / (2π × 1024) ≈ 3 Hz,在有效去除直流的同时完整保留了音频信号成分。

systemverilog// IIR高通滤波器
y_acc <= y_acc - (y_acc >>> 10)
+ x_fixed - xp_fixed;
x_prev <= data_in;
data_out <= y_acc[19:9];

五、增益控制模块(audio_gain)

去除直流后的信号幅度可能很小,需要通过可调增益放大至合适的动态范围。audio_gain 模块对 11-bit 有符号输入进行有符号乘法运算,增益系数由编码器实时调节。乘法结果经算术右移 6 位后截取低 11 位输出,并对溢出进行饱和限幅处理,防止信号削波。

systemverilogwire signed [18:0] dc_gain_mult;
assign dc_gain_mult = $signed(data_in) * $signed({1'b0, gain_value});

wire signed [12:0] dc_gain_shifted;
assign dc_gain_shifted = dc_gain_mult >>> 6;

// 饱和限幅
if (dc_gain_shifted > 13'sd511)
dc_sample_gained = 11'd511;
else if (dc_gain_shifted < -13'sd512)
dc_sample_gained = 11'b10000000000;
else
dc_sample_gained = dc_gain_shifted[10:0];

六、包络跟踪器(envelope_follower)

包络跟踪器负责从经过增益控制的音频信号中提取实时音量信息。模块首先对输入样本取绝对值,然后通过非对称 Attack/Decay 机制跟踪信号包络:

  • Attack 阶段:当当前采样绝对值大于包络值时,包络快速上升,上升步进为差值右移 2 位(约 25% 跟随速度)
  • Decay 阶段:当信号减小时,包络缓慢衰减,衰减速度由编码器控制的 decay_value 参数决定

最终包络值映射为 0–255 的 8-bit 音量等级输出,低于阈值 3 的噪声被置零。

systemverilogif (dc_abs > envelope) begin
// Attack:快速上升
envelope <= envelope + ((dc_abs - envelope) >> ATTACK_SHIFT);
end else begin
// Decay:缓慢衰减
if (decay_counter >= decay_period - 1) begin
envelope <= envelope - (envelope >> DECAY_SHIFT);
end
end

七、8 点 FFT 频谱分析(simple_fft)

频谱分析模块实现了 8 点 Cooley-Tukey 时域抽取 FFT,采用三级流水线蝶形运算结构。为适应 FPGA 资源受限的特点,所有旋转因子的 0.707 系数均通过移位近似实现(a × 0.707 ≈ a - (a>>>2) - (a>>>4)),完全避免了乘法器的使用,误差控制在 2.8% 以内。

Stage 1:4 组 2 点蝶形运算,旋转因子均为 W₈⁰=1,仅加减操作。

Stage 2:2 组 4 点蝶形运算,引入 W₈²=-j 旋转因子,对纯实信号乘以 -j 仅需交换实虚部并取反。

Stage 3:1 组 8 点蝶形运算,旋转因子 W₈¹ 和 W₈³ 的 0.707 系数通过移位近似内联展开:

systemverilog// 0.707近似:展开为内联wire,避免function
wire signed [12:0] m707_s2r5 = s2r[5] - (s2r[5] >>> 2) - (s2r[5] >>> 4);
wire signed [12:0] m707_s2i5 = s2i[5] - (s2i[5] >>> 2) - (s2i[5] >>> 4);

wire signed [13:0] tw1r = m707_s2r5 + m707_s2i5;
wire signed [13:0] tw1i = -m707_s2r5 + m707_s2i5;

FFT 输出的幅值采用曼哈顿近似 |X| ≈ |Re| + |Im|,避免了开方运算。模块还内置了 16 帧滑动平均和峰值保持逻辑,使频谱显示更加平滑稳定。最终输出 4 个频段(band_0 至 band_3)的 8-bit 能量值。

八、用户输入控制(param_control)

参数控制模块整合了按键消抖和旋转编码器解码功能,为用户提供直观的交互体验:

  • KEY[0](经 key_debounce 消抖):切换 OLED 显示模式(音量条形图 / 频谱柱状图)
  • 编码器旋转:以 2 的幂次步进调节增益值或衰减时间常数
  • 编码器按键:切换当前调节参数(增益 / 衰减)
systemverilog// 编码器左旋:参数以2的幂次减小
if (enc_left) begin
if (!param_select) begin
if (gain_value > 10'd1)
gain_value <= {1'b0, gain_value[9:1]}; // 右移一位
end else begin
if (decay_value > 10'd1)
decay_value <= {1'b0, decay_value[9:1]};
end
end

按键消抖模块 key_debounce 采用 50,000 周期(1 ms @ 50 MHz)的计数器消抖方案,当检测到按键从高电平稳定跳变为低电平时产生单周期 key_press 脉冲。旋转编码器模块 Encoder 以 500 μs 采样周期对 A/B 相信号进行三级采样滤波,并通过 A 相上升沿/下降沿与 B 相电平的组合判断旋转方向。

九、LED 条形音量显示(led_bar)

LED 条形音量表将 8-bit 音量等级映射为 8 个 LED 的亮灭状态(active low)。由于核心板 LED 采用低电平点亮方式,音量越大,对应位越多的 LED 被置为 0(点亮):

systemverilogif      (vol_level >= 224) led_bar = 8'b00000000;  // 全亮
else if (vol_level >= 192) led_bar = 8'b10000000; // 亮7个
else if (vol_level >= 160) led_bar = 8'b11000000; // 亮6个
// ... 逐级递减
else led_bar = 8'b11111111; // 全灭

十、RGB LED 颜色指示(debug_outputs)

RGB LED 模块提供两路视觉反馈:

  • RGB1:蓝色通道指示当前参数选择状态(增益调节时蓝灯亮,衰减调节时蓝灯灭)
  • RGB2:根据音量等级显示三档颜色——低音量绿色(volume < 85)、中音量黄色(85 ≤ volume < 170)、高音量红色(volume ≥ 170)
systemverilogif (volume_level < 8'd85)
rgb2 = 3'b101; // 绿色
else if (volume_level < 8'd170)
rgb2 = 3'b110; // 黄色(红+绿)
else
rgb2 = 3'b011; // 红色

十一、OLED 显示子系统

OLED 显示子系统由三个协同工作的模块组成:oled_initoled_render  oled_spi

初始化模块(oled_init)

上电后先拉低 OLED_RST 引脚 1 ms 进行硬件复位,释放后等待 10 ms 让内部电路稳定,然后依次发送 25 条 SSD1306 初始化命令(包括时钟分频、复用率、充电泵使能、页寻址模式、对比度设置等),完成后置位 init_done 信号。

渲染引擎(oled_render)

渲染引擎以页扫描方式(8 页 × 128 列)连续刷新 OLED 帧缓冲。根据 disp_mode 信号选择两种渲染模式:

  • 音量条形图模式(disp_mode = 0):在页 1–6 绘制矩形进度条,宽度与音量成正比;页 7 绘制位置指示点
  • 频谱柱状图模式(disp_mode = 1):在 128 列像素中等距排列 4 根频段柱(各 24 像素宽),柱高与对应频段能量成正比,从底部向上生长
systemverilogwire [7:0] pixel_byte = disp_mode ? spectrum_byte : volume_byte;

SPI 主机(oled_spi)

SPI 主机以 CPOL=0、CPHA=0 模式工作,时钟速率约 5 MHz(50 MHz / 10)。采用移位寄存器方式逐比特发送 8-bit 数据,MSB 优先。CS 引脚常低,DC 引脚由上层模块控制以区分命令与数据。

十二、七段数码管显示(seg_display)

七段数码管模块将当前调节参数(增益移位量或衰减移位量)以十六进制形式显示在两位数码管上。顶层模块通过优先编码器将 10-bit 的 gain_value  decay_value 转换为对应的移位量(0–10),便于用户直观了解当前参数设置:

systemverilogwire [7:0] seg_value = param_select ? decay_shift : gain_shift;

十三、UART 调试输出(uart_capture)

UART 调试模块以 921600 bps 的波特率将处理后的音频样本实时传输至上位机,便于使用 Python 脚本进行时域波形和频谱的可视化分析。

每帧数据采用 Ping-Pong 双缓冲结构:一个缓冲区收集 8 个采样点的同时,另一个缓冲区通过 UART 发送。帧格式为 AA 55(帧头)+ 00 10(长度 16 字节)+ 8 个 16-bit 采样值(高字节在前),总计 20 字节。上电后有 2047 个采样的预热期,确保 IIR 滤波器收敛后才开始传输有效数据。

十四、引脚约束与时序约束

引脚分配通过 Tcl 约束文件 pinout.tcl 完成,所有 I/O 统一设置为 3.3V LVTTL 标准。时序约束文件 timing.sdc 定义了 12 MHz 输入时钟(周期 83.333 ns),并对按键、拨码开关和 ADC 数据输入设置了 set_false_path,避免这些异步信号引发虚假的时序违例。

tclcreate_clock -name CLK_12M -period 83.333 [get_ports {CLK_12M}]
derive_pll_clocks
derive_clock_uncertainty
set_false_path -from [get_ports {KEY[*]}]
set_false_path -from [get_ports {SW[*]}]
set_false_path -from [get_ports {ADC_D[*]}]

效果展示

实物效果

频谱显示模式

音量显示模式

信号发生器测试

使用 ADALM2000 信号发生器输出正弦波(1.5 V 偏置)接入 ADC 模拟输入端,验证系统采集链的完整性:

Python 上位机波形与频谱分析

通过 UART 接收采样数据并使用 Python 脚本绘制时域波形与 FFT 频谱。下图显示 256 个采样点的时域波形和对应频谱,可清晰看到 1 kHz 附近的基频峰值:

FPGA资源占用报告

项目在 Quartus 下综合成功,资源占用情况如下:

逻辑单元占用率达 86%,说明 MAX10M02 的资源已被充分利用。嵌入式乘法器仅使用 2 个(用于 audio_gain 模块的有符号乘法),FFT 模块通过移位近似成功避免了乘法器的使用。

遇到的难题与解决办法

问题一:MAX10M02 逻辑资源紧张,FFT 模块若使用乘法器实现旋转因子会导致资源溢出

解法:对 0.707 旋转因子采用移位近似方案 a × 0.707 ≈ a - (a>>>2) - (a>>>4),仅使用加减和移位操作,误差控制在 2.8% 以内,对频谱显示效果的影响可忽略不计。同时将所有中间变量展开为内联 wire,避免 function 调用产生额外的逻辑开销。

问题二:ADC 采集的音频信号存在明显直流偏置,包络跟踪器输出始终饱和

解法:在 ADC 接口与增益控制之间插入 dc_remove 模块,采用一阶 IIR 高通滤波器。通过将输入信号左移 10 位进行定点对齐,确保滤波精度;21-bit 累加器保留 10 位小数部分,截止频率约 3 Hz,在有效去除直流的同时不影响语音频段信号。

问题三:包络跟踪器在安静环境下出现噪声跳动,LED 频繁闪烁

解法:在包络输出映射中设置死区阈值(envelope ≤ 3 时输出 0),并通过 decay_value 参数控制衰减速度,使用编码器可实时调节衰减时间常数,兼顾响应灵敏度与显示稳定性。

活动感想

通过本项目实践,我深入掌握了 FPGA 在数字信号处理领域的应用方法,包括 ADC 接口时序设计、IIR 滤波器的定点实现、无乘法器 FFT 的移位近似技巧以及 SSD1306 OLED 的 SPI 驱动协议。小脚丫 FPGA 开发板虽然资源有限(仅 2,304 个逻辑单元),但通过精心的架构设计和资源优化,成功实现了从麦克风模拟信号采集到多种显示输出的完整音频处理管线。整个设计过程中,需重点关注:

  • 定点数位宽分配与溢出保护策略
  • 多时钟域之间的同步处理(ADC 数据的两级同步)
  • 资源受限条件下算法的移位近似与流水线优化

同时,UART 调试输出配合 Python 上位机的联合验证方案极大提升了调试效率,使得信号链中每个环节的正确性都能得到快速确认。整个项目过程中,体会到 FPGA 设计需要在算法精度、资源占用和时序收敛之间反复权衡,每一个模块的优化都会对整体系统性能产生显著影响。

感谢硬禾科技举办的2026寒假练活动,祝硬禾的活动越办越好!

附件下载
mic_spectrum.rar
源代码
团队介绍
个人
团队成员
枫雪天
枫雪天
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号