用EDA设计音乐播放器
发布时间:2024-08-30
发布时间:2024-08-30
乐曲播放器的VHDL设计
1.设计背景
随着电子技术的飞速发展,微电子技术的进步主要表现在大规模集成电路加工技术即半导体工艺技术的发展上,使得表征半导体的工艺水平的线宽已经达到60nm,并在不断地缩小,表现在硅片单位面积上,集成了更多的晶体管。集成电路设计正在不断地向超大规模、极低功耗和超高速的方向发展,电子产品的功能越来越强大,体积越来越小,功耗越来越低。
传统电子电路的设计,首先要对系统进行分析,然后按功能对系统进行划分,接下来就要选择特定芯片,焊接成PCB电路板,最后对成品PCB电路板进行调试。这样的设计没有灵活性可言,搭成的系统需要的芯片种类多且数目大,而且对于电路图的设计和电路板的设计都需要很大的工作量,工作难度也很高。可编程逻辑器件和EDA 技术的使用使设计方法发生了质的变化。把以前“电路设计+硬件搭试+调试焊接”转化为“功能设计+软件模拟+仿真下载”。利用EDA 开发平台,采用可编程逻辑器件CPLD/FPGA 使硬件的功能可通过编程来实现,这种新的基于芯片的设计方法能够使设计者有更多机会充分发挥创造性思维,实现多种复杂数字逻辑系统的功能,将原来由电路板设计完成的工作放到芯片的设计中进行,减少了连线和体积,提高了集成度,降低了干扰,大大减轻了电路设计和PCB设计的工作量和难度,增强了设计的灵活性,有效地提高了工作效率,增加了系统的可靠性和稳定性,提高了技术指标。
这些技术使得各种电子产品迅速的进入了我们的生活,我们处在一个被电子产品深度包围的时代,在一个普通老百姓的家里,衣食住行,每一个产品的诞生都离不开EDA技术,从彩色电视机,到智能冰箱,到全自动洗衣机,电饭煲,到微波炉,电磁炉,电子琴,再到个人随身用的手机,MP3音乐播放器都需要EDA技术提供支持。
本次设计在EDA开发工具Quartus II 9.1平台上,应用语言层次化和模块化的设计方法,通过音符编码的设计思想,预先定制乐曲,在此基础上设计了一个乐曲硬件演奏电路,经过对整体进行模块化分析、编程、综合、仿真及最终下载,完整实现简易音乐器的播放功能。
2.设计方案
2.1音符准备知识
用以记录不同长短的音的进行的符号叫做音符。音符包括三个组成部分,即符头、符干和符尾。以节拍来划分音符的种类,如全音符为四拍;二分音符为二拍等。
在本次设计中采用了铃声《祝你生日快乐》作为要播放的乐曲,它的旋律下图所示
图2-1 《祝你生日快乐》歌曲简谱
为了便于理解,首先介绍一下硬件电路的发声原理。我们知道,声音的频谱范围约在几十到几千赫兹,若能利用程序来控制FPGA某个引脚输出一定频率的矩形波,接上扬声器就能发出相应频率的声音。而乐曲中的每一音符对应着一个确定的频率,因此,要想FPGA发出不用音符的音调,实际上只要控制它输出相应音符的频率即可。乐曲都是由一连串的音符组成,因此按照乐曲的乐谱依次输出这些音符所对应的频率,就可以在扬声器上连续地发出各个音符的音调。而要准确地演奏出一首乐曲,仅仅让扬声器能够发声是不够的,还必须准确地控制乐曲的节奏,即每个音符的持续时间。由此可见,乐曲中每个音符的发音频率及其持续的时间是乐曲能够连续演奏的两个基本要素,获取这两个要素所对应的数值以及通过纯硬件的手段来利用这些数值实现所希望乐曲的演奏效果是本实验的关键。
表2-1为简谱中音名与频率的对应关系。
表2-1 简谱音名与频率的关系
表2-2为频率点及音符与音谱对应定义。
表2-2音符语音谱定义
2.2系统总体设计方案
乐曲演奏电路主要包括音调发生器(ydfsq)模块、手动\自动选择(bmux)模块、音调编码器(ydbmq)模块及数控分频器(skfpq)模块。通过这几个模块的协调工作就可以完成相应的音乐播放功能。顶层框图如图2-2所示。
图2-2 乐曲演奏电路的结构示意
顶层乐曲演奏电路如下图2-3所示。
图 2-3 乐曲演奏电路原理图
2.3硬件电路设计
2.3.1音调发生器模块
在此模块中设置了一个8位二进制计数器(计数最大值为107),这个计数器的计数频率选为4Hz,即每一计数值的停留时间为0.25s,恰好为当全音符设为1s时,四四拍的4分音符的持续时间。例如,ydfsq在以下的VHDL逻辑描述中,《祝你生日快乐》乐曲的第一个音符为“5”,此音在逻辑中停留了4个时钟节拍,即为1s时间,相应地所对应“1”音符分频预置数为1409在skfpq的输入端停留了1s。随着ydfsq中的计数器按4Hz的时钟频率做加法计数时,乐谱逐次被选取,《祝你生日快乐》乐曲就开始自然连续而且循环的演奏起来了。
a.音调发生器模块的VHDL实现 library ieee;
use ieee.std_logic_1164.all; entity ydfsq is port(clk:in std_logic;
clkj:in std_logic_vector(1 downto 0);
rst:in std_logic;--工作时钟/输入信号/复位信号 toneindex:out integer range 0 to 14);--检测结果输出 end ydfsq;
architecture bhv of ydfsq is
signal counter:integer range 0 to 107;--定义8位二进制计数器的计数范围 signal clker:std_logic; signal count4:integer; begin process(clk) begin
if clk'event and clk='1' then if clkj="10" then count4<=count4+1; if count4>0 then
clker<=not clker;count4<=0; end if; elsif clkj="01" then count4<=count4+1; if count4>2 then
clker<=not clker;count4<=0; end if;
else count4<=count4+1; if count4>1 then
clker<=not clker;count4<=0; end if; end if; end if; end process;
process(clker,counter,rst) begin
if rst='1' then counter<=0;
elsif counter=107 then counter<=0;
elsif clker'event and clker = '1' then counter<=counter+1; end if; end process;
process(counter)—进入计数进程 begin
case counter is
when 0 to 3|8 to 11|24 to 27|32 to 35|48 to 51=>toneindex<=5; when 4 to 7|28 to 31|68 to 75=>toneindex<=6; when 16 to 23|64 to 67=>toneindex<=7;
when 12 to 15|40 to 47|60 to 63|92 to 95|100 to 107=>toneindex<=8; when 96 to 99=>toneindex<=9;
when 56 to 59|88 to 91=>toneindex<=10; when 84 to 87=>toneindex<=11; when 52 to 55=>toneindex<=12; when 76 to 83=>toneindex<=0;
when others=>NULL;--计其他数时输出为空 end case; end process; end bhv;
b.音调发生器模块的仿真图
图2-4音调发生器模块的仿真波形图
2.3.2手动\自动选择模块
根据设计的要求,该简易乐曲演奏器能实现手动或自动演奏乐曲的功能。于是,可通过一个按键cs来进行自动与手动的选择,当cs按下时,乐曲自动演奏,其他情况下均为手动演奏乐曲,即可以通过按下其他的按键(与cs相连的按键除外)来控制不同的音符。与此同时,还需要一个复位信号rst来控制该演奏器是否工作,当rst为1时,停止演奏,为0时,可以演奏。以上提到的手动与自动的选择只能在rst为0时有效。 a.手动\自动选择模块的VHDL实现 library ieee;
use ieee.std_logic_1164.all; entity bmux is
port(d1:in integer range 0 to 14; d2:in integer range 0 to 14;
cs,rst:in std_logic;--输入信号和复位信号 q:out integer range 0 to 14);--检测结果输出 end bmux;
architecture bhv of bmux is begin
process(cs,rst) begin
if rst='1'then
q<=0;--检测异步复位信号,复位信号后回到0 else case cs is
when '0'=>q<=d1;--进入状态0后输出d1 when '1'=>q<=d2;--进入状态1后输出d2 when others=>q<=d1;--进入其他状态后输出d1 end case; end if; end process; end bhv;--结束
b.手动\自动选择模块的仿真图
图2-5手动\自动选择模块的仿真波形图
此仿真图中输入cs代表手动\自动演奏的选择端,输入rst代表整体复位端,输入d1、d2分别代表手动和自动要演奏的音符,输出q代表经过选择后,要演奏的或是手动输入或是自动输入的音符。由此仿真图可清楚的看到当rst=1时,不论选择的是手动还是自动,输出都为零,达到了整体复位的功能;当rst=0且cs=1时,自动演奏乐曲,因为q与d2的值相同;当rst=0且cs=0时,手动演奏乐曲,因为这时的q与d1的值相同,从而也达到了演奏方式选择的功能。
2.3.3音调编码器模块
此模块的功能首先是为skfpq提供决定所发音符的分频预置数,而此数在skfpq输入端口停留的时间即为此音符的节拍值。ydbmq模块是乐曲简谱码对应的分频预置数查表电路,其中设置了《祝你生日快乐》乐曲全部音符所对应的分频预置数,共9个,每一音符的停留时间由音乐节拍和音调发生器模块ydfsq的clk输入频率决定,在此为4Hz。这9个值的输出由对应于ydbmq的4位输入值index[3..0]来确定。与此同时,code[3..0]和code1[3..0]这两个输出接2个数码管分别显示乐曲音符的高、中、低音(“0”代表低音,“1”代表中音,“2”代表高音)和乐曲演奏的音符(高、中、低1~7音符)。 a.音调编码器模块的VHDL实现 library ieee;
use ieee.std_logic_1164.all; entity ydbmq is
port(index:in integer range 0 to 14;--输入数据位 code:out integer range 0 to 15; code1:out integer range 0 to 15;
tone:out integer range 0 to 2047);检测结果输出
end ydbmq;
architecture bhv of ydbmq is begin
process(index) begin
case index is
when 0=>tone<=2047;code<=0;code1<=0;--输出0 when 1=>tone<=1091;code<=1;code1<=1; when 2=>tone<=1195;code<=2;code1<=1; when 3=>tone<=1288;code<=3;code1<=1; when 4=>tone<=1331;code<=4;code1<=1; when 5=>tone<=1409;code<=5;code1<=1; when 6=>tone<=1479;code<=6;code1<=1; when 7=>tone<=1541;code<=7;code1<=1;--输出1 when 8=>tone<=1569;code<=1;code1<=2; when 9=>tone<=1621;code<=2;code1<=2; when 10=>tone<=1668;code<=3;code1<=2; when 11=>tone<=1689;code<=4;code1<=2; when 12=>tone<=1728;code<=5;code1<=2; when 13=>tone<=1763;code<=6;code1<=2; when 14=>tone<=1794;code<=7;code1<=2;--输出2 when others=>NULL;--其他情况时输出为空 end case; end process; end bhv;
b.音调编码器模块的仿真图
图2-6音调编码器模块的仿真波形图
在此仿真图中从上到下依次代表输出code、输出code1、输入index和输出tone。通过此仿真图能清楚的看到当音符分别为3、5、8、13、0时,它们所对应的分频预置数tone分别是1288、1409、1569、1763、2047;所对应的音谱code分别是3、5、1、6、0;所对应的高中低音code1又分别是1(中)、1(中)、2(高)、2(高)、0(低)。其中code、code1能分别在两个数码管上显示,而tone则输入到数控分频模块作为分频的依据。于是,由仿真图印证了音调编码模块传送预置数及显示功能。
2.3.4数控分频器模块
该模块的clk端输入一个具有较高频率(本实验为12MHz)的信号,通过skfpq分频后由spkout输出。由于直接从数控分频器中出来的输出信号是脉宽极窄的脉冲信号,为了便于驱动喇叭,需另加一个D触发器均衡其占空比,也即作二分频处理。skfpq对clk输入信号的分频比由11位预置数tone[10..0]决定。spkout的输出频率将决定每一音符的音调,这样分频计数器的预置数tone[10..0]与spkout的输出频率就有了对应关系。例如在ydbmq模块中取tone[10..0]=1479,作为发音符为“6”音的信号频率。 a.数控分频器模块的VHDL实现 library ieee;
use ieee.std_logic_1164.all; entity skfpq is port(clk:in std_logic;
tone:in integer range 0 to 2047;--工作时钟和输入信号 spks:out std_logic);--检测结果输出 end skfpq;
architecture bhv of skfpq is signal preclk:std_logic; signal fullspks:std_logic; begin process(clk)
variable count4:integer range 0 to 14;--给count赋值 begin preclk<='0'; if count4>11 then preclk<='1';count4:=0;
elsif clk'event and clk='1' then
count4:=count4+1; end if;
end process;
process(preclk,tone)
variable count11:integer range 0 to 2047; begin
if preclk'event and preclk='1'then if count11=2047 then
count11:=tone;fullspks<='1'; else
count11:=count11+1;fullspks<='0'; end if; end if;
end process;
process(fullspks)
variable count2:std_logic; begin
if fullspks'event and fullspks='1' then count2:=not count2;
if count2='1'then
spks<='1';--如果计数为1,输出高电平 else
spks<='0';--否则,输出低电平 end if; end if;
end process; end;
b.数控分频器模块的仿真图
图2-7数控分频器模块的仿真波形图
在此仿真图中,输入clk是一个频率较大的时钟信号,输入tone代表着某个音符的分频预置数,输出spks则代表将输入clk先经过12次分频,再经过(预置数终值2048-tone)次分频,最终在进行二分频处理后的信号,而这个信号的频率就是我们需要演奏的音谱的频率,根据频率的不同,从而能通过喇叭听到不同的声音,这就是我们一直想要演奏的《祝你生日快乐》了!
2.4顶层文件
2.4.1顶层音乐演奏器源程序
library ieee;
use ieee.std_logic_1164.all; entity yyyzq is
port(clk1,clk2,cs,rst:in std_logic; d1:in integer range 0 to 14;
clkj:in std_logic_vector(1 downto 0);--工作时钟和输入信号 code,code1:out integer range 0 to 15; spks:out std_logic);--检测结果输出 end yyyzq;
architecture bhv of yyyzq is
signal x,y:integer range 0 to 14;
signal z:integer range 0 to 2047;--给变量赋值 component ydfsq is port(clk:in std_logic; rst:in std_logic;
clkj:in std_logic_vector(1 downto 0);--工作时钟和输入信号 toneindex:out integer range 0 to 14); --检测结果输出 end component; component bmux is
port(d1:in integer range 0 to 14;
d2:in integer range 0 to 14; cs,rst:in std_logic;--输入信号
q:out integer range 0 to 14);--检测结果输出 end component; component ydbmq is
port(index:in integer range 0 to 14;--输入数据位 code:out integer range 0 to 15; code1:out integer range 0 to 15;
tone:out integer range 0 to 2047);--检测结果输出
end component; component skfpq is port(clk:in std_logic;
tone:in integer range 0 to 2047;--工作时钟和输入信号 spks:out std_logic);--检测结果输出 end component; begin
u1:ydfsq port map(clk=>clk1,clkj=>clkj,toneindex=>x,rst=>rst); u2:bmux port map(d1=>d1,d2=>x,cs=>cs,rst=>rst,q=>y);
u3:ydbmq port map(index=>y,code=>code,code1=>code1,tone=>z); u4:skfpq port map(clk=>clk2,tone=>z,spks=>spks);--模块化语句 end bhv;
2.4.2顶层音乐演奏器原理图
经过了各个子模块的分析与验证后,我们只需将各个子模块之间的输入输出端、子模块与整体电路之间的输入输出端进行恰当的硬件连接就能得到正确的顶层乐曲演奏器的电路,结合以上程序,可转换为一个元件符号存盘,元件符号如图2-8所示:
yyyzq
clk1clk2
csrstd1[3..0]clkj[1..0]
code[3..0]code1[3..0]
spks
inst
图2-8乐曲演奏电路的顶层原理图
2.4.2顶层程序仿真波形图
图2-9顶层程序的仿真波形图
首先,介绍一下总体程序中各个引脚的作用及硬件连接情况:输入clk1是一个频率较小的时钟信号,在进行硬件下载时它与实验箱上clock0模块的16Hz频率相连,它决定着乐曲演奏的快慢;输入clk2是一个频率较大的时钟信号,因为要对它进行多次不同的分频,下载时它与实验箱上clock9模块的12MHz频率相连;输入d1[3..0]是当手动演奏时自己确定的音符,下载时它与实验箱上的按键6、5、4、3相连;输入rst和cs分别是整体复位端和演奏方式选择端,它们分别与实验箱上的按键2、1相连;输出code和code1分别用来显示音谱与高中低音,它们分别与实验箱上的数码管1和2相连(提示:选择工作模式为模式5);输出spks要与实验箱上的喇叭相连,用来发出声音。
其次,顶层电路的仿真波形图是否正确依赖于各个子模块的功能是否完善,同时顶层电路的功能实现又验证了各个子模块的正确性,二者相互依存。经过仔细分析,本次设计的顶层电路和各个子模块的波形都正确。
3.结果与结论
本次简易乐曲演奏器的设计经过了整体分析、模块化分析、整体与模块的仿真分析这样三个步骤,硬件实现了整体复位、按键选择演奏方式、循环演奏以及数码管显示乐谱的功能。
系统成功的实现了乐曲《祝你生日快乐》的播放,能自动从头开始循环播放,也可随时起停、整体复位、按键选择播放、循环播放以及发光二极管动态显示播放的音符。经过实际电路测试验证,基本达到了设计要求,但尚有需要改进的地方。随着乐谱的复杂程度加大,如果依然在音调发生器的程序中通过时钟计数来决定音符的输出,会加大编程的繁杂度,这时一个很好的解决办法就是把将要演奏的乐谱存放在人为开辟的存储空间里,这样只需要在相应地址中读出音符即可。
本次大作业设计,让我进一步了解了数控分频器的工作原理和功能作用,并学会了利用LPM模块制作ROM文件的方法,也更深一层地懂得了顶层文件通过比例语句与其他模块结合的好处,同时使我更加熟悉了VHDL语言,对QuartusⅡ软件的操作有了进一步了解,更加熟练掌握了EDA的文本编程设计方法和仿真波形的编辑。
上一篇:鲁教版初中化学前四单元测试题