两个数码管显示16位数

梦想与她 提交于 2020-01-15 04:15:01

背景

此篇文章是4x4矩阵键盘的衍生品。

4x4的矩阵键盘一共16个按键,每个按键对应相应的键值,分别为0~15。每按一个键,对应的键值并联到4个LED灯上,以二进制的形式表示(比如键值5,二进制为'b0101,对应的LED灯亮灭情况为:亮灭亮灭),同时也并联到数码管上,通过数码管显示出目前对应的键值。

然而,一个数码管足够显示0~F(即0到15)这16个键值,如果想完完全全转换成十进制来显示,即键值10显示为10而非a,键值11显示为11而非b,那该如何操作呢?

以上即为不安分守己的瞎折腾背景。

开始

之前,写过一篇关于小梅哥74HC595驱动设计的思考,该驱动可充分利用两片级联的74HC595芯片,将16位串行数据转换为16位并行数据。实际应用中,由于开发板上有8个数码管;每个数码管又有8个段,正好16位数据。驱动将16位并行数据data[15:0]输入,输出串行的16位数据ds,并利用两片级联芯片将16位数据ds再次转为并行数据输出,以此驱动数码管,并显示相应数据。选择对应的数码管(以sel[7:0]信号表示),数码管上对应需要显示的段(以seg[7:0]信号表示)。

目前74HC595驱动已经写好,只等数码管的位选和段选数据输入。产生16位数据的verilog文件以梅哥的hex8.v文件为参考,并作些许修改。其中有一两个小技巧一同分享。

分析

大于等于10的数据,其二进制编码有以下特点:

  1. 最高位dis_data[3]永远为1;
  2. dis_data[2]dis_data[1]取逻辑或,其值永远为1;

虽然键值8和9的最高位也为1,但是其dis_data[2]dis_data[1]为0,两者相或,值为0.

以上,足以利用该特点进行两个数码管显示0~15,即令
wire oct = dis_data[3] && (dis_data[2] || dis_data[1]) ;(英文oct即为十进制的意思)
oct == 1,即键值大于等于10,选择两个数码管进行显示,并将第二个数码管永远置为1;
oct == 0,既键值小于10,只需选择一个数码管进行显示。

产生位选sel[7:0]和段选seg[7:0]的代码如下:

/*=============================================================================
#     FileName: hex8.v
#         Desc: copy from 小梅哥
#       Author: 小梅哥
#        Email: 
#     HomePage: 
#      Version: 0.0.1
#   LastChange: 2019-11-30 16:49:52
#      History:
=============================================================================*/
module hex8(
    clk_50M ,
    rstn    ,
    en_hex8 ,
    dis_data,
    sel     ,
    seg 
);

input        clk_50M    ;
input        rstn       ;
input        en_hex8    ;
input [3:0]	 dis_data	;

output     [7:0] sel    ;
output reg [7:0] seg    ;

reg [15:0] cnt   ;
reg        clk_1K;
reg [7 :0] select;  //定义一个中间寄存器,数码管位选信号sel用来调用,详见代码第74行

parameter CNT_NUM = 16'd25_000;

wire oct = (dis_data[3] && (dis_data[2] || dis_data[1]));

//divide clk_50M to clk_1K
//用频率为1K的时钟进行8个数码管的扫描
always @ (posedge clk_50M or negedge rstn) begin
    if (!rstn)
        cnt <= 16'h0;
    else if (!en_hex8)
        cnt <= 16'h0;
    else begin
        if (cnt == CNT_NUM/2 - 1)
            cnt <= 16'h0;
        else
            cnt <= cnt + 1'b1;
	 end
end

always @ (posedge clk_50M or negedge rstn) begin
    if (!rstn)
        clk_1K <= 1'b0;
    else if (cnt == CNT_NUM/2 - 1)
        clk_1K <= ~clk_1K;
    else
        clk_1K <= clk_1K;
end

//gate control clk
//scanning the hex
always @ (posedge clk_1K or negedge rstn) begin
    if (!rstn)
		  select <= 8'b0000_0001;
    else if (oct) begin
	     if (select == 8'b0000_0010)  //只选择末端两个数码管
		      select <= 8'b0000_0001;
		  else
				select <= select << 1;
	 end
	 else
	     select <= 8'b0000_0001;
end

assign sel = (en_hex8 ? (oct ? select : 8'b0000_0001) : 8'b0);

reg [3:0] data_tmp;
always @ (*) begin
    if (oct) begin //如果键值大于等于10
        case (sel)
            8'b0000_0001 : data_tmp = dis_data[2 : 0];
            8'b0000_0010 : data_tmp = dis_data[3    ];
				default      : data_tmp = 4'b0           ;
        endcase
	 end
	 else  //如果键值小于10
	     data_tmp = dis_data[3 : 0];	
end

always @ (*) begin
    if (oct) begin
        case (data_tmp)
		    3'd1 : seg = 8'b1111_1001;  //第二个数码管显示1
            3'd2 : seg = 8'b1100_0000;  //第一个数码管显示其他数
            3'd3 : seg = 8'b1111_1001;
            3'd4 : seg = 8'b1010_0100;
            3'd5 : seg = 8'b1011_0000;
            3'd6 : seg = 8'b1001_1001;
            3'd7 : seg = 8'b1001_0010;
		    default : seg = 8'b1111_1111;
	     endcase
	 end
	 else
	     case(data_tmp)
		    4'd0 : seg = 8'b1100_0000;
            4'd1 : seg = 8'b1111_1001;
            4'd2 : seg = 8'b1010_0100;
            4'd3 : seg = 8'b1011_0000;
            4'd4 : seg = 8'b1001_1001;
            4'd5 : seg = 8'b1001_0010;
            4'd6 : seg = 8'b1000_0010;
            4'd7 : seg = 8'b1111_1000;
            4'd8 : seg = 8'b1000_0000;
            4'd9 : seg = 8'b1001_0000;
			default : seg = 8'b1111_1111;
        endcase
end
endmodule

比较巧妙的是,当键值大于等于10,将dis_data拆分成dis_data[3]dis_data[2:0];由于dis_data[3] == 1'b1,且

  1. 当键值为10,dis_data[2:0] == 2
  2. 当键值为11,dis_data[2:0] == 3
  3. 当键值为12,dis_data[2:0] == 4

  4. 而在case语句中,最高位dis_data[3]的取值正好不与后三位dis_data[2:0]的取值重合,故当oct == 1时,可直接在case语句中判断整个dis_data[3:0]的情况,并将其在数码管上显示出来。

将该文件的输出sel[7:0]seg[7:0],以{seg[7:0], sel[7:0]}的顺序位拼接并连上74HC595驱动上的data端口,调用ISSP可实现两个数码管显示16位数据。两文件调用方式如下:

module hex_top(
    clk     ,
    rstn    ,
	 
    ds      ,
    shcp    ,
    stcp    
);

input 		clk   	;
input 		rstn  	;

output  ds    ;
output  shcp  ;
output  stcp  ;

wire [3:0] dis_data;
wire [7:0] sel     ;
wire [7:0] seg     ;

hex_data_ISSP U_hex_data(
    .probe  (        ),
    .source (dis_data)
);

hex8 U_hex8(
    .clk_50M    (clk     ),
    .rstn       (rstn    ),
    .en_hex8    (1'b1    ),  
    .dis_data   (dis_data),
    .sel        (sel     ),
    .seg        (seg     )
);

HC595_driver U0_HC595_driver(
    .clk    (clk        ),
    .rstn   (rstn       ),
    .data   ({seg, sel} ),
    .en     (1'b1       ),
    .ds     (ds         ),
    .shcp   (shcp       ),
    .stcp   (stcp       )
);

endmodule

实际效果

由于此文作为矩阵键盘的一个衍生品,故实际验证的过程中本人将矩阵键盘与数码管显示相结合,当然调用ISSP只是为了降低此文写作难度(矩阵键盘还没想好怎么写–!),视频如下:

FPGA两段数码管显示0~15的数字

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!