自定義小系統的HDL設計與FPGA板級除錯——樂曲演奏電路設計
作者:毛蘢瑋 / 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
自定義小系統的HDL設計與FPGA板級除錯——樂曲演奏電路設計
一、實驗原理:
1.樂曲機理:
送別:簡譜
See you again:樂譜
基頻:f0=2MHZ 分頻係數:N=f0/f 計數器初值=4095-N
2.系統框圖:
二、實驗目的:
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;
5、管腳約束:
六、實驗資料及結果分析:
(1)特別模組模擬分析
pre_num模組波形模擬結果如下:
(2)實驗截圖:
七、總結及心得體會:
回顧此次課程設計,從書籍,網路不斷的尋找到設計電路,從拿到題目到完成整個設計,從理論到實踐,可以學到很多很多的的東西。對課本知識的進一步加深的同時學到了很多在書本上沒有學到過的知識。通過這次課程設計使我懂得了理論與實際相結合是很重要的,只有理論知識是遠遠不夠的。把所學的理論知識與實踐相結合起來,從理論中得出結論,才能真正提高自己的實際動手能力和獨立思考的能力。在設計的過程難免會遇到過各種各樣的問題,同時在設計的過程中發現了自己的不足之處,對以前所學過的知識理解得不夠深刻,掌握得不夠牢固,通過這次課程設計之後,一定把以前所學過的知識重新溫故。這次課程設計終於順利完成了,在設計中遇到的一些問題,最後在老師和助教的幫助下,終於解決了,從中學習到了很多。
八、對本實驗過程及方法、手段的改進建議:
本次實驗在EDA開發平臺上利用VHDL語言設計數控分頻器電路,利用數控分頻的原理設計音樂硬體演奏電路,除此之外我們還可以用ROM儲存音樂資料,以“see you again”樂曲為例,將音樂資料儲存到ROM,就達到了以純硬體的手段來實現樂曲的演奏效果。只要修改ROM所儲存的音樂資料,將其換成其他樂曲的音樂資料,再重新定製-ROM,連線到程式中就可以實現其它樂曲的演奏。