【数码管静态显示】

实验目标:驱动数码管实现000000-FFFFFF的循环静态显示,每个字符的显示时间为0.5s。


前言

数码管是一种半导体发光器件,基本单元是发光二极管。
在这里插入图片描述

共阳极数码管指的是所有的阳极都连接在一起,低电平点亮二极管。
分析一下内容为0的十六进制段码C0:
十六进制的C为二进制的1100,则C0表示为二进制为:1100 0000,可以综合二进制的段码分析出,这8位二进制数从右向左依次为abcdefg和dp。/从左往右:dp gfedcba
在这里插入图片描述
六位八段数码管需要8个段选+6个位选=14个IO口。
通过位选信号(sel[0]等)控制哪一个数码管点亮,段选是连接在一起的,故送入所有数码管的段选信号都是相同的,称为静态显示。
段选信号应该先传入最高位,再传入最低位。
在这里插入图片描述

使用74HC595芯片的原因是他只需要4个端口,可以节省10个端口。
连接到fpga芯片的端口为DS(将串行数据移位到fpga中)、SHCP(移位寄存器时钟输入,溢出的数据通过Q7S端口输出)、STCP(存储寄存器时钟)和OE(输出使能信号)端口。MR端口连接VCC,目的是防止数据的清零。
输出:74HC595芯片内部有一个8位寄存器,由STCP信号控制。在其上升沿时,会将移位寄存器中的数据写入存储寄存器中,当OE低电平时,就将存储寄存器中的数据通过15、1、2、3、4、5、6、7这8个端口传输出去,这8个端口与数码管相连接。这样就实现了串行输入并行输出的转换。


一、系统框图及波形图

在这里插入图片描述

将输入时钟信号和复位信号,输出为ds、shcp、stcp、oe信号的过程分为两个模块,第一个模块用于产生位选信号和段选信号,第二个模块将位选和段选信号与时钟信号和复位信号一同作为输入。
在这里插入图片描述
图中时钟和复位信号处的黑点表示导通。
在这里插入图片描述接下来介绍第二个模块的波形图:
在这里插入图片描述

图片为一部分节选,后续补充见下方文字叙述:

***data[13:0]***是十四位宽的变量,目的是拼接位选和段选数据,方便后面的传输。前面提到,按照seg[0]…seg[7] sel[7]…sel[0]的顺序传输数据。左边是最高位,对应段选信号的最低位

***cnt[1:0]***是声明的计数器,目的是对时钟进行分频,分频的原因是shcp和stcp是时钟信号,其频率不能太高也不能太低,这里进行四分频。50MHz的四分频(记数四次)是12.5MHz,计数器从0记数到最大值3就归零。

ds输出的是串行数据,14个数据为1个循环(需要再声明一个计数器,这个计数器(cnt_bit[3:0])对输出的比特进行记数),这14个数据有6个位选,8个段选。

当复位有效时,串行数据ds输出为0,其他时刻让其等于data[cnt_bit],当cnt_bit为0时,ds的值data[0]就是变量data的最低位,即sel[0]。

为了让移位时钟shcp信号准确地采集数据,shcp信号的上升沿应该对准串行数据ds信号的稳定状态,即其中间位置。即在分频时钟cnt[1:0]为2时将电平拉高,由于是时序逻辑,故延迟一拍。
存储寄存器时钟stcp初值为低电平,当所有的14位数据都传输完成时(延迟一拍)将其拉高(将移位寄存器中的数据输入到存储寄存器),保持两拍(bit器为0,分频计数器保持1和2,初始状态也保持这个高电平,但此时没有信号写入)的高电平。

使能信号oe是低电平有效,使其保持低电平即可。


二、rtl代码

此处为产生sel和seg信号部分的rtl代码:

module  seg595
#(
parameter CNT_MAX=25'd24_999_999
)
(
    input   wire            sys_clk     ,   //系统时钟,频率50MHz
    input   wire            sys_rst_n   ,   //复位信号,低电平有效

    output  reg     [5:0]   sel         ,   //数码管位选信号
    output  reg     [7:0]   seg             //数码管段选信号
);

//变量的定义
reg [24:0] cnt;
reg [3:0] data;//F是1111,4位宽
reg cnt_flag;

//变量的赋值
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt<=  25'd0;
    else    if(cnt == CNT_MAX)
        cnt<=  25'd0;
    else
        cnt<=  cnt + 1'b1;

always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        data<=4'd0;
  //else if((cnt == CNT_MAX)&&(data==4'd15))
    else if((cnt_flag==1'b1)&&(data==4'd15))
        data<=4'd0;
    else if(cnt == CNT_MAX)
        data<=data+1'b1;
    else
        data<=data;

//flag信号的目的是切换字符的显示,故将cnt == CNT_MAX换成cnt_flag==1'b1        
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)        
        cnt_flag<=1'b0;
    else if(cnt == CNT_MAX-1)
        cnt_flag<=1'b1;
    else
        cnt_flag<=1'b0;
        
//输出的赋值
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        sel<=6'b000_000;
    else
        sel<=6'b111_111;
        
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        seg<=8'hff;
    else case(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;
        4'd10:  seg    <=8'h88;
        4'd11:  seg    <=8'h83;
        4'd12:  seg    <=8'hc6;
        4'd13:  seg    <=8'ha1;
        4'd14:  seg    <=8'h86;
        4'd15:  seg    <=8'h8e;
        default:seg    <=8'hff;  //闲置状态,不显示
    endcase  
//输出是sel和seg,说明这只是第一个模块,还需要编写顶层模块
endmodule                                                                  你

这一部分是第一个模块部分的代码,还应编写一个顶层模块的代码,最后编写第二模块部分的代码。

顶层模块的代码

module dingcengmokuai
(
    input   wire            sys_clk     ,   //系统时钟,频率50MHz
    input   wire            sys_rst_n   ,   //复位信号,低电平有效
    output  wire            stcp        ,   //输出数据存储寄时钟
    output  wire            shcp        ,   //移位寄存器的时钟输入
    output  wire            ds          ,   //串行数据输入
    output  wire            oe              //输出使能信号
);

//引出位选和段选
wire [5:0] sel;
wire [7:0] seg;
//实例化
seg595
#(
.CNT_MAX(25'd24)
)
seg595_inst
(
    .sys_clk  (sys_clk  )   ,   //系统时钟,频率50MHz
    .sys_rst_n(sys_rst_n)   ,   //复位信号,低电平有效
    .sel      (sel      )   ,   //数码管位选信号
    .seg      (seg      )       //数码管段选信号
);
//第一个子功能模块的实例化完成
endmodule

第二个模块代码

module hc595_ctrl
(
    input   wire            sys_clk     ,
    input   wire            sys_rst_n   ,
    input   wire    [5:0]   sel,
    input   wire    [7:0]   seg,
    output  reg            ds          ,
    output  reg            stcp        ,
    output  reg            shcp        ,
    output  wire            oe    
);

//data使用assign语句,wire型
wire [13:0] data;
//计数器使用always语句,reg型
reg [1:0] cnt;
reg [3:0] cnt_bit;

assign data={seg[0],seg[1],seg[2],seg[3],seg[4],seg[5],seg[6],seg[7],sel[5:0]};

always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt<=2'd0;
    else if(cnt==2'd3)
        cnt<=2'd0;
    else
        cnt<=cnt+1'b1;
        
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_bit<=4'd0;
    else if((cnt_bit==4'd13)&&(cnt==2'd3))
        cnt_bit<=4'd0;
    else if(cnt==2'd3)
        cnt_bit<=cnt_bit+1'b1;
    else
        cnt_bit<=cnt_bit;
        
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        ds<=1'b0;
    else if(cnt==2'd3)
        ds<=data[cnt_bit];
    else
        ds<=ds;
        
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)    
        shcp<=1'b0;
    else if(cnt==2'd2)
        shcp<=1'b1;
    else if(cnt==2'd0)
        shcp<=1'b0;
    else
        shcp<=shcp;
    
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)     
        stcp<=1'b0;
    else if((cnt_bit==4'd0)&&(cnt==2'd0))
        stcp<=1'b1;
    else if((cnt_bit==4'd0)&&(cnt==2'd2))
        stcp<=1'b0;
    else
        stcp<=stcp;
    
//使能信号始终保持低电平,用assign语句进行赋值
assign oe=1'b0;  
    
endmodule

三、测试代码

此处为第一个模块的测试代码

`timescale  1ns/1ns
module  tb_seg595();
reg     sys_clk     ;
reg     sys_rst_n   ;
wire [5:0] sel;
wire [7:0] seg; 

//对sys_clk,sys_rst_n赋初始值
initial
    begin
        sys_clk     =   1'b1;
        sys_rst_n   <=  1'b0;
        #100
        sys_rst_n   <=  1'b1;
    end
    
//clk:产生时钟
always  #10 sys_clk <=  ~sys_clk;

seg595
#(
.CNT_MAX(25'd24)
)
seg595_inst
(
    .sys_clk  (sys_clk  )   ,   //系统时钟,频率50MHz
    .sys_rst_n(sys_rst_n)   ,   //复位信号,低电平有效
    .sel      (sel      )   ,   //数码管位选信号
    .seg      (seg      )       //数码管段选信号
);

endmodule

顶层模块的测试代码

`timescale  1ns/1ns
module  tb_dingcengmokuai();
reg     sys_clk     ;
reg     sys_rst_n   ;

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

initial
    begin
        sys_clk     =   1'b1;
        sys_rst_n   <=  1'b0;
        #100
        sys_rst_n   <=  1'b1;
    end
    
always  #10 sys_clk <=  ~sys_clk;

dingcengmokuai dingcengmokuai_inst
(
.sys_clk    (sys_clk  ),   //系统时钟,频率50MHz
.sys_rst_n  (sys_rst_n),   //复位信号,低电平有效
.stcp       (stcp     ),   //输出数据存储寄时钟
.shcp       (shcp     ),   //移位寄存器的时钟输入
.ds         (ds       ),   //串行数据输入
.oe         (oe       )    //输出使能信号
);

endmodule

四、仿真结果

此处为第一个模块的仿真结果,与波形图绘制一致。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小年痴槑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值