1. Basys3开发板与七段数码管初探
第一次拿到Basys3这块FPGA开发板时,我盯着板载的四位七段数码管看了好久。这排能显示0-9数字的小灯管,其实是嵌入式系统最经典的输出设备之一。相比直接控制单个LED,数码管通过不同段的组合显示字符,既节省引脚又提升可读性。
Basys3作为入门级FPGA开发板,搭载的是Xilinx Artix-7系列FPGA芯片(具体型号XC7A35T)。这块板子的妙处在于,它把数码管的段选和位选信号都通过FPGA引脚引出,让我们可以完全自定义显示逻辑。板载的四个数码管采用共阳极设计,意味着当FPGA输出低电平时对应段会被点亮。
我刚开始接触时犯过一个典型错误:以为直接给数码管送数据就能持续显示。实际上要让多个数码管同时显示不同内容,必须采用动态扫描方式——以足够快的速度轮流点亮每个数码管,利用人眼的视觉暂留效应形成"同时显示"的错觉。这个原理就像快速旋转的霓虹灯牌,虽然每个时刻只有部分灯亮着,但我们看到的却是完整图案。
2. 开发环境搭建与项目创建
在Windows系统上安装Vivado时,建议选择2020.1或更新版本。安装过程中记得勾选"SystemVerilog"支持,这是我们后续编写数码管驱动代码要用到的语言。第一次启动Vivado可能会觉得界面复杂,但FPGA开发的核心流程其实就几个关键步骤:
- 创建工程时选择RTL Project类型
- 添加或创建SystemVerilog源文件
- 编写测试仿真文件
- 配置引脚约束
- 生成比特流文件
- 下载到开发板
具体到我们的数码管项目,在新建工程向导中要注意几个细节:
- 项目路径不要包含中文或空格
- 选择正确的FPGA型号:xc7a35tcpg236-1
- 添加源文件时直接创建新文件,命名为display.sv
创建约束文件时有个小技巧:可以先用文本编辑器新建cons_self.xdc文件,等后面需要配置引脚时再添加具体内容。Vivado会自动识别.xdc后缀的文件为约束文件。
3. 数码管驱动逻辑设计
七段数码管的控制本质上是两组信号的组合:段选信号控制显示什么字符(0-9),位选信号决定哪个数码管亮。Basys3的四个数码管共用同一组段选线,所以必须通过快速切换位选来实现多位数显示。
下面这段SystemVerilog代码实现了核心驱动逻辑:
module display( input logic CLK, input logic SW_in, // 切换显示顺序 output logic [10:0] display_out ); parameter Time = 50000; // 刷新周期 logic [2:0] sel = 0; // 数码管选择 logic [19:0] count = 0; // 时钟计数器 always@(posedge CLK) begin if(SW_in==0) begin // 显示"4321" case(sel) 0: display_out <= 11'b0111_1001111; // 4 1: display_out <= 11'b1011_0010010; // 3 2: display_out <= 11'b1101_0000110; // 2 3: display_out <= 11'b1110_1001100; // 1 default: display_out <= 11'b1111_1111111; // 全灭 endcase end else begin // 显示"1234" case(sel) 0: display_out <= 11'b1110_1001111; // 1 1: display_out <= 11'b1101_0010010; // 2 2: display_out <= 11'b1011_0000110; // 3 3: display_out <= 11'b0111_1001100; // 4 default: display_out <= 11'b1111_1111111; endcase end end endmodule代码中display_out的11位输出很有意思:低7位控制段选(a-g段),高4位控制位选。这种编码方式把两类信号合并到一个端口,节省了FPGA引脚资源。参数Time控制刷新频率,值越大切换速度越慢,实测50000对应约1ms的显示间隔刚刚好。
4. 仿真验证与调试技巧
在烧录到板子前,先用仿真验证逻辑是否正确。创建tb_display.sv测试文件时,要注意时钟信号的生成方式:
module tb_display(); logic CLK; logic SW_in; logic [10:0] display_out; display dut(.*); // 实例化被测模块 initial begin CLK = 0; SW_in = 0; #20 SW_in = 1; // 20ns后切换显示顺序 end always #5 CLK = ~CLK; // 100MHz时钟 endmodule运行行为仿真时,重点关注三个信号:
- CLK时钟是否正常翻转
- SW_in切换时display_out的输出变化
- sel信号是否按预期循环0-3
如果仿真波形显示异常,常见问题可能是:
- 时钟极性搞反(posedge写成negedge)
- case语句没有覆盖所有情况
- 计数器位宽不足导致溢出
有个实用技巧:在Wave窗口右键信号选择Radix→Binary,可以更直观地观察位模式变化。比如当display_out变为0111_1001111时,对应的是最右边数码管显示数字4。
5. 引脚约束与硬件连接
Basys3的约束文件编写需要参考官方手册。数码管相关引脚对应关系如下:
set_property PACKAGE_PIN W5 [get_ports CLK] set_property IOSTANDARD LVCMOS33 [get_ports CLK] set_property PACKAGE_PIN V17 [get_ports SW_in] set_property IOSTANDARD LVCMOS33 [get_ports SW_in] # 数码管段选信号 set_property PACKAGE_PIN W7 [get_ports {display_out[6]}] # a段 set_property PACKAGE_PIN W6 [get_ports {display_out[5]}] # b段 set_property PACKAGE_PIN U8 [get_ports {display_out[4]}] # c段 set_property PACKAGE_PIN V8 [get_ports {display_out[3]}] # d段 set_property PACKAGE_PIN U5 [get_ports {display_out[2]}] # e段 set_property PACKAGE_PIN V5 [get_ports {display_out[1]}] # f段 set_property PACKAGE_PIN U7 [get_ports {display_out[0]}] # g段 # 数码管位选信号 set_property PACKAGE_PIN W4 [get_ports {display_out[10]}] # DIG1 set_property PACKAGE_PIN V4 [get_ports {display_out[9]}] # DIG2 set_property PACKAGE_PIN U4 [get_ports {display_out[8]}] # DIG3 set_property PACKAGE_PIN U2 [get_ports {display_out[7]}] # DIG4引脚分配有个易错点:不同数码管的段选信号是并联的,所以如果发现多个数码管同时显示相同内容,很可能是位选信号没配置正确。建议先用单个数码管测试,确认段选信号工作正常后再添加位选逻辑。
6. 进阶功能与优化建议
当基础功能实现后,可以尝试以下增强功能:
- 显示亮度调节:通过PWM控制位选信号的占空比
- 显示缓冲寄存器:添加中间寄存器避免显示闪烁
- BCD译码器:将二进制数自动转换为七段码
- 滚动显示效果:动态改变显示位置
这里给出一个带亮度调节的改进版本:
module display_enhanced( input logic CLK, input logic [3:0] brightness, // 亮度等级0-15 output logic [10:0] display_out ); logic [15:0] pwm_counter; logic pwm_out; // PWM亮度控制 always_ff @(posedge CLK) begin pwm_counter <= pwm_counter + 1; pwm_out <= (pwm_counter[15:12] < brightness); end // 原显示逻辑 always_ff @(posedge CLK) begin if(pwm_out) begin // 保持原有显示代码 end else begin display_out[10:7] <= 4'b1111; // 关闭所有位选 end end endmodule调试时如果发现数码管有轻微闪烁,可以尝试:
- 增加刷新频率(减小Time参数值)
- 检查时钟约束是否正确定义
- 确保位选信号切换时没有毛刺
7. 常见问题排查指南
在实际操作中,新手常会遇到这些问题:
问题一:数码管完全不亮
- 检查开发板供电是否正常
- 确认比特流文件下载成功
- 测量VCC和GND之间电压(应为3.3V)
- 检查约束文件中引脚号是否正确
问题二:显示内容错乱
- 确认段选信号编码正确(a-g段对应关系)
- 检查数码管是共阳还是共阴(Basys3是共阳)
- 用万用表测量各段选信号电压变化
问题三:显示闪烁严重
- 调整刷新频率参数Time
- 检查时钟信号是否稳定
- 确认没有多个always块同时操作display_out
问题四:只有部分数码管能亮
- 检查位选信号约束
- 确认位选使能逻辑正确
- 测试单个数码管是否损坏
记得每次修改代码后要重新综合生成比特流。Vivado的综合过程可能会优化掉一些看似无用的信号,如果发现某些信号在仿真中可见但实际板子上没反应,可以尝试添加(* keep = "true" *)属性防止优化。