FPGA新手必看:用Vivado和EGo1开发板,从零实现一个七段数码管显示0-F(附完整代码和约束文件)

张开发
2026/5/4 6:43:03 15 分钟阅读
FPGA新手必看:用Vivado和EGo1开发板,从零实现一个七段数码管显示0-F(附完整代码和约束文件)
FPGA实战从零构建七段数码管0-F显示系统VivadoEGo1开发板全流程指南当你第一次拿到FPGA开发板时最令人兴奋的莫过于让硬件真正动起来。七段数码管作为最直观的输出设备之一是数字电路入门的经典项目。本文将带你用Xilinx Vivado工具链和EGo1开发板完整实现一个能显示0-F的数码管系统。不同于单纯粘贴代码我会重点分享新手容易踩坑的实战细节——比如共阴/共阳判断错误导致显示乱码、引脚约束文件配置不当导致信号无法输出等问题。1. 七段数码管工作原理深度解析七段数码管本质上是由7个LEDa-g段和1个小数点dp组成的显示器件。理解其工作原理是避免后续硬件连接错误的关键。常见的数码管有共阴极和共阳极两种类型共阴极所有LED的阴极连接在一起接地阳极分别控制。需要给对应段高电平才能点亮共阳极所有LED的阳极连接在一起接VCC阴极分别控制。需要给对应段低电平才能点亮EGo1开发板使用的是共阴极数码管这决定了我们的代码逻辑。数码管各段与显示字符的对应关系如下-- a -- | | f b | | -- g -- | | e c | | -- d --每个数字/字母的显示对应一组7位二进制编码a-g。例如显示数字0需要点亮a、b、c、d、e、f段g段熄灭对应的二进制值为1111110最高位a最低位g。完整的编码表如下输入(hex)显示字符段码(a-g)二进制值0x00abcdef11111100x11bc01100000x22abged1101101............0xFFagef1000111注意不同型号开发板的段序可能不同务必查阅手册确认a-g对应引脚2. Vivado工程创建与Verilog编码技巧2.1 新建Vivado工程启动Vivado后按以下步骤创建项目选择Create Project → 命名项目如seven_segment选择RTL Project → 勾选Do not specify sources at this time选择正确的FPGA型号EGo1通常使用xc7a35tcsg324-1完成创建后添加新的Verilog源文件2.2 Verilog核心代码实现我们使用case语句实现4位二进制到7段码的译码器。以下是带详细注释的代码module seven_segment( input wire [3:0] num, // 4位输入范围0-15 output wire an, // 数码管使能信号EGo1需要常使能 output reg [6:0] seg // 7段输出(a-g) ); assign an 1b1; // EGo1开发板数码管常使能 always (*) begin case(num) 4h0: seg 7b1111110; // 0 4h1: seg 7b0110000; // 1 4h2: seg 7b1101101; // 2 4h3: seg 7b1111001; // 3 4h4: seg 7b0110011; // 4 4h5: seg 7b1011011; // 5 4h6: seg 7b1011111; // 6 4h7: seg 7b1110000; // 7 4h8: seg 7b1111111; // 8 4h9: seg 7b1111011; // 9 4hA: seg 7b1110111; // A 4hB: seg 7b0011111; // B 4hC: seg 7b1001110; // C 4hD: seg 7b0111101; // D 4hE: seg 7b1001111; // E 4hF: seg 7b1000111; // F default: seg 7b1111110; // 默认显示0 endcase end endmodule关键点说明使用case语句比if-else更简洁高效default分支处理意外输入增强鲁棒性段码顺序必须与硬件连接一致此处a为最高位2.3 仿真测试验证编写测试激励文件验证设计功能timescale 1ns / 1ps module tb_seven_segment(); reg [3:0] num; wire an; wire [6:0] seg; seven_segment uut (.num(num), .an(an), .seg(seg)); initial begin num 0; #100; // 观察波形确认显示正确 for (integer i0; i16; ii1) begin num i; #20; // 每个数字显示20ns end #100 $finish; end endmodule在Vivado中运行仿真后应该看到num从0递增到15时seg输出相应的段码。3. EGo1开发板引脚约束实战3.1 理解约束文件原理XDCXilinx Design Constraints文件将逻辑端口映射到物理引脚。EGo1开发板的数码管连接如下数码管段选连接到FPGA的IO Bank 35位选信号EGo1使用固定使能3.2 完整XDC约束文件创建constraints.xdc文件并添加以下内容# 输入信号约束使用拨码开关 set_property PACKAGE_PIN P2 [get_ports {num[0]}] set_property IOSTANDARD LVCMOS33 [get_ports {num[0]}] set_property PACKAGE_PIN P3 [get_ports {num[1]}] set_property IOSTANDARD LVCMOS33 [get_ports {num[1]}] set_property PACKAGE_PIN P4 [get_ports {num[2]}] set_property IOSTANDARD LVCMOS33 [get_ports {num[2]}] set_property PACKAGE_PIN P5 [get_ports {num[3]}] set_property IOSTANDARD LVCMOS33 [get_ports {num[3]}] # 数码管段选约束 set_property PACKAGE_PIN D2 [get_ports {seg[0]}] # g段 set_property IOSTANDARD LVCMOS33 [get_ports {seg[0]}] set_property PACKAGE_PIN E2 [get_ports {seg[1]}] # f段 set_property IOSTANDARD LVCMOS33 [get_ports {seg[1]}] set_property PACKAGE_PIN F3 [get_ports {seg[2]}] # e段 set_property IOSTANDARD LVCMOS33 [get_ports {seg[2]}] set_property PACKAGE_PIN F4 [get_ports {seg[3]}] # d段 set_property IOSTANDARD LVCMOS33 [get_ports {seg[3]}] set_property PACKAGE_PIN D3 [get_ports {seg[4]}] # c段 set_property IOSTANDARD LVCMOS33 [get_ports {seg[4]}] set_property PACKAGE_PIN E3 [get_ports {seg[5]}] # b段 set_property IOSTANDARD LVCMOS33 [get_ports {seg[5]}] set_property PACKAGE_PIN D4 [get_ports {seg[6]}] # a段 set_property IOSTANDARD LVCMOS33 [get_ports {seg[6]}] # 数码管使能信号 set_property PACKAGE_PIN G6 [get_ports an] set_property IOSTANDARD LVCMOS33 [get_ports an]警告错误的引脚分配可能导致短路或器件损坏务必对照官方原理图确认4. 上板调试与问题排查4.1 完整实现流程生成比特流文件在Vivado中运行综合(Synthesis) → 实现(Implementation) → 生成比特流(Generate Bitstream)成功后会自动打开硬件管理器下载到开发板连接USB线打开开发板电源在硬件管理器中Auto Connect → 右键FPGA设备选择Program Device选择生成的.bit文件并下载功能验证拨动SW0-SW3对应num[0]-num[3]观察数码管显示输入0000(0)到1111(F)应正确显示对应字符4.2 常见问题解决方案问题1数码管完全不亮检查开发板供电是否正常确认an信号是否设置为1EGo1需要高电平使能测量数码管VCC和GND连接问题2显示字符不正确确认段码顺序与硬件匹配重新检查a-g定义检查XDC文件中的引脚分配是否正确确认数码管是共阴还是共阳类型问题3部分段不亮检查对应段的FPGA引脚连接用万用表测量数码管段引脚是否损坏确认约束文件中IO标准设置为LVCMOS33调试技巧可以先用简单代码测试单个段如固定输出7b1000000只点亮a段逐步定位问题。5. 进阶优化与扩展思路基础功能实现后可以考虑以下增强方案动态扫描显示 当需要控制多个数码管时采用分时复用技术// 示例4位数码管动态扫描 reg [1:0] scan_cnt; always (posedge clk) begin scan_cnt scan_cnt 1; case(scan_cnt) 0: begin anodes 4b1110; seg_data digit0; end 1: begin anodes 4b1101; seg_data digit1; end // ...其他位数 endcase endBCD码转换 添加二进制到BCD码转换模块直接显示十进制数module bin2bcd( input [7:0] bin, output reg [3:0] hundreds, output reg [3:0] tens, output reg [3:0] ones ); integer i; always (*) begin // 初始化 hundreds 0; tens 0; ones 0; for(i7; i0; ii-1) begin // 大于等于5则加3 if(hundreds 5) hundreds hundreds 3; if(tens 5) tens tens 3; if(ones 5) ones ones 3; // 左移一位 hundreds hundreds 1; hundreds[0] tens[3]; tens tens 1; tens[0] ones[3]; ones ones 1; ones[0] bin[i]; end end endmodule亮度调节 通过PWM控制显示亮度reg [7:0] pwm_cnt; reg pwm_out; always (posedge clk) begin pwm_cnt pwm_cnt 1; pwm_out (pwm_cnt brightness) ? 1 : 0; end // 用pwm_out控制an信号在项目开发过程中我遇到最棘手的问题是段码顺序与文档描述不符导致显示乱码。后来用示波器抓取各引脚信号才发现开发板厂商的段序定义与常规不同。这也提醒我们硬件开发中文档仅供参考实际验证才是王道。

更多文章