作者:Saint
掘金:https://juejin.im/user/5aa1f89b6fb9a028bb18966a
微博:https://weibo.com/5458277467/profile?topnav=1&wvr=6&is_all=1
GitHub:github.com/saint-000
CSDN: https://me.csdn.net/qq_40531974
一、实验原理:
1.乐曲机理:
送别:简谱
See you again:乐谱
基频:f0=2MHZ 分频系数:N=f0/f 计数器初值=4095-N
二、实验目的:
1.学习VHDL基本单元电路的设计应用,进一步掌握EDA的多层次设计方法。
2.学习利用分频器设计硬件乐曲演奏电路。
3.利用可编程逻辑器件FPGA,设计乐曲硬件电路,可自动演奏乐曲。
4.理解其工作原理、设计思路及实现方法。
三、实验内容:
1.学习设计实际功能对应的模块电路,并仿真调试。
2.完成硬件乐曲演奏电路的框架设计及各模块之间时钟信号的规划,实现一套完整的具有播放乐曲功能的电路系统。
3.熟悉调试方法和对应FPGA上调试相应功能的技巧。
四、实验器材(设备、元器件):
Spartan 3XC3S200 Active-VHDL ISE
五、实验步骤:
1、组成乐曲的每个音符的发音频率值及其持续的时间是乐曲能演奏所需的两个基本要素:
(1)音符的频率可以由预置分频器获得,分频器的输出频率将决定每一个音符的音调。
(2)音符的持续时间需根据乐曲的速度和每个音符的节拍数来确定,模块num_control (可预置数数控分频器)的功能首先为clk_div(预分频器)提供决定所发音符的分频预置数,而此数在num_control 输入口停留时间即为此音符的节拍值,每一音符的停留时间由音乐节拍和乐曲发生器模块的clk的输入频率决定。
clk_div(预分频器)代码如下:
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.STD_LOGIC_unsigned.all;
entity clk_div is --预分频器
port(
clk : in STD_LOGIC;
clr : in STD_LOGIC;
clk_four : out STD_LOGIC;
clk_2m : out STD_LOGIC
);
end clk_div;
--}} End of automatically maintained section
architecture rtl of clk_div is
signal clk_div:std_logic_vector(23 downto 0);
begin
process(clk,clr)
begin
if clr='0' then
clk_div<="000000000000000000000000";
elsif clk'event and clk='1' then
if clk_div="111111111111111111111111" then
clk_div<="000000000000000000000000";
else
clk_div<=clk_div+1;
end if;
end if;
end process;
with clk_div(22 downto 0) select --将开发板上时钟信号分成4Hz的信号
clk_four<='1' when "10000000000000000000000",
'0' when others;
with clk_div(3 downto 0) select --将开发板上时钟信号分成2MHz的信号
clk_2m<='1' when "1000",
'0' when others;
-- enter your statements here --
end rtl;
num_control (可预置数数控分频器),代码如下:
library IEEE;
use IEEE.STD_LOGIC_1164.all;
entity num_control is
port(
clr : in STD_LOGIC;
clk : in STD_LOGIC;
tone : in INTEGER range 0 to 16#fff#;
spk : out BIT
);
end num_control;
--}} End of automatically maintained section
architecture yang of num_control is
signal spk_temp:bit;
begin
process(clk,clr)
variable count: integer range 0 to 16#FFF#;
begin
if clr='0' then
count:=0;
spk_temp<='0';
elsif clk'event and clk='1' then
if count=16#FFF# then --如果加到最大预置数
count:= tone;
spk_temp<='1'; --就返回到预置数重新计数
else
count:=count+1; --否则一直加1(从预置数开始)
spk_temp<='0';
end if;
end if;
end process;
process(spk_temp,clr) --抑制偶次谐波分量,使输出为占空比百分之五十的方波
variable count2:bit;
begin
if clr='0' then
count2:='0';
spk<='0';
elsif spk_temp'event and spk_temp='1' then
count2:= not count2;
if count2='1' then
spk<='1';
else
spk<='0';
end if;
end if;
end process;
-- enter your statements here --
end yang;
2、乐曲发生器(music)代码如下:
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.std_logic_unsigned.all;
entity music is
port(
clr : in STD_LOGIC;
clk : in STD_LOGIC;
sel : in std_logic ;
tone_index : out INTEGER range 0 to 21;
led : out std_logic_vector (7 downto 0)
);
end music;
--}} End of automatically maintained section
architecture yuequ of music is
signal song_bie:integer range 0 to 102;
signal see_you_again : integer range 0 to 144;
signal box,box2 : INTEGER range 0 to 21;
signal led_temp1,led_temp2:std_logic_vector (7 downto 0);
begin
process(clk,clr)
begin
if clr='0' then
song_bie<=0;
see_you_again<=0;
elsif clk'event and clk='1' then
if song_bie=102 then
song_bie<=0;
else
song_bie<=song_bie+1;
end if;
if see_you_again=144 then
see_you_again<=0;
else
see_you_again<=see_you_again+1;
end if ;
end if;
end process;
process(song_bie) --歌曲《送别》
begin
case song_bie is
when 0=>box<=11;led_temp1<="00011111"; --中音5
when 1=>box<=11;led_temp1<="00011111";
when 2=>box<=11;led_temp1<="00011111";
when 3=>box<=11;led_temp1<="00011111";
when 4=>box<=9;led_temp1<="00000111"; --中音3
when 5=>box<=9;led_temp1<="00000111";
when 6=>box<=11;led_temp1<="00011111"; --中音5
when 7=>box<=11;led_temp1<="00011111";
when 8=>box<=14;led_temp1<="11111111"; --高音1
when 9=>box<=14;led_temp1<="11111111";
when 10=>box<=14;led_temp1<="11111111";
when 11=>box<=14;led_temp1<="11111111";
when 12=>box<=14;led_temp1<="11111111";
when 13=>box<=21;led_temp1<="00000000"; --休止符
when 14=>box<=12;led_temp1<="00111111"; --中音6
when 15=>box<=12;led_temp1<="00111111";
when 16=>box<=12;led_temp1<="00111111";
when 17=>box<=12;led_temp1<="00111111";
when 18=>box<=14;led_temp1<="11111111"; --高音1
when 19=>box<=14;led_temp1<="11111111";
when 20=>box<=12;led_temp1<="00111111"; --中音6
when 21=>box<=12;led_temp1<="00111111";
when 22=>box<=11;led_temp1<="00011111"; --中音5
when 23=>box<=11;led_temp1<="00011111";
when 24=>box<=11;led_temp1<="00011111";
when 25=>box<=11;led_temp1<="00011111";
when 26=>box<=11;led_temp1<="00011111";
when 27=>box<=21;led_temp1<="00000000"; --休止符
when 28=>box<=11;led_temp1<="00011111"; --中音5
when 29=>box<=11;led_temp1<="00011111";
when 30=>box<=11;led_temp1<="00011111";
when 31=>box<=11;led_temp1<="00011111";
when 32=>box<=7;led_temp1<="00000001"; --中音1
when 33=>box<=7;led_temp1<="00000001";
when 34=>box<=8;led_temp1<="00000011"; --中音2
when 35=>box<=8;led_temp1<="00000011";
when 36=>box<=9;led_temp1<="00000111"; --中音3
when 37=>box<=9;led_temp1<="00000111";
when 38=>box<=9;led_temp1<="00000111";
when 39=>box<=9;led_temp1<="00000111";
when 40=>box<=8;led_temp1<="00000011"; --中音2
when 41=>box<=8;led_temp1<="00000011";
when 42=>box<=7;led_temp1<="00000001"; --中音1
when 43=>box<=7;led_temp1<="00000001";
when 44=>box<=8;led_temp1<="00000011"; --中音2
when 45=>box<=8;led_temp1<="00000011";
when 46=>box<=8;led_temp1<="00000011";
when 47=>box<=8;led_temp1<="00000011";
when 48=>box<=8;led_temp1<="00000011";
when 49=>box<=8;led_temp1<="00000011";
when 50=>box<=21;led_temp1<="00000000";--休止符
when 51=>box<=11;led_temp1<="00011111";--中音5
when 52=>box<=11;led_temp1<="00011111";
when 53=>box<=11;led_temp1<="00011111";
when 54=>box<=11;led_temp1<="00011111";
when 55=>box<=9;led_temp1<="00000111"; --中音3
when 56=>box<=9;led_temp1<="00000111";
when 57=>box<=11;led_temp1<="00011111"; --中音5
when 58=>box<=11;led_temp1<="00011111";
when 59=>box<=14;led_temp1<="11111111"; --高音1
when 60=>box<=14;led_temp1<="11111111";
when 61=>box<=14;led_temp1<="11111111";
when 62=>box<=14;led_temp1<="11111111";
when 63=>box<=14;led_temp1<="11111111";
when 64=>box<=14;led_temp1<="11111111";
when 65=>box<=13;led_temp1<="01111111"; --中音7
when 66=>box<=13;led_temp1<="01111111";
when 67=>box<=12;led_temp1<="00111111"; --中音6
when 68=>box<=12;led_temp1<="00111111";
when 69=>box<=12;led_temp1<="00111111";
when 70=>box<=12;led_temp1<="00111111";
when 71=>box<=14;led_temp1<="11111111"; --高音1
when 72=>box<=14;led_temp1<="11111111";
when 73=>box<=14;led_temp1<="11111111";
when 74=>box<=14;led_temp1<="11111111";
when 75=>box<=11;led_temp1<="00011111"; --中音5
when 76=>box<=11;led_temp1<="00011111";
when 77=>box<=11;led_temp1<="00011111";
when 78=>box<=11;led_temp1<="00011111";
when 79=>box<=11;led_temp1<="00011111";
when 80=>box<=21;led_temp1<="00000000";--休止符
when 81=>box<=11;led_temp1<="00011111"; --中音5
when 82=>box<=11;led_temp1<="00011111";
when 83=>box<=11;led_temp1<="00011111";
when 84=>box<=11;led_temp1<="00011111";
when 85=>box<=8;led_temp1<="00000011"; --中音2
when 86=>box<=8;led_temp1<="00000011";
when 87=>box<=9;led_temp1<="00000111"; --中音3
when 88=>box<=9;led_temp1<="00000111";
when 89=>box<=10;led_temp1<="00001111"; --中音4
when 90=>box<=10;led_temp1<="00001111";
when 91=>box<=10;led_temp1<="00001111";
when 92=>box<=10;led_temp1<="00001111";
when 93=>box<=10;led_temp1<="00001111";
when 94=>box<=10;led_temp1<="00001111";
when 95=>box<=6;led_temp1<="00000001"; --低音7
when 96=>box<=6;led_temp1<="00000001";
when 97=>box<=7;led_temp1<="00000001"; --中音1
when 98=>box<=7;led_temp1<="00000001";
when 99=>box<=7;led_temp1<="00000001";
when 100=>box<=7;led_temp1<="00000001";
when 101=>box<=7;led_temp1<="00000001";
when 102=>box<=7;led_temp1<="00000001";
when others=>null;
end case;
end process;
process (see_you_again)
begin
case see_you_again is
when 0=>box2<=7;led_temp2<="00000001" ; --中音1
when 1=>box2<=7;led_temp2<="00000001";
when 2=>box2<=9;led_temp2<="00000111";--3
when 3=>box2<=9;led_temp2<="00000111";
when 4=>box2<=11;led_temp2<="00011111";--5
when 5=>box2<=11;led_temp2<="00011111";
when 6=>box2<=12;led_temp2<="00111111";--6
when 7=>box2<=12;led_temp2<="00111111";
when 8=>box2<=12;led_temp2<="00111111";
when 9=>box2<=12;led_temp2<="00111111";
when 10=>box2<=12;led_temp2<="00111111";
when 11=>box2<=12;led_temp2<="00111111";
when 12=>box2<=11;led_temp2<="00011111";--5
when 13=>box2<=11;led_temp2<="00011111";
when 14=>box2<=11;led_temp2<="00011111";
when 15=>box2<=11;led_temp2<="00011111";
when 16=>box2<=11;led_temp2<="00011111";
when 17=>box2<=11;led_temp2<="00011111";
when 18=>box2<=21;led_temp2<="00000000"; --休止符
when 19=>box2<=21;led_temp2<="00000000";
when 20=>box2<=21;led_temp2<="00000000";
when 21=>box2<=7;led_temp2<="00000001"; --1
when 22=>box2<=8;led_temp2<="00000011"; --2
when 23=>box2<=8;led_temp2<="00000011";
when 24=>box2<=8;led_temp2<="00000011"; --2
when 25=>box2<=8;led_temp2<="00000011";
when 26=>box2<=7;led_temp2<="00000001"; --1
when 27=>box2<=7;led_temp2<="00000001";
when 28=>box2<=8;led_temp2<="00000011"; --2
when 29=>box2<=8;led_temp2<="00000011";
when 30=>box2<=21;led_temp2<="00000000"; --休止符
when 31=>box2<=21;led_temp2<="00000000";
when 32=>box2<=21;led_temp2<="00000000";
when 33=>box2<=21;led_temp2<="00000000";
when 34=>box2<=21;led_temp2<="00000000";
when 35=>box2<=9;led_temp2<="00000111";--3
when 36=>box2<=11;led_temp2<="00011111";--5
when 37=>box2<=12;led_temp2<="00111111";--6
when 38=>box2<=12;led_temp2<="00111111";
when 39=>box2<=12;led_temp2<="00111111";
when 40=>box2<=13;led_temp2<="01111111";--7
when 41=>box2<=12;led_temp2<="00111111";--6
when 42=>box2<=12;led_temp2<="00111111";
when 43=>box2<=11;led_temp2<="00011111"; --5
when 44=>box2<=11;led_temp2<="00011111";
when 45=>box2<=9;led_temp2<="00000111";--3
when 46=>box2<=9;led_temp2<="00000111";
when 47=>box2<=8;led_temp2<="00000011";--2
when 48=>box2<=8;led_temp2<="00000011";
when 49=>box2<=8;led_temp2<="00000011";--2
when 50=>box2<=8;led_temp2<="00000011";
when 51=>box2<=7;led_temp2<="00000001";--1
when 52=>box2<=7;led_temp2<="00000001";
when 53=>box2<=8;led_temp2<="00000011"; --2
when 54=>box2<=8;led_temp2<="00000011";
when 55=>box2<=8;led_temp2<="00000011";
when 56=>box2<=8;led_temp2<="00000011";
when 57=>box2<=8;led_temp2<="00000011";
when 58=>box2<=8;led_temp2<="00000011";
when 59=>box2<=7;led_temp2<="00000001"; --1
when 60=>box2<=7;led_temp2<="00000001";
when 61=>box2<=21;led_temp2<="00000000"; --休止符
when 62=>box2<=21;led_temp2<="00000000";
when 63=>box2<=21;led_temp2<="00000000";
when 64=>box2<=21;led_temp2<="00000000";
when 65=>box2<=21;led_temp2<="00000000";
when 66=>box2<=7;led_temp2<="00000001";--1
when 67=>box2<=9;led_temp2<="00000111";--3
when 68=>box2<=11;led_temp2<="00011111";--5
when 69=>box2<=12;led_temp2<="00111111";--6
when 70=>box2<=12;led_temp2<="00111111";
when 71=>box2<=12;led_temp2<="00111111";
when 72=>box2<=12;led_temp2<="00111111";
when 73=>box2<=12;led_temp2<="00111111";
when 74=>box2<=12;led_temp2<="00111111";
when 75=>box2<=11;led_temp2<="00011111";--5
when 76=>box2<=11;led_temp2<="00011111";
when 77=>box2<=11;led_temp2<="00011111";
when 78=>box2<=11;led_temp2<="00011111";
when 79=>box2<=11;led_temp2<="00011111";
when 80=>box2<=11;led_temp2<="00011111";
when 81=>box2<=21;led_temp2<="00000000";--休止符
when 82=>box2<=21;led_temp2<="00000000";
when 83=>box2<=21;led_temp2<="00000000";
when 84=>box2<=7;led_temp2<="00000001";--1
when 85=>box2<=8;led_temp2<="00000011";--2
when 86=>box2<=8;led_temp2<="00000011";
when 87=>box2<=8;led_temp2<="00000011";
when 88=>box2<=8;led_temp2<="00000011";
when 89=>box2<=7;led_temp2<="00000001";--1
when 90=>box2<=7;led_temp2<="00000001";
when 91=>box2<=8;led_temp2<="00000011";--2
when 92=>box2<=8;led_temp2<="00000011";
when 93=>box2<=21;led_temp2<="00000000";--休止符
when 94=>box2<=21;led_temp2<="00000000";
when 95=>box2<=21;led_temp2<="00000000";
when 96=>box2<=21;led_temp2<="00000000";
when 97=>box2<=21;led_temp2<="00000000";
when 98=>box2<=21;led_temp2<="00000000";
when 99=>box2<=9;led_temp2<="00000111";--3
when 100=>box2<=11;led_temp2<="00011111";--5
when 101=>box2<=12;led_temp2<="00111111";--6
when 102=>box2<=12;led_temp2<="00111111";
when 103=>box2<=14;led_temp2<="11111111";--高音1
when 104=>box2<=14;led_temp2<="11111111";
when 105=>box2<=15;led_temp2<="11111111";--高音2
when 106=>box2<=15;led_temp2<="11111111";
when 107=>box2<=15;led_temp2<="11111111";
when 108=>box2<=16;led_temp2<="11111111";--高音3
when 109=>box2<=15;led_temp2<="11111111";--高音2
when 110=>box2<=15;led_temp2<="11111111";
when 111=>box2<=14;led_temp2<="11111111";--高音1
when 112=>box2<=14;led_temp2<="11111111";
when 113=>box2<=12;led_temp2<="00111111";--6
when 114=>box2<=12;led_temp2<="00111111";
when 115=>box2<=14;led_temp2<="11111111";--高音1
when 116=>box2<=14;led_temp2<="11111111";
when 117=>box2<=15;led_temp2<="11111111";--高音2
when 118=>box2<=15;led_temp2<="11111111";
when 119=>box2<=15;led_temp2<="11111111";--高音2
when 120=>box2<=15;led_temp2<="11111111";
when 121=>box2<=14;led_temp2<="11111111";--高音1
when 122=>box2<=14;led_temp2<="11111111";
when 123=>box2<=14;led_temp2<="11111111";--高音1
when 124=>box2<=14;led_temp2<="11111111";
when 125=>box2<=12;led_temp2<="00111111";--6
when 126=>box2<=12;led_temp2<="00111111";
when 127=>box2<=14;led_temp2<="11111111";--高音1
when 128=>box2<=14;led_temp2<="11111111";
when 129=>box2<=15;led_temp2<="11111111";--高音2
when 130=>box2<=15;led_temp2<="11111111";
when 131=>box2<=16;led_temp2<="11111111";--高音2
when 132=>box2<=16;led_temp2<="11111111";
when 133=>box2<=15;led_temp2<="11111111";--高音1
when 134=>box2<=15;led_temp2<="11111111";
when 135=>box2<=15;led_temp2<="11111111";--高音1
when 136=>box2<=15;led_temp2<="11111111";
when 137=>box2<=21;led_temp2<="00000000";--休止符
when 138=>box2<=21;led_temp2<="00000000";
when 139=>box2<=21;led_temp2<="00000000";
when 140=>box2<=21;led_temp2<="00000000";
when 141=>box2<=21;led_temp2<="00000000";
when 142=>box2<=21;led_temp2<="00000000";
when 143=>box2<=21;led_temp2<="00000000";
when 144=>box2<=21;led_temp2<="00000000";
when others=>null;
end case ;
end process ;
process(sel)
begin
if sel='0' then
tone_index<=box;
led<=led_temp1;
else
tone_index<=box2;
led<=led_temp2;
end if ;
end process;
-- enter your statements here --
end yuequ;
3、pre_num模块生成预置数,代码如下:
library IEEE;
use IEEE.STD_LOGIC_1164.all;
entity pre_num is
port(
clr : in STD_LOGIC;
index : in INTEGER range 0 to 21;
tone : out INTEGER range 0 to 16#FFF# --计数初值
);
end pre_num;
--}} End of automatically maintained section
architecture skfp of pre_num is
begin
process(index,clr)
begin
if clr<='0' then
tone<=273;
else
case index is
when 0=>tone<= 273; --低音1
when 1=>tone<= 690; --低音2
when 2=>tone<= 1061; --低音3
when 3=>tone<= 1232; --低音4
when 4=>tone<= 1544; --低音5
when 5=>tone<= 1822; --低音6
when 6=>tone<= 2070; --低音7
when 7=>tone<= 2184; --中音1
when 8=>tone<= 2392; --中音2
when 9=>tone<= 2587; --中音3
when 10=>tone<= 2663; --中音4
when 11=>tone<= 2819; --中音5
when 12=>tone<= 2959; --中音6
when 13=>tone<= 3083; --中音7
when 14=>tone<= 3139; --高音1
when 15=>tone<= 3244; --高音2
when 16=>tone<= 3337; --高音3
when 17=>tone<= 3379; --高音4
when 18=>tone<= 3457; --高音5
when 19=>tone<= 3527; --高音6
when 20=>tone<= 3589; --高音7
when 21=>tone<=4095; --休止符
when others =>null;
end case;
end if;
end process;
end skfp;
4、顶层代码如下:
library IEEE;
use IEEE.STD_LOGIC_1164.all;
entity music_total is
port(
clk : in STD_LOGIC;
clr : in STD_LOGIC;
SEL : in std_logic;
spkout : out BIT;
LED : out std_logic_vector (7 downto 0)
);
end music_total;
--}} End of automatically maintained section
architecture total of music_total is
component clk_div--预分频器
port(
clk : in STD_LOGIC;
clr: in STD_LOGIC;
clk_four:out STD_LOGIC;
clk_2m:out STD_LOGIC
);
end component;
---------------------------------------------------
component music
port(
clr:in STD_LOGIC;
clk : in STD_LOGIC;
sel : in std_logic;
tone_index : out integer range 0 to 21;
led : out std_logic_vector(7 downto 0)
);
end component;
--------------------------------------------------
component pre_num
port(
clr: in STD_LOGIC;
index : in integer range 0 to 21;
tone : out integer range 0 to 16#FFF#
);
end component;
-----------------------------------------
component num_control
port(
clr:in STD_LOGIC;
clk : in STD_LOGIC;
tone : in integer range 0 to 16#FFF#;
spk : out bit
);
end component;
---------------------------------------------
signal clk_four1:STD_LOGIC;
signal clk_2m1:STD_LOGIC;
signal tone_index1: integer range 0 to 21;
signal tone1:integer range 0 to 16#FFF#;
begin
com1:clk_div
port map(clk,clr,clk_four1,clk_2m1);
com2: music
port map(clr,clk_four1,SEL,tone_index1,LED);
com3:pre_num
port map(clr,tone_index1,tone1);
com4: num_control
port map(clr,clk_2m1,tone1,spkout);
-- enter your statements here --
end total;
六、实验数据及结果分析:
(1)特别模块仿真分析
pre_num模块波形仿真结果如下:
(2)实验截图:
七、总结及心得体会:
回顾此次课程设计,从书籍,网络不断的寻找到设计电路,从拿到题目到完成整个设计,从理论到实践,可以学到很多很多的的东西。对课本知识的进一步加深的同时学到了很多在书本上没有学到过的知识。通过这次课程设计使我懂得了理论与实际相结合是很重要的,只有理论知识是远远不够的。把所学的理论知识与实践相结合起来,从理论中得出结论,才能真正提高自己的实际动手能力和独立思考的能力。在设计的过程难免会遇到过各种各样的问题,同时在设计的过程中发现了自己的不足之处,对以前所学过的知识理解得不够深刻,掌握得不够牢固,通过这次课程设计之后,一定把以前所学过的知识重新温故。这次课程设计终于顺利完成了,在设计中遇到的一些问题,最后在老师和助教的帮助下,终于解决了,从中学习到了很多。
八、对本实验过程及方法、手段的改进建议:
本次实验在EDA开发平台上利用VHDL语言设计数控分频器电路,利用数控分频的原理设计音乐硬件演奏电路,除此之外我们还可以用ROM存储音乐数据,以“see you again”乐曲为例,将音乐数据存储到ROM,就达到了以纯硬件的手段来实现乐曲的演奏效果。只要修改ROM所存储的音乐数据,将其换成其他乐曲的音乐数据,再重新定制-ROM,连接到程序中就可以实现其它乐曲的演奏。