FPGA实战数码管(1) 静态显示
led的花样点灯差不多了吧,接下来学习另一个基础的东西,数码管。
前言
数码管在FPGA开发板上占得位置不小,在工程开发中也必不可少,比如后边的温度传感器就会用数码管来显示温度。这里先不多介绍温度传感器,过一段时间就会发了。本篇文章先用数码管来做静态显示。也就是六个数码管显示一起显示一个数,一起变化。
一、数码管原理
Cyclone IV开发板中的数码管是共阳极,所以数码管中需要给低电平,对应的led段才会亮。
位选信号原理图如图所示,位选信号也是需要低电平有效。
这张图是C4板子上数码管的电路结构,是不是看起来很麻烦,那就先来张简单的:
从这张图可以看出:数码管一共有八个小led管组成(DP就是小数点)。数码管就是通过控制每一段led的亮灭来控制显示的数字。
例如:当想让数码管显示零的时候,也就是A/B/C/D/E/F亮,G/DP灭。所以
ZERO = 8'b1100_0000;
其中11就是指G/DP没有亮,其他的0都是点亮的led,就由这种亮灭情况,来显示相应的数字。
这里大家可以去想一想其他数字怎么写亮灭才能显示出来。
二、位选、段选
1.位选信号:
位选信号就是挑选六个数码管中的哪个来显示的信号。当选到这个数码管的时候,该数码管才会亮。
上边己经说过:
位选信号原理图如图所示,位选信号也是需要低电平有效。
咱这里让六个数码管一块亮一块灭,所以位选信号的六位同时变化就行。
//------------------------<位选信号>---------------------------
reg [5:0] sel_r ;//位选信号寄存
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
sel_r <= 6'b111_111 ;//复位时全灭
end
else if(end_cnt_1s)begin
sel_r <= 6'b111_111 ;
end
else begin
sel_r <= ~sel_r ;//全亮
end
end
2.段选信号:
段选信号就是上边数码管那张图,一共八段led,怎么选择哪一段亮灭,就是段选信号该做的。
数码管中需要给低电平,对应的led段才会亮
下面的代码就是从0-F段选该怎么选择亮灭显示相应的数。
//-------------------------<段选信号参数>------------------------------
parameter ZERO = 8'b1100_0000 ,
ONE = 8'b1111_1001 ,
TWO = 8'b1010_0100 ,
THREE = 8'b1011_0000 ,
FOUR = 8'b1001_1001 ,
FIVE = 8'b1001_0010 ,
SIX = 8'b1000_0010 ,
SEVEN = 8'b1111_1000 ,
EIGHT = 8'b1000_0000 ,
NINE = 8'b1001_0000 ,
NUM_A = 8'b1000_1000 ,
NUM_B = 8'b1000_0011 ,
NUM_C = 8'b1100_0110 ,
NUM_D = 8'b1010_0001 ,
NUM_E = 8'b1000_0110 ,
NUM_F = 8'b1000_1110 ;
下面就是用了一个case语句整理数码管显示的数字。
0-F 一共16位,所以这里用5位宽的cnt_num
来控制数码管的16次数字显示。
然后用dig段选
与相应的cnt_num
的值对应,让数码管“知道”该在啥时候显示啥数字。
//------------------------<cnt_num>---------------------------
reg [4:0] cnt_num ;//数码管显示的数字
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_num <= 0 ;
end
else if((cnt_num == 5'd15)&&(end_cnt_1s))begin
cnt_num <= 0 ;//计满归零
end
else if(end_cnt_1s)begin
cnt_num <= cnt_num + 1 ;//每秒加一
end
else begin
cnt_num <= cnt_num ;
end
end
//------------------------<段选信号>---------------------------
reg [7:0] dig_r ;//段选信号寄存
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
dig_r <= ZERO ;//复位归零
end
else begin
case (cnt_num)
5'd0 : dig_r <= ZERO ;
5'd1 : dig_r <= ONE ;
5'd2 : dig_r <= TWO ;
5'd3 : dig_r <= THREE ;
5'd4 : dig_r <= FOUR ;
5'd5 : dig_r <= FIVE ;
5'd6 : dig_r <= SIX ;
5'd7 : dig_r <= SEVEN ;
5'd8 : dig_r <= EIGHT ;
5'd9 : dig_r <= NINE ;
5'd10 : dig_r <= NUM_A ;
5'd11 : dig_r <= NUM_B ;
5'd12 : dig_r <= NUM_C ;
5'd13 : dig_r <= NUM_D ;
5'd14 : dig_r <= NUM_E ;
5'd15 : dig_r <= NUM_F ;
default: dig_r <= ZERO ;
endcase
end
end
三、代码
1、静态显示
对了,这里sel和dig是因为在模块信号列表中给sel和dig定义为wire信号,为了用时序逻辑赋值,就分别定义了reg类型的信号。
如果嫌麻烦就在信号列表里定义为reg信号就行。这里给wire、reg类型还不太了解的同学解释一下。加油,迟早会明白的。
/**************************************功能介绍***********************************
Date : 2023年9月30日 20:33:26
Author : Yang.
Project : 数码管静态显示
Require : 数码管全显,从1-F轮流显示
*********************************************************************************/
module seg_dynamic (
input clk ,
input rst_n ,
output [5:0] sel ,
output [7:0] dig
);
parameter ZERO = 8'b1100_0000 ,
ONE = 8'b1111_1001 ,
TWO = 8'b1010_0100 ,
THREE = 8'b1011_0000 ,
FOUR = 8'b1001_1001 ,
FIVE = 8'b1001_0010 ,
SIX = 8'b1000_0010 ,
SEVEN = 8'b1111_1000 ,
EIGHT = 8'b1000_0000 ,
NINE = 8'b1001_0000 ,
NUM_A = 8'b1000_1000 ,
NUM_B = 8'b1000_0011 ,
NUM_C = 8'b1100_0110 ,
NUM_D = 8'b1010_0001 ,
NUM_E = 8'b1000_0110 ,
NUM_F = 8'b1000_1110 ;
//------------------------<计时器>---------------------------
parameter MAX_1S = 26'd50_000_000 ;
reg [25:0] cnt_1s ;//1s计数器
wire add_cnt_1s ;
wire end_cnt_1s ;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_1s <= 0 ;
end
else if(add_cnt_1s)begin
if(end_cnt_1s)begin
cnt_1s <= 0 ;
end
else begin
cnt_1s <= cnt_1s + 1 ;
end
end
end
assign add_cnt_1s = 1'b1 ;
assign end_cnt_1s = add_cnt_1s && cnt_1s == MAX_1S - 1 ;
//------------------------<位选信号>---------------------------
reg [5:0] sel_r ;//位选信号寄存
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
sel_r <= 6'b111_111 ;//复位时全灭
end
else if(end_cnt_1s)begin
sel_r <= 6'b111_111 ;
end
else begin
sel_r <= ~sel_r ;//全亮
end
end
//------------------------<cnt_num>---------------------------
reg [4:0] cnt_num ;//数码管显示的数字
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_num <= 0 ;
end
else if((cnt_num == 5'd15)&&(end_cnt_1s))begin
cnt_num <= 0 ;//计满归零
end
else if(end_cnt_1s)begin
cnt_num <= cnt_num + 1 ;//每秒加一
end
else begin
cnt_num <= cnt_num ;
end
end
//------------------------<段选信号>---------------------------
reg [7:0] dig_r ;//段选信号寄存
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
dig_r <= ZERO ;//复位归零
end
else begin
case (cnt_num)
5'd0 : dig_r <= ZERO ;
5'd1 : dig_r <= ONE ;
5'd2 : dig_r <= TWO ;
5'd3 : dig_r <= THREE ;
5'd4 : dig_r <= FOUR ;
5'd5 : dig_r <= FIVE ;
5'd6 : dig_r <= SIX ;
5'd7 : dig_r <= SEVEN ;
5'd8 : dig_r <= EIGHT ;
5'd9 : dig_r <= NINE ;
5'd10 : dig_r <= NUM_A ;
5'd11 : dig_r <= NUM_B ;
5'd12 : dig_r <= NUM_C ;
5'd13 : dig_r <= NUM_D ;
5'd14 : dig_r <= NUM_E ;
5'd15 : dig_r <= NUM_F ;
default: dig_r <= ZERO ;
endcase
end
end
assign sel = sel_r ;
assign dig = dig_r ;
endmodule
2.仿真代码
`timescale 1ns/1ns
module tb_seg_dynamic();
//激励信号定义
reg tb_clk ;
reg tb_rst_n ;
//输出信号定义
wire [5:0] seg_sel ;
wire [7:0] seg_dig ;
//时钟周期参数定义
parameter CLOCK_CYCLE = 20;
//参数重新定义
defparam u_seg_dynamic.MAX_1S = 100;
//模块例化
seg_dynamic u_seg_dynamic(
/*input */.clk (tb_clk ),
/*input */.rst_n (tb_rst_n ),
/*output [5:0] */.sel (seg_sel),//位选
/*output [7:0] */.dig (seg_dig) //段选
);
//产生时钟
initial tb_clk = 1'b0;
always #(CLOCK_CYCLE/2) tb_clk = ~tb_clk;
//产生激励
initial begin
tb_rst_n = 1'b1;
#(CLOCK_CYCLE*2);
tb_rst_n = 1'b0;
#(CLOCK_CYCLE*20);
tb_rst_n = 1'b1;
#(CLOCK_CYCLE*10000);
$stop;
end
endmodule
总结
1、仿真结果
从图中可以看出,数码管的位选信号同时变化且符合变化条件,段选信号也符合变化条件,合理且成功。
2、上板效果
FPGA数码管静态显示文章来源:https://www.toymoban.com/news/detail-762537.html
数码管基础还是很简单的,接下来就是动态显示,然后就可以制作一个万能模版,以后有哪里用到,就直接套用模版。文章来源地址https://www.toymoban.com/news/detail-762537.html
到了这里,关于FPGA实战------数码管(1)静态显示的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!