基于Logisim与Verilog HDL的运动码表计时电路设计与DE2-70开发板验证

张开发
2026/5/30 18:55:22 15 分钟阅读
基于Logisim与Verilog HDL的运动码表计时电路设计与DE2-70开发板验证
1. 从零开始理解运动码表计时电路第一次接触数字电路设计时看到运动码表计时电路这个概念可能会觉得有点高大上。其实说白了这就是一个能记录分钟和秒钟的计时器跟我们平时用的秒表功能差不多。只不过这次我们要用Logisim画电路图再用Verilog HDL写代码最后在DE2-70开发板上跑起来。这个项目最有趣的地方在于它完美展示了数字电路设计的完整流程从逻辑设计到硬件实现。我刚开始做这个项目时对着真值表画电路图画到怀疑人生但后来看到数码管上的数字正常跳动时那种成就感真的无法形容。计时电路的核心是四个计数器分的十位0-5、分的个位0-9、秒的十位0-5、秒的个位0-9。这就像我们平时看时间的习惯一样只不过在电路里是用二进制实现的。特别要注意的是进位信号的处理——低位的进位信号就是高位的时钟信号这个设计思路非常巧妙。2. Logisim电路设计实战2.1 BCD码计数器设计在Logisim里设计BCD码计数器是第一个难点。我建议先搞定0-9的十进制计数器再改造成0-5的六进制计数器。具体来说一个完整的BCD码计数器需要两部分状态转换电路控制0到9的状态切换输出函数电路在状态从9跳回0时产生进位信号画电路图时有个小技巧先列出真值表然后用Logisim的组合分析功能自动生成电路。这比手动连线省事多了特别适合我们这些新手。不过自动生成的电路可能不够优化后期可以手动调整。记得我第一次做的时候忘了处理进位信号结果计数器到9就卡住了。后来发现是输出函数没设计好当计数器从9变0时必须输出一个高电平的进位信号。2.2 完整计时电路搭建四个计数器都做好后就要考虑它们之间的连接了。这里的关键是秒的个位(0-9)计数器时钟直接接外部时钟秒的十位(0-5)计数器时钟接秒个位的进位分的个位(0-9)计数器时钟接秒十位的进位分的十位(0-5)计数器时钟接分个位的进位在Logisim中测试时建议先用快速时钟信号比如1Hz观察计数过程。我第一次测试时用了太快的时钟结果数字跳得眼花缭乱根本看不清是否正确进位。后来改用慢速时钟配合单步调试才确认电路工作正常。3. Verilog HDL实现3.1 十进制计数器模块用Verilog写计数器比画电路图简单多了。下面是我调试通过的十进制计数器代码module bcd_counter1( input en_10, // 使能信号 input clk_10, // 时钟信号 input res_10, // 复位信号 input zin_10, // 进位输入 output zout_10, // 进位输出 output reg [3:0] qout_10 // 计数输出 ); always(posedge clk_10) begin if(res_10) qout_10 4b0; // 复位时清零 else if(en_10) if(qout_10 4h9) qout_10 4b0; // 到9后归零 else if(zin_10) qout_10 qout_10 1b1; // 正常计数 end assign zout_10 (qout_10 4h9); // 产生进位信号 endmodule这个模块有几个关键点使用posedge clk_10确保在时钟上升沿触发复位信号高电平有效只有在使能信号有效且收到进位信号时才计数3.2 六进制计数器模块秒的十位需要0-5的六进制计数器代码与十进制类似但判断条件不同module bcd_counter2( input en_6, input clk_6, input res_6, input zin_6, output zout_6, output reg [3:0] qout_6 ); always(posedge clk_6) begin if(res_6) qout_6 4b0; else if(en_6) if(qout_6 4h5) // 注意这里是5不是9 qout_6 4b0; else if(zin_6) qout_6 qout_6 1b1; end assign zout_6 (qout_6 4h5); // 到5时进位 endmodule3.3 顶层模块设计顶层模块负责把四个计数器连接起来module jishiqi( input en, input clk, input res, output [15:0] D_count_16 ); wire mone_mten_out; // 秒个位→秒十位 wire mten_fone_out; // 秒十位→分个位 wire fone_ften_out; // 分个位→分十位 bcd_counter1 mone( .en_10(en), .clk_10(clk), .res_10(res), .zin_10(1b1), // 秒个位始终允许计数 .zout_10(mone_mten_out), .qout_10(D_count_16[3:0]) ); bcd_counter2 mten( .en_6(en), .clk_6(clk), .res_6(res), .zin_6(mone_mten_out), .zout_6(mten_fone_out), .qout_6(D_count_16[7:4]) ); // 其他两个计数器类似... endmodule4. DE2-70开发板验证4.1 引脚分配在Quartus II中需要为输入输出信号分配实际引脚。根据DE2-70开发板的硬件布局我使用了以下配置SW0使能信号(en)SW1复位信号(res)KEY0手动时钟(clk)HEX0-HEX3分别显示秒个位、秒十位、分个位、分十位引脚分配完成后记得生成编程文件(.sof)。我第一次下载时忘了分配引脚结果开发板上的数码管完全没反应排查了好久才发现问题。4.2 实际测试技巧在开发板上测试时建议先用慢速手动时钟(KEY0)测试进位关系确认基本功能正常后再改用50MHz系统时钟分频得到1Hz时钟观察从59秒到1分钟的跳变是否正确测试复位功能是否正常工作我在测试时发现秒十位偶尔会显示6检查后发现是六进制计数器的判断条件写成了而不是导致在快速时钟下可能错过复位时机。这个小bug让我深刻理解了异步电路设计的注意事项。5. 常见问题与调试技巧做这个项目时我踩过不少坑这里分享几个常见问题计数器不工作检查时钟极性是否正确复位信号是否有效进位不正常确认低位计数器的进位输出确实连接到了高位计数器的时钟输入显示乱码检查数码管译码电路是否正确特别是BCD码到七段码的转换计时不准如果是用高频时钟分频检查分频计数器是否足够大调试时建议分模块验证先单独测试每个计数器再测试进位关系最后整合整个系统。用SignalTap II逻辑分析仪可以很方便地观察内部信号变化。6. 项目扩展思路基础功能实现后可以尝试以下扩展添加暂停/继续功能实现多组计时记忆增加报警功能如计时到设定时间时触发改用更精确的时钟源如开发板上的50MHz晶振分频我在完成基础功能后增加了通过VGA显示大号数字的功能。虽然花了不少时间调试但看到计时器在大屏幕上显示时感觉特别有成就感。这些扩展不仅能提升项目复杂度也能让你更深入地理解FPGA开发。

更多文章