北邮数电实验电子琴.doc
VHDL硬件描述语言程序设计简易电子琴演奏器目录一、设计课题的任务要求3二、系统设计3三、仿真波形及波形分析3四、源程序3五、功能说明3六、元器件清单及资源利用情况3七、故障及问题分析3八、总结和结论3一、 设计课题的任务要求根本要求:1、 用8×8点阵显示“1 2 3 4 5 6 7七个音符构成的电子琴键盘。其中点阵的第一列用一个LED点亮表示音符“1,第二列用二个LED点亮表示音符“2,依此类推,如下列图所示。1 2 3 4 5 6 7图1 点阵显示的电子琴键盘2、 用BTN1BTN7七个按键模拟电子琴手动演奏时的“1 2 3 4 5 6 7七个音符。当*个按键按下时,数码管显示相应的音符,点阵上与之对应的音符显示列全灭,同时蜂鸣器演奏相应的声音;当按键弹开时数码管显示的音符灭掉,点阵显示恢复,蜂鸣器停顿声音的输出。下列图所示为按下BTN3按键时点阵的显示情况。1 2 3 4 5 6 7图2 按键按下后的点阵显示3、 由拨码开关切换选择高、中、低音,并用数码管进展相应的显示。4、 通过按键BTN0进展复位,控制点阵显示图1的初始状态。提高要求:1、 可通过一个拨码开关进展手动/自动演奏的切换,并与点阵显示配合增加自动演奏乐曲的功能。2、 增加手动演奏的音符存储、播放功能。二、 系统设计1. 设计思路简易电子琴的制作主要是利用不同频率的波来驱动蜂鸣器发出声响。通过输入不同的音符来设置不同的分频系数,使得50MHz的主频分频出不同频率的波。同时,演奏的音符还可以通过数码管和8*8点阵来动态显示。根据系统设计要求,该电子琴设计采用自顶向下的设计方法。整体的功能通过不同的底层模块配合来完成电子琴的功能。底层模块主要包括乐曲自动演奏模块、分频预置值产生模块和数控分频模块,数码管显示模块,8*8点阵显示模块五局部组成。用这种设计思路把整个系统分为了假设干个模块,然后再在顶层文件中将各个模块组合在一起,从而表达出超、高速硬件描述语言VHDL的优势,关于提高要求过一个拨码开关进展手动/自动演奏的切换,并与点阵显示配合增加自动演奏乐曲的功能,我打算将一首曲子的音符储存在自动播放的数组里面,然后通过计数器来顺序播放储存的音符。关于提高要求中的手动演奏的音符存储、播放功能,我打算通过编程实现类似数据构造中队列的模块,来储存手动输入的音符,然后在要播放的时候,队列里面的音符依次出队,从而实现音符储存播放的功能。2. 总体框图数码管显示8*8点阵显示蜂鸣器输出音符输入上下音,自动手动图3简易电子琴总体构造框图是否自动播放查找对应的频率值完毕开场输入按键点阵显示蜂鸣器输出数码管显示选择上下音是否图4简易电子琴逻辑流程图图5简易电子琴VHDL电路原理图3. 分块设计(1) 分频模块div0由于实验电路板的主频是50Mhz,为了数码管和点阵的刷新显示,我们必须将50Mhz的频率进展分频。分频的程序来自电路中心的上面。在这个模块里,我设置分频系数为cnt=2499。从实验结果看,这个分频对数码管和点阵的显示有很好的效果(2) 数码管显示模块shuma我使用了2个数码管,第一个数码管显示17的音符,第二个数码管显示相关的信息,比方高音用H表示,低音用L表示,自动播放用A表示。两个数码管分别刷新,但由于刷新频率太快,人眼不能发觉,以为是两个数码管是同时亮的。在程序中我们通过duan : out std_logic_vector(7 downto 0)和 cat : out std_logic_vector(5 downto 0)来控制数码管的显示。当输入不同的音符和不同的控制信息时,duan和cat向量都有不同的值与之对应。(3) 8*8点阵显示模块dianzhen8*8点阵的显示和数码管的显示运用了同样的原理,在程序中我们通过row : out std_logic_vector(7 downto 0)和col : out std_logic_vector(7 downto 0)这两个向量来控制点阵的显示。当输入不同的音符时,点阵显示相应的形状。(4) 音符产生模块auto。这个模块的功能是,选择的不同模式来产生不同的音符。中选择自动播放模式时,随着计数器count的值增加,即地址值递增时,程序自动读取出事先储存的音符,并把这个音符输出。中选择手动演奏模式时,直接将通过BTN1BTN7输入的向量当做音符输出yin :out std_logic_vector(6 downto 0);。(5) 分频预置值产生模块该模块的功能是通过音符以及上下音选项来查表找到对应的频率值。在程序中设置了全部音符对应的分频预置数。通过判断音符产生模块输出的音符 yin :in std_logic_vector(6 downto 0),以及拨码开关的上下音highlow :in std_logic_vector(1 downto 0)控制键,来查找出该音符的频率值,然后将该频率赋值给tone :out integer range 0 to 2000000);。(6) 数控分频发声模块从实验板上面输入的时钟是50MHz的,必须经过分频后由clk_out输出,驱动蜂鸣器发声。Clk_out的输出频率就对应着音符的音调。分频系数由来自分频预置值模块的tone :out integer range 0 to 2000000)。由于直接从数控分频器中出来的输出信号是脉宽极窄的脉冲式信号。为了利用驱动蜂鸣器,需要再增加一个进程,多波形进展整理,均衡占空比三、 仿真波形及波形分析1. 数码管显示模块仿真波形波形分析:不同的yin,和highlow组合,数码管显示不同的字符。duan0duan7对应着数码管的a段到h段,cat0cat5控制不同的数码管2. 点阵显示模块仿真波形波形分析:不同的yin输入,点阵的col和row会有不同的波形,利用clk_in的上升沿来动态扫描点阵。从而得显示出指定的图形。3. 自动播放模块仿真波形波形分析:当auto 置1,clear置0,yin_out输出储存在程序里面的曲子音符,这时候相当于自动播放。当auto置0,clear置0,yin_out输出从BTN1BTN7读取的手动输入的信号,这时候相当于手动演奏。4. 分频预置值产生模块仿真波形波形分析:输入不同的上下音highlow,和音符yin,输出不同的tone。而tone将作为发声模块的分频预置值四、 源程序1. 分频模块源程序library ieee;use ieee.std_logic_1164.all;use ieee.std_logic_unsigned.all;entity div0 isport(clk_in : in std_logic; -输入时钟 clk_tmp : out std_logic); -输出时钟end;architecture b of div0 is signal clk : std_logic;beginp0:process(clk_in) variablet : integer range 0 to 2499;beginif (clk_in'event and clk_in='1') thenift=2499 then -分频系数为2499cnt:=0;clk<= not clk;elsecnt:=cnt+1; -每个输入时钟上升沿到来时cnt加1end if;end if;end process p0;clk_tmp<=clk;end b ;2. 数码管显示源程序library ieee;use ieee.std_logic_1164.all;use ieee.std_logic_unsigned.all;entity shuma isport(clk_in : in std_logic; -以分频的时钟输入 yin : in std_logic_vector(6 downto 0); -输入音符 highlow :in std_logic_vector(1 downto 0); -输入上下音 auto : in std_logic; -自动播放 auto1 : in std_logic; -自动播放1 duan : out std_logic_vector(7 downto 0); cat : out std_logic_vector(5 downto 0) );end;architecture b of shuma issignal duant : std_logic_vector(7 downto 0);signal catt : std_logic_vector(5 downto 0);beginp1: process(clk_in,yin,highlow,auto,auto1) beginif auto ='1' thencatt<="111101"duant<="00111111" -显示“8表示自动播放elsif auto1 ='1' then catt<="111101"duant<="01111111"-显示“0表示试音elsif auto ='0' then if(clk_in ='0') thencase yin iswhen "0000001" => catt<="111110"duant<="00000110" -显示“1when "0000010" => catt<="111110"duant<="01011011"-显示“2when "0000100" => catt<="111110"duant<="01001111"-显示“3when "0001000" => catt<="111110"duant<="01100110"-显示“4when "0010000" => catt<="111110"duant<="01101101"-显示“5when "0100000" => catt<="111110"duant<="01111101"-显示“6when "1000000" => catt<="111110"duant<="00000111"-显示“7when others=> catt<="111111"duant<="00000000"end case; elsif (clk_in ='1') thencase highlow iswhen "10" => catt<="111101"duant<="01110100" -显示“Lwhen "01" => catt<="111101"duant<="00111000" -显示“Hwhen others=> catt<="111111"duant<="00000000" end case; end if;end if; end process p1; cat<= catt; duan<= duant;end b;3. 点阵显示源程序library ieee;use ieee.std_logic_1164.all;use ieee.std_logic_unsigned.all;entity dianzhen isport(clk_in : in std_logic; -时钟输入 yin : in std_logic_vector(6 downto 0); -输入音符 row : out std_logic_vector(7 downto 0); -点阵行向量 col : out std_logic_vector(7 downto 0) -点阵列向量 );end;architecture b of dianzhen issignal count : integer range 0 to 6;signal rowt : std_logic_vector(7 downto 0);signal colt : std_logic_vector(7 downto 0);beginp1:process(clk_in)beginif (clk_in'event and clk_in='1') thenif count = 6 then count<=0;elsecount<=count+1;-用count来记数end if;end if;end process p1;p2: process(count,yin) begin if (yin = "0000001") thencase count is-点阵显示,表示“1音符 when 0=> rowt<="11111110"colt<="01111110" when 1=> rowt<="11111101"colt<="01111110" when 2=> rowt<="11111011"colt<="01111100" when 3=> rowt<="11110111"colt<="01111000" when 4=> rowt<="11101111"colt<="01110000" when 5=> rowt<="11011111"colt<="01100000" when 6=> rowt<="10111111"colt<="01000000" when others=> rowt<="11111111"colt<="00000000"end case;elsif (yin = "0000010") thencase count is-点阵显示,表示“2音符 when 0=> rowt<="11111110"colt<="01111101" when 1=> rowt<="11111101"colt<="01111100" when 2=> rowt<="11111011"colt<="01111100" when 3=> rowt<="11110111"colt<="01111000" when 4=> rowt<="11101111"colt<="01110000" when 5=> rowt<="11011111"colt<="01100000" when 6=> rowt<="10111111"colt<="01000000" when others=> rowt<="11111111"colt<="00000000"end case;elsif (yin = "0000100") thencase count is-点阵显示,表示“3音符 when 0=> rowt<="11111110"colt<="01111011" when 1=> rowt<="11111101"colt<="01111010" when 2=> rowt<="11111011"colt<="01111000" when 3=> rowt<="11110111"colt<="01111000" when 4=> rowt<="11101111"colt<="01110000" when 5=> rowt<="11011111"colt<="01100000" when 6=> rowt<="10111111"colt<="01000000" when others=> rowt<="11111111"colt<="00000000"end case;elsif (yin = "0001000") thencase count is-点阵显示,表示“4音符 when 0=> rowt<="11111110"colt<="01110111" when 1=> rowt<="11111101"colt<="01110110" when 2=> rowt<="11111011"colt<="01110100" when 3=> rowt<="11110111"colt<="01110000" when 4=> rowt<="11101111"colt<="01110000" when 5=> rowt<="11011111"colt<="01100000" when 6=> rowt<="10111111"colt<="01000000" when others=> rowt<="11111111"colt<="00000000"end case;elsif (yin = "0010000") thencase count is -点阵显示,表示“5音符 when 0=> rowt<="11111110"colt<="01101111" when 1=> rowt<="11111101"colt<="01101110" when 2=> rowt<="11111011"colt<="01101100" when 3=> rowt<="11110111"colt<="01101000" when 4=> rowt<="11101111"colt<="01100000" when 5=> rowt<="11011111"colt<="01100000" when 6=> rowt<="10111111"colt<="01000000" when others=> rowt<="11111111"colt<="00000000"end case;elsif (yin = "0100000") thencase count is-点阵显示,表示“6音符 when 0=> rowt<="11111110"colt<="01011111" when 1=> rowt<="11111101"colt<="01011110" when 2=> rowt<="11111011"colt<="01011100" when 3=> rowt<="11110111"colt<="01011000" when 4=> rowt<="11101111"colt<="01010000" when 5=> rowt<="11011111"colt<="01000000" when 6=> rowt<="10111111"colt<="01000000" when others=> rowt<="11111111"colt<="00000000"end case;elsif (yin = "1000000") thencase count is-点阵显示,表示“7音符 when 0=> rowt<="11111110"colt<="00111111" when 1=> rowt<="11111101"colt<="00111110" when 2=> rowt<="11111011"colt<="00111100" when 3=> rowt<="11110111"colt<="00111000" when 4=> rowt<="11101111"colt<="00110000" when 5=> rowt<="11011111"colt<="00100000" when 6=> rowt<="10111111"colt<="00000000" when others=> rowt<="11111111"colt<="00000000"end case;else case count is-点阵显示,表示不输入音符 when 0=> rowt<="11111110"colt<="01111111" when 1=> rowt<="11111101"colt<="01111110" when 2=> rowt<="11111011"colt<="01111100" when 3=> rowt<="11110111"colt<="01111000" when 4=> rowt<="11101111"colt<="01110000" when 5=> rowt<="11011111"colt<="01100000" when 6=> rowt<="10111111"colt<="01000000" when others=> rowt<="11111111"colt<="00000000"end case;end if; end process p2; row<= rowt; col<= colt;end b;4. 选择音符及自动播放源程序library ieee;use ieee.std_logic_1164.all;use ieee.std_logic_unsigned.all;entity auto isport( clk_in :in std_logic;-输入时钟 auto :in std_logic;-自动播放 auto1 :in std_logic;-试音播放 clear :in std_logic;-复位 yin_in :in std_logic_vector(6 downto 0);-输入音符 yin_out :out std_logic_vector(6 downto 0); -输出音符end auto;architecture a of auto issignal count : integer range 0 to 35;signal n: integer range 0 to 6;signal yin : std_logic_vector(6 downto 0);beginp1:process(clk_in,clear)variable i : integer range 0 to 63000000;begin if clear ='1' then count<=0; elsif (clk_in'event and clk_in='1') thenif (i=20000000) then i:=0; if count =15 then count <=0;-自动播放count记数 else count <=count+1; end if; if n =6 then n <=0; -试音播放n记数 else n <=n+1; end if;else i:=i+1;end if; end if;end process p1;p2:process(count,auto,yin_in,clear)begin if clear='1' then yin<="0000000"-音符清零elseif auto ='1' then-自动播放歌曲case count iswhen 1 => yin<="0000100"-3when 2 => yin<="1000000"-7when 3 => yin<="0000100"-3when 4 => yin<="0100000"-6when 5 => yin<="0010000"-5when 6=> yin<="0100000"-6when 7 => yin<="0000001"-1when 8 => yin<="0000100"-3when 9 => yin<="0010000"-5when 10 => yin<="0000100"-3when 11 => yin<="0000100"-3when 12 => yin<="0001000"-4when 13 => yin<="0000100"-2when 14 => yin<="0001000"-4when others => yin<="0000000"end case;elsif auto1 ='1' thencase n is-试音播放when 0 => yin<="0000001"-1when 1 => yin<="0000010"-2when 2 => yin<="0000100"-3when 3 => yin<="0001000"-4when 4 => yin<="0010000"-5when 5 => yin<="0100000"-6when 6 => yin<="1000000"-7when others => yin<="0000000"end case;elseyin<=yin_in;-动手演奏end if;end if;end process p2;yin_out<=yin;end a;5. 预置分频系数模块源代码library ieee;use ieee.std_logic_1164.all;use ieee.std_logic_unsigned.all;entity seletone isport( highlow :in std_logic_vector(1 downto 0); -上下音 yin :in std_logic_vector(6 downto 0);-要演奏的音符 tone :out integer range 0 to 2000000); -预置分频系数end seletone;architecture a of seletone issignal tone0 :integer range 0 to 2000000;beginprocess(highlow,yin)beginif highlow ="00" thencase yin is-中音局部when "0000001"=> tone0<=523;when "0000010"=> tone0<=587;when "0000100"=> tone0<=659;when "0001000"=> tone0<=698;when "0010000"=> tone0<=784;when "0100000"=> tone0<=880;when "1000000"=> tone0<=988;when others => tone0<=2000000;end case;elsif highlow ="10" then-高音局部case yin iswhen "0000001"=> tone0<=1045;when "0000010"=> tone0<=1174;when "0000100"=> tone0<=1318;when "0001000"=> tone0<=1396;when "0010000"=> tone0<=1568;when "0100000"=> tone0<=1760;when "1000000"=> tone0<=1975;when others => tone0<=2000000;end case;elsif highlow ="01" then-低音局部case yin iswhen "0000001"=> tone0<=261;when "0000010"=> tone0<=293;when "0000100"=> tone0<=329;when "0001000"=> tone0<=349;when "0010000"=> tone0<=392;when "0100000"=> tone0<=440;when "1000000"=> tone0<=494;when others => tone0<=2000000;end case; end if;end process ;tone <=tone0;end a;6. 分频发音模块源代码library ieee;use ieee.std_logic_1164.all;use ieee.std_logic_unsigned.all;entity div isport(clk_in : in std_logic;-输入时钟 tone: in integer range 0 to 2000000;-预置频率 clk_out : out std_logic);-输出时钟end div;architecture a of div issignal clk_tmp0 : std_logic;signal clk_tmp1: std_logic;beginp0:process(clk_in, tone)variablet : integer range 0 to 49999999;beginif (clk_in'event and clk_in='1') then ift <12999999/tone then-分频系数 t:=cnt+1; clk_tmp0<='1' else t:=0; clk_tmp0<='0' end if;end if;end process p0;p1:process(clk_tmp0)variable count :std_logic;beginif(clk_tmp0'event and clk_tmp0='1') thencount:=not count;if count ='1' thenclk_tmp1 <='1'-输出平稳的波形elseclk_tmp1 <='0'end if;end if;end process p1;clk_out<=clk_tmp1;end a;7. 电子琴顶层设计library ieee;use ieee.std_logic_1164.all;use ieee.std_logic_unsigned.all;entity dianzq isport(clk : in std_logic;-时钟输入 yin : in std_logic_vector(6 downto 0);-音符输入 highlow: in std_logic_vector(1 downto 0);-上下音输入 auto_in: in std_logic;-自动播放 auto1 : in std_logic;-试音播放 clear : in std_logic;-复位 clk_out: out std_logic;-输出时钟 row : out std_logic_vector(7 downto 0);-点阵显示 col : out std_logic_vector(7 downto 0);-点阵显示 duan : out std_logic_vector(7 downto 0);-数码管显示 cat : out std_logic_vector(5 downto 0);-数码管显示end dianzq;architecture a of dianzq iscomponent div0 is-分频模块port(clk_in : in std_logic; clk_tmp : out std_logic);end component;component dianzhen is-点阵显示模块port(clk_in : in std_logic; yin : in std_logic_vector(6 downto 0); row : out std_logic_vector(7 downto 0); col : out std_logic_vector(7 downto 0) );end component;component shuma is-数码管显示模块port(clk_in : in std_logic; yin : in std_logic_vector(6 downto