基于FPGA的信号测量与显示系统项目总结报告
一、所选任务介绍
本项目所选任务为数字系统综合设计类课题,具体题目为基于FPGA的信号测量与显示系统。任务要求以可编程逻辑器件为核心,设计并实现一套能够对周期性模拟信号进行实时采集、参数提取与可视化显示的嵌入式系统。系统需具备以下基本功能:
- 对外部输入的模拟信号进行采集,提取频率、幅值(电压)和占空比等关键参数;
- 内置信号源,支持在无外部信号输入时进行自测;
- 通过交互界面允许用户调节测量量程和切换工作模式;
- 将所有测量结果以数字、图形等多种形式实时显示于屏幕。
该任务综合考查了数字逻辑设计、硬件描述语言编程、接口时序控制以及资源约束优化等多方面能力,具有较强的工程实践价值。
二、项目整体介绍
本系统以Intel MAX10系列FPGA芯片(型号10M02SCM153C8G)为核心控制器,采用Verilog硬件描述语言进行全数字化设计。系统工作时钟由片内振荡器提供,经8分频后约为10MHz,可用逻辑资源为2304个逻辑单元(LE)、144个逻辑阵列块(LAB)以及11块M9K嵌入式存储块。
系统支持两种工作模式:
- 测量模式:通过外部ADC芯片(3PA1030)对0~2V范围内的模拟信号进行实时采样,经FPGA内部算法计算后得到频率、峰峰值电压及占空比,显示于OLED屏幕;
- 自测模式:调用内部DDS(直接数字频率合成)波形发生器产生已知频率的方波信号,经由相同的信号处理链路完成测量与显示,便于系统验证和调试。
用户通过一个旋转编码器和两个按键与系统交互,实现量程调节、模式切换和帮助页面翻阅等操作。所有测量结果通过一块0.96寸128×64像素OLED屏幕实时呈现,界面包含数字读数、动态条形图、中文模式标识及多级帮助系统。
整个工程代码量约750行Verilog,使用Quartus Prime 18.1 Lite Edition完成综合、布局布线和下载。
三、硬件介绍
3.1 FPGA开发板
核心芯片为Intel MAX10系列的10M02SCM153C8G,采用153引脚MBGA封装。该芯片内含2304个自适应逻辑模块(ALM)等效逻辑单元、约101Kbits的M9K嵌入式块RAM,以及片内ADC和振荡器资源。其最大特点是支持单芯片配置存储,上电即可自动加载程序,无需外部配置芯片,适合小型化设计。
3.2 OLED显示模块
采用0.96寸单色OLED显示屏,分辨率为128×64像素,驱动芯片为SSD1306,通过SPI接口与FPGA通信。OLED具有自发光特性,对比度高,在弱光环境下显示效果尤为清晰,功耗低,响应速度快,适合嵌入式实时显示场景。
3.3 外部ADC芯片(3PA1030)
3PA1030是一款10位串行输出模数转换芯片,支持0~2V单端模拟输入,采用SPI兼容串行接口将转换结果输出至FPGA。其10位分辨率对应1024个量化级别,在0~2V量程下理论分辨率约为1.95mV/LSB,满足本系统对幅值测量精度的需求。
3.4 旋转编码器
旋转编码器通过A、B两相正交信号输出旋转方向和步进量,用于调节频率测量量程。FPGA通过判断A、B相的先后跳变顺序确定旋转方向,每次有效旋转触发一次量程档位切换。
3.5 按键
系统配备两个独立按键,分别承担模式切换和帮助页面翻阅功能。所有按键均在FPGA内部进行软件消抖处理,消抖时间约为20ms,有效避免机械抖动导致的误触发。
四、方案框图说明与设计思路

系统的整体设计思路是模块化分层设计。最底层为硬件接口层,负责与外部芯片的时序通信;中间层为信号处理层,完成数据的算法运算;最上层为显示与交互层,负责结果呈现和用户输入响应。各层模块之间通过寄存器接口传递数据,解耦合度高,便于独立调试。
设计初期,将整个系统划分为五个独立Verilog模块,各模块功能单一、接口明确,在顶层模块(top.v)中进行统一例化和连线。信号的采集、处理、显示形成一条完整的数据流水线,各模块并行运行,互不阻塞。

OLED的显示被划分为左侧主内容区(0~102列)和右侧模式标识区(105~118列),中间以一条1像素竖线分隔。左侧按8页(每页8行像素)组织显示内容,包括频率数字、电压与占空比读数、两条动态条形图以及模式字符行。右侧固定显示当前工作模式的中文名称(测量模式 / 自测模式 / 帮助模式),采用14×14像素汉字点阵。
五、开发工具、编程语言、软件流程图及关键代码介绍
5.1 开发工具与编程语言
- 开发工具:Quartus Prime 18.1.0 Lite Edition,用于综合、布局布线、时序分析及FPGA下载;
- 辅助工具:Python(Pillow库)用于离线生成汉字点阵hex文件;VSCode用于代码编辑;
- 编程语言:Verilog HDL(IEEE 1364-2001标准);
- 下载方式:USB-Blaster JTAG接口,使用
quartus_pgm命令行工具完成SOF文件下载。
5.2 软件模块介绍
(1)adc_capture 模块 负责产生3PA1030 ADC所需的片选信号、采样时钟和串行数据接收时序。内部采用计数器分频产生1MHz左右的ADC时钟,按照芯片手册的SPI时序逐位接收10位转换结果,拼接后输出给信号处理模块。核心设计点是严格的建立/保持时间对齐。
(2)sig_process 模块 该模块是系统的算法核心,包含三条并行处理路径:频率测量采用等精度测量法,通过计数标准时钟周期数和信号周期数后做比值运算;幅值提取通过维护一个刷新窗口内的最大/最小采样值,计算峰峰值后换算为电压(mV);占空比计算通过统计高电平持续时间占完整周期的比例得出百分比。模块内还集成了一个32位相位累加器结构的DDS发生器,通过lpm_mult IP核实现频率字与相位的乘积运算,输出方波作为自测模式的信号源。
(3)hmi_ctrl 模块 管理所有人机交互逻辑。按键消抖采用计数器滤波,稳定时间约20ms。编码器解码通过检测A相上升沿时B相的电平状态判断旋转方向。模式切换和量程调节通过内部状态寄存器维护,共支持5档量程(对应不同频率测量上限)和3种工作模式(测量/自测/帮助)。
(4)oled_driver 模块 实现SSD1306驱动芯片的SPI通信协议,内部状态机依次完成上电初始化序列(约20条配置命令)和循环刷新流程(8页×128列字节逐一发送)。初始化完成后输出oled_init_done信号,触发渲染模块开始工作。
(5)oled_render 模块 像素数据的生成引擎,采用7状态状态机(RS_IDLE、RS_SET_PAGE、RS_CMD_WAIT、RS_GEN_DATA、RS_NEXT_COL、RS_NEXT_PAGE、RS_ROM_WAIT)驱动逐列像素计算。

频率等数值的十进制转换采用共享串行BCD转换器,每次刷新触发时依次对频率(24位)、电压(11位)、占空比(7位)各执行一轮24周期的移位加3算法,结果寄存后供渲染使用。汉字显示通过预先将54个字模(每字14×14像素)以Intel Hex格式存储,在综合时通过$readmemh指令加载至M9K块RAM,渲染时按地址索引读取。

六、功能展示及说明
【帮助模式oled】
系统在实际运行中,OLED左侧主区实时更新频率(Hz)、电压(V,精度至小数点后两位)和占空比(%)的数字读数,并同步更新两条长度随测量值变化的动态条形图。右侧汉字区域根据当前模式显示对应中文标识。进入帮助模式后,全屏切换为5级功能说明页,用户通过按键翻页浏览。编码器旋转时,频率量程实时切换,屏幕显示内容平滑过渡,响应延迟约200ms(受刷新定时器控制)。整体功能完整,显示清晰,交互稳定。
fpga资源占用报告

七、遇到的难题及解决方法
7.1 逻辑资源(LE)溢出问题
问题描述: 在实现OLED汉字显示功能时,最初计划将全部54个汉字(共756个14像素列,每列16位)的字模数据以组合逻辑case语句的形式写入Verilog代码中。综合后发现,760个case分支产生了大量纯组合逻辑,导致LE用量猛增至2431个,超出芯片上限2304个,布局布线失败。
解决过程: 分析可知,大规模查找表型数据映射正是M9K嵌入式块RAM的适用场景。将all字模数据预先通过Python脚本(调用Pillow库渲染SimHei字体)生成标准Intel Hex格式文件(cn14_rom.hex),然后在Verilog中声明一个1024深度×16位宽的reg数组,添加(* ramstyle = "M9K" *)综合属性约束其映射至M9K,并使用initial $readmemh("cn14_rom.hex", cn14_mem)在综合阶段完成数据初始化。此方案将汉字字模的存储从纯组合LE彻底迁移至M9K,LE用量回落至合法范围,同时读取速度也不受影响。
7.2 M9K同步读取时序问题
问题描述: M9K块RAM为同步读取模式:在某一时钟上升沿给出地址,输出数据需等到下一个时钟上升沿才稳定有效。在原始状态机中,渲染逻辑在RS_GEN_DATA状态同时计算地址并使用数据,导致M9K输出的数据实际上是上一周期地址对应的内容,汉字像素出现系统性错位(整体偏移一列)。
解决过程: 在RS_GEN_DATA之前插入专用等待状态RS_ROM_WAIT,该状态仅完成地址的组合逻辑输出,不产生任何像素数据,状态机在此停留一个时钟周期后跳转至RS_GEN_DATA,此时M9K输出已稳定。该修改仅增加每列约1个时钟周期的延迟,对整体刷新速率几乎无影响,但彻底解决了像素错位问题。
7.3 BCD转换与LE资源的平衡问题
问题描述: 系统需要将频率(24位二进制)、电压(11位)、占空比(7位)三个数值转换为BCD码后显示。初期考虑到串行BCD(移位加3算法)每轮需要24个时钟周期、三轮共72周期,一度改为纯组合逻辑实现(通过多级数值比较链直接得出各BCD位)。但综合后发现,组合BCD的比较链(特别是十分位/百分位的0~9档比较)消耗了大量LE,总用量达到2395个,再次触碰上限。
解决过程: 回归串行BCD方案,但同步进行其他部位的资源压缩:将刷新计时器(refresh_timer)位宽从24位降至21位(计数上限约200万,21位即已足够),节省出约3个多余寄存器所占用的LAB资源,最终使总用量降至144 LABs以内,在资源临界状态下成功完成布局布线。
7.4 Verilog语法限制导致的综合错误
问题描述: 在计算右侧汉字区域的列偏移量时,初始写法为(col_idx - 8'd105)[3:0],即对运算表达式直接进行位选操作。综合工具报错,提示Verilog-2001标准不允许对表达式结果直接做位域选择。
解决过程: 引入一个中间wire变量:
wire [3:0] rz_sub_col = col_idx[3:0] - 4'd9;
利用位宽自动截断特性,等效实现原本的列偏移计算,同时满足Verilog语法规范,综合通过。
八、心得体会
通过本次项目的完整设计周期,从方案构思到代码实现,再到硬件调试和最终演示,积累了诸多宝贵的工程实践经验。
首先,深刻认识到资源约束是FPGA设计区别于软件编程的核心挑战。在LE严重不足的情况下,每一个设计决策——无论是ROM的实现方式、BCD转换算法的选择,还是计时器的位宽设置——都需要在功能正确性和资源消耗之间仔细权衡。这种"寸土必争"的思维方式是FPGA工程师必须具备的基本素养。
其次,分模块设计与先单元测试后系统联调的开发流程极大提升了效率。各模块接口清晰,功能单一,使得问题定位和修改都相对容易,避免了"牵一发而动全身"的困境。
第三,深入阅读数据手册是解决接口时序问题的根本方法。M9K的同步读延迟、ADC的SPI时序、OLED的初始化序列,这些问题最终都是回到芯片手册中寻找到了准确答案,而非依赖猜测或试错。
最后,本次项目也让我认识到系统设计实际上是一个持续迭代优化的过程。初始方案往往并不完美,在综合反馈、测试现象和资源报告的驱动下,不断调整和改进方案,才能最终得到一个可落地的可靠设计。这种工程化的思维方式将对今后的学习和工作产生持久的影响。