赞
踩
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,就由这种亮灭情况,来显示相应的数字。
这里大家可以去想一想其他数字怎么写亮灭才能显示出来。
位选信号就是挑选六个数码管中的哪个来显示的信号。当选到这个数码管的时候,该数码管才会亮。
上边己经说过:
位选信号原理图如图所示,位选信号也是需要低电平有效。
咱这里让六个数码管一块亮一块灭,所以位选信号的六位同时变化就行。
//------------------------<位选信号>---------------------------
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
段选信号就是上边数码管那张图,一共八段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
对了,这里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
`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
从图中可以看出,数码管的位选信号同时变化且符合变化条件,段选信号也符合变化条件,合理且成功。
FPGA数码管静态显示
数码管基础还是很简单的,接下来就是动态显示,然后就可以制作一个万能模版,以后有哪里用到,就直接套用模版。
代码工程都放进baidu网盘了,自行提取
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。