EDA 课程设计报告书
课题名称 4位十进制频率计的设计
姓 名 刘智 学 号 0812201-45 院 系 物理与电信工程系 专 业 电子信息工程 指导教师
周来秀讲师
2011年 6月10日
※※※※※※※※※ ※※
※※ ※
※
※※※※※※※※※
2008级学生
EDA 课程设计
4位十进制频率计的设计
刘智
(湖南城市学院物理与电信工程系电子信息工程专业,湖南益阳,41300)
1设计目的
1)学习掌握频率计的设计方法。
2)掌握动态扫描输出电路的实现方法。
3)学习较复杂的数字系统设计方法。
2设计的主要内容和要求
4位十进制频率计外部接口设计,顶层文件设计,包含4个模块,Tctl,Reg16,scan_led和一个x4cnt10(4个十进制计数器)。
3 整体设计方案
根据频率计的定义和频率测量的基本原理:频率计即是指单位时间1秒内输入脉冲个数并显示出来的电路。因为要显示被测信号的,只要限制计数器的计数过程为一秒则计数器的结果即为被测信号的频率。频率值为:Fx=N,N为一秒内计数器所计脉冲个数。因为是4位十进制频率计计数器是模为9999的十进制加法计数器,可以由4个模为10的十进制计数器级联而成,所以可以显示的频率范围是1-9999HZ。因此,频率计的功能分割成四个模块:计数器,测频控制信号发生器和输出锁存器,。各个模块均用VHDL语言描述。待测信号的转速必须有一个脉宽为1秒的输入信号脉冲计数允许的信号;1秒计数结束后,计数值锁入锁存器的锁存信号,并有为下一测频计数周期作准备的计数器清零信号。这三个信号可以由一个测频控制信号发生器产生。当系统正常工作时,标准信号提供频率为1 Hz的输入信号,经过测频控制信号发生器进行信号变换,产生计数信号,将被测信号当做脉冲信号送入计数器模块,计数模块对输入的脉冲个数进
行计数数结束后,将计数结果送入锁存器中,保证系统可以稳定显示数据,计数结果能够显示在七段数码显示管上。
根据系统设计要求,要实现一个4位十进制数字频率计,则要设计测频控制信号发生器模块tct1、四位十进制加法计数器模块x4cnt10、reg16锁存器模块, 七段译码显示模块scan_led 其原理框图如下图所示。
图3.1 主控制流程图 四位十进制频率计顶层文件源程序如下:
library ieee;
use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity dc is
port(fin,clk_1hz:in std_logic;
ledout:out std_logic_vector(27 downto 0));
end dc;
architecture one of dc is
component Tct1 ---Tct1元件声明 port( clk: in std_logic;
en,rst,load :out std_logic); end component;
component x4cnt10 ----x4cnt10元件声明 port(clk,rst,en: in std_logic;
q0,q1,q2,q3 :out std_logic_vector(3 downto 0);
cout:out std_logic);
end component;
component reg16 ---reg16元件声明
port(load: in std_logic;
di : in std_logic_vector(15 downto 0);
dout : out std_logic_vector(15 downto 0) );
end component;
component scan_led ---scan_led元件声明
port(din: in std_logic_vector(15 downto 0);
sg :out std_logic_vector(6 downto 0);
bt: out std_logic_vector(1 downto 0));
end component;
signal x,z,f:std_logic;
signal h:std_logic_vector(3 downto 0);
signal g0,g1,g2,g3:std_logic_vector(3 downto 0);
signal h0,h1,h2,h3:std_logic_vector(3 downto 0);
signal leds:std_logic_vector(27 downto 0);
begin ---元件例化产生电路,完成设计
u1: Tct1 port map(clk=>clk_1hz, en=>x,rst=>z,load=>f);
u2: x4cnt10 port map(clk=>fin,rst=>z,en=>x,q0=>g0,q1=>g1,q2=>g2,q3=>g3);
u3:reg16 port map(load=>f,di(3 downto 0)=>g0,di(7 downto 4)=>g1,di(11 downto 8)=>g2,di(15 downto 12)=>g3,dout(3 downto 0)=>h0,dout(7 downto
4)=>h1,dout(11 downto 8)=>h2,dout(15 downto 12)=>h3);
u4: scan_led port map(din(3 downto 0)=>h0(3 downto 0),sg(6 downto
0)=>leds(6 downto 0));
u5: scan_led port map(din(7 downto 4)=>h1(3 downto 0),sg(6 downto
0)=>leds(13 downto 7));
u6: scan_led port map(din(11 downto 8)=>h2(3 downto 0),sg(6 downto
0)=>leds(20 downto 14));
u7: scan_led port map(din(15 downto 12)=>h3(3 downto 0),sg(6 downto
0)=>leds(27 downto 21));
ledout<=leds;
end;
图3.2 顶层文件生成4位十进制频率计外部接口图
(1)本设计中重要端口说明
Clk_1hz:给Tctl模块提供1hz的频率输入。
Fin;被测频率输入。
scan_led:给scan_led模块提供扫描输入频率输入。
bt[1..0]:片选信号输出。
Sg[6..0]:译码信号输出。
Cout:进位输出
4 各个模块的设计和功能的具体分析。
(1)x4Cnt10 模块说明
x4Cnt10为含异步清零和同步时钟使能的十进制计数器,采用级联的方法进行计数,计数范围与所用x4cnt10级联个数有关,本次设计采用的是4个cnt10的级联,所有测频范围是0—9999hz。
计数模块x4cnt10的源代码程序:
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity x4cnt10 is
port(clk,rst,ena: in std_logic; --端口定义q0,q1,q2,q3 : buffer std_logic_vector(3 downto 0); --分别定义四个计cout:out std_logic); 数端口,用于4
end x4cnt10; 个cnt10的设计architecture three of x4cnt10 is
signal en1,en2,en3 : std_logic; ---使能信号声明
begin
process(clk,rst,ena,q0) ---控制q0端的十进制计数begin
if rst='1' then q0 <= "0000";
elsif clk'event and clk='1' then
if ena='1' then
if q0="1001" then q0<="0000";else q0<=q0+1;end if;
end if;
end if;
if q0="1001" then en1 <='1'; else en1 <='0';end if;
end process;
process(clk,rst,en1,q1) ---控制q1端的十进制计数begin
if rst='1' then q1 <= "0000";
elsif clk'event and clk='1' then
if en1='1' then
if q1="1001" then q1<="0000";else q1<=q1+1;end if;
end if;
end if;
if q1="1001" then en2 <='1'; else en2 <='0';end if;
end process;
process(clk,rst,en2,q2) --控制q2端的十进制计数begin
if rst='1' then q2 <= "0000";
elsif clk'event and clk='1' then
if en2='1' and en1='1'then
if q2="1001" then q2<="0000";else q2<=q2+1;end if;
end if;
end if;
if q2="1001" then en3 <='1'; else en3 <='0';end if;
end process;
process(clk,rst,en3,q3) ---控制q3端的十进制计数begin
if rst='1' then q3 <= "0000";
elsif clk'event and clk='1' then
if en3='1' and en2='1' and en1='1'then
if q3="1001" then q3<="0000";else q3<=q3+1;end if;
end if;
end if;
if q3="1001" then cout<='1'; else cout<='0';end if;
end process;
end three;
library ieee; ---将设计元x4cnt10的声明装入my_pkg程序包中use ieee.std_logic_1164.all;
package my_pkg1 is
component x4cnt10
port(clk,rst,ena: in std_logic;
q0,q1,q2,q3 : buffer std_logic_vector(3 downto 0);
cout:out std_logic);
end component;
end;
图 4.1 x4Cnt10仿真波形图
(2) Tctl 模块说明
根据频率的定义和测量的基本原理,测定信号的频率必须有一个脉宽为1秒的对输入信号脉冲计数允许的信号;1秒计数结束后,计数值锁入锁存器信号和为下一测频计数周期做准备的计数器清零信号。这三个信号可以由一个测频控制信号发生器Tctl产生,其设计要求是Tctl的计数使能信号en能产生一个一秒脉宽的周期信号,并且对频率计的每一计数器cnt10的en使能进行同步控制,当en高定平时,允许继续:低电平时停止计数,并保持其所计数的脉冲数,在停止计数期间,首先需要一个锁存信号load的上升沿将计数器前一秒钟的计数值锁存进各锁存器reg16中,并由外部的译码器译出并显示计数值,锁存信号之后,必须有一个清零信号rst对计数器进行清零,为下一秒的计数操作做准备。
控制模块Tctl源代码程序;
Library ieee;
Use ieee.std_logic_1164.all;
Use ieee.std_logic_unsigned.all;
Entity Tct1 is
Port(clk:in std_logic; ---端口定义
ena,rst,load:out std_logic);
End Tct1;
Architecture one of Tct1 is
Signal divclk :std_logic; ---divclk信号声明
Begin
Process(clk)
Begin
if clk'event and clk='1' then divclk<=not divclk; ---clk上升沿时,divclk取反end if ;
End process;
Process(clk,divclk)
Begin
If clk='0' and divclk='0' then ---clk和divclk同时为0时,rst置1,否则rst清0 rst<='1';
Else rst<='0';
End if;
End process;
Load<=not divclk; ----将divclk取反赋值到load端,将divclk的值赋给ena ena<=divclk;
end one;
图4.2 频率计测频控制器Tctl侧可控时序图图中clk的频率为1hz,en的高电平程序时间为1秒,此时计数器开始工作,当en低电平时停止计数,并将计数值经过load锁存的reg16中最后通过scan_led 译码器显示输入,en的计时长度为一秒,即en的周期为2秒,计数器在en高电平计数完成即en从高电平跳跃到低电平时将计数结果锁存在reg16中,rst在满足clk和en均为低电平的时候对已锁存入锁存器的计数信号进行清零工作,为下一个计数周期作准备。
(3)锁存器reg16模块的说明
设置锁存器的目的是使显示的数据稳定,不会由于周期性的清零信号而不断闪烁,每次显示数据位上一计数周期锁存的计数值。
Reg16锁存器源代码程序:
Library ieee;
Use ieee.std_logic_1164.all;
Entity reg16 is
Port(load:in std_logic; ----端口定义
din:in std_logic_vector(15 downto 0);
dout:out std_logic_vector(15 downto 0));
End reg16;
Architecture one of reg16 is
Begin
Process(load,din)
Begin
if(load'event and load='1') then ---load为上升沿时,把din值锁存到dout中dout<=din;
end if;
end process;
End one;
图4.3 reg16锁存器仿真波形图
(4)scan_led 模块说明
动态显示是把所有的led管的输入信号连在一起,每次向LED写数据时,通过片选信号选通其中一个Led管并把数据写入,因此每一个时刻只有一个Led是亮的。为了能够程序看到led上面的显示内容,必须对led管进行扫描,即一次循环点亮各个led管,利用人眼的视觉暂留,加上发光器件的余晖效益,在一定的扫描频率下,人眼就会看到多个led管一起点亮。扫描的大小必须合适才能达到很好的效果。如果扫描频率太低,就会产生闪烁;而扫描频率太高会造成Led 的频率开启和关断,增加led的功耗。通常扫描频率选择50Hz比较合适。
本次设计为4为led动态扫描输出,clkwie扫描时钟,考虑到cnt8的分频作用,此时设计采用的频率为200hz:sg[6..0]为当前正在显示的led地址的已译码数据;bt[1..0]为输出的片选信号,决定某led在某时某刻显示数据:din[15..0]为4为一组的bcd码,共4位供显示输出的数据输入,scan_led的内部以下三个进程:计数模块扫描时钟发生器,多路选通器模块和译码模块。
led动态扫描输出scan_led源代码程序;
Library ieee;
Use ieee.std_logic_1164.all;
Use ieee.std_logic_unsigned.all;
Entity scan_led is
Port(clk:in std_logic; ---端口定义
din:in std_logic_vector(15 downto 0); --16位的输入端口
sg:out std_logic_vector(6 downto 0 ); --7位的输出端口(译码地址)bt:out std_logic_vector(1 downto 0)); -- 2位的输出端口(片选信号)end;
Architecture one of scan_led is
Signal cnt8 :std_logic_vector(1 downto 0); ---信号声明
Signal q:std_logic_vector(3 downto 0);
Begin
p1: Process(cnt8) ---多路选通模块
begin
case cnt8 is ---通过cnt8的值来选择片选信号bt when "00" =>bt<="00";q<=din(3 downto 0); 决定某led在某时某刻的
when "01" =>bt<="01";q<=din(7 downto 4); 显示数据
when "10" =>bt<="10";q<=din(11 downto 8);
when "11" =>bt<="11";q<=din(15 downto 12);
when others =>null;
end case;
end process p1;
p2: process(clk) ---计数模块扫描时钟发生器
begin ---时钟信号clk有效上升沿时,cnt8开始计数if clk'event and clk='1' then cnt8<=cnt8+1;
end if;
end process p2;
p3: process(q) ---译码模块
begin
case q(3 downto 0) is ---通过q的值来选择sg的led地址的已译码数据when "0000" =>sg<="0111111";
when "0001" =>sg<="0000110";
when "0010" =>sg<="1011011";
when "0011" =>sg<="1001111";
when "0100" =>sg<="1100110";
when "0101" =>sg<="1101101";
when "0110" =>sg<="1111101";
when "0111" =>sg<="0000111";
when "1000" =>sg<="1111111";
when "1001" =>sg<="1101111";
when others =>null;
end case;
end process p3;
end one;
图4.4 动态扫描仿真波形图
Clk_1hz输入周期为1hz;
Fin为测量频率,频率范围是100-9999hz,此次波形仿真设计时间为1khz;
Clk为扫描时钟,周期为200hz;
Sg为输出led七段显示译码结果,bt为输出片选信号,由波形仿真图可以看出片选0(00)时led显示为0,片选1(01)时led显示为0,片选2(10)时led显示为0,片选3(11)时led显示为1;即此时频率计计数频率为1000hz,与实际输入频率相同。
Clk_1hz的1hz输入频率通过DE2内部clock_50的50M晶振分频来实现。输入频率信号和扫描频率也由50M晶振分频得到,又由于仿真设计中采用的是一个led现实动态数据,所以下载到DE2板上是还要进行动态显示电路的设计。
(5)分频的主要源程序:
Library ieee;
Use ieee.std_logic_1164.all;
Use ieee.std_logic_unsigned.all;
Entity count is
Port(clk50:in std_logic; ---端口定义
qlk:out std_logic);
End count;
Architecture one of count is
Begin
HZ:process(clk50)
Variable cout:integer:=0; ---设置变量cout,且其初值为0
Begin
If clk50'event and clk50='1' then ---时钟信号clk50上升沿时,cout值就增1 cout:=cout+1;
If cout<=24999 then qlk<='0'; ---cout<=249999时,qlk置0
Else if cout<=49999 then qlk<='1'; ---24999 Else cout:=0; ---cout>49999时,cout值清零 End if; End if; End if; End process HZ; End one; (可以通过改变不同的cout值来进行不同的分频) 图4.5 分频仿真波形图 (6)动态显示源程序; Library ieee; Use ieee.std_logic_1164.all; Use ieee.std_logic_unsigned.all; Entity export is Port(q2:in std_logic; ---端口定义 bt:in std_logic_vector(1 downto 0); sg:in std_logic_vector(6 downto 0 ); led0,led1,led2,led3:out std_logic_vector(6 downto 0)); End; Architecture one of export is Begin Process(q2) Begin If q2'event and q2='1' then ---当q2为上升沿时,通过bt的值来选择sg,Case bt is ---把sg的值赋给相应的led When "00"=>led0<=sg(6 downto 0); When "01"=>led1<=sg(6 downto 0); When "10"=>led2<=sg(6 downto 0); When "11"=>led3<=sg(6 downto 0); When others=>null; End case; end if; End process; End one; 图4.6 动态显示仿真波形 编译仿真完成后,定义输入,输出脚(cout输出定义为SW[1],七段数码管显示为HEX[0], HEX1], HEX[2], HEX[3],输入频率为clock_50),再次编译将定义引脚锁存到顶层文件后下载到DE2板上实现频率计的测试。设计下载过程中输入频率也可以由DE2板上50MHz,27Mhz经过各种分频得到最后的输出计数频率,或者定义EXT_CLOCK由外部时钟来定义输出也可以。但是在此设计中没有外接电路,所以只能通过分频来得到相应的频率计数结果。 5 总结 通过这次的eda设计使我更加的了解了vhdl语言的应用,尤其是在世界最后的静态输出的过程中,由于书上设计的是由单个七段数码管进行动态显示,但是这种情况下不利于读取数据,所以应该设计成由四个七段数码管静态输出结果的方式,但书上没有源程序可以参考,所以只能通过自己一边查资料一边一步一步的尝试,在编程程序过程中出现错误提示后通过一个个错误的分析和解决,最后终于圆满的完成了本次设计。 参考文献 [1] 江国强.EDA技术与应用(第3版)2010,4(11):10-22 [2] 潘松,黄继业.EDA技术实用教程(第二版).科学出版社,1998. [3] 黄正瑾.系统编程技术及其应用.南京:东南大学出版社,1997. [4] 宋万杰,罗丰,吴顺军.CPLD技术及其应用.西安:西安电子科技大学出版社,1999. [5] Yalamanchili S.,VHDL Starter's Guide,Englewood Cliffs,NJ:Prentice Hall,1998. [6] 徐志军王金明.EDA技术与PLD设计.人民邮电出版社. [7] 姜雪松,张海风可编程逻辑器件EDA应用开发技术. 机械工业出版社. [8] 康华光.电子技术基础(数字部分).高等教育出版社.