verilog 数字显示模块
对于有FPGA开发板的同学们来说,数码管是一个比较常见的外设,那么如何驱动它就是一个问题?本次小白用4位8段的共阳极数码管,来做本次小实验。对了那什么叫做位?什么叫做段?
简单来说,开发板上有几个数码管就是几位数码管,4个就是4位数码管;而对于每一个数码管来说,其分成了一段一段的LED灯,共七段,小数点算一段,加起来就是8段。如果还不能理解的话,看下图,我们的a,b,c,d,e,f,g,小数点dp,这就是这8段。用verilog表示出来就是:
reg [3:0] sel; //数码管的位选
reg [7:0] seg; //数码管的段选
/*简单解释以下就是这种对应关系
seg[7:0] = {dp,g,f,e,d,c,b,a};
*/
不过七段数码管跟共阴极数码管的同学也不要划走,因为8段数码管也就是比七段数码管多了一个小数点,小数点如果我们不用的话,8段数码管跟七段数码管那是一样的。再说,共阳极跟共阴极,简单理解(对段选而言):
共阳极:0亮,1不亮。 共阴极:1亮,0不亮。
seg[7:0] = 8'b1100_0000; //共阳极表示阿拉伯数字0
seg[7:0] = 8'b0011_1111; //共阴极表示阿拉伯数字0
原理解释到此,本次呢就用4位的8段数码管来表示阿拉伯数字0-9,当然也可以表示英文字母,那都是一样的。如果你想用数码管显示个数,最起码得有个数吧!!!
本次写了一个加法器,很简单的一个 c = a + b;主要是数码管的显示!!!话不多说,直接上代码!!!
module add(
clk ,
rst ,
a ,
b ,
seg ,
sel
);
input clk ;
input rst ;
input [9:0] a ;
input [9:0] b ;
output reg [3:0] sel ; //数码管位选
output reg [7:0] seg ; //数码管段选
parameter sys_clk = 50_000_000 ;
localparam cnt1_max = 24 ; //仿真1MHZ,1us
//localparam cnt1_max = sys_clk/1000/2-1 ;//下板子1ms
reg [15:0] seg_data ; //BCD译码结果给它
reg [3:0] selcnt ; //数码管位选计数器
reg [30:0] cnt2 ;
reg [3:0] disp_data ;
reg clk1ms ;
reg [3:0] qianwei ; //4个数码管,所以千位
reg [3:0] baiwei ; //百位
reg [3:0] shiwei ; //十位
reg [3:0] gewei ; //个位
reg [15:0] c ; //中间寄存器,用来存放加后的数据
always@ (posedge clk or negedge rst)
begin
if (!rst)
c <= 0;
else
c <= a + b;
end
always@ (posedge clk or negedge rst) //从这往后都可以套,数码管显示通用的!!!
begin
if (!rst) begin
qianwei <= 0;
baiwei <= 0;
shiwei <= 0;
gewei <= 0;
end
else begin //二进制转BCD码
qianwei <= c/1000 ; //取整
baiwei <= (c/100)%10 ; //取余
shiwei <= (c/10)%10 ;
gewei <= c%10 ;
end
end
always@ (posedge clk or negedge rst)//输出加的结果C
begin
if (!rst)
seg_data <= 0;
else begin
seg_data <={
qianwei,
baiwei ,
shiwei ,
gewei
};
end
end
always@(posedge clk or negedge rst)//1KHZ时钟分频计数器
begin
if (!rst)
cnt2 <= 0;
else if (cnt2 == cnt1_max)
cnt2 <= 0;
else
cnt2 <= cnt2+1'b1;
end
always@(posedge clk or negedge rst)
begin
if (!rst)
clk1ms <= 0;
else if (cnt2 == cnt1_max)
clk1ms <= ~clk1ms;
end
always@(posedge clk1ms or negedge rst)//数码管位选计数器
begin
if (!rst)
selcnt <= 4'd0;
else if (selcnt == 4'd3) //如果5位 else if (selcnt == 4'd4),就改这!!!
selcnt <= 4'd0;
else
selcnt <= selcnt + 1'b1;
end
always@(*)//数码管位选显示
begin
if(!rst)
disp_data <= 4'd0;
else begin
case(selcnt) //数码管位选计数器
4'd0 : begin sel = 4'h7; disp_data = seg_data[15:12]; end
4'd1 : begin sel = 4'hb; disp_data = seg_data[11:8] ; end
4'd2 : begin sel = 4'hd; disp_data = seg_data[7:4] ; end
4'd3 : begin sel = 4'he; disp_data = seg_data[3:0] ; end
default : disp_data = 4'd0;
endcase
end
end
/*
always@(*)//数码管位选显示,如果5位!!!
begin
if(!rst)
disp_data <= 4'd0;
else begin
case(selcnt) //数码管位选计数器
4'd0 : begin sel = 4'h7; disp_data = seg_data[19:16]; end
4'd1 : begin sel = 4'hb; disp_data = seg_data[15:12] ; end
4'd2 : begin sel = 4'hd; disp_data = seg_data[11:8] ; end
4'd3 : begin sel = 4'he; disp_data = seg_data[7:4] ; end
4'd4 : begin sel = 4'he; disp_data = seg_data[3:0] ; end
default : disp_data = 4'd0;
endcase
end
end*/
always@(*)
begin
if(!rst)
seg <= 8'hff;
else begin //数码管段选显示
case(disp_data)
4'd0 : seg = 8'hc0;
4'd1 : seg = 8'hf9;
4'd2 : seg = 8'ha4;
4'd3 : seg = 8'hb0;
4'd4 : seg = 8'h99;
4'd5 : seg = 8'h92;
4'd6 : seg = 8'h82;
4'd7 : seg = 8'hf8;
4'd8 : seg = 8'h80;
4'd9 : seg = 8'h90;
default : seg = 8'hff;
endcase
end
end
endmodule
下面关于代码有几点需要给大家做一个提醒!!!
1:本次实验是四位8段的共阳极数码管,如果是共阴极,就得去查它的段选值,你不用算,百度搜索一下,跟我的段选值做一个替换就行了!!!段选模块在代码中注释了。
2:位选怎么套呢?有一个位选计数器 selcnt ,如果 5个数码管,计数器加1,如果4个,计数器减1。我在代码上提供了一种如果是5位的位选模块的改法,大家可以参照!!!
上述的位选跟段选都是通过组合逻辑写的,所以直接套!!!
3:seg_data是干嘛的?seg_data为什么是16位?
因为我们计算的数据需要通过BCD译码进行显示,BCD码大家知道是4位的,而我们的数码管是4位的,所以seg_data的位宽就是4*4 =16,seg_data[15:0];假设我们的数码管是5位的,那么seg_data的位宽就是4*5 = 20,seg_data[19:0]。所以需要BCD译码,大家可以直接套!!!
4:为什么会有个下板子?和仿真时钟?
上述代码中,我把下板子的代码注释掉了,因为数码管是动态扫描刷新来显示数据的,所以就得有个扫描时钟,一般而言,由于数码管的余辉效应跟人眼的视觉暂留效果,扫描时钟不大于10ms,所以我用了1ms的clk1ms来作为数码管的扫描时钟,但是,这个时钟对仿真来说太久了,所以得定义一个仿真用的时钟。
下面把仿真的代码附在下面!!!
`timescale 1 ps/ 1 ps
module add_vlg_tst();
// constants
// general purpose registers
// test vector input registers
reg [9:0] a;
reg [9:0] b;
reg clk;
reg rst;
// wires
wire [7:0] seg;
wire [3:0] sel;
// assign statements (if any)
add i1 (
// port map - connection between master ports and signals/registers
.a(a),
.b(b),
.clk(clk),
.rst(rst),
.seg(seg),
.sel(sel)
);
initial
begin
clk = 0;
rst = 0;
a = 0;
b = 0;
#100
rst = 1'b1;
#100
a = 10'd1000;
b = 10'd1000;
#5000
a = 10'd1020;
b = 10'd1020;
#10000 $stop;
end
always #10 clk = ~clk;
endmodule
具体的仿真流程,如上篇博客所示!!!
仿真结果如下图所示:
有什么看不懂的,可以给小白评论,小白看到了会第一时间回复你的。
推荐阅读
-
verilog 数字显示模块
-
基于 FPGA 的数字信号处理 (6) 如何确定 Verilog 表达式的符号 - 一般规则
-
网络安全(中级组)-模块 B:服务远程控制-2.通过渗透测试平台 windows 7 在本地 PC 上打开服务器场景下的终端服务,并提交服务端口号,在此操作的结果中显示为 FLAG;
-
用 three.js (webgl) 构建智能楼宇、设备检测和数字孪生 - 第 13 课 使用 webgl(three.js) 构建三维智能公园、三维大屏幕、三维建筑、智能灯杆三维显示屏、三维灯杆、网页版三维、bim 管理系统 - 第 6 课
-
模块化建筑|集成建筑与数字乡村振兴
-
labview 图形显示正弦曲线信号发生器频率振幅相位数字示波器滤波器频谱分析
-
[51 微控制器]初学者必学的矩阵键盘基础项目 -(读取 LCD 屏幕上显示的矩阵键盘数字) (7)
-
微控制器白色学习路径 (IX) - 在数码管上显示数字的矩阵键
-
GD32F470_GY30 光传感器模块 数字光强 BH1750FVI 光级-2.29.2 规格
-
Qt 图形图像开发中的曲线图模块 QChart 库的坐标轴与数据不对应,密集散点图无法显示的问题解决方案