EDA系统设计实例.docx
《EDA系统设计实例.docx》由会员分享,可在线阅读,更多相关《EDA系统设计实例.docx(133页珍藏版)》请在冰点文库上搜索。
![EDA系统设计实例.docx](https://file1.bingdoc.com/fileroot1/2023-5/22/d2d5c7ae-bdff-4fc0-af9f-b4e74f155500/d2d5c7ae-bdff-4fc0-af9f-b4e74f1555001.gif)
EDA系统设计实例
第9章电子电路的VHDL综合设计
本章给出了12个综合设计实例,其中前8个设计实例涉及到电子设计系统常用的显示功能(如LED数码管显示、点阵显示及液晶显示)、键盘输入功能、模/数和数/模转换控制功能,以及信息传输过程中用到的同步编/解码和差错控制编码。
后面4个综合性的设计实例,如任意波形信号发生器、密码锁、多功能闹钟和音乐演奏发生器作为电子设计大赛的部分功能也会经常涉及到。
每一个设计实例都给出了仿真图和在硬件平台上验证时的导线连接,若读者的硬件平台和作者的不一样,读者只需读懂实例代码,然后对代码作适当的修改及导线连接的修改即可。
本章由简入深的介绍方式,能帮助读者及时地巩固前面章节所学的VHDL语言知识,相信当读者把这些项目全部训练完后,其设计能力会得到大大的提高。
9.1六位数码动态扫描显示电路的设计
9.1.1数码管动态扫描显示原理
多位LED数码管显示可以分为静态和动态显示。
静态显示时要求每一位数码管都应有一个数据锁存器驱动LED的a~g及小数点DP端,用于锁存各位数码管要显示的不同数据,另外,每一位数码管的公共端还需要接有效电平。
显然,显示的位数越多,需要的锁存器也就越多,这样很不经济。
一般多位数码管显示多采用动态扫描显示。
动态扫描显示时,将所有数码管的各个控制端(a~g、dp)并行的连接到同一数据线上,而各显示器的公共端COM有位扫描信号控制,互不干扰。
为了使各显示器显示不同的内容,先在数据线上送出第一位要显示数字或内容的段码,然后位扫描信号使得第一位数码管的公共端有效,这样第一位数码管显示相应的字符或数字,而其他各位数码管熄灭,接着熄灭第一位数码管,点亮第二位数码管,依次轮流点亮,直到最后一位被点亮,然后重复点亮第一位数码管,如此循环。
当扫描的频率比较低时,数码管显示的信息会出现闪烁,但当扫描频率提高到使得每个数码管每秒的点亮次数大于24次(一般取50次以上)时,由于数码管的余晖效应及人眼的视觉暂留效应,人眼就感觉不出闪烁了,好像是多位数码管被同时点亮的。
但扫描的位数过多,为了满足每位数码管的扫描频率,势必要减少每位数码管导通的时间,这样数码管显示信息的亮度将会降低。
9.1.2设计要求与设计思路
设计要求:
设计一个8位数码管共阴极动态扫描显示控制电路,要显示的信息比如”93434080”,其他要显示的信息读者可自行修改代码。
设计思路:
该设计功能很简单,只需要三个进程即可完成该设计要求。
一个进程用于产生位扫描信号;第二个进程完成对8个数码管选通扫描和送出对应位要显示的字符;而第三个进程完成显示的字符或数字到7段字型码的译码输出。
9.1.3 VHDL代码设计
libraryieee;
useieee.std_logic_1164.all;
useieee.std_logic_unsigned.all;
entityledscanis
port(clk:
instd_logic;
sg:
outstd_logic_vector(6downto0);--段控制信号输出
bt:
outstd_logic_vector(2downto0));--位控制信号输出
endledscan;
architectureoneofledscanis
signalcnt8:
std_logic_vector(2downto0);
signalA:
integerrange0to15;
begin
P1:
process(clk)
begin
ifclk'eventandclk='1'then
cnt8<=cnt8+1;
endif;
endprocessP1;
P2:
process(cnt8)
begin
casecnt8is
--8位显示的数为93434080,这8位数可以根据需要灵活改变
when"000"=>bt<="000";A<=9;
when"001"=>bt<="001";A<=3;
when"010"=>bt<="010";A<=4;
when"011"=>bt<="011";A<=3;
when"100"=>bt<="100";A<=4;
when"101"=>bt<="101";A<=0;
when"110"=>bt<="110";A<=8;
when"111"=>bt<="111";A<=0;
whenothers=>null;
endcase;
endprocessP2;
P3:
process(A)--十六进制数转换成共阴极字形码的译码电路
begin
caseAis
when0=>sg<="0111111";
when1=>sg<="0000110";
when2=>sg<="1011011";
when3=>sg<="1001111";
when4=>sg<="1100110";
when5=>sg<="1101101";
when6=>sg<="1111110";
when7=>sg<="0000111";
when8=>sg<="1111111";
when9=>sg<="1101111";
when10=>sg<="1110111";
when11=>sg<="1111100";
when12=>sg<="0111001";
when13=>sg<="1011110";
when14=>sg<="1111001";
when15=>sg<="1110001";
whenothers=>null;
endcase;
endprocessP3;
end;
9.1.4 时序仿真
仿真结束时间设定为100ms,clk的周期设定为5ms(频率为200Hz),bt显示基数值设定为“UnsigendDecimal”即无符号十进制数,如下图9-1所示。
仿真前的波形如图9-2所示,仿真后所得的波形图如9-3所示。
图9-1bt基数值设定图
图9-2仿真前波形设置图
图9-3仿真后的波形图
从上面的仿真图9-3可以看出,在bt等于0的时刻,段控制输出信号sg输出的是8位数“93434080”的第一个数字即9的字形码“11101111”,仿真完全正确。
9.1.5硬件逻辑验证
对输入输出端口锁定引脚后再重新编译,然后连接导线,并将代码下载到ACEX1K家族的FPGA芯片EP1K30TC144-3中验证。
导线连接说明:
(1)输入端口clk分配的引脚接实验箱上时钟分频器模块的clk2或clk3插孔,使clk2或clk3输出的频率大于等于400Hz(临界闪烁频率为50Hz,有8只数码管需要刷新,故刷新频率应大于50Hz×8=400Hz);
(2)段控制输出端口sg分配的引脚接实验箱上8位8字形共阴极数码管区的a,b,c,d,e,f,g插孔;
(3)位控制输出端口bt分配的引脚接16*16点阵区的sel0,sel1和sel2插孔。
注:
sel2sel1sel0=000时,最右边的一只数码管亮,sel2sel1sel0=111时,最左边的一只数码管亮。
硬件验证方法:
将上述代码下载编程到CPLD器件后,数码管区显示“93434080”。
9.2矩阵式键盘接口电路的设计
9.2.1.键盘扫描与识别原理
1.键盘的分类
键盘是最常见的计算机输入设备,它广泛应用于微型计算机和各种终端设备上。
下面对键盘分类作一简单的介绍。
按键值编码方式可分为:
(硬件)编码键盘与非(硬件)编码键盘。
编码键盘采用专用的编码/译码器件,被按下的键由该器件译码输出相应的键码/键值。
增加了硬件开销,但编程简单,适用于规模大的键盘。
非编码键盘采用软件编码/译码的方式,通过扫描,对每个被按下的键判别输出相应的键码/键值。
不增加硬件开销,编码灵活,适用于小规模的键盘,特别是单片机系统。
但编程较复杂,占CPU时间。
按键组合连接方式可分为:
独立式键盘与矩阵式键盘。
独立式键盘的每个键相互独立,如图9-4所示,各自与一条I/O线相连,通过直接读取该I/O线的高/低电平状态识别按键。
一线一键,占I/O口线多,但按键识别(编程)简单,判键速度快,多用于设置控制键、功能键等键数少的场合。
矩阵式键盘的键按矩阵排列,如图9-5所示,各键处于矩阵行/列的结点处,通过对连在行(列)的I/O线送已知电平的信号,然后读取列(行)线的状态信息。
键多时占用I/O口线少,但判键速度慢,多用于设置数字键等键数多的场合。
图9-4独立式按键结构图图9-5矩阵式按键结构图
2.键盘扫描的基本原理
键盘的扫描要完成有无按键的识别及其求对应按键的键号。
扫描过程具体包括以下步骤:
(1)判别有无键按下;
(2)去除键的机械抖动影响
机械按键输出的电压波形如下图9-6所示,在按下键及其按键释放时出现了电压的高低波动,称为抖动,抖动时间一般在5ms~10ms之间,为了保证按下一次键只做一次响应处理,必须消除按键的抖动影响。
可以采用硬件或软件方案消除抖动影响。
图9-6按键抖动波形图
硬件方案可以通过施密特或双稳态去抖电路(如图9-7)。
软件方案可以延时10ms~20ms后再次判断。
图9-7双稳态去抖电路图
(3)扫描获取闭合键的行、列值;
(4)根据行列值用计算法或查表法得到键值;
(5)判断闭合键是否释放,如没释放则继续等待;
(6)保存闭合键号
3.键盘扫描的方式
键盘扫描的方式有编程扫描方式、定时扫描方式、中断扫描方式三种。
后两种一般是利用单片机来实现。
定时扫描方式是通过设定单片机内部的定时器产生一定时间(例如10ms)的定时,当定时时间到就产生定时器溢出中断,CPU响应中断后在中断服务程序中完成键盘的扫描功能。
该方式的缺点是CPU工作效率仍不够高,因为定时时间到后,即使没有键按下,也要完成一次键盘的扫描。
为进一步提高CPU工作效率,可采用中断扫描工作方式。
即当无键按下时,CPU处理自己的工作,当有键按下时,产生中断请求(需要增加一个与门电路),CPU转去执行键盘扫描中断服务程序,并识别键号。
本实验项目采用编程扫描方式,针对图9-5所示的矩阵式按键结构,其原理如下:
先让第0列处于低电平,其余列处于高电平,检查是否有行线变低,若某一行变为低电平,则第0列与该行的交叉点处有按键按下;若所有的行都为高电平,则表示第0列无键按下;再让第1列处在低电平,其余列处于高电平,依此循环,这种方式称为键盘扫描。
键号=列首键号(0、4、8、12、16、20、24、28)+行号(0、1、2、3)
9.2.2设计要求与设计思路
设计要求:
完成4*8小键盘的识别,并将其中的16个键定义为数字键:
0、1、2…A、B…F(其余的16个键可以定义成功能键),并将按下的这16个键的数字左移动显示在8位数码管上。
设计思路:
要完成按键的识别和显示,除了需要4*8的矩阵键盘电缆外,还需要以下电路模块:
(1)时钟产生电路。
该系统需要三种不同频率的时钟:
系统时钟(它的频率最高,其它的频率的时钟可以由它分频得到)、消除键盘抖动时钟以及键盘列扫描和8位七段数码管显示扫描共用的时钟。
(2)键盘扫描电路。
该电路用来产生扫描键盘8列所需要的顺序变化的序列:
000→001→010→011→100→101→110→111→000…,周而复始。
当扫描信号为000时,经过74LS138译码后扫描的是键盘的第1列,并读取该列四行的状态,若有键按下,则停止列扫描并完成按键编码和移位存储;若该列没有键按下,则继续扫描下一列。
(3)消除抖动电路。
对机械式开关的按键,在按下和释放时会出现图9-6所示的电压波形起伏的抖动现象,在灵敏度高(或扫描频率高)的电路,一次按键的动作可能会被误判成几次按键动作。
为了克服这个问题,需要一个计数器,计数器的位数根据系统的时钟频率高低而定,每当识别到有键按下时,计数器就清零,按键释放后计数器要在系统时钟的作用下计满溢出后,才认为是一次按键动作完成。
实验箱上4*8的键盘的定义如下:
0
1
2
3
MEM
ESC
4
5
6
7
REG
EXC
8
9
A
b
LAST
STEP
C
d
E
F
NEXT
ENTER
CRTL
空
空
空
空
SHIFT
没有
没有
按行的方向依次取6个位置键排成一行就是实验箱上键盘的实际布局,具体如下:
0
1
2
3
MEM
ESC
4
5
6
7
REG
EXC
8
9
A
b
LAST
STEP
C
d
E
F
NEXT
ENTER
CRTL
空
空
空
空
SHIFT
9.2.3 VHDL代码设计
libraryieee;
useieee.std_logic_1164.all;
useieee.std_logic_unsigned.all;
entitykeyboardis
port(
clk,clr:
instd_logic;
columndecode:
outstd_logic_vector(2downto0);
--用于驱动138译码器选取4*8键盘的8条列线的输出端口
inkeyrow:
instd_logic_vector(3downto0);
--读取4*8键盘的4条行线的端口
segout:
outstd_logic_vector(7downto0)
--输出字形端口,用于连接7段led的a,b,c,d,e,f,g段
);
endkeyboard;
architecturedoitofkeyboardis
signalclkdiv_val:
std_logic_vector(1downto0);
signalcolumnscan:
std_logic_vector(2downto0);
signalcounter:
std_logic_vector(7downto0);
signalreg0,reg1,reg2,reg3,reg4,reg5,reg6,reg7:
std_logic_vector(3downto0);
--存放0~F的数字寄存器信号
signalnum_encode:
std_logic_vector(3downto0);
signalrow_col_code:
std_logic_vector(6downto0);
signalzx_reg0,zx_reg1,zx_reg2,zx_reg3,zx_reg4,zx_reg5,zx_reg6,zx_reg7:
std_logic_vector(7downto0);--存放字形码的寄存器信号
signalclk1,test,koff:
std_logic;
componentdecode
port(
ssin:
instd_logic_vector(3downto0);
ssout:
outstd_logic_vector(7downto0)
);
endcomponent;
begin
test<=inkeyrow(3)andinkeyrow
(2)andinkeyrow
(1)andinkeyrow(0);
P1:
process(clr,clk)
begin
if(clr='0')then
clkdiv_val<="00";--分频系数
elsif(clk'eventandclk='1')then
clkdiv_val<=clkdiv_val+1;
endif;
endprocessP1;
clk1<='0'whenclkdiv_val<="01"else--列扫描时钟clk1是clk的4次分频
'1';
P2:
process(clr,clk1,test)
begin
if(clr='0')then
columnscan<="000";--列扫描信号为0,停止扫描键盘,第一位数码管亮
elsif(clk1'eventandclk1='1')then
if(test='0')or(koff='0')then
columnscan<=columnscan;
--有键按下时,columnscan值不变,停止扫描列,同时也停止扫描8位数码管
else
columnscan<=columnscan+1;
--没有键按下时,columnscan加1扫描下一列,同时也动态扫描8位数码管
endif;
endif;
endprocessP2;
columndecode<=columnscan;--信号columnscan送列译码输出端口columndecode
row_col_code<=columnscan&inkeyrow;--将按下键的列和行位置码并置起来
P3:
process(clk,test)
begin
if(clk'eventandclk='0')then
if(row_col_code="0001110")then--对行列位置码进行从0,1,2,...,F的编码
num_encode<=x"0";
elsif(row_col_code="0011110")then
num_encode<=x"1";
elsif(row_col_code="0101110")then
num_encode<=x"2";
elsif(row_col_code="0111110")then
num_encode<=x"3";
elsif(row_col_code="1101110")then
num_encode<=x"4";
elsif(row_col_code="1111110")then
num_encode<=x"5";
elsif(row_col_code="0001101")then
num_encode<=x"6";
elsif(row_col_code="0011101")then
num_encode<=x"7";
elsif(row_col_code="1001101")then
num_encode<=x"8";
elsif(row_col_code="1011101")then
num_encode<=x"9";
elsif(row_col_code="1101101")then
num_encode<=x"a";
elsif(row_col_code="1111101")then
num_encode<=x"b";
elsif(row_col_code="0101011")then
num_encode<=x"c";
elsif(row_col_code="0111011")then
num_encode<=x"d";
elsif(row_col_code="1001011")then
num_encode<=x"e";
elsif(row_col_code="1011011")then
num_encode<=x"f";
elsif(test='0')then
num_encode<=x"f";
endif;
endif;
endprocessP3;
P4:
process(test,clk,clr)--防止按键抖动的进程
begin
if(clr='0')then
counter<="00000000";--清零输入端clr为有效电平0时,信号counter清零
koff<='1';
elsif(clk'eventandclk='1')then
if(test='0')then
counter<="00000000";--有键按下时,信号(或称计数器)counter清零
koff<='0';--按键断开标志,koff=0表示按键没有断开
elsif(counter<"11111110")then--按键释放后,若counter<254则counter加1
counter<=counter+1;
elsif(counter="11111110")then
koff<='1';--counter=254时,koff=1表示按键已经断开或释放。
endif;
endif;
endprocessP4;
P5:
process(koff,clr)
begin
if(clr='0')then--clr=0时寄存器全部清0
reg0<="0000";
reg1<="0000";
reg2<="0000";
reg3<="0000";
reg4<="0000";
reg5<="0000";
reg6<="0000";
reg7<="0000";
elsif(koff'eventandkoff='1')then
reg0<=num_encode;--koff从0变到1,说明有键按下并释放,读取该次按键并左移显示
reg1<=reg0;
reg2<=reg1;
reg3<=reg2;
reg4<=reg3;
reg5<=reg4;
reg6<=reg5;
reg7<=reg6;
endif;
endprocessP5;
--数字寄存器信号regi与字形译码器输入端口相连,字形译码器输出端口与信号zx_regi相连
U1:
decodeportmap(ssin=>reg0,ssout=>zx_reg0);
U2:
decodeportmap(ssin=>reg1,ssout=>zx_reg1);
U3:
decodeportmap(ssin=>reg2,ssout=>zx_reg2);
U4:
decodeportmap(ssin=>reg3,ssout=>zx_reg3);
U5:
decodeportmap(ssin=>reg4,ssout=>zx_reg4);
U6:
decodeportmap(ssin=>reg5,ssout=>zx_reg5);
U7:
decodeportmap(ssin=>reg6,ssout=>zx_reg6);
U8:
decodeportmap(ssin=>reg7,ssout=>zx_reg7);
segout<=zx_reg0whencolumnscan="000"else--字形码寄存器信号送segout端口
zx_reg1whencolumnscan="001"else
zx_reg2whencolumnscan="010"else
zx_reg3whencolumnscan="011"else
zx_reg4whencolumnscan="100"else
zx_reg5whenc