具有自动乐曲演奏功能的电子琴设计

先给出设计结果视频链接:

具有自动乐曲演奏功能的电子琴-视频实录

优酷:具有自动乐曲演奏功能的电子琴-视频实录

       再给出完整文档和工程代码链接:   VHDL的具有自动乐曲演奏功能的电子琴设计.rar,访问密码: 6872。  基于VHDL的具有自动乐曲演奏功能的电子琴设计

        本文为本人于2012年下学期做的EDA数字系统设计,文章详细介绍了“具有自动乐曲演奏功能的电子琴”的FPGA设计原理与方法,使用了ROM存储音符和节拍,矩阵键盘控制整个系统。

一、选题目的

      电子设计自动化,简称EDA(Electronic Design Automation),发展迅速,应用范围日益扩大。它以计算机为工具,设计者在EDA软件平台上,用硬件描述语言VHDL完成设计文件,然后由计算机自动地完成逻辑编译、化简、分割、综合、优化、布局、布线和仿真,直至对于特定目标芯片的适配编译、逻辑映射和编程下载等工作。EDA技术的出现,极大地提高了电路设计的效率和可操作性,减轻了设计者的劳动强度。

     本文应用VHDL硬件描述语言,以QuartusⅡ8.0为开发工具设计了一个具有自动演奏乐曲功能的电子琴系统,它能将预先存储在ROM中的多首乐曲自动播放出来,并同时显示音符,此外,还具有电子琴弹奏的功能。选题新颖、实用,趣味性、综合性较强。

二、设计目标

1. 采用44矩阵键盘作为:电子琴按键,高、中、低音选择键,自动播放和电子琴弹奏功能选择键,乐曲选择键。
2. 使用ROM存储乐曲,达到只要在其中存储乐曲音符节拍的信息即可自动播放的目的,对乐曲的编码要简单易用。
3. 可自动播放《世上只有妈妈好》、《长亭送别》、《十年》三首歌曲,带选歌和自动循环播放的功能。

4. 实时显示正在播放的音符。

5. 设计要具有模块化,层次化的特点。

6. 波形仿真时采用时序仿真,以更加贴近实际,使系统的实际效果达到最佳。

三、实现方案

1. 原理框图

       具有自动乐曲演奏功能的电子琴系统的原理结构框图如下:

 Center

图1 硬件系统结构框图

      

Center

图2 软件系统结构框图

Center

图3 乐曲自动播放模块结构框图

Center   

图4 乐曲弹奏模块结构框图

2. 设计流程图

       具有自动乐曲演奏功能的电子琴系统的VHDL程序设计流程图如下图5:

Center

图5 程序设计流程图

四、设计过程

1. 音乐基础知识

        简谱应该说是一种比较简单易学的音乐记谱法。它的最大好处是仅用7个阿拉伯数字----1234567,就能将万千变化的音乐曲子记录并表示出来,并能使人很快记住而终身不忘;同时涉及其他的音乐元素也基本可以正确显示。简谱虽然不是出现在中国,但是好像只有在中国得到非常广泛的传播。

       乐音的特性:它由四个方面组成:音高、音值、音量、音色。
       音高:由物体在一定的时间内震动的次数决定,震动次数多,因则高,反之,则低。
       音值:即音的长短,是由音的延续时间的不同而决定的,音的延续时间长,音则长,反之,则短。
       音量:即音的强与弱,由震幅的大小决定,震幅大,音则强,反之,则弱。
       音色:有发音体的性质决定,发音体的形状及泛音的多少决定音色的不同,例如,小提琴、钢琴等各种乐器的音色都是不同的,在合奏时,人们可清楚地辨认。
       乐音体系:在音乐使用中有固定音高的音的总和叫乐音体系。
       音级:乐音体系中的各音叫音级,音级有基本音级与变化音级两种。
       基本音级:在音乐中经常使用的七个具有独立名称的音叫基本音级。基本音级的名称用字母或唱名两种方式来标记。
       音名:用C、D、E、F、G、A、B来标记基本音级的叫音名,它表示一定的音高,简谱中用1,2,3,4,5,6,7来标记。
       唱名:用do、re、mi、fa、sol、la、si作为音级名称的叫唱名。

       音符:用以记录音的长短高低的符号叫音符(以符头在谱表上的位置来表示音的高低,以形状表示音的长短,音符有符头、符干、符尾三部分或其中某些部分组成,而在简谱中以1 2 3 4 5 6 7或其上下加点来表示不同音高,以短下划线(_)或横(—)来表示音的长短)。

       下面重点介绍组成音乐的两个最基本的要素:每个音符发音的频率及其持续的时间。

       1.1 音符和频率的关系

       乐曲的十二平均律规定:每2 个八度音(如简谱中的中音1与高音1)之间的频率相差一倍。在2个八度音之间,又可分为12个半音,每2个半音的频率比为20130912155241750。另外,简谱中的低音6的频率为440Hz,音符7到1之间、3到4之间为半音,其余为全音。由此可计算出简谱中从低音1至高音7之间每个音符的频率,如下表1所示:

表1 简谱中音符与频率的关系

音名

频率(Hz)

音名

频率(Hz)

音名

频率(Hz)

低音1

261.6

中音1

523.3

高音1

1046.5

低音2

293.7

中音2

587.3

高音2

1174.7

低音3

329.6

中音3

659.3

高音3

1318.5

低音4

349.2

中音4

698.5

高音4

1396.9

低音5

392

中音5

784

高音5

1568

低音6

440

中音6

880

高音6

1760

低音7

493.9

中音7

987.8

高音7

1975.5

    1.2 音符的长短

       表示音乐的长短需要有一个相对固定的时间概念。简谱里将音符分为全音符、二分音符、四分音符、十六分音符、三十二分音符等,如下表2。在这几个音符里面最重要的是四分音符,它是一个基本参照度量长度,即四分音符为一拍。这里一拍的概念是一个相对时间度量单位。一拍的长度没有限制,可以是1秒 也可以是2秒或半秒。假如一拍是一秒的长度,那么二拍就是两秒;一拍定为半秒的话,两拍就是一秒的长度。一旦这个基础的一拍定下来,那么比一拍长或短的符号就相对容易了。正如五线谱的附点一样,数字后方加一点会将音符长度增加一半。

表2 简谱中音符长度

音符名称

记法

时值

编码

全音符

5 — — —

四拍

16

二分音符

5 —

二拍

8

四分音符

5

一拍

4

八分音符

20130912155948156

半拍

2

十六分音符

20130912160015828

四分之一拍

1

三十二分音符

20130912160104640

八分之一拍

2.  VHDL程序设计

     2.1 顶层文件

       采用原理图输入法设计,其原理图如下:

Center

图6 music_player原理图

       各模块的设计如下。

     2.2 音符的编码及音乐的存储

      2.2.1 音符的编码

        休止符的编码为:0;音符长度的编码见表2。音名的编码如下表3所示:

表3音名的编码

音名

编码

音名

编码

音名

编码

低音1

11

中音1

1

高音1

21

低音2

12

中音2

2

高音2

22

低音3

13

中音3

3

高音3

23

低音4

14

中音4

4

高音4

24

低音5

15

中音5

5

高音5

25

低音6

16

中音6

6

高音6

26

低音7

17

中音7

7

高音7

27

     

       2.2.1 音乐的存储     

       以十进制将音乐分别存储于note_rom和tick_rom两个模块中,前者为音 符,后者为音符的长度,下面给出《世上只有妈妈好》的存储过程。在Quartus II 主窗体中选择Tools—MegaWizard Pluge-In Manager…,弹出如下对话框,单击Next继续。

Center

图 7  MegaWizard Pluge-In Manager第一页

按下图选择并填入文件名,点击Next。

Center

图 8  MegaWizard Pluge-In Manager第二页

         由于音符编在16到32之间,所以q选为5位即可,容量选为1024个字如下图所示

Center

图 9  数据和地址宽度的选择

在下图中取消“q’output port”前面的勾,点击Next继续。

Center

图 10  寄存器信号的选择

在下图中,按图示选择并填入note_rom.mif,点击Next继续。

Center

图 11  指明ROM初始化文件

Center

图 12  完成ROM的定制

     2.2.3 建立ROM初始化文件

     初始化ROM的数据文件有.mif格式和.hex格式,这里采用.mif格式,可以用文本编辑器编辑,也可以用Quartus II自带的功能产生ROM数据文件。

 定制一个512*8的ROM的初始化.mif文件可按下列步骤进行:选择菜单File | New,在对话框中选择Memory Initialization File,然后输入512和8,如下图13(a)所示,点击OK随即打开.mif文件,如下图13(b)所示,然后可在其中输入数据,默认为十进制。

Center                Center

(a)ROM的初始化设置        (b)初始化的空的ROM文件

图13 ROM初始化

           2.2.4 在ROM中存储音乐

以《世上只有妈妈好》为例,其简谱如下图所示:

Center

图14 世上只有妈妈好简谱

        根据前述对音符的编码规则,可知第一个音为中音6,时值为20130912155529296拍,存储为6和6;再如第二个音符为中音5,时值20130912155551218拍,存储为5和2;又如第五个音符为高音5,时值1拍,存储为21和4。依此类推,可将整首乐曲存储如下:

  Center

 (a)note_rom.mif文件                   (b)tick_rom.mif文件

图15 世上只有妈妈好简谱在ROM中的存储

其它两首乐曲的存储与此类似,且存储在同一个note_rom.mif和tick_rom.mif文件中即可。

3. 键盘控制模块

3.1 扫描及编码

        矩阵式键盘是一种常见的输入装置,根据其电路连接有共阴极和共阳极两种连接方式,可以采用逐行或逐列扫描法获得按键值。本文根据实验室提供的共阳极矩阵键盘设计,其示意图如图16所示,定义各功能键如下图17所示,其中1~7对应简谱的1~7音符,H、M、L分别为高、中、低音选择键,AP(Auto Play)为自动播放选择键,EO(Electronic Organ)为电子琴弹奏选择键,Song为自动播放时的歌曲选择键,三个短“-”代表休止符。

     Center           Center

图16 共阳极矩阵式键盘示意图            图17本系统矩阵键盘功能键示意

        下面介绍扫描原理,采取逐行扫描法,以0111、1011、1101、1110的顺序依次扫描,然后读取列引脚的电平信号即可判断哪个按键按下。例如,当扫描信号为1011时,表示在扫描第二行,若列读出的电平信号为1101,则可知第3列的键被按下,即第二行第3列的键“7”被按下,其它按键依次类推,下表4中列出了4*4矩阵键盘扫描时的情况

表4 按键扫描信息对照表

   列

行  

0111

1011

1101

1110

1111

0111

1

2

3

4

1011

5

6

7

H

1101

AP

EO

Song

M

1110

-

-

-

L

对该矩阵键盘按从上到下、从左至右的顺序编码,依次为:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 。

扫描时,选取的扫描频率为1KHz,由1MHz分频得到,具体见源程序。

3.2 消抖

      通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动,如右图。抖动时间的长短由按键的机械特性决定,一般为5ms~10ms。这是一个很重要的时间参数,在很多场合都要用到。

Center     

 图18 按键抖动

        常见的消抖方法有采样型防抖微分电路、D型触发器、移位寄存器、计数器,结合各消抖法的特点,本文采用计数器法。

3.3 键盘控制模块

如右图19所示 

            Center

              图19 键盘控制模块

        键盘控制模块(Key_Control.vhd)仿真波形如下图20所示:

Center

图20键盘控制模块仿真波形

        为方便仿真及观察仿真结果,仿真时采用周期为100ns的clk,且将Key_Control.vhd中的获得clk_kb信号的程序中的if cnt=499 then,(即具体见源程序中斜体注释部分)改为if cnt=10 then,再将延时近程中clk_kb改为clk,count改为3位,然后对KBCol[0..3]赋值(如图),即可得到上图所示结果。

        注意观察图中圈圈的部分,可以看出,第一个圈,对应“AP”键,他不在音符范围内,故Key_Note为0,且play,sel均为1,表示选择自动播放;第二个圈,对应“5”键,在音符范围内,故Key_Note为5;第三个圈,对应“EO”键,他不在音符范围内,故Key_Note为0,且play,sel均为0,表示选择电子琴弹奏;第四个圈,对应“Song”键,产生一个脉冲,仔细分析还应当发现由于消抖,使输出迟了1个扫描时钟(0.001s),但对本系统无影响。

4. 乐曲自动播放模块

        此模块的框图如图3所示,元件符号如右图下21所示,包含以下模块:ROM数据读取控制、音符ROM、节拍ROM,如下图22所示:

 Center

图21 乐曲自动播放模块符号

Center

图22 乐曲自动播放模块

        4.1 ROM数据读取控制

Center

        该分模块从tick_rom中读取节拍,控制读取ROM数据的速度,从而实现音乐的节奏。该模块通过可调模值计数器实现,具体参见源程序。

        乐曲自动播放模块仿真波形如下:           

                        Center

图23 乐曲自动播放模块仿真波形

        为方便仿真,将counter_rom.vhd源程序中的CLK_FREQ这一常量的值改为仿真时的时钟信号频率100Hz,三首乐曲的前几个音符如右图所示,第一首的起始音符为中音6,时值一又二分之一拍;第二首的起始音符为中音5,时值为1拍;第三首的起始音符为0,接着为1,2,时值分别为二分之一拍、四分之一拍、四分之一拍。对照仿真结果,可知仿真结果正确,程序正确。

Center

图24 三首乐曲的第一句简谱

5. 电子琴模块

        此模块主要完成将上一级键盘控制模块送来的音符按键进行译码输出,元件符号如右图11所示。

        如图12所示,使能有效后,开始输出音符码:2,第一个圈中key值为8,说明下面的音符将转为高音,如后面的23,26;第二个圈中key值为12, 说明下面的音符将转为中音,如其后的6。可见,程序正确无误。                      

Center

图 25  电子琴模块

Center

图26 电子琴模块仿真波形

6. 音符发声及显示模块

        该模块主要将前端送来的音符码转换成相应的声音频率以驱动扬声器发声,并在七段数码管(实验室提供的为

Center

八段数码管,但本设计只需七段)上显示音符,且有高低音指示灯。元件符号如右图。

       七段数码管的原理及使用很简单,在此不再熬述! 

        本文设计的具有自动乐曲演奏功能的电子琴系统选择的基基准频率为1MHz,根据表1可计算出对应的音符的分频系数(divider_mod),如下表5:

表5  1MHz下简谱中音符分频数

音名

分频系数

音名

分频系数

音名

分频系数

低音1

3283

中音1

1911

高音1

956

低音2

3405

中音2

1703

高音2

851

低音3

3034

中音3

1517

高音3

758

低音4

2863

中音4

1432

高音4

716

低音5

2551

中音5

1276

高音5

638

低音6

2273

中音6

1137

高音6

568

低音7

2025

中音7

1012

高音7

506

        为方便仿真,需将Aud_Pro_Dis.vhd源程序中的divider_mod值改的小些,下面给出的仿真波形中,将中音6、低音6、及高音6对应的divider_mod值分别改为40、80、20,clk周期为100ns,分别给note_code赋以下值:6,16,26,即中音6、低音6、及高音6。

Center

图27音符发声及显示模块仿真波形

        由上图可见,数码管显示均为6,但高低音指示灯的值不同,pitch为高低音指示灯,pitch[1]为高位,且“00”代表中音,“01”代表低音,“10”代表高音。可见仿真结果及程序均正确。

7. 五二输入或门

        由于本系统有自动播放和电子琴弹奏两个模块组成,每次只能其中一个有效,但要共用音符发声及显示模块,故需要将输出的音符码相或后送予后级处理,由于本模块比较简单,在此仅给出仿真波形,如下。

Center

图28五二输入或门

五、遇到问题及解决方法

        1. 矩阵键盘的扫描是一个重点和难点,程序写好之初效果并不理想,仔细分析后,发现是没有消抖造成的,后来加入了消抖的进程,效果得到改善。

        2. 电子琴弹奏模块的实现过程中也出现了一个问题:高、中、低音的选择不起作用,此模块仅有一个进程,如下:

process(key,play,EN)

variable sign :integer range 0 to 2:=0;

begin

if play='0' and EN='0' then

              if key=12 then

                     sign:=0;--mid

              elsif key=16 then

                     sign:=1;--low

              elsif key=8 then

                     sign:=2;--high

              end if;

              if key>=1 and key<=7 then--1~7

                     note_code<=key+10*sign;

              else

                     note_code<=0;--stop

              end if;

       end if;

else

       note_code<=0;

       sign:=0;

end if;

end process;

        但仿真结果令人惊讶,只能输出高音,仿真波形如下,错误处已经用叉号标记出来

Center       

图29 未加入clk时钟信号时的仿真结果

解决办法:给该模块加入时钟驱动,用上升沿触发事件,结果得到了想要的结果,如下图,程序见该模块源代码。

Center

图30 加入clk时钟信号时的仿真结果

六、实现结果

1.拨动开关,使start为1,此时数码管显示短横,扬声器不发声;

2.按下矩阵键盘的“AP”键,系统开始自动播放乐曲,顺序:《世上只有妈妈好》、《长亭送别》、《十年》循环播放;

3.按下歌曲选择键:“Song”,自动切换到下一首播放;

4.按下“EO”键,扬声器不发声,按数字键1~7,发出do~si的音符;

5.按下“H”、“M”、“L”键,可切换高、中、低音;

6.拨动开关,使start为0,此时数码管显示短横,扬声器不发声;

本文设计的具有自动演奏乐曲功能的电子琴系统,实现了所有设计目标,效果优秀,极具综合性、趣味性!

此系统的功能还可以进一步完善,如增加手动改变音乐播放的节奏、长时间无按键系统自动关闭、手动输入音符再自动播放(当然需将ROM换成RAM)等功能。

此系统是我独立开发的一个小型系统,且实现的效果很好,很有成就感,更加激发我搞科研的热情!

七、编程调试

1. 具有自动演奏乐曲功能的电子琴系统的所有VHDL源代码

1.1 顶层文件music_player.bdf如下:

   Center   

图31 music_player.bdf文件

1.2 键盘控制模块(Key_Control.vhd)VHDL源程序如下:

Center

library ieee;

use ieee.std_logic_1164.all;

use ieee.std_logic_unsigned.all;

entity Key_Control is

port (clk,start: in std_logic;

KBCol:in std_logic_vector(0 to 3);

song:buffer std_logic;

play:out std_logic;--1music_play; 0:Elc_keyboard;

sel:out std_logic;--1:music_play; 0:Elc_keyboard;

Key_Note:buffer integer range 0 to 16;

KBRow:buffer std_logic_vector(0 to 3)            

);

end entity;

architecture control of Key_Control is

signal clk_kb:std_logic;

signal cnt:integer range 0 to 1000;

signal temp:STD_LOGIC_VECTOR (7 downto 0);

signal state:std_logic_vector(1 downto 0):="00";

signal count:std_logic_vector(1 downto 0):="00";

signal key:integer range 0 to 16;

signal keynum:integer range 0 to 16;

signal TempKey:integer range 0 to 16;

signal song_chg:std_logic:='0';

begin

process(clk) --产生键盘扫描

begin

if rising_edge(clk)then

        if start='1' then

               if cnt=499 then--仿真时改为if cnt=10 then

                      clk_kb<=not clk_kb;

                      cnt<=0;

               else

                      cnt<=cnt+1;

               end if;

        end if;

    end if;

end process;

process(clk_kb)

begin

        if rising_edge(clk_kb) then

               if state="11" then

                      state<="00";

               else

                      state<=state+1;

               end if;

        end if;

end process;

process(state)

begin

case state is

        when "00"=>KBRow<="1110";

        when "01"=>KBRow<="1101";

        when "10"=>KBRow<="1011";

        when "11"=>KBRow<="0111";

        when others =>KBRow<="1111";

end case;

end process;

temp<=KBRow & KBCol;

process(clk_kb)

begin

if start='1' then

if falling_edge(clk_kb) then

        if KBCol="1111" then

               if count="11" then

                      key<=0;

                      count<="00";

               else

                      count<=count+1;

               end if;

        else

        --count<="00";

        case temp is

               when"11101110"=>key<=16;

               when"11101101"=>key<=15;

               when"11101011"=>key<=14;

               when"11100111"=>key<=13;

               when"11011110"=>key<=12;

               when"11011101"=>key<=11;

               when"11011011"=>key<=10;

               when"11010111"=>key<=9;

               when"10111110"=>key<=8;

               when"10111101"=>key<=7;

               when"10111011"=>key<=6;

               when"10110111"=>key<=5;

               when"01111110"=>key<=4;

               when"01111101"=>key<=3;

               when"01111011"=>key<=2;

               when"01110111"=>key<=1;

               when others =>key<=0;

               end case;

        end if;

end if;

else

key<=0;

end if;

end process;

process(clk)

variable count:std_logic_vector(4 downto 0);--仿真时改为(1 downto 0)

begin

if rising_edge(clk) then

        if key/=TempKey then

               TempKey<=key;

               count:="00000"; -- 仿真时改为”00”

        else

               if count="11111" then--仿真时改为”11”

                      keynum<=key;

                      count:="00000";--仿真时改为”00”

               else

                      count:=count+1;

               end if;

        end if;

end if;

end process;

process(clk_kb)

begin

if start='1'then

if(rising_edge(clk_kb))then

        if(keynum<9 or keynum=12 or keynum=16)then

               Key_Note<=keynum;

               song<='0';

        else

               Key_Note<=0;

               if keynum=11 then

                      song<='1';--产生脉冲

               else

                      song<='0';

                      if(keynum=9)then

                             play<='1';

                             sel<='1';

                      elsif(keynum=10)then

                             play<='0';

                             sel<='0';

                      end if;

               end if;   

        end if;          

end if;

else

play<='1';

sel<='0';

Key_Note<=0;--stop

song<='0';

end if;

end process;

end control;

1.3 电子琴(Electronic_keyboard.vhd)源文件

Center

library ieee;

use ieee.std_logic_1164.all;

entity Electronic_keyboard is

port (clk:in std_logic;

play:in std_logic;

EN:in std_logic;--0:Elc_keyboard;

key:in integer range 0 to 16;

note_code:OUT integer range 0 to 31

);

end entity;

architecture Elc_keyBoard of Electronic_keyboard is

begin

process(key,play,EN,clk)

variable sign :integer range 0 to 2:=0;

begin

if play='0' and EN='0' then

if(rising_edge(clk))then

        if key=12 then

               sign:=0;--mid

        elsif key=16 then

               sign:=1;--low

        elsif key=8 then

               sign:=2;--high

        end if;

        if key>=1 and key<=7 then--1~7

               note_code<=key+10*sign;

        else

               note_code<=0;--stop

        end if;

end if;

else

note_code<=0;

sign:=0;

end if;

end process;

end Elc_keyBoard;

1.4 乐曲自动演奏模块(music_Play.bdf)文件

Center

图32 music_Play.bdf文件

1.4.1 ROM数据读取控制模块(counter_rom.vhd)源文件

Center

library ieee;

use ieee.std_logic_1164.all;

entity counter_rom is

port

(      tick_num  : in integer range 0 to 31;

        clk           : in std_logic;

        song       : in std_logic;

        play      : in std_logic;

        EN        : in std_logic;

        q              : out integer range 0 to 511

);

end entity;

architecture rtl of counter_rom is

constant CLK_FREQ :integer:=1000000;--1MHz

constant SONG1_ADDR:integer:=7;--乐曲1存储地址

constant SONG2_ADDR:integer:=80;--乐曲2存储地址

constant SONG3_ADDR:integer:=184;--乐曲3存储地址

constant SONG_LEN:integer:=500;

signal SONG_ADDR:integer range 0 to 1000:=0;--乐曲存储地址

signal song_chg :std_logic:='0';

signal chg_ok :std_logic:='0';

signal num:integer range 0 to 2:=0;

signal start:std_logic:='0';

--signal meter

begin

process(song,play,EN)

begin

if(start='1')then

        if (song'event and song='1') then

               if num<2 then

                      num<=num+1;

               else

                      num<=0;

               end if;

               song_chg<='1';

        else

        end if;

        case num is

               when 0=>SONG_ADDR<=SONG1_ADDR;--song1

               when 1=>SONG_ADDR<=SONG2_ADDR;--song2

               when 2=>SONG_ADDR<=SONG3_ADDR;--song3

               when others=>SONG_ADDR<=null;

        end case;

else

        num<=0;

end if;

if(chg_ok='1')then

        song_chg<='0';   

end if;

end process;

--------------------

process(play,EN)

begin

start<=play and EN;

end process;

---------ROM数据读取控制--------------

process (clk,start)

variable count       : integer range 0 to SONG_LEN;

variable cnt    : integer range 0 to 4*CLK_FREQ;

begin

if (start='1'and count<SONG_LEN) then

if(rising_edge(clk))then

        if song_chg = '1' then

               -- Reset the counter to SONG_ADDR

               count := SONG_ADDR;

               chg_ok<='1';

               cnt:=0;

        else

               chg_ok<='0';

        end if;

        if(cnt<(tick_num*CLK_FREQ)/4)then

               cnt:=cnt+1;

        else

               cnt:=0;

               count:=count+1;

        end if;

end if;

else

count:=0;

cnt:=0;

end if;

-- Output the current count

q <= count;

end process;

end rtl;

1.4.2 note_rom和tick_rom文件的建立,见2.2.2音乐的存储

1.5 音符发声和显示模块

Center

library ieee;

use ieee.std_logic_1164.all;

entity Aud_Pro_Dis is

  port(clk : in std_logic;

         note_code:in integer range 0 to 31;

         pitch :out std_logic_vector(1 downto 0);

         disp7 :out  std_logic_vector(6 downto 0); --digtal display

         sound_out:out std_logic

         );

end entity;

        

architecture behav of Aud_Pro_Dis is

signal divider_mod:integer range 0 to 4096;

signal count: integer range 0 to 4096 :=0;

begin

process(note_code)    --divider_mod

begin

case note_code is

  when 11 => divider_mod<=3822;pitch<="01";disp7<="0110000";--low1

  when 12 => divider_mod<=3405;pitch<="01";disp7<="1101101";

  when 13 => divider_mod<=3034;pitch<="01";disp7<="1111001";

  when 14 => divider_mod<=2863;pitch<="01";disp7<="0110011";

  when 15 => divider_mod<=2551;pitch<="01";disp7<="1011011";

  when 16 => divider_mod<=2273;pitch<="01";disp7<="1011111";

  when 17 => divider_mod<=2025;pitch<="01";disp7<="1110000";--low7

  when 1 => divider_mod<=1911;pitch<="00";disp7<="0110000";--mid1

  when 2 => divider_mod<=1703;pitch<="00";disp7<="1101101";

  when 3 => divider_mod<=1517;pitch<="00";disp7<="1111001";

  when 4 => divider_mod<=1432;pitch<="00";disp7<="0110011";

  when 5 => divider_mod<=1276;pitch<="00";disp7<="1011011";

  when 6 => divider_mod<=1137;pitch<="00";disp7<="1011111";--1137

  when 7 => divider_mod<=1012;pitch<="00";disp7<="1110000";--mid7

  when 21 => divider_mod<=956;pitch<="10";disp7<="0110000";--high1

  when 22 => divider_mod<=851;pitch<="10";disp7<="1101101";

  when 23 => divider_mod<=758;pitch<="10";disp7<="1111001";

  when 24 => divider_mod<=716;pitch<="10";disp7<="0110011";

  when 25 => divider_mod<=638;pitch<="10";disp7<="1011011";

  when 26 => divider_mod<=568;pitch<="10";disp7<="1011111";

  when 27 => divider_mod<=506;pitch<="10";disp7<="1110000";--high7

  when 0 => divider_mod<=0;pitch<="00";disp7<="0000001";--stop

  when others => divider_mod<=0;pitch<="00";disp7<="0000001";--

end case;

end process;  

process(clk)        --50%占空比偶数分频

begin

  if (clk'event and clk='1') then

         if(divider_mod/=0)then

                if(count<divider_mod-1)then

                       count<=count+1;

                else

                       count<=0;

                end if;

                if(count<divider_mod/2)then

                       sound_out<='1';

                else

                       sound_out<='0';

                end if;   

         else

                sound_out<='0';

         end if;

  end if;   

end process;

end behav;

1.6 五二输入或门(five_or2.vhd)

library ieee;

use ieee.std_logic_1164.all;

entity five_or2 is

port(INA:in std_logic_vector(4 downto 0);

INB:in std_logic_vector(4 downto 0);

OUTY:out std_logic_vector(4 downto 0):="00000"

);

end five_or2;

architecture f_or2 of five_or2 is

begin

OUTY<=INA or INB;

end f_or2;

2. 调试下载运行效果图

        各管脚的分配如下图33所示。

Center

      

图33 引脚分配

              运行效果图如下图34所示。

Center

图34 运行效果图

八、对该课程的实施意见及建议

        希望能多给些时间练习,做一些更具挑战性,更加高级的设计,以使我们完全掌握FPGA的设计方法,并将其应用到工程项目,实际生活中。

附录

附录1  长亭送别简谱

Center

附录2  十年简谱

 Center

        附录3 存储有《世上只有妈妈好》、《长亭送别》、《十年》乐曲的note_rom.mif和tick_rom.mif文件。

               note_rom.mif                                          tick_rom.mif

Addr  +0  +1  +2   +3  +4   +5   +6   +7        +0     +1   +2   +3  +4   +5   +6  +7

0        0     0     0     0     0     0     0     0           0       1     0     1     0     1     0     1

8        6     5     3     5     21   6     5     6           6       2     4     4     4     2     2     8

16      3     5     6     5     3     2     1     16         4       2     2     4     2     2     2     2

24      5     3     2     0     2     3     5     0           2       2     8     0     6     2     4     0

32      5     6     3     2     1     5     3     2           2       2     6     2     8     6     2     2

40      1     16   1     15   6     5     3     5           2       2     2     8     6     2     4     4

48      21   6     5     6     3     5     6     5           4       2     2     8     4     2     2     4

56      3     2     1     16   5     3     2     2           2       2     2     2     2     2     8     4

64      3     5     5     6     3     2     1     5           2       4     2     2     6     2     8     6

72      3     2     1     16   1     15   0     0           2       2     2     2     2     8     2     2

80      0     0     5     3     5     21   6     21         2       2     4     2     2     8     4     4

88      5     5     1     2     3     2     1     2           8       4     2     2     4     2     2     8

96      0     0     5     3     5     21   7     6           4       4     4     2     2     6     2     4

104    21   5     5     2     3     4     17   1           4       8     5     2     2     6     2     8

112    0     0     5     21   21   7     6     7           4       4     4     4     8     4     2     2

120    21   5     7     21   6     5     3     3           8       2     2     2     2     2     2     2

128    1     2     0     0     5     3     5     21         2       8     4     4     4     2     2     6

136    7     6     21   5     5     2     3     4           2       4     4     8     4     2     2     6

144    17   1     0     0     5     3     5     21         2       8     4     4     4     2     2     8

152    6     21   5     5     1     2     3     2           4       4     8     4     2     2     4     2

160    1     2     0     0     5     3     5     21         2       8     4     4     4     2     2     6

168    7     6     21   5     5     2     3     4           2       4     4     8     4     2     2     6

176    17   1     0     0     0     0     0     0           4       8     4     4     1     2     1     4

184    0     0     0     1     2     3     3     2           2       2     2     1     1     2     2     2

192    3     2     1     17   16   0     13   16         2       1     1     1     1     2     1     1

200    15   16   17   16   15   16   0     1           3       1     1     1     1     1     4     1

208    17   16   17   16   0     0     13   1           1       1     1     4     4     3     1     1

216    17   16   17   16   0     0     0     1           1       1     1     4     4     4     2     1

224    2     3     3     2     3     2     3     5           1       2     2     2     2     1     1     1

232    1     0     3     2     2     2     1     17         1       3     1     2     2     1     1     1

240    1     0     1     1     17   16   17   1           1       3     1     1     1     1     1     2

248    16   16   0     13   1     17   16   15         2       4     3     1     1     1     1     1

256    16   17   16   0     0     0     15   3           2       2     4     4     4     3     1     2

264    2     1     2     3     2     2     15   0           1       1     2     1     1     2     2     1

272    1     16   17   1     6     5     1     3           1       1     1     1     1     1     1     4

280    0     3     2     3     4     0     2     3           1       1     1     1     4     4     3     1

288    3     4     3     0     1     2     5     3           2       2     8     4     1     1     1     1

296    3     3     3     4     5     6     2     2           3       1     1     1     1     1     2     2

304    2     4     3     2     1     13   13   2           1       1     1     1     3     1     1     1

312    1     17   1     16   16   0     1     17         1       1     1     1     2     1     1     1

320    16   17   4     3     2     17   1     1           1       2     1     1     2     1     1     4

328    0     1     2     3     6     3     4     5           1       1     1     1     3     1     2     1

336    3     3     2     1     2     5     3     3           1       2     2     1     1     1     1     3

344    3     3     4     5     6     2     2     2           1       1     1     1     1     3     1     1

352    4     3     2     1     13   13   2     1           1       1     1     3     1     1     1     1

360    17   1     16   16   0     1     17   16         1       1     1     2     1     1     1     1

368    17   4     3     2     3     2     2     1           2       1     1     2     1     1     1     3

376    0     1     2     3     6     6     3     1           1       1     1     1     3     1     2     1

384    2     2     1     17   1     0     0     0           1       6     1     1     8     4     4     4

392    0     0     1     2     3     3     2     3           4       4     1     1     2     2     2     2

400    2     3     5     1     0     3     2     2           1       1     1     1     2     1     2     2

408    2     1     17   1     1     1     17   16         1       1     2     2     1     1     1     1

416    1     16   0     15   3     2     1     2           1       8     3     1     1     1     1     1

        

欢迎指出错误及交流!

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐