感谢野火开源教程的支持!EmbedFire – 东莞野火电子技术有限公司

本实验采用阳极6位数码管,其中段选是共用的。

一、实验目标

让六位数码管显示从十进制数 0 开始计数,每 0.1s 加 1,一直到加到十进制数 999999。到达 999999 之后回到 0 开始重新计数。

二、电路图

2.1 六位数码管电路图

2.2 八段数码管示意图

1.3 数码管编码译码表

1.4 数码管数据驱动电路

数码管驱动原理图

74HC595 是一个 8 位串行输入、并行输出的位移缓存器。其内部具有 8 位移位寄存器 和一个存储器,具有三态输出功能。本实验采用两片串行74HC595实现数码管的位选和段选,共8(段选)+ 6(位选)=14位宽数据。14位数据输出形式为 {段选[0:7],位选[5:0]}。

三、关键设计技术

3.1  基于视觉暂留的数码管动态扫描

视频其实是由一帧帧图片组成的,只不过在一秒钟内播放的图片数量足够多时,我们就觉得它一直在动,这种视频就是视觉暂留现象。在本次实验中利用该原理,每次只选中某一位的数码管,然后输入相应的段选信息完成一位数码管的显示。每循环分别选中6位数码管显示6次,实现对一个6位数据的一轮显示。

3.2  基于BCD码的整数 个、十、百、千、万、十万位数字分离

在本实验中,我们希望只是输进去一个十进制整数就实现动态数码管的显示,但是对于每一位数码管,我们则需要对应的整数位来进行编码。比如整数23,我们首先要分离解码得到十位2,再对2进行编码得到8‘b0010_0101,输入到数码管段选管脚才能使得数码管在相应的位选位置正常显示。由于在FPGA中使用浮点运算、除法、求余数操作是很耗费资源的,因此我们需要一种新的方法来进行整数位分离工作,这种方法就是8421码方法。

 转换分离步骤示意图

如上图所示,十进制数 234 其对应的二进制数为 1110_1010,首先第一步我们在其前面补上若干个 0,那么这个 0 的数量是如何决定的呢?参与转换的十进制有多少位,就需 要多少个相应的 BCD 码,比如 234,该十进制数是 3 位,而一位十进制数的 BCD 码是4位,所以这里我们就需要 12 位 BCD 码,故我们就在前面补 12 个 0。其余位数的十进制补 0 数量也是这样进行计算。

第二步我们需要进行判断运算移位操作,首先判断每一个 BCD 码其对应的十进制数是 否大于 4,如果大于 4 就对 BCD 码做加 3 操作,若小于等于 4 就让其值保持不变。当对每 一个 BCD 码进行判断运算后,都需要将运算后的数据像左移 1 位。移完位后我们仍按前面 所述进行判断运算,判断运算后需再次移位,以此循环,当我们进行 8 次判断移位后的 BCD 码部分数据就是我们转换的数据,如图 26-6 所示,当第 8 次移位后的 8421BCD 码数 据对应的十进制正是 234。这里需要注意的是我们输入转换的二进制码有多少位我们就需 要进行多少次判断移位操作,这里输入的是 8 位二进制,我们就进行 8 次判断移位操作。

3.3 循环步长的分解与计数

*例子分析(基于BCD编码器块)

由实验目标可知,本次需要显示的最大数字为999_999,则按照上述BCD码原理需要6(整数位数)*4(一位整数位数需要的bcd码位数)=24位宽二进制数据才能描述数字999_999,又因为999_999需要20位二进制数保存,因此我们首先需要设计一个20+24=44位宽缓存器,以统计修改或者输出数据

其次,由于bcd码的特性,20 bit表示的999_999一共需要移位20次,并且在之前有1次补零操作,在之后有1次做移位结束以后的数据处理和数据输出,因此每次循环过程我们需要20+1+1=22个步骤,我们需要一个计数器去表示它作为循环

接下来我们需要确定22个步骤一个循环里面,每个步骤最短需要做几件事。由3.2可知,我们要在每一个步骤里进行判断是否要加3和移位惭怍两个小步,因此我们一个步骤的时钟周期是2个系统时钟周期,。我们可以设计一个计数器来实现0~1的两个状态计数,可是这是不必要的,因为一个1位二进制的翻转就能解决这个问题。综上,我们还需要设计二分频的一个时钟信号

小结:在程序设计之前我们要理清楚我们的一个循环计数过程需要的步骤(取步骤最多的那个子线程为准)。并且设置一个统一的数据缓存器作为线程间数据交换的统一管理媒介,而不是各自设计一个存储空间,这不利于统一输入输出格式管理,增加算法判断条件复杂性

3.4 使用状态枚举方法 代替 复杂逻辑判断动态生成相应状态

在本次实验中,如果我们需要显示一个数 - 756,那么我们希望数码管输出**-765,其中*代表数码管熄灭不显示。同理我们需要显示 -32,则数码管应显示***-32,。显示456,数码管显示***456.面对特殊情况 -999_999,由于已经超过6位数码管显示999_999。

面对上述的功能要求,在c语言软件编程中,我们寄希望于设计一系列判断逻辑条件来动态计算决定数码管每一位是怎么显示的,这样的代码无疑更加“算法”、适用性更高、更加优美。但是也带来了算法逻辑设计复杂、输出的不同状态的条件更难判断。这意味着,对于我们显示一个未知位数的数字是很有优势的。

但是对于数字IC或者FPGA来说,分配给某个模块的输入输出数据位宽或者存储空间往往是确定大小的,因此面对较大单位数据量时,我们仍然推荐使用上述算法设计方法。但是相反地,当面对较小的数据算法时,我们可以直接枚举所有的可能来完成算法的快速设计。本实验中,仅仅只有6位数码管的形式,一次我们使用条件判断枚举显示状态的方式来做。

四、模块框架设计

* 我们将这个实验大块top_seg_595分成三个功能块

1. data_gen 显示数据生成模块

2. seg_danamic 数码管动态显示驱动模块(BCD转码子块bcd_8421+综合块seg_danamic本身,用于小数点显示+负号显示+数位显示的逻辑规则制定)

3. hc595_ctrl   74HC595 控制模块(用于将数据打包成74HC595 硬件接受的形式,并输出74HC595驱动时钟)

* 数据块的功能约束设计为

1. data_gen 显示数据0~999_999,设定数据更新频率为0.1s加1。

2. bcd_8421 将整数位进行分解,且越快越好。由3.2可知,最小步长是两个系统时钟周期(移位+判断加三)。本实验的系统时钟频率为50MHZ。那么每个步骤就是40ns,一次bcd在6位整数下需要22个步骤(0步:计算数数位补齐 + 1~20步:6位整数需要20位二进制数,因此需要移位20次+21步:赋值输出各个整数位),则整个bcd转码时间为22*40 = 0.88us。

3. seg_danamic 数码管动态显示驱动模块,设计需要每1ms更新一次位选,也就是说数码管更新显示动态扫描频率为1000HZ。

4. hc595_ctrl需要输出74HC595芯片的移位存储频率,我们设计为系统时钟的4分频即12MHZ(74HC595芯片手册里要求在20MHZ以下,所以二分频的25MHZ不行)。即每80ns芯片74HC595会移入芯片读取一位数据,本实验中共需要读取14位数据即{段选seg[0:7],位选sel[5:0]},则有80*14 = 1.12us一个周期的74HC595存储其数据克隆周期。

五、设计子程序模块

5.1 data_gen 模块设计

模块管脚分布及管脚数据位宽设计

 data:数据最大值999_999是20位二进制的数据。

        point:6位数码管,6个数码管显示位。

sign: 负号输出标志信号,高有效。

数据生成波形图绘制

其中要注意的小技巧是,cnt_flag是在cnt_100ms = 4999_998时跳变为高电平,在cnt_100ms = 0时跳变为低电平的。这是一个占空比很小的方波,并不是占空比50%的方波。当cnt_flag为高本设置为输出条件时(条件判断触发依然是系统时钟上升沿时进行的),这有助于data输出的频率和标准系统时钟频率对齐,不会发生传输延时。(这其实是由物理时钟传输介质材质决定的,系统时钟传输介质比较好,寄存器之间的信号传输介质延时性能较差)

* 模块代码

`timescale  1ns/1ns// Module Name   : data_gen
// Project Name  : top_seg_595
// Target Devices: Altera EP4CE10F17C8N
// Tool Versions : Quartus 13.0
// Description   : 生成数码管显示数据(000_000 ~ 999_999)module  data_gen
#(parameter   CNT_MAX = 23'd4999_999, //100ms计数值parameter   DATA_MAX= 20'd999_999   //显示的最大值
)
(input   wire            sys_clk     ,   //系统时钟,频率50MHzinput   wire            sys_rst_n   ,   //复位信号,低电平有效output  reg     [19:0]  data        ,   //数码管要显示的值output  wire    [5:0]   point       ,   //小数点显示,高电平有效output  reg             seg_en      ,   //数码管使能信号,高电平有效output  wire            sign            //符号位,高电平显示负号
);//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************////reg   define
reg     [22:0]  cnt_100ms   ;   //100ms计数器
reg             cnt_flag    ;   //100ms标志信号//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************////不显示小数点以及负数
assign  point   =   6'b000_000;
assign  sign    =   1'b0;//cnt_100ms:用50MHz时钟从0到4999_999计数即为100ms
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)cnt_100ms   <=  23'd0;else    if(cnt_100ms == CNT_MAX)cnt_100ms   <=  23'd0;elsecnt_100ms   <=  cnt_100ms + 1'b1;//cnt_flag:每100ms产生一个标志信号
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)cnt_flag    <=  1'b0;else    if(cnt_100ms == CNT_MAX - 1'b1)cnt_flag    <=  1'b1;elsecnt_flag    <=  1'b0;//数码管显示的数据:0-999_999
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)data    <=  20'd0;else    if((data == DATA_MAX) && (cnt_flag == 1'b1))data    <=  20'd0;else    if(cnt_flag == 1'b1)data    <=  data + 1'b1;elsedata    <=  data;//数码管使能信号给高即可
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)seg_en  <=  1'b0;elseseg_en  <=  1'b1;endmodule

5.2 bcd_8421 模块设计

模块管脚分布及管脚数据位宽设计

整数位为6位,因此有6个输出引脚。个位最大的数是9即4’b1001 ,因此被设置为4位输出位宽。

数据生成波形图绘制

波形设置详情请结合3.2和3.3的解释。

* 模块代码

`timescale  1ns/1ns// Module Name   : bcd_8421
// Project Name  : top_seg_595
// Target Devices: Altera EP4CE10F17C8N
// Tool Versions : Quartus 13.0
// Description   : 二进制数转BCD码module  bcd_8421
(input   wire            sys_clk     ,   //系统时钟,频率50MHzinput   wire            sys_rst_n   ,   //复位信号,低电平有效input   wire    [19:0]  data        ,   //输入需要转换的数据output  reg     [3:0]   unit        ,   //个位BCD码output  reg     [3:0]   ten         ,   //十位BCD码output  reg     [3:0]   hun         ,   //百位BCD码output  reg     [3:0]   tho         ,   //千位BCD码output  reg     [3:0]   t_tho       ,   //万位BCD码output  reg     [3:0]   h_hun           //十万位BCD码
);//reg   define
reg     [4:0]   cnt_shift   ;   //移位判断计数器
reg     [43:0]  data_shift  ;   //移位判断数据寄存器
reg             shift_flag  ;   //移位判断标志信号//cnt_shift:从0到21循环计数
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)cnt_shift   <=  5'd0;else    if((cnt_shift == 5'd21) && (shift_flag == 1'b1))cnt_shift   <=  5'd0;else    if(shift_flag == 1'b1)cnt_shift   <=  cnt_shift + 1'b1;elsecnt_shift   <=  cnt_shift;//data_shift:计数器为0时赋初值,计数器为1~20时进行移位判断操作
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)data_shift  <=  44'b0;else    if(cnt_shift == 5'd0) // 补0data_shift  <=  {24'b0,data};else    if((cnt_shift <= 20) && (shift_flag == 1'b0)) // 判断要不要加3begindata_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]);endelse    if((cnt_shift <= 20) && (shift_flag == 1'b1)) // 移位data_shift  <=  data_shift << 1;elsedata_shift  <=  data_shift;//shift_flag:移位判断标志信号,用于控制移位判断的先后顺序
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)shift_flag  <=  1'b0;elseshift_flag  <=  ~shift_flag;//当计数器等于20时,移位判断操作完成,对各个位数的BCD码进行赋值
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)beginunit    <=  4'b0;ten     <=  4'b0;hun     <=  4'b0;tho     <=  4'b0;t_tho   <=  4'b0;h_hun   <=  4'b0;endelse    if(cnt_shift == 5'd21) // 数据输出beginunit    <=  data_shift[23:20];ten     <=  data_shift[27:24];hun     <=  data_shift[31:28];tho     <=  data_shift[35:32];t_tho   <=  data_shift[39:36];h_hun   <=  data_shift[43:40];endendmodule

5.3 seg_danamic 模块设计

模块管脚分布及管脚数据位宽设计

8段数码管即段选seg[0:7],6位数码管即位选sel[5:0]。

数据生成波形图绘制

dot_disp:当前数码管显示的小数点,我们输入的 point 信号是点亮第二个数码管的小 数点,而我们的数码管是低电平点亮,所以这里当扫描到第二个数码管时让 dot_disp 信号 为低即可

data_reg:数码管待显示内容寄存器,因为这里我们假设输入要显示的十进制数为 9876,并且显示负号,所以前五个数码管就会显示-9876 的数值,此时最高位数码管什么都 不显示,我们用 X 表示,所以这里六个数码管显示的内容就是:X-9876。

data_disp:当前点亮数码管显示的值。若我们此时点亮的是第一个数码管,那么我们 就需要给第一个数码管显示值 6,若刷新到第二个数码管,那么我们就需要给第二个数码 管显示值 7,以此类推;当刷新到第五个数码管时,此时显示的是负号,那么我们该如何 表示呢?这里我们让该信号的值为 10 来表示,也就是说当 data_disp 的值为 10 时就让数码 管显示负号,同理这里我们定义 data_disp 的值为 11 时让数码管什么也不显示,即不点亮 数码管。(小技巧:这里就像状态机一样,对所有可能显示的状态进行编码

设计思想可参考3.4

* 模块代码

`timescale  1ns/1ns// Module Name   : seg_dynamic
// Project Name  : top_seg_595
// Target Devices: Altera EP4CE10F17C8N
// Tool Versions : Quartus 13.0
// Description   : 数码管动态显示module  seg_dynamic
(input   wire            sys_clk     , //系统时钟,频率50MHzinput   wire            sys_rst_n   , //复位信号,低有效input   wire    [19:0]  data        , //数码管要显示的值input   wire    [5:0]   point       , //小数点显示,高电平有效input   wire            seg_en      , //数码管使能信号,高电平有效input   wire            sign        , //符号位,高电平显示负号output  reg     [5:0]   sel         , //数码管位选信号output  reg     [7:0]   seg           //数码管段选信号
);//parameter define
parameter   CNT_MAX =   16'd49_999;  //数码管刷新时间计数最大值//wire  define
wire    [3:0]   unit        ;   //个位数
wire    [3:0]   ten         ;   //十位数
wire    [3:0]   hun         ;   //百位数
wire    [3:0]   tho         ;   //千位数
wire    [3:0]   t_tho       ;   //万位数
wire    [3:0]   h_hun       ;   //十万位数//reg   define
reg     [23:0]  data_reg    ;   //待显示数据寄存器
reg     [15:0]  cnt_1ms     ;   //1ms计数器
reg             flag_1ms    ;   //1ms标志信号
reg     [2:0]   cnt_sel     ;   //数码管位选计数器
reg     [5:0]   sel_reg     ;   //位选信号
reg     [3:0]   data_disp   ;   //当前数码管显示的数据
reg             dot_disp    ;   //当前数码管显示的小数点// ******枚举显示情况
//data_reg:控制数码管显示数据always@(posedge sys_clk or  negedge sys_rst_n)if(sys_rst_n == 1'b0)data_reg    <=  24'b0;
//若显示的十进制数的十万位为非零数据或需显示小数点,则六个数码管全显示else    if((h_hun) || (point[5]))data_reg    <=  {h_hun,t_tho,tho,hun,ten,unit};
//若显示的十进制数的万位为非零数据或需显示小数点,则值显示在5个数码管上
//打比方我们输入的十进制数据为20’d12345,我们就让数码管显示12345而不是012345else    if(((t_tho) || (point[4])) && (sign == 1'b1))//显示负号data_reg <= {4'd10,t_tho,tho,hun,ten,unit};//4'd10我们定义为显示负号else    if(((t_tho) || (point[4])) && (sign == 1'b0))data_reg <= {4'd11,t_tho,tho,hun,ten,unit};//4'd11我们定义为不显示
//若显示的十进制数的千位为非零数据或需显示小数点,则值显示4个数码管else    if(((tho) || (point[3])) && (sign == 1'b1))data_reg <= {4'd11,4'd10,tho,hun,ten,unit};else    if(((tho) || (point[3])) && (sign == 1'b0))data_reg <= {4'd11,4'd11,tho,hun,ten,unit};
//若显示的十进制数的百位为非零数据或需显示小数点,则值显示3个数码管else    if(((hun) || (point[2])) && (sign == 1'b1))data_reg <= {4'd11,4'd11,4'd10,hun,ten,unit};else    if(((hun) || (point[2])) && (sign == 1'b0))data_reg <= {4'd11,4'd11,4'd11,hun,ten,unit};
//若显示的十进制数的十位为非零数据或需显示小数点,则值显示2个数码管else    if(((ten) || (point[1])) && (sign == 1'b1))data_reg <= {4'd11,4'd11,4'd11,4'd10,ten,unit};else    if(((ten) || (point[1])) && (sign == 1'b0))data_reg <= {4'd11,4'd11,4'd11,4'd11,ten,unit};
//若显示的十进制数的个位且需显示负号else    if(((unit) || (point[0])) && (sign == 1'b1))data_reg <= {4'd11,4'd11,4'd11,4'd11,4'd10,unit};
//若上面都不满足都只显示一位数码管elsedata_reg <= {4'd11,4'd11,4'd11,4'd11,4'd11,unit};//cnt_1ms:1ms循环计数
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)cnt_1ms <=  16'd0;else    if(cnt_1ms == CNT_MAX)cnt_1ms <=  16'd0;elsecnt_1ms <=  cnt_1ms + 1'b1;//flag_1ms:1ms标志信号
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)flag_1ms    <=  1'b0;else    if(cnt_1ms == CNT_MAX - 1'b1)flag_1ms    <=  1'b1;elseflag_1ms    <=  1'b0;//cnt_sel:从0到5循环数,用于选择当前显示的数码管
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)cnt_sel <=  3'd0;else    if((cnt_sel == 3'd5) && (flag_1ms == 1'b1))cnt_sel <=  3'd0;else    if(flag_1ms == 1'b1)cnt_sel <=  cnt_sel + 1'b1;elsecnt_sel <=  cnt_sel;//数码管位选信号寄存器
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)sel_reg <=  6'b000_000;else    if((cnt_sel == 3'd0) && (flag_1ms == 1'b1))sel_reg <=  6'b000_001;else    if(flag_1ms == 1'b1)sel_reg <=  sel_reg << 1;elsesel_reg <=  sel_reg;//控制数码管的位选信号,使六个数码管轮流显示
always@(posedge sys_clk or  negedge sys_rst_n)if(sys_rst_n == 1'b0)data_disp    <=  4'b0;else    if((seg_en == 1'b1) && (flag_1ms == 1'b1))case(cnt_sel)3'd0:   data_disp    <=  data_reg[3:0]  ;  //给第1个数码管赋个位值3'd1:   data_disp    <=  data_reg[7:4]  ;  //给第2个数码管赋十位值3'd2:   data_disp    <=  data_reg[11:8] ;  //给第3个数码管赋百位值3'd3:   data_disp    <=  data_reg[15:12];  //给第4个数码管赋千位值3'd4:   data_disp    <=  data_reg[19:16];  //给第5个数码管赋万位值3'd5:   data_disp    <=  data_reg[23:20];  //给第6个数码管赋十万位值default:data_disp    <=  4'b0        ;endcaseelsedata_disp   <=  data_disp;//dot_disp:小数点低电平点亮,需对小数点有效信号取反
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)dot_disp    <=  1'b1;else    if(flag_1ms == 1'b1)dot_disp    <=  ~point[cnt_sel];elsedot_disp    <=  dot_disp;//控制数码管段选信号,显示数字
always@(posedge sys_clk or  negedge sys_rst_n)if(sys_rst_n == 1'b0)seg <=  8'b1111_1111;else    case(data_disp)4'd0  : seg  <=  {dot_disp,7'b100_0000};    //显示数字04'd1  : seg  <=  {dot_disp,7'b111_1001};    //显示数字14'd2  : seg  <=  {dot_disp,7'b010_0100};    //显示数字24'd3  : seg  <=  {dot_disp,7'b011_0000};    //显示数字34'd4  : seg  <=  {dot_disp,7'b001_1001};    //显示数字44'd5  : seg  <=  {dot_disp,7'b001_0010};    //显示数字54'd6  : seg  <=  {dot_disp,7'b000_0010};    //显示数字64'd7  : seg  <=  {dot_disp,7'b111_1000};    //显示数字74'd8  : seg  <=  {dot_disp,7'b000_0000};    //显示数字84'd9  : seg  <=  {dot_disp,7'b001_0000};    //显示数字94'd10 : seg  <=  8'b1011_1111          ;    //显示负号4'd11 : seg  <=  8'b1111_1111          ;    //不显示任何字符default:seg  <=  8'b1100_0000;endcase//sel:数码管位选信号赋值
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)sel <=  6'b000_000;elsesel <=  sel_reg;//---------- bsd_8421_inst ----------
bcd_8421    bcd_8421_inst
(.sys_clk     (sys_clk  ),   //系统时钟,频率50MHz.sys_rst_n   (sys_rst_n),   //复位信号,低电平有效.data        (data     ),   //输入需要转换的数据.unit        (unit     ),   //个位BCD码.ten         (ten      ),   //十位BCD码.hun         (hun      ),   //百位BCD码.tho         (tho      ),   //千位BCD码.t_tho       (t_tho    ),   //万位BCD码.h_hun       (h_hun    )    //十万位BCD码
);endmodule

5.4  hc595_ctrl  模块设计

模块管脚分布及管脚数据位宽设计

设计详情请看章节四,并结合章节二中的74芯片介绍。

数据生成波形图绘制

shcp:移位寄存器时钟,上升沿时将数据写入移位寄存器中。我们在 ds 数据的中间状 态拉高产生上升沿,这样可以使 shcp 采得的 ds 数据更加稳定。如波形图所示我们在 cnt=2 时拉高,cnt=0 时拉低,即可产生该时钟,其频率即为系统时钟四分频(12.5MHz)。

stcp:存储寄存器时钟。当我们 14 位数码管控制信号传输完之后我们需要拉高一个 stcp 时钟来将信号存入存储寄存器之中。最后一个数据是在 cnt_bit=13 且 cnt=2 时传输的, 所以我们就在下一个时钟(cnt_bit=13 且 cnt=3 时)将 stcp 拉高一个时钟产生上升沿即可。

ds:串行数据输出(对我们 FPGA 芯片来说其是输出,对 74HC595 来说其是输入, stcp 和 shcp 信号也是如此)。这里我们回到原理图 (第二章),可以看到第二片的 Q5 引脚连到 了数码管的 DIG6,也就是最右侧的数码管,而我们最右侧数码管对应的是我们位选信号的 最低位,即 sel[0]。所以我们第一位应传输的数据为 sel[0],依次类推根据原理图传输相应 的数据,具体的传输数据如波形图所示。当一次数据传完之后再次回到状态 0 开始新一轮 的数码管信号传输。

oe:存储寄存器数据输出使能信号,低电平有效,这里我们将复位信号取反的值赋给 该信号即可。

* 模块代码

`timescale  1ns/1ns// Module Name   : hc595_ctrl
// Project Name  : seg_595_static
// Target Devices: Altera EP4CE10F17C8N
// Tool Versions : Quartus 13.0
// Description   : 595控制模块module  hc595_ctrl
(input   wire            sys_clk     ,   //系统时钟,频率50MHzinput   wire            sys_rst_n   ,   //复位信号,低有效input   wire    [5:0]   sel         ,   //数码管位选信号input   wire    [7:0]   seg         ,   //数码管段选信号output  reg             stcp        ,   //数据存储器时钟output  reg             shcp        ,   //移位寄存器时钟output  reg             ds          ,   //串行数据输入output  wire            oe              //使能信号,低有效
);//reg   define
reg     [1:0]   cnt_4   ;   //分频计数器
reg     [3:0]   cnt_bit ;   //传输位数计数器//wire  define
wire    [13:0]  data    ;   //数码管信号寄存//将数码管信号寄存
assign  data = {seg[0],seg[1],seg[2],seg[3],seg[4],seg[5],seg[6],seg[7],sel};//将复位取反后赋值给其即可
assign oe = ~sys_rst_n;//分频计数器:0~3循环计数
always@(posedge sys_clk or  negedge sys_rst_n)if(sys_rst_n == 1'b0)cnt_4 <=  2'd0;else    if(cnt_4 == 2'd3)cnt_4 <=  2'd0;elsecnt_4 <=  cnt_4 +   1'b1;//cnt_bit:每输入一位数据加一
always@(posedge sys_clk or  negedge sys_rst_n)if(sys_rst_n == 1'b0)cnt_bit   <=  4'd0;else    if(cnt_4 == 2'd3 && cnt_bit == 4'd13)cnt_bit   <=  4'd0;else    if(cnt_4  ==  2'd3)cnt_bit   <=  cnt_bit   +   1'b1;elsecnt_bit   <=  cnt_bit;//stcp:14个信号传输完成之后产生一个上升沿
always@(posedge sys_clk or  negedge sys_rst_n)if(sys_rst_n == 1'b0)stcp    <=  1'b0;else    if(cnt_bit == 4'd13 && cnt_4 == 2'd3)stcp    <=  1'b1;elsestcp    <=  1'b0;//shcp:产生四分频移位时钟
always@(posedge sys_clk or  negedge sys_rst_n)if(sys_rst_n == 1'b0)shcp    <=  1'b0;else    if(cnt_4 >= 4'd2)shcp    <=  1'b1;elseshcp    <=  1'b0;//ds:将寄存器里存储的数码管信号输入即
always@(posedge sys_clk or  negedge sys_rst_n)if(sys_rst_n == 1'b0)ds  <=  1'b0;else    if(cnt_4 == 2'd0)ds  <=  data[cnt_bit];elseds  <=  ds;endmodule

5.5 大模块 top_seg_595的子模块连线整合

*整合关系及子模块链接

* 模块代码

`timescale  1ns/1ns
module top_seg_595#(parameter   CNT_MAX = 23'd4999_999, //100ms计数值, 数据产生频率parameter   DATA_MAX= 20'd999_999   //显示的最大值
)
(input   wire            sys_clk     ,   //系统时钟,频率50MHzinput   wire            sys_rst_n   ,   //复位信号,低电平有效output  wire            stcp        ,   //输出数据存储寄时钟output  wire            shcp        ,   //移位寄存器的时钟输入output  wire            ds          ,   //串行数据输入output  wire            oe              //输出使能信号
);// data_gen输出wire    [19:0]  data    ;   //数码管要显示的值wire    [5:0]   point   ;   //小数点显示,高电平有效top_seg_595wire            seg_en  ;   //数码管使能信号,高电平有效wire            sign    ;   //符号位,高电平显示负号// seg_dymamic 输出wire    [5:0]   sel; // 位选wire    [7:0]   seg;// 数据子模块实例化
data_gen #(.CNT_MAX    (CNT_MAX ),.DATA_MAX   (DATA_MAX)
)
data_gen_inst(.sys_clk    (sys_clk  ),.sys_rst_n  (sys_rst_n),.data       (data     ),.point      (point    ),.sign       (sign     ),.seg_en     (seg_en   )
);//转码器子模块实例化
seg_dynamic seg_dynamic_inst(.sys_clk    (sys_clk   ),   //系统时钟,频率50MHz.sys_rst_n  (sys_rst_n ),   //复位信号,低有效.data       (data      ),   //数码管要显示的值.point      (point     ),   //小数点显示,高电平有效.sign       (sign      ),   //符号位,高电平显示负号.seg_en     (seg_en    ),   //数码管使能信号,高电平有效.sel        (sel       ),   //输出数码管位选信号.seg        (seg       )    //输出数码管段选信号
);//74H芯片驱动子模块实例化
hc595_ctrl(.sys_clk    (sys_clk   ),   //系统时钟,频率50MHz.sys_rst_n  (sys_rst_n ),   //复位信号,低有效.sel        (sel       ),   //输出数据存储寄时钟.seg        (seg       ),   //移位寄存器的时钟输入.stcp       (stcp      ),   //输出数据存储寄时钟.shcp       (shcp      ),   //移位寄存器的时钟输入.ds         (ds        ),   //串行数据输入.oe         (oe        )    //输出使能信号);
endmodule

至此我们的动态数码管显示例程已经介绍完毕了。

数字IC-1.8 子模块组建整模块-动态数码管设计代码实例相关推荐

  1. 数字IC笔试题(2)——降低动态IR DROP

    (华为海思芯片多选)降低动态IR DROP的方法包括()(注意:题目中的IP DROP实际应该是IR DROP) A. 降低工作频率: B. 增加接入的电源地IO数量: C. 增加LVT cell的比 ...

  2. 数字IC设计工程师是做什么的?

    随着我国半导体产业的发展,近几年的新入行的从业人员,除了微电子相关专业的,还有就是物理.机械.数学.计算机等专业,很多人对这一高薪行业充满了好奇,那么数字IC设计工程师到底是做什么的? 首先来看看数字 ...

  3. 2022届FPGA/数字IC秋招笔试面试汇总帖(题目来源:FPGA探索者)

    目录 001--什么是STA静态时序分析,有什么作用? 具有代表性的STA工具: 静态时序分析STA: (1)setup time (2)hold time (3)STA 的时序路径 (4)recov ...

  4. 入行芯片设计选模拟IC还是数字IC?一文为你讲解清楚

    数字IC设计与模拟IC设计有什么区别,很多入行的新人都不知道该怎么选择? 数字IC设计和模拟IC设计,其实是不同的两个方向. 喜欢挑战,喜欢分析电脑屏幕上可见的MOS管组成的具体电路的,可以选择模拟I ...

  5. 【数字IC验证快速入门】1、浅谈数字IC验证,了解专栏内容,明确学习目标

    导读:作者有幸在中国电子信息领域的排头兵院校"电子科技大学"攻读研究生期间,接触到前沿的数字IC验证知识,旁听到诸如华为海思.清华紫光.联发科技等业界顶尖集成电路相关企业面授课程, ...

  6. 数字IC设计——功耗分析

    一.概述 芯片的整体功耗很难通过简单的电流,电压或者电阻值的的相乘来计算.其原因在于,由于芯片作为具有复杂功能的器件,其功耗会根据其不同时段的不同行为,不同的外部条件而发生很大的变化. 1.1 功耗的 ...

  7. 建议收藏:不能不刷的100道数字IC笔/面试题!

    一.IC设计流程及相应EDA开发工具 前端设计(逻辑设计) 1.规格制定 根据客户需求(具体的功能和性能要求)制定芯片规格Spec 2.详细设计 设计方案,具体实现架构,模块划分 3.HDL编码 将实 ...

  8. FPGA数字IC刷题58道Verilog题解代码及视频讲解【FPGA探索者】【同步/异步FIFO】【跨时钟】

    牛客 Verilog 刷题入门篇1~24 + 进阶篇1~34 题解代码,所有代码均能通过测试,配合视频讲解效果更佳.为避免内容冗余,本文只给出代码,部分题目给出必要说明. 很多题目本身出题有些问题,着 ...

  9. 数字IC前端面试问题总结

    本篇主要参考了 1.新芯设计 (3条消息) 新芯设计的博客_CSDN博客-如何成为一名高级数字 IC 设计工程师,数字 IC 技能拓展,基于 SoC 的卷积神经网络车牌识别系统设计领域博主 2.小汪的 ...

最新文章

  1. 四十三、文件存储空间管理
  2. mysql基于传统的log_file及log_pos主从复制
  3. 第3章 Python 数字图像处理(DIP) - 灰度变换与空间滤波3 -幂律变换、伽马变换
  4. hadoop 回收站Trash
  5. Python之路(第三篇) 模块
  6. 2021考研数学二汤家凤接力题典1800【解答册】
  7. 推荐一款免费的内网端口映射工具
  8. ERP原理与应用教程-第一章
  9. 职称最新消息:2022年开始湖北全面实行职称电子证书
  10. 爬取徐州市自然资源和规划局土地数据
  11. 如何在不重装系统的情况下换固态硬盘?
  12. 架构师成长之路(3)--如何成为架构师(方法)
  13. 面试官的技术面试技巧与步骤
  14. VBA获取区域的起始行、结束行
  15. 程序员好几年才能成为架构师_成为更好的企业架构师
  16. 一键查看pip已装模块的安装路径
  17. PyCharm:选择性忽略 PEP8 警告
  18. wps打开文件很慢很卡怎么办?
  19. 【BIM入门实战】Revit中的墙体层次以及常见问题解答
  20. deepin运行Linux应用,在Ubuntu上安装Deepin的那些应用

热门文章

  1. Web直播播放器1.0——腾讯sdk网页直播播放器
  2. 谈一谈Java中的深拷贝和浅拷贝
  3. 【Java核心技术大会 PPT分享】张家驹:云原生时代的Java — Quarkus及其最新进展...
  4. Easy3D开发——点云孔洞填充
  5. 手把手教你使用java对接微信公众号-获取地理位置信息
  6. 人性中的最黑暗面——《电锯惊魂》影…
  7. NORTON 杀毒怎么了?
  8. Codeforces C. Strange Birthday Party(cin读入数据超时)
  9. webstorm问题 Cannot load settings from file...
  10. python爬虫获取steam验证码