基于VHDL的具有自动乐曲演奏功能的电子琴设计
具有自动乐曲演奏功能的电子琴设计先给出设计结果视频链接:具有自动乐曲演奏功能的电子琴-视频实录优酷:具有自动乐曲演奏功能的电子琴-视频实录再给出完整文档和工程代码链接:基于VHDL的具有自动乐曲演奏功能的电子琴设计本文为本人于2012年下学期做的EDA数字系统设计,文章详细介绍了“具有自动乐曲演奏功能的电子琴”的FPGA设计原理与...
具有自动乐曲演奏功能的电子琴设计
先给出设计结果视频链接:
再给出完整文档和工程代码链接: 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. 原理框图
具有自动乐曲演奏功能的电子琴系统的原理结构框图如下:
图1 硬件系统结构框图
图2 软件系统结构框图
图3 乐曲自动播放模块结构框图
图4 乐曲弹奏模块结构框图
2. 设计流程图
具有自动乐曲演奏功能的电子琴系统的VHDL程序设计流程图如下图5:
图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个半音的频率比为。另外,简谱中的低音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 |
八分音符 | 半拍 | 2 | |
十六分音符 | 四分之一拍 | 1 | |
三十二分音符 | 八分之一拍 |
2. VHDL程序设计
2.1 顶层文件
采用原理图输入法设计,其原理图如下:
图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继续。
图 7 MegaWizard Pluge-In Manager第一页
按下图选择并填入文件名,点击Next。
图 8 MegaWizard Pluge-In Manager第二页
由于音符编在16到32之间,所以q选为5位即可,容量选为1024个字如下图所示
图 9 数据和地址宽度的选择
在下图中取消“q’output port”前面的勾,点击Next继续。
图 10 寄存器信号的选择
在下图中,按图示选择并填入note_rom.mif,点击Next继续。
图 11 指明ROM初始化文件
图 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)所示,然后可在其中输入数据,默认为十进制。
(a)ROM的初始化设置 (b)初始化的空的ROM文件
图13 ROM初始化
2.2.4 在ROM中存储音乐
以《世上只有妈妈好》为例,其简谱如下图所示:
图14 世上只有妈妈好简谱
根据前述对音符的编码规则,可知第一个音为中音6,时值为拍,存储为6和6;再如第二个音符为中音5,时值拍,存储为5和2;又如第五个音符为高音5,时值1拍,存储为21和4。依此类推,可将整首乐曲存储如下:
(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为自动播放时的歌曲选择键,三个短“-”代表休止符。
图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。这是一个很重要的时间参数,在很多场合都要用到。
图18 按键抖动
常见的消抖方法有采样型防抖微分电路、D型触发器、移位寄存器、计数器,结合各消抖法的特点,本文采用计数器法。
3.3 键盘控制模块
如右图19所示
图19 键盘控制模块
键盘控制模块(Key_Control.vhd)仿真波形如下图20所示:
图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所示:
图21 乐曲自动播放模块符号
图22 乐曲自动播放模块
4.1 ROM数据读取控制
该分模块从tick_rom中读取节拍,控制读取ROM数据的速度,从而实现音乐的节奏。该模块通过可调模值计数器实现,具体参见源程序。
乐曲自动播放模块仿真波形如下:
图23 乐曲自动播放模块仿真波形
为方便仿真,将counter_rom.vhd源程序中的CLK_FREQ这一常量的值改为仿真时的时钟信号频率100Hz,三首乐曲的前几个音符如右图所示,第一首的起始音符为中音6,时值一又二分之一拍;第二首的起始音符为中音5,时值为1拍;第三首的起始音符为0,接着为1,2,时值分别为二分之一拍、四分之一拍、四分之一拍。对照仿真结果,可知仿真结果正确,程序正确。
图24 三首乐曲的第一句简谱
5. 电子琴模块
此模块主要完成将上一级键盘控制模块送来的音符按键进行译码输出,元件符号如右图11所示。
如图12所示,使能有效后,开始输出音符码:2,第一个圈中key值为8,说明下面的音符将转为高音,如后面的23,26;第二个圈中key值为12, 说明下面的音符将转为中音,如其后的6。可见,程序正确无误。
图 25 电子琴模块
图26 电子琴模块仿真波形
6. 音符发声及显示模块
该模块主要将前端送来的音符码转换成相应的声音频率以驱动扬声器发声,并在七段数码管(实验室提供的为
八段数码管,但本设计只需七段)上显示音符,且有高低音指示灯。元件符号如右图。
七段数码管的原理及使用很简单,在此不再熬述!
本文设计的具有自动乐曲演奏功能的电子琴系统选择的基基准频率为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。
图27音符发声及显示模块仿真波形
由上图可见,数码管显示均为6,但高低音指示灯的值不同,pitch为高低音指示灯,pitch[1]为高位,且“00”代表中音,“01”代表低音,“10”代表高音。可见仿真结果及程序均正确。
7. 五二输入或门
由于本系统有自动播放和电子琴弹奏两个模块组成,每次只能其中一个有效,但要共用音符发声及显示模块,故需要将输出的音符码相或后送予后级处理,由于本模块比较简单,在此仅给出仿真波形,如下。
图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;
但仿真结果令人惊讶,只能输出高音,仿真波形如下,错误处已经用叉号标记出来
图29 未加入clk时钟信号时的仿真结果
解决办法:给该模块加入时钟驱动,用上升沿触发事件,结果得到了想要的结果,如下图,程序见该模块源代码。
图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如下:
图31 music_player.bdf文件
1.2 键盘控制模块(Key_Control.vhd)VHDL源程序如下:
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)源文件
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)文件
图32 music_Play.bdf文件
1.4.1 ROM数据读取控制模块(counter_rom.vhd)源文件
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 音符发声和显示模块
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所示。
图33 引脚分配
运行效果图如下图34所示。
图34 运行效果图
八、对该课程的实施意见及建议
希望能多给些时间练习,做一些更具挑战性,更加高级的设计,以使我们完全掌握FPGA的设计方法,并将其应用到工程项目,实际生活中。
附录
附录1 长亭送别简谱
附录2 十年简谱
附录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
欢迎指出错误及交流!
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)