zoukankan      html  css  js  c++  java
  • 进阶项目(8)数码管显示设计讲解

    写在前面的话

    项目设计中我们通常需要一些显示设备来显示我们需要的信息,可以选择的显示设备更是种类繁多,玲琅满目,数码管无疑是最常用,最简单的显示设备之一。本节,梦翼师兄和大家一起学习数码管的显示原理和驱动方式,为我们以后项目的开发做好准备。

    项目需求

    设计一个数码管的驱动电路,使数码管能够同时显示出任意的六位数字(梦翼师兄使用的开发板集成的数码管为六位连体数码管)。

    原理分析

    数码管作为一种外设,我们首先需要了解它的工作原理以及它的对应电路连接关系,七段数码管结构示意图如下:

    顾名思义,七段数码管就是使用七段点亮的线段来拼成常见的数字和某些字母,这种显示方式我们在数字电路中非常容易见到。再加上右下角显示的小数点,实际上一个显示单元包括了8根信号线。根据电路设计的不同,这些信号线可能高有效也可能低有效。我们通过FPGA控制这些线段的亮灭,就可以达到相应的显示效果。

    对于多个数码管的显示模块,将每一个都连接到FPGA的管脚会耗用大量FPGA的管脚资源。因此我们同样引入一种类似矩阵键盘的扫描方式。任何时刻我们只使用8根信号点亮一个数码管,但是8个数码管是随着时钟步调交替点亮的,只要时钟的速度够快,我们观察到数码管就好像几个同时点亮一样。梦翼师兄使用的开发板原理图如下:

    如图所示,我们的开发板使用的是六位共阳极数码管,六个PNP型三极管分别作为六组数码管电源的输入开关,也就是我们常说的位选信号,PNP三极管为低电平导通,所以我们的位选信号低有效。在这里,为了节约FPGA的IO资源,我们把六个位选信号连接到了三八译码器74HC138D,该三八译码器的真值表如下:

     

    由此,我们可以得出结论,当{SEL2, SEL1, SEL0}=3’b000时,Y0变为低电平,而由于Y0连接到了第一个数码管,所以第一个数码管点亮。当{SEL2, SEL1, SEL0}=3’b001时,对应第二个数码管点亮,以此类推。SEG_0到SEG_7分别对应二极管a-g以及“小数点”,即我们所说的段选信号。由于是共阳极数码管,所以二极管只要给低电平就可以点亮,根据点亮的二极管不同,就可以显示出不同的字符。假如我们要点亮第一个数码管,并且显示出字符“A”,那么我们就只需要选中第一个数码管{SEL2, SEL1, SEL0}=3’b000,而且SEG=8’b1000_1000。

    如果要让数码管“全部亮起来”,并同时显示相同字符,那我们只能通过比较快速的切换位选信号来实现这一目的。但是切换频率如果过高,数码管显示也会出现不稳定的状态,这和器件的工艺有关,我们可以选择切换的经验频率1KHZ。那么这时,我们就需要用到分频模块,将50MHZ的晶振时钟分频成我们所需要的1KHZ。

    单个数码管显示

    单个数码管显示的系统架构

    单个数码管显示最大的数字是十六进制中的F(15),15对应的二进制数是4’b1111,所以我们的输入应该是四位。

    单个数码管显示的模块模块功能介绍

    模块名

    功能描述

    SEG7

    输出控制线

    单个数码管显示模块的端口描述

    端口名

    端口说明

    clk

    系统时钟输入

    Rst_n

    系统复位

    Data[3:0]

    数据输入

    sel[2:0]

    片选信号输出

    seg[7:0]

    段选信号输出

    代码解释

    SEG7模块代码

    /****************************************************          

     *   Engineer      :   梦翼师兄

     *   QQ             :   761664056

     *   The module function:控制单个数码管显示任意的数字

    *****************************************************/

    00 module SEG7 (

    01 clk, //系统时钟

    02 rst_n,//系统复位

    03 data, //输入数据

    04 seg,//数码管段选

    05 sel//数码管位选

    06 );

    07 //系统输入

    08 input clk;//系统时钟

    09 input rst_n;//系统复位

    10 input [3:0] data;//输入数据

    11 //系统输出

    12 output reg [7:0] seg;//数码管段选

    13 output reg [2:0] sel;//数码管位选

    14

    15 always @ (posedge clk or negedge rst_n)

    16 begin

    17 if (!rst_n)//复位的时候选择第一个数码管

    18 begin

    19 sel <= 0;

    20 end

    21 else

    22 begin//选择第一个数码管

    23 sel <= 0;

    24 end

    25 end

    26

    27 always @ (*)//用组合逻辑进行输出段选信号

    28 begin

    29 if (!rst_n)//复位的时候数码管熄灭

    30 begin

    31 seg = 8'b1111_1111;

    32 end

    33 else

    34 begin

    35 case(data)

    36 0 : seg = 8'b1100_0000;//显示“0”

    37 1 : seg = 8'b1111_1001;//显示“1”

    38 2 : seg = 8'b1010_0100;//显示“2”

    39 3 : seg = 8'b1011_0000;//显示“3”

    40 4 : seg = 8'b1001_1001;//显示“4”

    41 5 : seg = 8'b1001_0010;//显示“5”

    42 6 : seg = 8'b1000_0010;//显示“6”

    43 7 : seg = 8'b1111_1000;//显示“7”

    44 8 : seg = 8'b1000_0000;//显示“8”

    45 9 : seg = 8'b1001_0000;//显示“9”

    46 10 : seg = 8'b1000_1000;//显示“A”

    47 11 : seg = 8'b1000_0011;//显示“B”

    48 12 : seg = 8'b1100_0110;//显示“C”

    49 13 : seg = 8'b1010_0001;//显示“D”

    50 14 : seg = 8'b1000_0110;//显示“E”

    51 15 : seg = 8'b1000_1110;//显示“F”

    52 default : seg = 8'b1111_1111;//全灭

    53 endcase

    54 end

    55 end

    56

    57 endmodule

    仿真代码

    /****************************************************          

     *   Engineer      :   梦翼师兄

     *   QQ             :   761664056

     *   The module function:测试SEG7模块,并显示“A”

    *****************************************************/

    00 `timescale 1ns/1ps //定义时间单位和精度

    01

    02 module SEG7_tb;

    03 //系统输入

    04 reg clk;//系统时钟

    05 reg rst_n;//系统复位

    06 reg [3:0] data;//输入数据

    07 //系统输出

    08 wire [7:0] seg;//数码管段选

    09 wire [2:0] sel;//数码管位选

    10

    11 initial begin

    12 clk = 1;

    13 rst_n = 0;

    14 data = 10;//data = 4'hA; 这两种方式都可以

    15 # 200.1 //复位200ns

    16 rst_n = 1;

    17 end

    18

    19 always # 10 clk = ~clk;//50M的时钟

    20

    21 SEG7 SEG7 (

    22 .clk(clk), //系统时钟

    23 .rst_n(rst_n),//系统复位

    24 .data(data), //输入数据

    25 .seg(seg),//数码管段选

    26 .sel(sel)//数码管位选

    27 );

    28

    29 endmodule

     在本模块中,梦翼师兄只是测试了显示”A”,有兴趣的话,可以把0~9A~F,全部测试一下。

     单个数码管显示的仿真分析

     

    在复位期间,seg信号全部为“1”,数码管熄灭。当复位信号拉高以后,seg信号变成了“10001000”,正好是“A”的段选,而sel(位选)一直就是0(选择第一个数码管),证明我们的设计是正确的。

     六个数码管显示

    六个数码显示的系统架构

    应用六个数码管去显示任意数字,每个数码管显示的数字需要用4位二进制数去表示,那么六个数码管一共需要24位二进制数

    数码管各模块功能介绍

    模块名

    功能描述

    SEG7

    输出数码管控制位选和段选信号

    freq

    时钟分频模块,输出1KHz时钟

    top

    顶层模块,负责模块级联

    端口和内部连线描述

    顶层端口

    端口名

    端口说明

    clk

    系统时钟输入

    Rst_n

    系统复位

    Data[23:0]

    数据输入

    sel[2:0]

    片选信号输出

    seg[7:0]

    段选信号输出

    模块内部连线

    连线名

    连线说明

    Clk_1K

    数码管的切换时钟

    代码解释

    freq模块代码

    /****************************************************          

     *   Engineer      :   梦翼师兄

     *   QQ             :   761664056

     *   The module function: 产生慢时钟

    *****************************************************/

    00 module freq (

    01 clk, //系统时钟

    02 rst_n, //系统复位

    03 clk_1K//切换时钟

    04 );

    05 //系统输入

    06 input clk;//系统时钟

    07 input rst_n;//系统复位

    08 //系统输出

    09 output reg clk_1K;//切换时钟

    10 //定义中间寄存器

    11 reg [19:0] count;//定义一个计数的寄存器

    12

    13 always @ (posedge clk or negedge rst_n)

    14 begin

    15 if (!rst_n)

    16 begin

    17 clk_1K <= 1;

    18 count <= 0;

    19 end

    20 else

    21 begin

    22 if (count < 24999)// 50000分频,得出1K的时钟

    23 count <= count + 1;

    24 else

    25 begin

    26 count <= 0;

    27 clk_1K <= ~clk_1K;

    28 end

    29 end

    30 end

    31

    32 endmodule

    SEG7模块代码

    /****************************************************          

     *   Engineer      :   梦翼师兄

     *   QQ             :   761664056

     *   The module function: 产生段选信号和位选信号

    *****************************************************/

    000 module SEG7 (

    001 clk, //模块时钟

    002 rst_n,//系统复位

    003 data, //输入数据

    004 seg,//数码管段选

    005 sel//数码管位选

    006 );

    007 //系统输入

    008 input clk;//模块时钟

    009 input rst_n;//系统复位

    010 input [23:0] data;//输入数据

    011 //系统输出

    012 output reg [7:0] seg;//数码管段选

    013 output reg [2:0] sel;//数码管位选

    014 //定义中间寄存器

    015 reg [3:0] data_temp;//数码管显示的数值

    016 reg [2:0] state;//状态寄存器

    017

    018 always @ (posedge clk or negedge rst_n)

    019 begin

    020 if (!rst_n)//复位的时候选择第一个数码管

    021 begin

    022 sel <= 0;

    023 data_temp <= 0;

    024 state <= 0;

    025 end

    026 else

    027 begin

    028 case (state)

    029 0 : begin//将最高位的数显示在第一个数码管上

    030 sel <= 0;

    031 data_temp <= data[23:20];

    032 state <= 1;

    033 end

    034

    035 1 : begin//将第2位的数显示在第二个数码管上

    036 sel <= 1;

    037 data_temp <= data[19:16];

    038 state <= 2;

    039 end

    040

    041 2 : begin//将第3位的数显示在第三个数码管上

    042 sel <= 2;

    043 data_temp <= data[15:12];

    044 state <= 3;

    045 end

    046

    047 3 : begin//将第4位的数显示在第四个数码管上

    048 sel <= 3;

    049 data_temp <= data[11:8];

    050 state <= 4;

    051 end

    052

    053 4 : begin//将最5位的数显示在第五个数码管上

    054 sel <= 4;

    055 data_temp <= data[7:4];

    056 state <= 5;

    057 end

    058

    059 5 : begin//将最低位的数显示在第六个数码管上

    060 sel <= 5;

    061 data_temp <= data[3:0];

    062 state <= 0;

    063 end

    064

    065 default : state <= 0;

    066 endcase

    067 end

    068 end

    069

    070 always @ (*)//根据data_temp的中的值,用组合逻辑进行输出段选信号

    071 begin

    072 if (!rst_n)//复位的时候数码管熄灭

    073 begin

    074 seg = 8'b1111_1111;

    075 end

    076 else

    077 begin

    078 case(data_temp)

    079 0 : seg = 8'b1100_0000;//显示“0”

    080 1 : seg = 8'b1111_1001;//显示“1”

    081 2 : seg = 8'b1010_0100;//显示“2”

    082 3 : seg = 8'b1011_0000;//显示“3”

    083 4 : seg = 8'b1001_1001;//显示“4”

    084 5 : seg = 8'b1001_0010;//显示“5”

    085 6 : seg = 8'b1000_0010;//显示“6”

    086 7 : seg = 8'b1111_1000;//显示“7”

    087 8 : seg = 8'b1000_0000;//显示“8”

    088 9 : seg = 8'b1001_0000;//显示“9”

    089 10 : seg = 8'b1000_1000;//显示“A”

    090 11 : seg = 8'b1000_0011;//显示“B”

    091 12 : seg = 8'b1100_0110;//显示“C”

    092 13 : seg = 8'b1010_0001;//显示“D”

    093 14 : seg = 8'b1000_0110;//显示“E”

    094 15 : seg = 8'b1000_1110;//显示“F”

    095 default : seg = 8'b1000_1110;//显示“F”

    096 endcase

    097 end

    098 end

    099

    100 endmodule

    top模块代码

    /****************************************************          

     *   Engineer      :   梦翼师兄

     *   QQ             :   761664056

     *   The module function: 顶层模块

    *****************************************************/

    00 module top (

    01 clk, //系统时钟

    02 rst_n, //系统复位

    03 data, //输入数据

    04 seg, //数码管段选

    05 sel//数码管位选

    06 );

    07 //系统输入

    08 input clk;//系统时钟

    09 input rst_n;//系统复位

    10 input [23:0] data;//输入数据

    11 //系统输出

    12 output [7:0] seg;//数码管段选

    13 output [2:0] sel;//数码管位选

    14 //定义中间连线

    15 wire clk_1K;//定义切换时钟

    16 //调用pll(锁相环)

    17 freq freq(

    18 .clk( clk ),//外部时钟

    19 .rst_n(rst_n),//系统复位

    20 .clk_1K( clk_1K ) //切换时钟

    21 );

    22 //实例化SEG7

    23 SEG7 SEG7 (

    24 .clk(clk_1K), //切换时钟

    25 .rst_n(rst_n),//系统复位

    26 .data(data), //输入数据

    27 .seg(seg),//数码管段选

    28 .sel(sel)//数码管位选

    29 );

    30

    31 endmodule

    编写完可综合代码之后查看RTL视图如下:

    RTL视图可知代码综合以后得到的电路和我们设计的系统框图一致,说明顶层连接关系正确,接下来编写测试代码如下:

    /****************************************************          

     *   Engineer      :   梦翼师兄

     *   QQ             :   761664056

     *   The module function: 测试数码管模块

    *****************************************************/

    00 `timescale 1ns/1ps //定义时间单位和精度

    01

    02 module top_tb;

    03 //系统输入

    04 reg clk;//系统时钟

    05 reg rst_n;//系统复位

    06 reg [23:0] data;//输入数据

    07 //系统输出

    08 wire [7:0] seg;//数码管段选

    09 wire [2:0] sel;//数码管位选

    10

    11 initial begin

    12 clk = 1;

    13 rst_n = 0;

    14 data = 24'h123456;

    15 # 200.1 //复位200.1ns

    16 rst_n = 1;

    17 end

    18

    19 always # 10 clk = ~clk;//50M的时钟

    20

    21 top top(

    22 .clk(clk), //系统时钟

    23 .rst_n(rst_n),//系统复位

    24 .data(data), //输入数据

    25 .seg(seg),//数码管段选

    26 .sel(sel)//数码管位选

    27 );

    28

    29 endmodule

     仿真分析

     

    在对应的数码管上,给出对应数值的段选信号,经过比对,我们的设计都是正确的。



  • 相关阅读:
    Qt应用如何发布
    关于在windows下部署发布QT程序的总结
    干净地发布QT程序
    解析 Qt 程序在Windows 下发布
    Qt 5.2.0 和 VS 2012集成
    Unable to find a qt build, to solve this problem specify a qt build
    运行python程序不显示cmd的方法
    py2exe使用方法
    python 类
    在Pygtk和Glade使用Gtkbuilder
  • 原文地址:https://www.cnblogs.com/mengyi1989/p/11521053.html
Copyright © 2011-2022 走看看