FPGA实战------数码管(1)静态显示

led的花样点灯差不多了吧,接下来学习另一个基础的东西,数码管。



前言

数码管在一些产品中使用的频率不小,比如后边的温度传感器就会用数码管来显示温度。这里先不多介绍温度传感器,过一段时间就会发了。本篇文章先用数码管来做静态显示。也就是六个数码管显示一起显示一个数,一起变化。


一、数码管原理

我所使用的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数码管静态显示

数码管基础还是很简单的,接下来就是动态显示,然后就可以制作一个万能模版,以后有哪里用到,就直接套用模版。

代码工程都放进baidu网盘了,自行提取

链接:https://pan.baidu.com/s/1lQqqWZXfb3i6XHwkKf52zg
提取码:yang

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值