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

具体的仿真流程,如上篇博客所示!!!

仿真结果如下图所示:

 有什么看不懂的,可以给小白评论,小白看到了会第一时间回复你的。

评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值