进阶项目(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

 仿真分析

 

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



猜你喜欢

转载自www.cnblogs.com/mengyi1989/p/11521053.html