数码管动态显示

数码管显示分为静态显示和动态显示。静态显示其实并没有用,和led灯没区别。而动态显示用处很大,基本上数码管的使用都是动态显示。其原理很简单:视觉暂留效应。数码管从右到左,一个接一个的亮起熄灭,让其总的速度加快,人眼看上去就像是一直亮着一样。扫描时间间隔建议为20ms以内,人眼才不会感到闪烁。一般来说一位数码管扫描1ms就能得到不错的效果了。

一、方法1

  第一种数码管动态显示:采用分频时钟扫描方法

//======================================================================
// --- 名称 : seg_display
// --- 作者 : 木子
// --- 日期 : 2024-8-30
// --- 描述 : 数码管动态显示模块,改自小梅哥FPGA
//======================================================================

module seg_display
//---------------------<端口声明>---------------------------------------
(
input                   clk                 , //时钟,50Mhz
input                   rst_n               , //复位,低电平有效
input                   en                  , //数码管显示使能
input      [31:0]       data                , //输入数据
output     [ 7:0]       seg_sel             , //数码管位选
output reg [ 7:0]       seg_data              //数码管段选,即内容显示
);
//---------------------<信号定义>---------------------------------------
reg  [14:0]             cnt                 ;
wire                    add_cnt             ;
wire                    end_cnt             ;
reg                     clk_1k              ;
reg  [ 7:0]             seg_sel_r           ;
reg  [ 3:0]             data_tmp            ;

//----------------------------------------------------------------------
//--   1k分频,扫描一个数目管时间为1ms
//----------------------------------------------------------------------

//计数
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        cnt <= 0;
    else if(add_cnt)begin
        if(end_cnt)
            cnt <= 0;
        else
            cnt <= cnt + 1;
    end
    else
        cnt <= cnt;
end

assign add_cnt = en;    //en为1才允许计数
assign end_cnt = add_cnt && cnt==25000-1;

//分频
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        clk_1k <= 0;
    else if(end_cnt)
        clk_1k <= ~clk_1k;
    else
        clk_1k <= clk_1k;
end

//----------------------------------------------------------------------
//--   数码管扫描,8位循环扫描,频率为1k
//----------------------------------------------------------------------
always @(posedge clk_1k or negedge rst_n)begin
    if(!rst_n)
        seg_sel_r <= 8'b0000_0001;
    else if(seg_sel==8'b1000_0000)
        seg_sel_r <= 8'b0000_0001;
    else
        seg_sel_r <= seg_sel << 1;
end

//----------------------------------------------------------------------
//--   位选,不同计数对应不同位选编码,也对应分割的不同数据
//----------------------------------------------------------------------
always @(*)begin
    case(seg_sel_r)
        8'b0000_0001: data_tmp = data[ 3: 0] ; // 位1
        8'b0000_0010: data_tmp = data[ 7: 4] ; // 位2
        8'b0000_0100: data_tmp = data[11: 8] ; // 位3
        8'b0000_1000: data_tmp = data[15:12] ; // 位4
        8'b0001_0000: data_tmp = data[19:16] ; // 位5
        8'b0010_0000: data_tmp = data[23:20] ; // 位6
        8'b0100_0000: data_tmp = data[27:24] ; // 位7
        8'b1000_0000: data_tmp = data[31:28] ; // 位8
        default:      data_tmp = 4'b0000     ;
    endcase
end

assign seg_sel = en ? seg_sel_r : 8'b0000_0000; // 位选使能

//----------------------------------------------------------------------
//--   段选,将不同分割数据进行段选编码
//----------------------------------------------------------------------
always @(*)begin
    case(data_tmp)
        4'h0:   seg_data = 8'b1100_0000;
        4'h1:   seg_data = 8'b1111_1001;
        4'h2:   seg_data = 8'b1010_0100;
        4'h3:   seg_data = 8'b1011_0000;
        4'h4:   seg_data = 8'b1001_1001;
        4'h5:   seg_data = 8'b1001_0010;
        4'h6:   seg_data = 8'b1000_0010;
        4'h7:   seg_data = 8'b1111_1000;
        4'h8:   seg_data = 8'b1000_0000;
        4'h9:   seg_data = 8'b1001_0000;
        4'ha:   seg_data = 8'b1000_1000;
        4'hb:   seg_data = 8'b1000_0011;
        4'hc:   seg_data = 8'b1100_0110;
        4'hd:   seg_data = 8'b1010_0001;
        4'he:   seg_data = 8'b1000_0110;
        4'hf:   seg_data = 8'b1011_1111;
        default:seg_data = 8'b1111_1111;
    endcase
end


endmodule

二、方法2

  第二种数码管动态显示方法:采用直接计数扫描方法

//======================================================================
// --- 名称 : seg_display
// --- 作者 : 木子
// --- 日期 : 2024-8-30
// --- 描述 : 数码管动态显示模块
//======================================================================

module seg_display
//---------------------<参数定义>---------------------------------------
#(
parameter SEG_SEL       = 8                 , //数码管位数:8
parameter SEG_DATA      = 8                 , //数码管段数:8
parameter TIME_1MS      = 50000             , //1ms时间
parameter TIME_1MS_W    = 16                  //1ms时间位宽
)
//---------------------<端口声明>---------------------------------------
(
input                       clk             , //时钟,50Mhz
input                       rst_n           , //复位,低电平有效
input                       en              , //数码管显示使能
input      [SEG_SEL*4-1:0]  data            , //输入数据
output     [SEG_SEL  -1:0]  seg_sel         , //数码管位选
output reg [SEG_DATA -1:0]  seg_data          //数码管段选,即内容显示
);
//---------------------<信号定义>---------------------------------------
reg  [TIME_1MS_W-1:0]   cnt0                ;
wire                    add_cnt0            ;
wire                    end_cnt0            ;
reg  [2:0]              cnt1                ;
wire                    add_cnt1            ;
wire                    end_cnt1            ;
reg  [3:0]              data_tmp            ;
reg  [SEG_SEL-1:0]      seg_sel_r           ;

//----------------------------------------------------------------------
//--   1个数码管亮1ms
//----------------------------------------------------------------------
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        cnt0 <= 0;
    else if(add_cnt0)begin
        if(end_cnt0)
            cnt0 <= 0;
        else
            cnt0 <= cnt0 + 1;
    end
    else
        cnt0 <= cnt0;
end

assign add_cnt0 = en; // 使能有效才计数
assign end_cnt0 = add_cnt0 && cnt0==TIME_1MS-1;

//----------------------------------------------------------------------
//--   对各位数码管不断扫描
//----------------------------------------------------------------------
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        cnt1 <= 0;
    else if(add_cnt1)begin
        if(end_cnt1)
            cnt1 <= 0;
        else
            cnt1 <= cnt1 + 1;
    end
    else
        cnt1 <= cnt1;
end

assign add_cnt1 = end_cnt0;
assign end_cnt1 = add_cnt1 && cnt1==SEG_SEL-1;

//----------------------------------------------------------------------
//--   位选,不同计数对应不同位选编码,也对应分割的不同数据
//----------------------------------------------------------------------
always @(*)begin
    case(cnt1)
      3'd0  : begin seg_sel_r = 8'b0000_0001; data_tmp = data[ 3: 0]; end // 位1
      3'd1  : begin seg_sel_r = 8'b0000_0010; data_tmp = data[ 7: 4]; end // 位2
      3'd2  : begin seg_sel_r = 8'b0000_0100; data_tmp = data[11: 8]; end // 位3
      3'd3  : begin seg_sel_r = 8'b0000_1000; data_tmp = data[15:12]; end // 位4
      3'd4  : begin seg_sel_r = 8'b0001_0000; data_tmp = data[19:16]; end // 位5
      3'd5  : begin seg_sel_r = 8'b0010_0000; data_tmp = data[23:20]; end // 位6
      3'd6  : begin seg_sel_r = 8'b0100_0000; data_tmp = data[27:24]; end // 位7
      3'd7  : begin seg_sel_r = 8'b1000_0000; data_tmp = data[31:28]; end // 位8
    default : begin seg_sel_r = 8'b0000_0000; data_tmp = 4'b0000;     end
    endcase
end

assign seg_sel = en ? seg_sel_r : 8'b0000_0000; // 位选使能

//----------------------------------------------------------------------
//--   段选,将不同分割数据进行段选编码
//----------------------------------------------------------------------
always @(*)begin
    case(data_tmp)
        4'h0:   seg_data = 8'b1100_0000;
        4'h1:   seg_data = 8'b1111_1001;
        4'h2:   seg_data = 8'b1010_0100;
        4'h3:   seg_data = 8'b1011_0000;
        4'h4:   seg_data = 8'b1001_1001;
        4'h5:   seg_data = 8'b1001_0010;
        4'h6:   seg_data = 8'b1000_0010;
        4'h7:   seg_data = 8'b1111_1000;
        4'h8:   seg_data = 8'b1000_0000;
        4'h9:   seg_data = 8'b1001_0000;
        4'ha:   seg_data = 8'b1000_1000;
        4'hb:   seg_data = 8'b1000_0011;
        4'hc:   seg_data = 8'b1100_0110;
        4'hd:   seg_data = 8'b1010_0001;
        4'he:   seg_data = 8'b1000_0110;
        4'hf:   seg_data = 8'b1000_1110;
        default:seg_data = 8'b1111_1111;
    endcase
end


endmodule

/*
//----------------------------------------------------------------------
//--   位选这样写也行,代码变得很秀
//----------------------------------------------------------------------
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        seg_sel <= {SEG_SEL{1'b0};
    else if(en)
        seg_sel <= ~(1'b1<<cnt1);
    else
        seg_sel <= {SEG_SEL{1'b0};
end

always  @(*)begin
    data_tmp = data[(cnt1+1)*4-1 -:4];
end

*/

三、HC595

  附上小梅哥FPGA的HC595模块的代码,可以直接搭配数码管动态显示模块使用,在顶层例化连接一下就行了。

//======================================================================
// --- 名称 : HC595
// --- 作者 : 木子
// --- 日期 : 2024-8-30
// --- 描述 : HC595驱动
//======================================================================

module HC595
//---------------------<参数定义>---------------------------------------
#(
parameter SEG_SEL       = 8                 , //8位
parameter SEG_DATA      = 8                   //8段
)
//---------------------<端口声明>---------------------------------------
(
input                   clk                 , //时钟,50Mhz
input                   rst_n               , //复位,低电平有效
input                   en                  , //数码管使能信号
input  [SEG_SEL -1:0]   seg_sel             , //数码管位选
input  [SEG_DATA-1:0]   seg_data            , //数码管段选
output reg              ST_CP               , //存储寄存器时钟输出
output reg              SH_CP               , //移位寄存器时钟输出
output reg              DS                    //串行数据输入
);
//---------------------<信号定义>---------------------------------------
reg  [ 1:0]             cnt0                ;
wire                    add_cnt0            ;
wire                    end_cnt0            ;
reg  [ 5:0]             cnt1                ;
wire                    add_cnt1            ;
wire                    end_cnt1            ;
wire                    sclk                ; //分频时钟信号
wire [ 5:0]             sclk_cnt            ; //序列机计数器

//----------------------------------------------------------------------
//--   4分频计数器
//----------------------------------------------------------------------
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        cnt0 <= 0;
    else if(add_cnt0)begin
        if(end_cnt0)
            cnt0 <= 0;
        else
            cnt0 <= cnt0 + 1;
    end
    else
        cnt0 <= cnt0;
end

assign add_cnt0 = en;
assign end_cnt0 = add_cnt0 && cnt0==4-1;

assign sclk = end_cnt0;

//----------------------------------------------------------------------
//--   序列机计数器
//----------------------------------------------------------------------
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        cnt1 <= 0;
    else if(add_cnt1)begin
        if(end_cnt1)
            cnt1 <= 0;
        else
            cnt1 <= cnt1 + 1;
    end
    else
        cnt1 <= cnt1;
end

assign add_cnt1 = sclk;
assign end_cnt1 = add_cnt1 && cnt1==35-1;

assign sclk_cnt = cnt1;

//-------------------------------------------------------------------------------
//--   SHCP:移位寄存器的时钟输入,上升沿时移位寄存器中的数据依次移动一位
//--   STCP:存储寄存器的时钟输入,上升沿时移位寄存器中的数据进入存储寄存器
//--   通常STCP置为低电平,移位结束后再在ST_CP端产生一个正脉冲更新显示数据
//-------------------------------------------------------------------------------
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        ST_CP <= 0;
        SH_CP <= 0;
        DS    <= 0;
    end
    else begin
        case(sclk_cnt)
             0: begin             SH_CP <= 0;                    end
             1: begin ST_CP <= 0; SH_CP <= 1;                    end
             2: begin             SH_CP <= 0; DS <= seg_data[7]; end
             3: begin             SH_CP <= 1;                    end
             4: begin             SH_CP <= 0; DS <= seg_data[6]; end
             5: begin             SH_CP <= 1;                    end
             6: begin             SH_CP <= 0; DS <= seg_data[5]; end
             7: begin             SH_CP <= 1;                    end
             8: begin             SH_CP <= 0; DS <= seg_data[4]; end
             9: begin             SH_CP <= 1;                    end
            10: begin             SH_CP <= 0; DS <= seg_data[3]; end
            11: begin             SH_CP <= 1;                    end
            12: begin             SH_CP <= 0; DS <= seg_data[2]; end
            13: begin             SH_CP <= 1;                    end
            14: begin             SH_CP <= 0; DS <= seg_data[1]; end
            15: begin             SH_CP <= 1;                    end
            16: begin             SH_CP <= 0; DS <= seg_data[0]; end
            17: begin             SH_CP <= 1;                    end
            18: begin             SH_CP <= 0; DS <= seg_sel[7];  end
            19: begin             SH_CP <= 1;                    end
            20: begin             SH_CP <= 0; DS <= seg_sel[6];  end
            21: begin             SH_CP <= 1;                    end
            22: begin             SH_CP <= 0; DS <= seg_sel[5];  end
            23: begin             SH_CP <= 1;                    end
            24: begin             SH_CP <= 0; DS <= seg_sel[4];  end
            25: begin             SH_CP <= 1;                    end
            26: begin             SH_CP <= 0; DS <= seg_sel[3];  end
            27: begin             SH_CP <= 1;                    end
            28: begin             SH_CP <= 0; DS <= seg_sel[2];  end
            29: begin             SH_CP <= 1;                    end
            30: begin             SH_CP <= 0; DS <= seg_sel[1];  end
            31: begin             SH_CP <= 1;                    end
            32: begin             SH_CP <= 0; DS <= seg_sel[0];  end
            33: begin             SH_CP <= 1;                    end
            34: begin ST_CP <= 1;                                end
            default:
                begin ST_CP <= 0; SH_CP <= 0; DS <= 0          ; end
        endcase
    end
end

endmodule

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值