【数码管】— 动态数码管

做了个啥:

数码管动态显示,最低位数码管1秒加1,一直加到999_999

基础知识:

  • 什么是BCD码:一种编码,用4位2进制表示1位10进制4:2——1:10;二进制、BCD都是对十进制的一种编码
  • 为什么要用BCD码:需要将一个十进制数如398用3个BCD码分别将个位、十位、百位分别表示出来,然后用来产生位选、段选信号;如果直接使用二进制表示398,无法分离出3位数字
  • 如何将十进制的二进制码转换成十进制的BCD码:
    • 十进制数234有3位,就在A栏填3*4个0
    • 从B栏知道234用二进制表示为:1110_1010
    • 将B栏中的3个BCD码与4比较:

        >4:+3

        <=4:不变

    • 判断完成后,将A、B两栏的数据统一往左移一位
    • 回到第3步
    • 由于B栏原来的二进制码1110_1010的位宽=8,于是将二进制转换成BCD一共需要8次

实践:

一、设计文件

模块1:数据生成模块

 设计文件:

复制代码
module data_bring
(
input clk,
input rst_n,

output  reg  [19:0]    data,
output           [5:0]    point,
output                      sign,
output  reg             en


);
// 0.1s 后自加1
//parameter MAX_CNT = 23'd4999_999;
// 1s 后自加1
parameter MAX_CNT = 26'd49_999_999;
parameter MAX_DATA = 20'd999_999;


//reg         [22:0]            cnt            ;
reg         [25:0]            cnt            ;
reg                             cnt_flage    ;

assign    point = 6'b000_000            ;
assign     sign = 1'b0                       ;

 
always@(posedge clk or negedge rst_n)begin 
if(rst_n == 1'b0)
//    cnt <= 23'b0;
cnt <= 26'b0;
else if(cnt == MAX_CNT)
//    cnt <= 23'b0;
cnt <= 26'b0;
else 
//    cnt <= cnt + 23'b1;
    cnt <= cnt + 26'b1;
end

always@(posedge clk or negedge rst_n)begin 
if(rst_n == 1'b0)
    cnt_flage <= 1'b0;
else if(cnt == MAX_CNT - 1'b1)
    cnt_flage <= 1'b1;
else 
    cnt_flage <= 1'b0;
end

always@(posedge clk or negedge rst_n)begin 
if(rst_n == 1'b0)
    data <= 20'd0;
else if(data == MAX_DATA && cnt_flage == 1'b1)
    data <= 20'd0;
else if(cnt_flage == 1'b1)
    data <= data + 20'd1;
else
    data <= data;
end

always@(posedge clk or negedge rst_n)begin 
if(rst_n == 1'b0)
    en <= 1'b0;
else
    en <= 1'b1;
end

endmodule 
复制代码

 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

模块2.1:二进制转BCD码模块

 设计文件:

复制代码
module dcb_8421
(
input             clk                        ,
input             rst_n                        ,
input  [19:0]    data                        ,

output reg [3:0]    ge                            ,
output reg [3:0]    shi                        ,
output reg [3:0]    bai                        ,
output reg [3:0]    qian                        ,
output reg [3:0]    wan                        ,
output reg [3:0]    shi_wan                    

);

parameter MAX_CNT = 21                    ;

reg [4:0]        cnt_shift                ;
reg [43:0]        data_shift                ;
reg                 shift_flage                ;

// 999_999的二进制有20位,因此需要计数:20+1首次补0=21次
always@(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)
    cnt_shift <= 5'd0;
else if((cnt_shift == MAX_CNT) && (shift_flage == 1'b1))
    cnt_shift <= 5'd0;
else if(shift_flage == 1'b1) 
    cnt_shift <= cnt_shift + 5'd1;    
else 
    cnt_shift <= cnt_shift;
end


always@(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)
    shift_flage <= 1'b0;
else 
    shift_flage <= ~ shift_flage;// 隔一个时钟周期取反:因为是要等到时钟上升沿才执行
end


always@(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)
    data_shift <= 44'd0;
else if(cnt_shift == 5'b0)
    data_shift <= {24'd0,data};
else if((cnt_shift < MAX_CNT) && (shift_flage == 1'b0)) // shift_flage时低电平时,做判断运算
    begin
        data_shift[23:20] <= (data_shift[23:20]>4)?(data_shift[23:20]+2'd3):(data_shift[23:20]);
        data_shift[27:24] <= (data_shift[27:24]>4)?(data_shift[27:24]+2'd3):(data_shift[27:24]);
        data_shift[31:28] <= (data_shift[31:28]>4)?(data_shift[31:28]+2'd3):(data_shift[31:28]);
        data_shift[35:32] <= (data_shift[35:32]>4)?(data_shift[35:32]+2'd3):(data_shift[35:32]);
        data_shift[39:36] <= (data_shift[39:36]>4)?(data_shift[39:36]+2'd3):(data_shift[39:36]);
        data_shift[43:40] <= (data_shift[43:40]>4)?(data_shift[43:40]+2'd3):(data_shift[43:40]);
                
    end 
else if((cnt_shift < MAX_CNT) && (shift_flage == 1'b1)) // shift_flage时高电平时,做左移位运算
    data_shift = data_shift << 1;
else 
    data_shift <= data_shift ;
end


always@(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
    ge      <= 4'd0        ;
    shi     <= 4'd0        ;
    bai       <= 4'd0        ;
    qian      <= 4'd0        ;
    wan     <= 4'd0        ;
    shi_wan <= 4'd0        ;
    end
else if(cnt_shift == MAX_CNT)begin
    ge      <= data_shift[23:20] ;
    shi     <= data_shift[27:24] ;
    bai       <= data_shift[31:28] ;
    qian      <= data_shift[35:32] ;
    wan     <= data_shift[39:36] ;
    shi_wan <= data_shift[43:40] ;
    end


end


endmodule 
复制代码

 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 模块2.2:驱动模块

 

 设计文件:

复制代码
module sge_01
(
input                     clk            ,
input                     rst_n            ,
input    [19:0]         data            ,
input    [5:0]           point            ,
input                     sign            ,
input                        en                ,

output reg [5:0]        wei            ,
output reg [7:0]        duan            

);

wire     [3:0]     ge             ; 
wire     [3:0]     shi             ;
wire     [3:0]     bai             ;
wire     [3:0]     qian             ;
wire     [3:0]     wan             ;
wire     [3:0]     shi_wan         ;

reg      [23:0]     data_reg     ;
reg     [15:0]     cnt_1ms         ;
reg                  flage_1ms     ;
reg     [2:0]     cnt_wei         ;       //位选信号
reg     [5:0]   cnt_wei_reg ;   //数码管位选计数器   
reg     [3:0]   data_disp   ;   //当前数码管显示的数据
reg             dot_disp    ;   //当前数码管显示的小数点

parameter MAX_CNT = 16'd49_999;


always@(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)
    data_reg <= 24'd0;
else if((shi_wan) || (point[5])) 
    data_reg <= {shi_wan, wan, qian, bai, shi, ge};    //123456    
else if(((wan) || (point[4])) && (sign==1'b1))
    data_reg <= {4'd10, wan, qian, bai, shi, ge};    //-23456        
else if(((wan) || (point[4])) && (sign==1'b0))
    data_reg <= {4'd11, wan, qian, bai, shi, ge};    //23456
else if(((qian) || (point[3])) && (sign==1'b1))
    data_reg <= {4'd11, 4'd10, qian, bai, shi, ge};    //-3456        
else if(((qian) || (point[3])) && (sign==1'b0))
    data_reg <= {4'd11, 4'd11, qian, bai, shi, ge};    //3456    
else if(((bai) || (point[2])) && (sign==1'b1))
    data_reg <= {4'd11, 4'd11, 4'd10, bai, shi, ge};    //-456        
else if(((bai) || (point[2])) && (sign==1'b0))
    data_reg <= {4'd11, 4'd11, 4'd11, bai, shi, ge};    //456    
else if(((shi) || (point[1])) && (sign==1'b1))
    data_reg <= {4'd11, 4'd11, 4'd11, 4'd10, shi, ge};    //-56    
    else if(((shi) || (point[1])) && (sign==1'b0))
    data_reg <= {4'd11, 4'd11, 4'd11, 4'd11, shi, ge};    //56
else if(((ge) || (point[1])) && (sign==1'b1))
    data_reg <= {4'd11, 4'd11, 4'd11, 4'd11, 4'd10, ge};    //-6
else //if(((ge) || (point[1])) && (sign==1'b0))
    data_reg <= {4'd11, 4'd11, 4'd11, 4'd11, 4'd11, ge};    //6        
end


    
// 1ms计数器
always@(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)
    cnt_1ms <= 16'd0;
else if(cnt_1ms == MAX_CNT)
    cnt_1ms <= 16'd0;
else
    cnt_1ms <= cnt_1ms + 16'd1;

end


// 1ms标志信号
always@(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)
    flage_1ms <= 1'b0;
else if(cnt_1ms == MAX_CNT - 16'd1)
    flage_1ms <= 1'b1;
else 
    flage_1ms <= 1'b0;
end


// 位选计数
always@(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)
    cnt_wei <= 3'd0;
else if(cnt_wei == 3'd5 && flage_1ms == 1'b1)
    cnt_wei <= 3'd0;
else if(flage_1ms == 1'b1)
    cnt_wei <= cnt_wei + 3'd1;
else 
    cnt_wei <= cnt_wei;
end

// 位选寄存器,用于后面的打拍操作
always@(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)
    cnt_wei_reg <= 6'b000_000;
else if((cnt_wei == 3'd0) && (flage_1ms == 1'b1))
    cnt_wei_reg <= 6'b000_001;    //选中第一个数码位
else if(flage_1ms == 1'b1)
    cnt_wei_reg <= cnt_wei_reg << 1;    //选中其他数码位 ??? 一直这样左移,移完后1会回到最低位吗
else 
    cnt_wei_reg <= cnt_wei_reg;
end


//待显示的数据
always@(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)
    data_disp <= 4'd0;
else if((en == 1'b1) && (flage_1ms == 1'b1))
    case(cnt_wei)
    3'd0:    data_disp <= data_reg[3:0];
    3'd1:    data_disp <= data_reg[7:4];
    3'd2:    data_disp <= data_reg[11:8];
    3'd3:    data_disp <= data_reg[15:12];
    3'd4:    data_disp <= data_reg[19:16];
    3'd5:    data_disp <= data_reg[23:20];
    default:data_disp <= 4'd0;
    endcase
    else 
        data_disp <= data_disp;
    end
    
// 点亮小数点    
always@(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)
    dot_disp <= 1'd0;
else if(flage_1ms == 1'b1)
    dot_disp <= ~point[cnt_wei];//共阳极,低电平点亮,输入的信号是高电平,于是需要取反点亮小数点
else 
    dot_disp <= dot_disp;
end

// 输出信号:段选
always@(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)
    duan <= 8'b1111_1111;
else 
    case(data_disp)
          
            4'd0  : duan  <=  {dot_disp,7'b100_0000};    //显示数字0
            4'd1  : duan  <=  {dot_disp,7'b111_1001};    //显示数字1
            4'd2  : duan  <=  {dot_disp,7'b010_0100};    //显示数字2
            4'd3  : duan  <=  {dot_disp,7'b011_0000};    //显示数字3
            4'd4  : duan  <=  {dot_disp,7'b001_1001};    //显示数字4
            4'd5  : duan  <=  {dot_disp,7'b001_0010};    //显示数字5
            4'd6  : duan  <=  {dot_disp,7'b000_0010};    //显示数字6
            4'd7  : duan  <=  {dot_disp,7'b111_1000};    //显示数字7
            4'd8  : duan  <=  {dot_disp,7'b000_0000};    //显示数字8
            4'd9  : duan  <=  {dot_disp,7'b001_0000};    //显示数字9
            4'd10 : duan  <=  8'b1011_1111          ;    //显示负号
            4'd11 : duan  <=  8'b1111_1111          ;    //不显示任何字符
            default:duan  <=  8'b1100_0000;
   endcase
    
end
// 输出信号:位选
always@(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)
    wei <= 6'd0;
else
    wei <= cnt_wei_reg;
end

dcb_8421 dcb_8421_inst
(
.clk(clk)                    ,
.rst_n(rst_n)                ,
.data    (data)                ,

.ge(ge)                        ,
.shi(shi)                    ,
.bai(bai)                    ,
.qian(qian)                    ,
.wan(wan)                    ,
.shi_wan(shi_wan)                    

);

endmodule 
复制代码

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

模块2的顶层文件:

复制代码
module seg_595_dynamic
(
input           clk        ,
input           rst_n        ,
input[19:0] data        ,
input[5:0]  point        ,
input         sign        ,
input            en            ,

output        ds            ,
output        oe            ,
output        shcp        ,
output        stcp        

);

wire [5:0]wei;
wire [7:0]duan;

sge_01 sge_01_inst
(
.clk(clk)            ,
.rst_n(rst_n)            ,
.data(data)            ,
.point(point)            ,
.sign(sign)            ,
.en(en)                ,

.wei(wei)            ,
.duan(duan)            

);

seg_595 seg_595_inst
(
.clk(clk)        ,
.rst_n(rst_n)        ,
.wei(wei)        ,
.duan(duan)        ,

.ds(ds)            ,
.shcp(shcp)        ,
.stcp(stcp)        ,
.oe(oe)

);
endmodule
复制代码

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

模块3:74HC595芯片控制模块

复制代码
module seg_595
(
input                     clk        ,
input                     rst_n        ,
input         [5:0]        wei        ,
input           [7:0]        duan        ,

output        reg        ds            ,
output        reg        shcp        ,
output        reg        stcp        ,
output                    oe

);

wire  [13:0]        data            ;
reg    [1:0]            cnt_4            ;
reg    [3:0]            cnt_bit_14    ;

assign    data    =    {duan[0],duan[1],duan[2],duan[3],duan[4],duan[5],duan[6],duan[7],wei};
assign     oe = ~ rst_n;


// 4分频计数器

always@(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)
    cnt_4 <= 2'b0    ;
else if(cnt_4 == 2'd3)
    cnt_4 <= 2'b0    ;
else
    cnt_4 <= cnt_4 + 2'b1    ;     
end


// 传输14位计数器

always@(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)
    cnt_bit_14 <= 4'b0;
else if(cnt_bit_14 == 4'd13 && cnt_4 == 2'd3)
    cnt_bit_14 <= 4'b0;
else if(cnt_4 == 2'd3)
    cnt_bit_14 <= cnt_bit_14 + 4'b1;
else 
    cnt_bit_14 <= cnt_bit_14;    
end

// -------------------- 输出  ----------------------------------

always@(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)
    shcp <= 1'b0;
else if(cnt_4>=2'd2)
    shcp <= 1'b1;
else
    shcp <= 1'b0;    

end


always@(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)
    stcp <= 1'b0;
else if(cnt_bit_14 == 4'd0 && cnt_4 == 2'd0)
    stcp <= 1'b1;
else 
    stcp <= 1'b0;
end


always@(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)
    ds <= 1'b0;
else if(cnt_4 == 1'b0)
    ds <= data[cnt_bit_14];
else
    ds <= ds;
end


endmodule 
复制代码

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

顶层模块:

 

复制代码
module top_seg
(

input                     clk        ,
input                     rst_n        ,

output                ds            ,
output                shcp        ,
output                stcp        ,
output                oe
);
wire [19:0]data;
wire [5:0]point;
wire sign;
wire en;

data_bring data_bring_inst
(
.clk(clk),
.rst_n(rst_n),

.data(data),
.point(point),
.sign(sign),
.en(en)


);


seg_595_dynamic seg_595_dynamic_inst
(
.clk(clk)        ,
.rst_n(rst_n)        ,
.data(data)        ,
.point(point)        ,
.sign(sign)        ,
.en(en)            ,

.ds(ds)            ,
.oe(oe)            ,
.shcp(shcp)        ,
.stcp(stcp)        

);


endmodule 
复制代码

二、测试文件

模块1:数据生成模块

复制代码
`timescale 1ns/1ns

module tb_data_bring;

reg clk;
reg rst_n;

wire data;
wire point;
wire sign;
wire en;

initial begin 
clk = 1'b0;
rst_n = 1'b0;
#10;
rst_n = 1'b1;


end 

always #10 clk = ~ clk ;



data_bring 

#(
    .MAX_CNT(23'd24),
    .MAX_DATA(20'd9)

)
a1
(
.clk(clk),
.rst_n(rst_n),

.data(data),
.point(point),
.sign(sign),
.en(en)


);

endmodule 
复制代码

模块2.1:BCD码

复制代码
module tb_dcb_8421;
reg clk;
reg rst_n;
reg [19:0]data;

wire ge;
wire shi;
wire bai;
wire qian;
wire wan;
wire shi_wan;

initial begin 
clk = 1'b0;
rst_n = 1'b0;
#10;
rst_n = 1'b1;
data = 20'b0101_0010_1010_1010_1011;
end

always #10 clk = ~ clk;


dcb_8421 a1
(
. clk(clk)                       ,
. rst_n(rst_n)                    ,
. data(data)                        ,

.    ge(ge)                            ,
.    shi(shi)                        ,
.    bai(bai)                        ,
.    qian(qian)                        ,
.    wan(wan)                        ,
.    shi_wan(shi_wan)                    

);

endmodule 
复制代码

模块2.2:位选、段选驱动

复制代码
`timescale 1ns/1ns

module tb_seg_01;
reg             clk;
reg             rst_n;
reg  [19:0] data;
reg  [5:0]  point;
reg             sign;
reg             en;

wire  [5:0]       wei;
wire  [7:0]        duan;

initial begin
clk = 1'b0;
rst_n = 1'b0;
data <= 20'd0;
point <= 6'd0;
sign <= 1'b0;
en <= 1'b0;

#30;
rst_n = 1'b1;
data <= 20'd9876;
point <= 6'd000_010;
sign <= 1'b1;
en <= 1'b1;

end 
always #10 clk = ~ clk;


defparam sge_01_inst.MAX_CNT = 20'd5;

sge_01 sge_01_inst
(
.clk(clk)            ,
.rst_n(rst_n)            ,
.data(data)            ,
.point(point)            ,
.sign(sign)            ,
.en(en)                ,

.wei(wei)            ,
.duan(duan)            

);

endmodule 
复制代码

顶层测试

复制代码
`timescale 1ns/1ns

module tb_top_seg;


reg             clk;
reg             rst_n;

wire            ds;
wire            shcp;
wire            stcp;
wire            oe;

initial begin
clk = 1'b0;
rst_n = 1'b0;

#30;
rst_n = 1'b1;

end 
always #10 clk = ~ clk;

top_seg top_seg_inst
(

.clk(clk)        ,
.rst_n(rst_n)        ,

.ds(ds)            ,
.shcp(shcp)        ,
.stcp(stcp)    ,
.oe(oe)
);


endmodule 
复制代码

 

 

三、波形图

四、RTL图

五、上板验证

 

 

 

posted @   刘小颜  阅读(412)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示