1. FPGA双显秒表项目概述
在嵌入式系统开发领域,FPGA因其并行处理能力和硬件可重构特性,成为高精度定时器开发的理想平台。这个基于DE2-115开发板的双通道秒表项目,通过同时驱动数码管和1602液晶屏实现计时显示,不仅考验基础外设控制能力,更是状态机设计与时序协调的经典案例。我在实际教学中发现,能完整实现这个项目的开发者,通常已经掌握了FPGA开发的三个核心能力:硬件描述语言(Verilog/VHDL)的模块化设计、外设接口时序的精确控制,以及多任务并发的资源分配策略。
2. 硬件架构设计解析
2.1 DE2-115开发板资源分配
Altera Cyclone IV EP4CE115F29C7芯片提供114,480个逻辑单元,我们主要使用以下资源:
- 50MHz系统时钟(经PLL分频后供各模块使用)
- 6位7段数码管(采用动态扫描方式驱动)
- 1602液晶屏(4位数据总线模式)
- 3个独立按键(启动/暂停、复位、模式切换)
关键提示:开发板上的数码管实际是共阳连接,段选信号低电平有效,这与很多初学者习惯的共阴接法相反,务必在代码中做相应处理。
2.2 双显示接口对比
| 显示设备 | 接口类型 | 刷新方式 | 功耗 | 显示内容 |
|---|---|---|---|---|
| 数码管 | 并行8线 | 动态扫描 | 较高 | 精简数字 |
| 1602 LCD | 4/8位并行 | 指令控制 | 低 | 文本+数字 |
3. 核心模块实现细节
3.1 计时基准生成
采用三级分频链获得精确的1Hz信号:
verilog复制// 50MHz -> 1kHz -> 10Hz -> 1Hz
reg [15:0] cnt_1k;
always @(posedge clk_50M) begin
if(cnt_1k == 49999) begin
cnt_1k <= 0;
clk_1k <= ~clk_1k;
end else
cnt_1k <= cnt_1k + 1;
end
// 后续同理生成10Hz和1Hz
3.2 有限状态机设计
秒表包含5个状态:
- IDLE:显示全零,等待启动
- RUN:正常计时
- PAUSE:暂停显示
- LAP:圈数记录
- RESET:复位所有寄存器
状态转换图通过case语句实现,每个状态对应特定的显示更新逻辑和控制信号。
3.3 数码管动态扫描
6位数码管采用时分复用驱动,扫描频率需大于100Hz以避免闪烁:
verilog复制parameter SCAN_FREQ = 200; // Hz
reg [2:0] scan_cnt;
always @(posedge clk_scan) begin
scan_cnt <= (scan_cnt == 5) ? 0 : scan_cnt + 1;
case(scan_cnt)
0: begin seg_sel <= 6'b111110; seg_data <= minute_10; end
// ...其他位选择
endcase
end
3.4 1602液晶驱动
采用4位数据总线模式初始化序列:
- 上电延时15ms
- 发送0x03指令三次
- 切换4位模式(0x02)
- 设置显示行数、光标等参数
数据显示需注意:
- 每行16字符,地址0x80和0xC0
- 写入数据前检查忙标志
- 字符生成使用ROM查找表
4. 关键问题解决方案
4.1 显示冲突处理
当数码管扫描与LCD写操作同时发生时,会导致总线竞争。解决方案:
- 采用时间片轮转机制,为每个显示设备分配固定时间窗口
- 使用多路复用器切换控制信号
- 在状态机中设置显示优先级
4.2 计时误差补偿
实测发现,单纯计数器分频会产生累计误差。改进方案:
- 使用PLL生成精确的1kHz时钟
- 引入误差补偿寄存器
- 定期校准(每24小时误差<0.5秒)
4.3 按键消抖处理
机械按键抖动时间约5-20ms,采用积分法消抖:
verilog复制reg [19:0] key_cnt;
always @(posedge clk_1k) begin
if(key_in != key_reg)
key_cnt <= 0;
else if(key_cnt < 20)
key_cnt <= key_cnt + 1;
if(key_cnt == 19)
key_out <= key_in;
end
5. 性能优化技巧
5.1 资源复用策略
- 共用BCD转换模块
- 时分秒计数器复用
- 显示缓冲区共享
5.2 时序约束设置
在Quartus中需添加以下约束:
code复制create_clock -name clk_50M -period 20 [get_ports clk_50M]
set_input_delay -clock clk_50M 2 [all_inputs]
set_output_delay -clock clk_50M 3 [all_outputs]
5.3 功耗控制方法
- 未使用的IO口设置为三态
- 动态关闭未激活显示模块电源
- 时钟门控技术
6. 扩展功能实现
6.1 正/倒计时切换
通过模式寄存器实现:
verilog复制reg mode; // 0:正计时 1:倒计时
always @(posedge clk_1Hz) begin
if(mode)
counter <= (counter==0) ? 0 : counter-1;
else
counter <= counter+1;
end
6.2 分段计时功能
添加lap寄存器组存储分段时间:
verilog复制reg [15:0] lap_time[0:3];
always @(posedge lap_trigger) begin
lap_idx <= (lap_idx==3) ? 0 : lap_idx+1;
lap_time[lap_idx] <= current_time;
end
6.3 串口数据输出
添加UART模块将计时数据发送到PC:
- 波特率115200
- 数据格式:时分秒,毫秒\n
- 使用FIFO缓冲显示数据
7. 调试与验证方法
7.1 虚拟逻辑分析仪
使用SignalTap II抓取关键信号:
- 添加时钟、复位信号
- 监控状态机变量
- 捕获显示数据总线
7.2 功能测试用例
设计验证场景:
- 连续运行24小时检查计时误差
- 快速切换模式测试状态机稳定性
- 极限值测试(59分59秒→1小时)
7.3 资源利用率分析
编译后查看Logic Utilization报告:
- 总逻辑单元使用<15%
- 存储器块使用1个
- PLL使用1个
8. 常见问题排查指南
8.1 数码管显示异常
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 全暗 | 位选信号错误 | 检查共阳/共阴配置 |
| 部分段不亮 | 段选电阻过大 | 减小限流电阻值 |
| 显示错乱 | 扫描频率过低 | 提高刷新率至200Hz以上 |
8.2 LCD显示乱码
- 检查初始化序列是否完整
- 验证RS/RW/E时序
- 测量VDD电压(4.5-5.5V)
8.3 计时不准
- 用示波器测量1Hz信号
- 检查计数器溢出值
- 确认时钟源稳定性
9. 项目进阶方向
9.1 添加无线模块
通过蓝牙或WiFi实现:
- 手机APP远程控制
- 多设备同步计时
- 数据云端存储
9.2 语音报时功能
集成语音芯片:
- 分段触发播报
- 数字语音合成
- 音量调节
9.3 运动传感器接口
连接加速度计:
- 运动自动启停
- 计步功能
- 速度计算
在完成这个项目过程中,最大的收获是对FPGA并行处理本质的理解——各个显示模块看似同时工作,实际上是通过精细的时间片分配实现的伪并行。建议初学者在实现基本功能后,尝试将数码管扫描频率逐步提高,观察直到多少Hz时人眼开始察觉闪烁,这个实验能深刻理解视觉暂留现象与硬件设计的关系。
