FPGA之点亮四位数码管
距离比赛开始还有五天,这五天从入门到精通赛灵思的FPGA,请跟我一起加油。本学习笔记使用正点原子的达芬奇PROT35开发板,板载型号为xc7a35tfgg484-2,在Vivado中选型时注意选择正确的型号。
一.新建工程
- 双击打开Vivado
2.新建工程
创建文件,名称跟着工程名一致即可
创建模块名
如此,一个模块创建好了
二.阅读原理图
前面的步骤主要用来熟悉软件操作,下一步阅读原理图,分析数码管的驱动方式。分模块进行设计,最后实现一个四位数码管的显示计数的功能。
根据扩展板原理图可知,四位数码管的位选信号是由一个74LS138芯片,及38译码器组成,位码0,1,2,3分别对应数码管K1,K2,K3,K4。通过译码器按时序依次给K1,K2,K3,K4供电。通过位选信号对数码管的位置进行刷新选择显示,通过段选信号对单个数码管显示具体数字或者字母。
该数码管是共阴极数码管,共阴数码管是数码管所有的负极连接在一起,这个端口被称之为位选端口 ;
其余的数码管引脚a-h都为段选端口 。
下图所示真值表。
段选信号(八进制) | 显示值 | dp | g | f | e | d | c | b | a |
---|---|---|---|---|---|---|---|---|---|
3F | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 |
06 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 |
5B | 2 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | 1 |
4F | 3 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 |
66 | 4 | 0 | 1 | 1 | 0 | 1 | 1 | 0 | 1 |
6D | 5 | 0 | 1 | 1 | 0 | 1 | 1 | 0 | 1 |
7D | 6 | 0 | 1 | 1 | 1 | 1 | 1 | 0 | 1 |
07 | 7 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 |
7F | 8 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
6F | 9 | 0 | 1 | 1 | 0 | 1 | 1 | 1 | 1 |
77 | A | 0 | 1 | 1 | 0 | 0 | 1 | 1 | 0 |
7C | B | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
39 | C | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 1 |
5E | D | 0 | 1 | 0 | 1 | 1 | 1 | 1 | 0 |
79 | E | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 1 |
71 | F | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 1 |
76 | H | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 0 |
38 | L | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 0 |
37 | n | 0 | 0 | 1 | 1 | 0 | 1 | 1 | 1 |
3E | u | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 0 |
73 | P | 0 | 1 | 1 | 1 | 0 | 0 | 1 | 1 |
5C | o | 0 | 1 | 0 | 1 | 1 | 1 | 0 | 0 |
40 | - | 0 | 4 | 0 | 0 | 0 | 0 | 0 | 0 |
00 | 熄灭 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
模块一.数码管驱动模块
数码管驱动模块输入信号有sys_clk,sys_rst_n,num,分别对应系统时钟,系统复位信号,输入数字。输出信号为dis_sel,dis_seg,dis_dp,分别对应数码管位选信号,数码管段选信号,小数点信号。
`timescale 1ns / 1ps
module Nixie(
input sys_clk,//定义系统时钟
input sys_rst_n,//定义复位信号
input [15:0] num,//定义输入信号的数字
output reg [2:0] dis_sel,//定义数码管位选信号
output reg [6:0] dis_seg,//定义数码管段选信号
output dis_dp//定义数码管小数点的信号
);
reg [31:0] cnt_1ms; //定义1ms计数器位宽
reg [15:0] split_num; //定义显示数字位宽
reg [2:0] dis_num; //定义位选信号位宽
wire [3:0] num_r1; //定义第一位显示的数字
wire [3:0] num_r2; //定义第二位显示的数字
wire [3:0] num_r3; //定义第三位显示的数字
wire [3:0] num_r4; //定义第四位显示的数字
parameter num0 = 8'h3f; //显示"0"
parameter num1 = 8'h06; //显示"1"
parameter num2 = 8'h5b; //显示"2"
parameter num3 = 8'h4f; //显示"3"
parameter num4 = 8'h66; //显示"4"
parameter num5 = 8'h6d; //显示"5"
parameter num6 = 8'h7d; //显示"6"
parameter num7 = 8'h07; //显示"7"
parameter num8 = 8'h7f; //显示"8"
parameter num9 = 8'h6f; //显示"9"
parameter numA = 8'h77; //显示"a"
parameter numB = 8'h7c; //显示"b"
parameter numC = 8'h39; //显示"c"
parameter numD = 8'h5e; //显示"d"
parameter numE = 8'h79; //显示"e"
parameter numF = 8'h71; //显示"f"
//时序逻辑,设置1ms定时器
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)//复位初始化
cnt_1ms <= 2'b0;
else if(cnt_1ms == 49_999)//计数到清零
cnt_1ms <= 2'b0;
else
cnt_1ms <= cnt_1ms+2'b1;//自增
end
//每1ms扫描一位数码管
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin//复位初始化
dis_sel <= 1'b0;
end
else if(cnt_1ms == 49_999)begin//1ms定时到,扫描下一位,依次0,1,2,3四位数码管
dis_sel <= dis_sel + 1'b1;
if(dis_sel == 3)//位选计数到3时清零
dis_sel <= 1'b0;
end
else
dis_sel <= dis_sel;//复位保持
end
//将数字信号取位
assign dis_dp = 1'b0;//小数点置零
assign num_r1 = num % 10; //个位
assign num_r2 = num / 10 % 10; //十位
assign num_r3 = num / 100 % 10; //百位
assign num_r4 = num / 1000 % 10; //千位
//简易BCD码转换
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)//复位清零
split_num <= 16'haaaa;
else if(num_r4)//千位数
split_num <= {num_r4,num_r3,num_r2,num_r1};
else if(num_r3)begin//百位数
split_num[11:0] <= {num_r3,num_r2,num_r1};
split_num[15:12] <= 4'ha;
end
else if(num_r2)begin//十位数
split_num[7:0] <= {num_r2,num_r1};
split_num[15:8] <= 8'haa;
end
else if(num_r1)begin//个位数
split_num[3:0] <= num_r1;
split_num[15:4] <= 12'haaa;
end
end
//位选
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)//复位清零
dis_num <= 0;
else
case(dis_sel)//位选显示对应数字
0: dis_num <= split_num[3:0];
1: dis_num <= split_num[7:4];
2: dis_num <= split_num[11:8];
3: dis_num <= split_num[15:12];
default:dis_num <= 0;
endcase
end
//段选
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)//复位置一
dis_seg <= 7'b111_1111;
else
case(dis_num)//将十进制数转化成对应的段码
4'h0 : dis_seg <= num0;
4'h1 : dis_seg <= num1;
4'h2 : dis_seg <= num2;
4'h3 : dis_seg <= num3;
4'h4 : dis_seg <= num4;
4'h5 : dis_seg <= num5;
4'h6 : dis_seg <= num6;
4'h7 : dis_seg <= num7;
4'h8 : dis_seg <= num8;
4'h9 : dis_seg <= num9;
default:dis_seg <= 7'b000_0000;//无对应的数字不显示
endcase
end
endmodule
模块二.计数器
计数器模块分别设置了一个10Hz的分频信号和一个0~9999的计数器,作为数字信号输出给数码管模块
`timescale 1ns / 1ps
module Get_data(
input sys_clk,//时钟引脚定义
input sys_rst_n,//系统复位引脚定义
output reg [15:0] data//输出数字信号
);
reg [31:0] cnt_100ms;
//5MHz->10Hz
always @ (posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
cnt_100ms <= 0;
else if(cnt_100ms == (5000_000 - 1))
cnt_100ms <= 0;
else
cnt_100ms = cnt_100ms + 1;
end
//1234->9999
always @ (posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)//复位给初始值1234
data <= 1234;
else if(cnt_100ms == (5_000_000 - 1))begin//100ms定时到,计数加一
if(data == 9999)//清零
data <= 0;
else
data = data + 1;
end
end
endmodule
顶层模块设计
顶层模块调用两个模块,将两个模块结合到一起,实现模块化设计。
`timescale 1ns / 1ps
//输入输出引脚定义
module Nixie_top(
input sys_clk,
input sys_rst_n,
output [2:0] dis_sel,
output [6:0] dis_seg,
output dis_dp
);
wire [15:0] data;//数据位
//例化数码管模块
Nixie u_Nixie(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.num (data),
.dis_sel (dis_sel),
.dis_seg (dis_seg),
.dis_dp (dis_dp)
);
//例化计数器模块
Get_data u_Get_data(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.data (data)
);
endmodule
仿真文件编写
程序文件编写完成后,编写访问文件。
`timescale 1ns / 1ps
module tb_Nixie(
);
//在模块外实例化引脚
reg sys_clk;//输入用reg
reg sys_rst_n;
wire [15:0] num;//输出用wire
wire [2:0] dis_sel;
wire [6:0] dis_seg;
wire dis_dp;
//初始化激励信号
initial begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
#201 sys_rst_n <= 1'b1;
end
always #10 sys_clk <= ~sys_clk;//模拟时钟
//例化接口
Nixie u_Nixie(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.num (num),
.dis_sel (dis_sel),
.dis_seg (dis_seg),
.dis_dp (dis_dp)
);
Get_data u_Get_data(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.data (num)
);
endmodule
仿真波形,由仿真波形可看出位码和段码都正确
仿真波形,由仿真波形可看出位码和段码都正确
引脚配置
在配置引脚的时候,犯了个小错误导致数码管不能正常显示。注意引脚配置,扩展板和主板要共地。
参考博客:https://blog.youkuaiyun.com/wuzhikaidetb/article/details/121586283