乐曲硬件演奏电路设计.docx
《乐曲硬件演奏电路设计.docx》由会员分享,可在线阅读,更多相关《乐曲硬件演奏电路设计.docx(17页珍藏版)》请在冰豆网上搜索。
乐曲硬件演奏电路设计
江西理工大学应用学院
SOPC/EDA综合课程设计报告
设计题目:
乐曲硬件演奏电路设计
设计者:
学号:
班级:
指导老师:
王忠锋
完成时间:
2012年1月4日
设计报告
综合测试
总评
格式
(10)
内容
(40)
图表
(10)
答辩
(20)
平时
(20)
乐曲硬件演奏电路设计
-------《梁祝》
引言:
20世纪90年代,国际上电子和计算机技术较先进的国家,一直在积极探索新的电子电路设计方法,并在设计方法、工具等方面进行了彻底的变革,取得了巨大成功。
在电子技术设计领域,可编程逻辑器件(如CPLD、FPGA)的应用,已得到广泛的普及,这些器件为数字系统的设计带来了极大的灵活性。
这些器件可以通过软件编程而对其硬件结构和工作方式进行重构,从而使得硬件的设计可以如同软件设计那样方便快捷。
这一切极大地改变了传统的数字系统设计方法、设计过程和设计观念,促进了EDA技术的迅速发展。
EDA技术就是以计算机为工具,设计者在EDA软件平台上,用硬件描述语言VHDL完成设计文件,然后由计算机自动地完成逻辑编译、化简、分割、综合、优化、布局、布线和仿真,直至对于特定目标芯片的适配编译、逻辑映射和编程下载等工作。
EDA技术的出现,极大地提高了电路设计的效率和可操作性,减轻了设计者的劳动强度。
利用EDA工具,电子设计师可以从概念、算法、协议等开始设计电子系统,大量工作可以通过计算机完成,并可以将电子产品从电路设计、性能分析到设计出IC版图或PCB版图的整个过程的计算机上自动处理完成。
现在对EDA的概念或范畴用得很宽。
包括在机械、电子、通信、航空航天、化工、矿产、生物、医学、军事等各个领域,都有EDA的应用。
目前EDA技术已在各大公司、企事业单位和科研教学部门广泛使用。
例如在飞机制造过程中,从设计、性能测试及特性分析直到飞行模拟,都可能涉及到EDA技术。
乐曲演奏原理:
由于组成乐曲的每个音符的频率值(音调)及其持续时间(音长)是乐曲演奏的2个基本数据,因此需要控制到扬声器的激励信号的频率高低和该频率信号持续的时间。
频率高低决定了音调的高低,所有不同频率的信号都是从统一基准频率分频出来的。
随着FPGA集成度的提高,价格下降,EDA设计工具更新换代,功能日益普及与流行,使这种方案的应用越来越多。
如今的数字逻辑设计者面临日益缩短的上市时间的压力,不得不进行上万门的设计,同时设计者不允许以牺牲硅的效率达到保持结构的独特性。
使用现今的EDA软件工具来应付这些问题,并不是一件简单的事情。
FPGA预装了很多已构造好的参数化库单元LPM器件。
通过引入支持LPM的EDA[1]软件工具,设计者可以设计出结构独立而且硅片的使用效率非常高的产品。
一;设计功能
利用数控分频的原理设计音乐硬件演奏电路,能够很好的演奏出“梁祝”的谱子,就达到了以纯硬件的手段来实现乐曲的演奏效果;如果将其换成其他乐曲的音乐数据,就可以演奏其他的歌曲谱子。
二;设计原理
,先介绍一下硬件电路的发声原理。
声音的频谱范围约在几十到几千赫兹,若能利用程序来控制FPGA某个引脚输出一定频率的矩形波,接上扬声器就能发出相应频率的声音。
乐曲中的每一音符对应着一个确定的频率,要想FPGA发出不同音符的音调,实际上只要控制它输出相应音符的频率即可。
乐曲都是由一连串的音符组成,因此按照乐曲的乐谱依次输出这些音符所对应的频,就可以在扬声器上连续地发出各个音符的音调。
而要准确地演奏出一首乐曲,仅仅让扬声器能够发生是不够的,还必须准确地控制乐曲的节奏,即乐曲中每个音符的发生频率及其持续时间是乐曲能够连续演奏的两个关键因素。
,多个不同频率的信号可通过对某个基准频率进行分频器获得。
由于各个音符的频率多为非整数,而分频系数又不能为小数,故必须将计算机得到的分频系数四舍五入取整。
若基准频率过低,则分频系数过小,四舍五入取整后的误差较大。
若基准频率过高,虽然可以减少频率的相对误差,但分频结构将变大。
实际上应该综合考虑这两个方面的因素,在尽量减少误差的前提下,选取合适的基准频率。
本文中选取750KHz的基准频率。
由于现有的高频时钟脉冲信号的频率为12MHz,故需先对其进行16分频,才能获得750KHz的基准频率。
对基准频率分频后的输出信号是一些脉宽极窄的尖脉冲信号(占空比=1/分频系数)。
为提高输出信号的驱动能力,以使扬声器有足够的功率发音,需要再通过一个分频器将原来的分频器的输出脉冲均衡为对称方波(占空比=1/2),但这时的频率将是原来的1/2。
表1中各音符的分频系数就是从750KHz的基准频率二分频得到的375KHz频率基础上计算得到的。
在NOTETABS模块中设置了一个8位二进制计数器(计数最大值为256),作为音符数据ROM的地址发生器。
这个计数器的计数频率选为4Hz,即每一计数值的停留时间为0.25s,恰为当全音符设为1s时,四四拍的4分音符持续时间。
例如,《梁祝》乐曲的第一个音符低音3(1拍),停留的时间需用4个计数时钟节拍,即1s。
相应地,所对应的"低音3"音符分频预置值为1036,其值在SPKS输出端停留了1s。
随着NONETABS模块中的计数器按4Hz的时钟速率作加法计数时,乐曲就开始连续自然地演奏起来了。
三;设计具体内容
【1】源程序
这个计数器的计数频率选为4Hz,即每一计数值的停留时间为0.25秒,恰为当全音符设为1秒,四四拍的4分音符持续时间。
例如,地址发生器在以下的VHDL逻辑描述中,“梁祝”乐曲的第一个音符为“3”,此音在逻辑中停留了4个时钟节拍,即1秒时间。
那么相应随着程序[4]中的计数器按4Hz的时钟频率作加法计数时,即随地址递增时,将从音符数据ROM中将连续取出4个音符“3”通过toneindex[3..0]端口输向分频预置数模块。
这样梁祝乐曲中的音符就一个接一个的通过toneindex[3..0]端口输向分频预置数模块。
程序如下
LIBRARYIEEE;
USEIEEE.STD_LOGIC_1164.ALL;--指明所使用的库中的STD_LOGIC_1164程序包
ENTITYSongerIS--实体
PORT(CLK12MHZ:
INSTD_LOGIC;--音调频率信号
CLK8HZ:
INSTD_LOGIC;--节拍频率信号
key:
INSTD_LOGIC;--选择播放模式
key1:
INSTD_LOGIC;--手动模式下歌曲的选择
CODE1:
OUTSTD_LOGIC_VECTOR(3DOWNTO0);--简谱码输出显示
HIGH1:
OUTSTD_LOGIC;--高8度指示
SPKOUT:
OUTSTD_LOGIC);--声音输出
END;
ARCHITECTUREoneOFSongerIS
COMPONENTNoteTabs--对调用的NoteTabs元件做声明
PORT(clk:
INSTD_LOGIC;
ToneIndex:
OUTSTD_LOGIC_VECTOR(3DOWNTO0);
key:
INSTD_LOGIC;
key1:
INSTD_LOGIC);
ENDCOMPONENT;
COMPONENTToneTaba--对调用的NoneTaba元件做声明
PORT(Index:
INSTD_LOGIC_VECTOR(3DOWNTO0);
CODE:
OUTSTD_LOGIC_VECTOR(3DOWNTO0);
HIGH:
OUTSTD_LOGIC;
Tone:
OUTSTD_LOGIC_VECTOR(10DOWNTO0));
ENDCOMPONENT;
COMPONENTSpeakera--对调用的Speakera元件做声明
PORT(clk:
INSTD_LOGIC;
Tone:
INSTD_LOGIC_VECTOR(10DOWNTO0);
SpkS:
OUTSTD_LOGIC);
ENDCOMPONENT;
COMPONENTmux21a--对调用的mux21a元件做声明
PORT(a,b:
INSTD_LOGIC_VECTOR(7DOWNTO0);
s:
INSTD_LOGIC;
y:
OUTSTD_LOGIC_VECTOR(7DOWNTO0));
ENDCOMPONENT;
COMPONENTmux21a_1--对调用的mux21a_1元件做声明
PORT(a1,b1:
INSTD_LOGIC_VECTOR(7DOWNTO0);
s1:
INSTD_LOGIC;
y1:
OUTSTD_LOGIC_VECTOR(7DOWNTO0));
ENDCOMPONENT;
SIGNALTone:
STD_LOGIC_VECTOR(10DOWNTO0);--定义标准逻辑位矢数据类型
SIGNALToneIndex:
STD_LOGIC_VECTOR(3DOWNTO0);
BEGIN--元件与实体中元件间及端口的连接说明
u1:
NoteTabsPORTMAP(clk=>CLK8HZ,ToneIndex=>ToneIndex,key=>key,key1=>key1);
u2:
ToneTabaPORTMAP(Index=>ToneIndex,Tone=>Tone,CODE=>CODE1,HIGH=>HIGH1);
u3:
SpeakeraPORTMAP(clk=>CLK12MHZ,Tone=>Tone,SpkS=>SPKOUT);
END;
--SPEAKERA模块
LIBRARYIEEE;
LIBRARYIEEE;
USEIEEE.STD_LOGIC_1164.ALL;--使用的库中的STD_LOGIC_1164程序包
USEIEEE.STD_LOGIC_UNSIGNED.ALL;--使用STD_LOGIC_UNSIGNED程序包
ENTITYSpeakeraIS--实体
PORT(clk:
INSTD_LOGIC;
Tone:
INSTD_LOGIC_VECTOR(10DOWNTO0);
SpkS:
OUTSTD_LOGIC);
END;
ARCHITECTUREoneOFSpeakeraIS--结构体
SIGNALPreCLK,FullSpkS:
STD_LOGIC;
BEGIN
DivideCLK:
PROCESS(clk)--以clk为输入信号的进程
VARIABLECount4:
STD_LOGIC_VECTOR(3DOWNTO0);
BEGIN
PreCLK<='0';--将CLK进行16分频,PreCLK为CLK的16分频
IFCount4>11THENPreCLK<='1';Count4:
="0000";
ELSIFclk'EVENTANDclk='1'THENCount4:
=Count4+1;
ENDIF;
ENDPROCESS;--以PreCLK,Tone为输入信号的进程
GenSpkS:
PROCESS(PreCLK,Tone)--11位可预置计数器
VARIABLECount11:
STD_LOGIC_VECTOR(10DOWNTO0);
BEGIN
IFPreCLK'EVENTANDPreCLK='1'THEN
IFCount11=16#7FF#THENCount11:
=Tone;FullSpkS<='1';
ELSECount11:
=Count11+1;FullSpkS<='0';ENDIF;
ENDIF;
ENDPROCESS;--以FullSpkS为输入信号的进程
DelaySpkS:
PROCESS(FullSpkS)--将输出再2分频,展宽脉冲,使扬声器有足够功率发音
VARIABLECount2:
STD_LOGIC;
BEGIN
IFFullSpkS'EVENTANDFullSpkS='1'THENCount2:
=NOTCount2;
IFCount2='1'THENSpkS<='1';
ELSESpkS<='0';ENDIF;
ENDIF;
ENDPROCESS;
END;
在前面的VHDL设计中,我们只能通过程序输出“梁祝”曲子中的13个音符的分频预置数(即计数初值),但是在其它的乐曲中可能会用到另外的那些音符,因此对程序进行修改完善它的功能,使其能输出另外那些音符的分频预置数。
程序修改如下:
LIBRARYIEEE;
USEIEEE.STD_LOGIC_1164.ALL;
ENTITYTONETABAIS
PORT(INDEX:
INSTD_LOGIC_VECTOR(4DOWNTO0);
CODE:
OUTSTD_LOGIC_VECTOR(4DOWNTO0);
HIGH:
OUTSTD_LOGIC;
TONE:
OUTSTD_LOGIC_VECTOR(10DOWNTO0));
END;
ARCHITECTUREONEOFTONETABAIS
BEGIN
SEARCH:
PROCESS(INDEX)
BEGIN
CASEINDEXIS
WHEN"00000"=>TONE<="11111111111";CODE<="00000";HIGH<='0';--2047,休止符
WHEN"00001"=>TONE<="01100000101";CODE<="00001";HIGH<='0';--773,低音1
WHEN"00010"=>TONE<="01110010000";CODE<="00010";HIGH<='0';--912,低音2
WHEN"00011"=>TONE<="10000001100";CODE<="00011";HIGH<='0';--1036,低音3
WHEN"00100"=>TONE<="10000110101";CODE<="00100";HIGH<='0';--1077,低音4
WHEN"00101"=>TONE<="10010101101";CODE<="00101";HIGH<='0';--1197,低音5
WHEN"00110"=>TONE<="10100001010";CODE<="00110";HIGH<='0';--1290,低音6
WHEN"00111"=>TONE<="10101011100";CODE<="00111";HIGH<='0';--1372,低音7
WHEN"01000"=>TONE<="10110000010";CODE<="01000";HIGH<='1';--1410,中音1
WHEN"01001"=>TONE<="10111001000";CODE<="01001";HIGH<='1';--1480,中音2
WHEN"01010"=>TONE<="11000000110";CODE<="01010";HIGH<='1';--1542,中音3
WHEN"01011"=>TONE<="11000101000";CODE<="01011";HIGH<='1';--1576,中音4
WHEN"01100"=>TONE<="11001010110";CODE<="01100";HIGH<='1';--1622,中音5
WHEN"01101"=>TONE<="11010000100";CODE<="01101";HIGH<='1';--1668,中音6
WHEN"01110"=>TONE<="11010110101";CODE<="01110";HIGH<='1';--1717,中音7
WHEN"01111"=>TONE<="11011000000";CODE<="10001";HIGH<='1';--1728,高音1
WHEN"10000"=>TONE<="11011101010";CODE<="10010";HIGH<='1';--1770,高音2
WHEN"10001"=>TONE<="11100000111";CODE<="10011";HIGH<='1';--1799,高音3
WHEN"10010"=>TONE<="11100010110";CODE<="10100";HIGH<='1';--1814,高音4
WHEN"10011"=>TONE<="11100101111";CODE<="10101";HIGH<='1';--1839,高音5
WHEN"10100"=>TONE<="11101000110";CODE<="10110";HIGH<='1';--1862,高音6
WHEN"10101"=>TONE<="11101011010";CODE<="10111";HIGH<='1';--1882,高音7
WHENOTHERS=>NULL;
ENDCASE;
ENDPROCESS;
END;
--NOTETABS模块
LIBRARYIEEE;
USEIEEE.STD_LOGIC_1164.ALL;--使用的库中的STD_LOGIC_1164程序包
USEIEEE.STD_LOGIC_UNSIGNED.ALL;--使用STD_LOGIC_UNSIGNED程序包
ENTITYNoteTabsIS--实体
PORT(clk:
INSTD_LOGIC;
ToneIndex:
OUTSTD_LOGIC_VECTOR(3DOWNTO0);
key:
INSTD_LOGIC;
key1:
INSTD_LOGIC);
END;
ARCHITECTUREoneOFNoteTabsIS--结构体
COMPONENTMUSIC--对音符数据ROM元件调用声明
PORT(address:
INSTD_LOGIC_VECTOR(7DOWNTO0);
inclock:
INSTD_LOGIC;
q:
OUTSTD_LOGIC_VECTOR(3DOWNTO0));
ENDCOMPONENT;
COMPONENTmux21a--对调用的mux21a元件做声明
PORT(a,b:
INSTD_LOGIC_VECTOR(7DOWNTO0);
s:
INSTD_LOGIC;
y:
OUTSTD_LOGIC_VECTOR(7DOWNTO0));
ENDCOMPONENT;
COMPONENTmux21a_1--对调用的mux21a_1元件做声明
PORT(a1,b1:
INSTD_LOGIC_VECTOR(7DOWNTO0);
s1:
INSTD_LOGIC;
y1:
OUTSTD_LOGIC_VECTOR(7DOWNTO0));
ENDCOMPONENT;
SIGNALCounter:
STD_LOGIC_VECTOR(7DOWNTO0);
SIGNALCounter1:
STD_LOGIC_VECTOR(7DOWNTO0);
SIGNALCounter2:
STD_LOGIC_VECTOR(7DOWNTO0);
SIGNALyy:
STD_LOGIC_VECTOR(7DOWNTO0);
SIGNALyy1:
STD_LOGIC_VECTOR(7DOWNTO0);
BEGIN
CNT8:
PROCESS(clk,Counter,Counter1,Counter2)
BEGIN
IFCounter=232THENCounter<="00000000";--自动循环播放
ELSIF(clk'EVENTANDclk='1')
THENCounter<=Counter+1;
ENDIF;
IFCounter1=138THENCounter1<="00000000";--第一首歌曲循环播放
ELSIF(clk'EVENTANDclk='1')
THENCounter1<=Counter1+1;
ENDIF;
IFCounter2=232THENCounter2<="00000000";--第二首歌曲循环播放
ELSIF(clk'EVENTANDclk='1')
THENCounter2<="10010000";
Counter2<=Counter2+1;
ENDIF;
ENDPROCESS;--元件与实体中元件间及端口的连接说明
U1:
mux21aPORTMAP(s=>key,a=>Counter,b=>yy1,y=>yy);
U2:
mux21a_1PORTMAP(s1=>key1,a1=>Counter1,b1=>Counter2,y1=>yy1);
U3:
MUSICPORTMAP(address=>yy,q=>ToneIndex,inclock=>clk);
END;
--mux21a
LIBRARYIEEE;
USEIEEE.STD_LOGIC_1164.ALL;--使用的库中的STD_LOGIC_1164程序包
ENTITYmux21ais--实体
PORT(a,b:
INSTD_LOGIC_VECTOR(7DOWNTO0);
s:
INSTD_LOGIC;
y:
OUTSTD_LOGIC_VECTOR(7DOWNTO0));
ENDENTITYmux21a;
ARCHITECTUREoneOFmux21aIS--结构体
BEGIN
y<=aWHENs='0'ELSEb;
ENDARCHITECTUREone;
--mux21a_1
LIBRARYIEEE;
USEIEEE.STD_LOGIC_1164.ALL;--使用的库中的STD_LOGIC_1164程序包
ENTITYmux21a_1is--实体
PORT(a1,b1:
INSTD_LOGIC_VECTOR(7DOWNTO0);
s1:
INSTD_LOGIC;
y1:
OUTSTD_LOGIC_VECTOR(7DOWNTO0));
ENDENTITYmux21a_1;
ARCHITECTUREoneOFmux21a_1IS--结构体
BEGIN
y1<=a1WHENs1='0'ELSEb1;
ENDARCHITECTUREone;
--音符数据ROM