AC620教程 第十五节 8位7段数码管驱动设计与验证

本章导读

电子系统中常用的显示设备有数码管、LCD液晶以及VGA显示器等。其中数码管又可分为段式显示(7段、米字型等)以及点阵显示(8*816*16等),LCD液晶的应用可以分为字符式液晶(160212864等)以及真彩液晶屏,VGA显示器一般是现在的电脑显示器。芯航线开发板对以上三种设备均提供了硬件接口。

本章将实现FPGA驱动数码管动态显示并提取出实现的电路结构,从电路结构入手编写代码,仿真对设计进行验证。最终板级调试时使用In system sources and probes editorISSP,系统的源和探测器工具),输入需要显示的数据,数码管则显示对应数值。本节课核心不再是代码,而是电路结构,电路结构确定后编写代码只是照图施工的过程。这也是越来越接近FPGA设计本质的硬件思维。

数码管驱动原理

        其中8段数码管的结构图如图15.1所示,

    

15.1 8段数码管结构图

    由上图可以看出数码管有两种结构:共阴极与共阳极。这两者的区别在于,公共端是连接到地还是高电平,对于共阴数码管需要给对应段以高电平才会使其点亮,而对于共阳极数码管则需要给低电平才会点亮。AC620上板载的是共阳数码管。同时为了显示数字或字符,必须对数字或字符进行编码译码。这里先不考虑小数点也就是简化为7段数码管,其编码译码格式如表15.1所示:

15.1 数码管编码译码表

段式数码管工作方式有两种:静态显示方式和动态显示方式。静态显示的特点是每个数码管的段选必须接一个8位数据线来保持显示的字形码。当送入一次字形码后,显示字形可一直保持,直到送入新字形码为止。这种方法由于每一个数码管均需要独立的数据线因此硬件电路比较复杂,成本较高,很少使用。

为了节约IO以及成本一般采用如图15.2所示的电路结构,这样3个数码管接在一起就比静态的少了7*2I/O

15.2三位数码管等效电路图

这样就实现了另一种显示模式,动态显示。动态显示的特点是将所有位数码管的段选线并联在一起,由位选线控制是哪一位数码管有效。选亮数码管采用动态扫描显示。所谓动态扫描显示即轮流向各位数码管送出字形码和相应的位选,利用发光管的余辉和人眼视觉暂留作用,使人的感觉好像各位数码管同时都在显示。

    现在举例假设将扫描时间定为1S,这三个数码管分成3s,第1秒时sel数据线上为`b100,这时数码管0被选中,这时a=0,数码管0LED0就可以点亮;第2秒时sel数据线上为`b010,这时数码管1被选中,这时b=0,数码管1LED1就可以点亮;第3秒时sel数据线上为`b001,这时数码管2被选中,这时c=0,数码管2LED2就可以点亮。这时的效果就会是数码管0LED0亮一秒后数码管1LED1亮一秒最后是数码管2LED2亮一秒,这样再次循环。

这样如果使用1ms刷新时间的话由于数码管的余辉效应以及人的视觉暂留这样就会出现数码管0LED0、数码管1LED1以及数码管2LED2 "同时"亮,并不会有闪烁感。

15.3 78位的数码管原理图

三线制数码管电路设计

AC620开发板上配备的是88位的数码管,如果按照图15.3电路进行设计,可以看出仍需要16IO进行驱动。下面提出另外一种三线制数码电路设计方法,其电路图如图15.4。这样的电路设计仍旧属于动态显示,但是这里通过外接了由两片874HC595移位寄存器级联后构成16位移位寄存器并将级联后的输出连接到位选及段选口,可以直接通过三个IO即可控制88段数码管。

15.4 三线制数码管电路图

74HC5958位串行移位寄存器,带有存储寄存器和三态寄存器,其中移位寄存器和存储寄存器分别采用不同的时钟。其内部结构图如图15.5所示,其可以把串行的信号转为行的信号,因此常用做各种数码管以及点阵屏的驱动芯片。芯片的IO功能描述如表15.2所示。

15.5 74HC595内部结构图

15.2 74HC595 IO功能描述

数码管动态扫描驱动设计

模块接口设计及内部功能划分

    由上面的分析可以得出图15.4的框图,其接口列表如表15.2所示:

15.4 数码管模块框图

15.2 模块接口列表

根据以上的分析可知,首先要有一个周期为1ms的驱动时钟,因此需要一个分频电路;在进行数码管的位选时,需要一个循环移位;在选择位后,需要选择器来选通数据输入位;要实现表15.1的功能需要一个由译码器。

数码管驱动模块逻辑电路图可以简化成如图15.5所示的,其中每一部分的作用如表15.3所示。

15.5 数码管驱动模块逻辑电路图

15.3 子功能块功能描述

名称

功能描述

divder

分频产生1KHz的扫描时钟

Shift8

8位循环移位寄存器

MUX8

数据输入选择

MUX2

使能选择

LUT

数据译码器

扫描时钟模块设计

从系统时钟50M分频得到1KHz的扫描时钟,计数器值即为25000d,这样计数器的位宽定义为15位即可。

 1     reg [14:0]divider_cnt;
 2     reg clk_1K;
 3 
 4     always@(posedge Clk or negedge Rst_n)
 5     if(!Rst_n)
 6         divider_cnt <= 15'd0;
 7     else if(!En)
 8         divider_cnt <= 15'd0;
 9     else if(divider_cnt == 15'd24999)
10         divider_cnt <= 15'd0;
11     else
12         divider_cnt <= divider_cnt + 1'b1;
13         
14     always@(posedge Clk or negedge Rst_n)
15     if(!Rst_n)
16         clk_1K <= 1'b0;
17     else if(divider_cnt == 15'd24999)
18         clk_1K <= ~clk_1K;
19     else
20         clk_1K <= clk_1K;

数码管位选模块设计

接下来编写8位循环移位寄存器,这里利用循环移位寄存器实现0000_0001b1000_0000b的变化,进而实现数码管的位选,即实现每个扫描时钟周期选择一个数码管。移位寄存器输出值与数码管选通的对应关系如表15.4所示,其中sel7为高位。

15.4 移位寄存器与数码管对应关系

sel0

sel1

sel2

sel3

sel4

sel5

sel6

sel7

被选通数码管

1

0

0

0

0

0

0

0

数码管0

0

1

0

0

0

0

0

0

数码管1

0

0

1

0

0

0

0

0

数码管2

……

               

0

0

0

0

0

0

0

1

数码管7

1     reg [7:0]sel_r;    
2 
3     always@(posedge clk_1K or negedge Rst_n)
4     if(!Rst_n)
5         sel_r <= 8'b0000_0001;
6     else if(sel_r == 8'b1000_0000)
7         sel_r <= 8'b0000_0001;
8     else
9         sel_r <=  sel_r << 1;

数码管数据显示设计

利用81多路器,选择端为当前扫描到的数码管也就是循环移位寄存器的输出端,利用多路器将待显示数据输送到对应到数码管上。

 1     reg [3:0]data_tmp;   
 2     always@(*)
 3         case(sel_r)
 4             8'b0000_0001:data_tmp = disp_data[3:0];
 5             8'b0000_0010:data_tmp = disp_data[7:4];
 6             8'b0000_0100:data_tmp = disp_data[11:8];
 7             8'b0000_1000:data_tmp = disp_data[15:12];
 8             8'b0001_0000:data_tmp = disp_data[19:16];
 9             8'b0010_0000:data_tmp = disp_data[23:20];
10             8'b0100_0000:data_tmp = disp_data[27:24];
11             8'b1000_0000:data_tmp = disp_data[31:28];
12             default:data_tmp = 4'b0000;
13         endcase

 

显示数据译码设计

前面所说如果要使数码管显示数字或字符,须对数字或字符进行编码译码。这里利用一个4输入查找表,来实现7位的输出显示译码。

 1 always@(*)
 2         case(data_tmp)
 3             4'h0:seg = 7'b1000000;
 4             4'h1:seg = 7'b1111001;
 5             4'h2:seg = 7'b0100100;
 6             4'h3:seg = 7'b0110000;
 7             4'h4:seg = 7'b0011001;
 8             4'h5:seg = 7'b0010010;
 9             4'h6:seg = 7'b0000010;
10             4'h7:seg = 7'b1111000;
11             4'h8:seg = 7'b0000000;
12             4'h9:seg = 7'b0010000;
13             4'ha:seg = 7'b0001000;
14             4'hb:seg = 7'b0000011;
15             4'hc:seg = 7'b1000110;
16             4'hd:seg = 7'b0100001;
17             4'he:seg = 7'b0000110;
18             4'hf:seg = 7'b0001110;
19         endcase

    

模块使能设计

模块化的设计理念是,使得每个模块独立化,其端口设计要便于以后被调用与控制。基于这种理念,这里需要加入使能信号。关于使能子模块,直接利用一个二选一多路器即可实现。

assign sel = (En)?sel_r:8'b0000_0000;

数码管显示模块仿真测试

以下生成了复位信号以及使能信号、待显示数据的初始化以及切换,分别在数码管上显示"87654321"以及"89abcdef"。

 1   initial begin
 2         Rst_n = 1'b0;
 3         En = 1;
 4         disp_data = 32'h12345678;
 5         #(`clk_period*20);
 6         Rst_n = 1;
 7         #(`clk_period*20);
 8         #20000000;
 9         disp_data = 32'h87654321;
10         #20000000;
11         disp_data = 32'h89abcdef;
12         #20000000;
13         $stop;
14     end

设置好仿真脚本后进行功能仿真,可以看到如图15.6所示的局部波形文件,可以看出在复位信号置高之前数码管均显示0,在复位结束后数码管才开始正常显示,且当待显示数据为89ABCDEFhMSB)后,数码管从18依次被选通且分别显示为FEDCBA98hLSB)。即仿真通过。

15.6 数码管功能仿真波形图

74HC595驱动设计

在数据手册中可以看出,不同工作温度和工作电压下的芯片工作频率值不相同,分别如表15.515.6所示。由于在学习板中芯片采用3.3V供电,这样在设计其工作频率时,直接使用50M晶振四分频后的时钟作为其工作时钟。

15.5 芯片工作频率与温度对照

SYMBOL

PARAMETER

Vcc(V)

MIN

TYP

MAX

UNIT

 

maximum clock frequency SH_CP and ST_CP

2.0

9

30

-

MHz

4.5

30

91

-

MHz

6.0

35

108

-

MHz

 

15.6芯片工作频率与温度对照

SYMBOL

PARAMETER

Vcc(V)

MIN

TYP

MAX

UNIT

 

maximum clock frequency SH_CP and ST_CP

2.0

4.8

-

-

MHz

4.5

24

-

-

MHz

6.0

28

-

-

MHz

 

74HC595模块接口设计

从上面的分析可得出如图15.10所示的框图,其接口列表15.7如所示

15.10 74HC595驱动模块框图

15.7 74HC595驱动模块接口功能描述

信号名称

I/O

功能描述

Clk

I

50M时钟

Rst_n

I

复位信号

En

I

数码管使能信号1使能,0关闭

Data[15:0]

I

8个数码管待显示数据,每四位组成一个BCD

DS

O

串行数据输出

SH_CP

O

移位寄存器的时钟输出

ST_CP

O

存储寄存器的时钟输出

74HC595驱动模块设计

首先工作时钟产生,对50M时钟进行4分频。

 1     parameter CNT_MAX = 4; //分频数
 2     reg [15:0] divider_cnt;//分频计数器
 3 
 4     always@(posedge Clk or negedge Rst_n)
 5     if(!Rst_n)
 6         divider_cnt <= 16'd0;
 7     else if(divider_cnt == CNT_MAX)
 8         divider_cnt <= 16'd0;
 9     else
10         divider_cnt <= divider_cnt + 1'b1;
11     
12     wire sck_pluse;
13     assign sck_pluse = (divider_cnt == CNT_MAX);

 

sck_pluse进行计数,用于查找表实现数据的串行输入以及移位时钟SH_CP与存储时钟ST_CP的产生。

 1     reg [4:0]SHCP_EDGE_CNT;//SH_CP EDGE counter
 2 
 3    always@(posedge Clk or negedge Rst_n)
 4     if(!Rst_n)
 5         SHCP_EDGE_CNT <= 5'd0;
 6     else if(sck_pluse)begin
 7         if(SHCP_EDGE_CNT ==  5'd31)
 8             SHCP_EDGE_CNT <= 5'd0;
 9         else
10             SHCP_EDGE_CNT <= SHCP_EDGE_CNT + 1'd1;
11     end
12     else
13         SHCP_EDGE_CNT <= SHCP_EDGE_CNT;

查找表实现状态输出。

 1 always@(posedge Clk or negedge Rst_n)
 2     if(!Rst_n)begin
 3         SH_CP <= 1'b0;
 4         ST_CP <= 1'b0;
 5         DS <= 1'b0; 
 6     end
 7     else begin
 8         case(SHCP_EDGE_CNT)
 9             5'd0:begin SH_CP <= 1'b0; ST_CP <= 1'b1; DS <= r_data[15]; end
10             5'd1:begin SH_CP <= 1'b1; ST_CP <= 1'b0;end
11             5'd2:begin SH_CP <= 1'b0; DS <= r_data[14];end
12             5'd3:begin SH_CP <= 1'b1; end
13             5'd4:begin SH_CP <= 1'b0; DS <= r_data[13];end
14             5'd5:begin SH_CP <= 1'b1; end
15             5'd6:begin SH_CP <= 1'b0; DS <= r_data[12];end
16             5'd7:begin SH_CP <= 1'b1; end
17             5'd8:begin SH_CP <= 1'b0; DS <= r_data[11];end
18             5'd9:begin SH_CP <= 1'b1; end
19             5'd10:begin SH_CP <= 1'b0; DS <= r_data[10];end
20             5'd11:begin SH_CP <= 1'b1; end
21             5'd12:begin SH_CP <= 1'b0; DS <= r_data[9];end
22             5'd13:begin SH_CP <= 1'b1; end
23             5'd14:begin SH_CP <= 1'b0; DS <= r_data[8];end
24             5'd15:begin SH_CP <= 1'b1; end
25             5'd16:begin SH_CP <= 1'b0; DS <= r_data[7];end
26             5'd17:begin SH_CP <= 1'b1; end
27             5'd18:begin SH_CP <= 1'b0; DS <= r_data[6];end
28             5'd19:begin SH_CP <= 1'b1; end
29             5'd20:begin SH_CP <= 1'b0; DS <= r_data[5];end
30             5'd21:begin SH_CP <= 1'b1; end
31             5'd22:begin SH_CP <= 1'b0; DS <= r_data[4];end
32             5'd23:begin SH_CP <= 1'b1; end
33             5'd24:begin SH_CP <= 1'b0; DS <= r_data[3];end
34             5'd25:begin SH_CP <= 1'b1; end
35             5'd26:begin SH_CP <= 1'b0; DS <= r_data[2];end
36             5'd27:begin SH_CP <= 1'b1; end
37             5'd28:begin SH_CP <= 1'b0; DS <= r_data[1];end
38             5'd29:begin SH_CP <= 1'b1; end
39             5'd30:begin SH_CP <= 1'b0; DS <= r_data[0];end
40             5'd31:begin SH_CP <= 1'b1; end
41         endcase 
42     end

74HC595驱动模块仿真测试

此处仿真与前面类似,不再重复。部分波形如图15.11所示,可以看出在模块复位结束后每在移位时钟SH_CP上升沿时串行数据依次输出且高位在前。移位结束后ST_CP产生一个高电平更新显示数据,因此各信号工作正常。

15.11 74HC595驱动仿真

ISSP生成及使用

为了更便捷地进行板级调试,这里介绍Qusrtus Prime自带的的In system sources and probes editorISSP)调试工具,测试数码管可以只用其提供的源,而至于其探针功能的使用会在后面的RAM章节再做详细介绍。这样测试整体模块框图就可以简化为如图15.12所示。

15.12 整体测试框图

这里ISSP是以IP核的形式提供的,因此第一步单击Tools来启动IP Catalog来新建一个定制IP核。这里需要指出的是这里与Quartus II不同,在Quartus Prime中与qsys相关的IP核均集中到Qsys中。在Quartus II的使用方法在本节末也有相应讲解,可以对比参考。

在弹出的图15.13 IP目录中,找到Simulation;Debug and Verification下的Altera In-System Source & Probes,并将输出目录确定为工程文件夹下的ip文件夹,并以hex_data保存,单击Next

15.13 新建ISSP

15.14设置ISSP名称及文件位置

    在弹出的图15.15配置界面中将源位宽定义为32,探针位宽定义为0,然后单击Next并将语言类型选择为Verilog HDL

15.15 ISSP配置界面

15.16 语言选择成Verilog HDL并启动转换

在图15.16点击Generate后软件会将ISSP先保存为Qsys系统在提示系统保存成功后点击Close会自动启动转换。弹出如图15.18界面,标志完成。

 

15.17 Qsys系统保存完成

15.18 转换完成

单击Close,然后在主界面点击Finish。此时会弹出如图15.19所示的汇总信息,确认无误后单击Close即可。

15.19 汇总信息

这时会弹出图15.20提示需要手动将Qsys系统添加到工程中。这样直接在Files右键选择Add/Remove Files in Project,找到对应的文件即可。

15.20 添加qsys到工程

15.21 手动添加qsys到工程

ISSP依旧属于IP核,因此在使用前需要将其在工程文件中进行例化。ISSP在生成的文件中含有例化模板可以直接打开进行复制修改。可以打开生成的IP目录下的hex_data_inst.v文件。

15.22 例化文件所在文件夹位置

15.23 例化模板

板级调试与验证

新建顶层文件HEX_top.v,并将编写好的数码管驱动模块以及ISSP例化到顶层文件中。

 1 module HXE_top(Clk,Rst_n,SH_CP,ST_CP,DS);
 2 
 3     input Clk;  //50M
 4     input Rst_n;
 5     
 6     output SH_CP;   //shift clock
 7     output ST_CP;   //latch data clock
 8     output DS;  //shift serial data
 9     
10     wire[31:0]disp_data;
11     wire [7:0] sel;//数码管位选(选择当前要显示的数码管)
12     wire [6:0] seg;//数码管段选(当前要显示的内容)
13     
14     hex_data  hex_data(
15         .probe(),
16         .source(disp_data)
17     );
18 
19     
20     HXE8 HXE8(
21         .Clk(Clk),
22         .Rst_n(Rst_n),
23         .En(1'b1),
24         .disp_data(disp_data),
25         .sel(sel),
26         .seg(seg)
27     );
28     
29     
30     HC595_Driver HC595_Driver(
31         .Clk(Clk),
32         .Rst_n(Rst_n),
33         .Data({1'b1,seg,sel}),
34         .S_EN(1'b1),
35         .SH_CP(SH_CP),
36         .ST_CP(ST_CP),
37         .DS(DS)
38     );
39     
40 endmodule

    

分配引脚并全编译无误后下载到开发板中。这时可以看到数码管全显示0,与设计一致。在Quartus Prime中点击ToolsIn-System Source and Probes Editor启动ISSP,启动后的界面如图15.24所示。这里需手动选择下载器,并将数据格式改为设计中的hex格式。

15.24 启动ISSP

15.25 ISSP操作界面

15.26 切换数据格式

这样ISSP界面的Data中输入相应的数据即可在数码管上看到与之对应的显示,如图15.27所示。至此完成了数码管的动态显示。

15.27 数码管显示数据

本章中介绍了数码管的两种驱动方式,静态扫描与动态扫描。最终实现了三线制数码管的驱动显示。得益于模块化设计的优势,如果需要实现FPGA直接驱动数码管,只需直接调用HEX8文件即可。在板级验证中使用了In system sources and probes editorISSP)调试工具。

在本章的基础上,可以结合第14章的内容,设计一款具有清零及暂停功能的秒表。

Quartus II中ISSP使用方法

    在Quartus II中使用方式如下所示:

这里ISSP是以IP核的形式提供的,因此第一步单击ToolsMega Wizard Plug-In Manager来启动Mega Wizard插件管理器,并新建一个定制IP核;

15.28 启动Mega Wizard插件管理器

在弹出的图15.29 Mega Wizard插件管理器的参数设置界面中,找到JTAG-accessible Extensions下选择In-System Source and Probes,并将输出目录确定为工程文件夹下的ip文件夹,并以hex_data保存,单击Next

    在弹出的图15.30配置界面中将源位宽定义为32,探针位宽定义为0,然后单击Next即可。

15.29 Mega Wizard插件管理器参数设置

15.30.1 ISSP配置界面

15.30.2 ISSP配置界面

15. 30.3 ISSP配置界面

在调用时打开生成后的hex_data.v复制端口并进行相应修改即可。剩余使用方式就与Quartus Prime一致了。

 
1     hex_data hex_data(
2 
3         .probe(),
4 
5         .source(disp_data)
6 
7     );
原文地址:https://www.cnblogs.com/xiaomeige/p/6915097.html