北邮数电实验电子琴

VHDL 硬件描述语言程序设计

简易电子琴演奏器

姓名:chi

目录

一、

设计课题的任务要求 ............................................................................................ 3 系统设计 . ............................................................................................................... 4

二、 三、 四、 五、 六、 七、 八、

仿真波形及波形分析 ............................................................................................ 7

源程序 . ................................................................................................................... 9

功能说明 . ............................................................................................................. 20

元器件清单及资源利用情况 .............................................................................. 21

故障及问题分析 .................................................................................................. 22

总结和结论 .......................................................................................................... 23

一、 设计课题的任务要求

基本要求:

1、 用8×8点阵显示“1 2 3 4 5 6 7”七个音符构成的电子琴键盘。其中点阵的第一列

用一个LED 点亮表示音符“1”,第二列用二个LED 点亮表示音符“2”,依此类推,如下图所示。

图1 点阵显示的电子琴键盘

2、 用BTN1~BTN7七个按键模拟电子琴手动演奏时的“1 2 3 4 5 6 7”七个音符。当某

个按键按下时,数码管显示相应的音符,点阵上与之对应的音符显示列全灭,同时蜂鸣器演奏相应的声音;当按键弹开时数码管显示的音符灭掉,点阵显示恢复,蜂鸣器停止声音的输出。下图所示为按下BTN3按键时点阵的显示情况。

图2 按键按下后的点阵显示

3、 由拨码开关切换选择高、中、低音,并用数码管进行相应的显示。 4、 通过按键BTN0进行复位,控制点阵显示图1的初始状态。 提高要求:

1、 可通过一个拨码开关进行手动/自动演奏的切换,并与点阵显示配合增加自动演奏

乐曲的功能。

2、 增加手动演奏的音符存储、播放功能。

二、 系统设计

1. 设计思路

简易电子琴的制作主要是利用不同频率的波来驱动蜂鸣器发出声响。通过输入不同的音符来设置不同的分频系数,使得50MHz 的主频分频出不同频率的波。同时,演奏的音符还可以通过数码管和8*8点阵来动态显示。

根据系统设计要求,该电子琴设计采用自顶向下的设计方法。整体的功能通过不同的底层模块配合来完成电子琴的功能。底层模块主要包括乐曲自动演奏模块、分频预置值产生模块和数控分频模块,数码管显示模块,8*8点阵显示模块五部分组成。用这种设计思路把整个系统分为了若干个模块,然后再在顶层文件中将各个模块组合在一起,从而体现出超、高速硬件描述语言VHDL 的优势,

关于提高要求中通过一个拨码开关进行手动/自动演奏的切换,并与点阵显示配合增加自动演奏乐曲的功能,我打算将一首曲子的音符储存在自动播放的数组里面,然后通过计数器来顺序播放储存的音符。

关于提高要求中的手动演奏的音符存储、播放功能,我打算通过编程实现类似数据结构中队列的模块,来储存手动输入的音符,然后在要播放的时候,队列里面的音符依次出队,从而实现音符储存播放的功能。

2. 总体框图

图3

简易电子琴总体结构框图

图4

简易电子琴逻辑流程图

图5

简易电子琴VHDL 电路原理图

3. 分块设计

(1) 分频模块div0

由于实验电路板的主频是50Mhz, 为了数码管和点阵的刷新显示,我们必须将50Mhz 的频率进行分频。分频的程序来自电路中心的网站上面。在这个模块里,我设置分频系数为cnt=2499。从实验结果看,这个分频对数码管和点阵的显示有很好的效果

(2) 数码管显示模块shuma 我使用了2个数码管,第一个数码管显示1~7的音符,第二个数码管显示相关的信息,比如高音用H 表示,低音用L 表示,自动播放用A 表示。两个数码管分别刷新,但由于刷新频率太快,人眼不能察觉,以为是两个数码管是同时亮的。在程序中我们通过duan : out std_logic_vector(7 downto 0)和 cat : out std_logic_vector(5 downto 0)来控制数码管的显示。当输入不同的音符和不同的控制信息时,duan 和cat 向量都有不同的值与之对应。

(3) 8*8点阵显示模块dianzhen

8*8点阵的显示和数码管的显示运用了同样的原理,在程序中我们通过row : out

std_logic_vector(7 downto 0)和col : out std_logic_vector(7 downto 0)这两个向量来控制点阵的显示。当输入不同的音符时,点阵显示相应的形状。

(4) 音符产生模块auto 。

这个模块的功能是,选择的不同模式来产生不同的音符。当选择自动播放模式时,随着计数器count 的值增加,即地址值递增时,程序自动读取出事先储存的音符,并把这个音符输出。当选择手动演奏模式时,直接将通过BTN1~BTN7输入的向量当做音符输出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 组合,数码管显示不同的字符。duan[0]~duan[7]对应着数码管的a 段到h 段,cat[0]~cat[5]控制不同的数码管

2. 点阵显示模块仿真波形

波形分析:不同的yin 输入,点阵的col 和row 会有不同的波形,利用clk_in的上升沿来动态扫描点阵。从而得显示出指定的图形。

3. 自动播放模块仿真波形

波形分析:当auto 置1,clear 置0,yin_out输出储存在程序里面的曲子音符,这时候相当于自动播放。当auto 置0,clear 置0,yin_out输出从BTN1~BTN7读取的手动输入的信号,这时候相当于手动演奏。

4. 分频预置值产生模块仿真波形

波形分析:输入不同的高低音highlow, 和音符yin ,输出不同的tone 。而tone 将作为发声模块的分频预置值

四、 源程序

1. 分频模块源程序

library ieee;

use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all;

entity div0 is port(clk_in : in std_logic; clk_tmp : out std_logic); end;

architecture b of div0 is signal clk : std_logic; begin p0:process(clk_in) variable cnt : integer range 0 to 2499; begin if (clk_in'event and clk_in='1') then

cnt 加1

--输入时钟 --输出时钟

if cnt=2499 then cnt:=0; clk

--分频系数为2499

--每个输入时钟上升沿到来时

end if; end process p0; clk_tmp

2. 数码管显示源程序

library ieee;

use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all;

entity shuma is

port(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 is signal duant : std_logic_vector(7 downto 0); signal catt : std_logic_vector(5 downto 0);

begin p1: process(clk_in,yin,highlow,auto,auto1) begin if auto ='1' then catt

catt

when "0000001" => catt catt catt catt catt catt catt catt

elsif (clk_in ='1') then case highlow is when "10" => catt catt catt

cat

3. 点阵显示源程序

library ieee;

use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all;

entity 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;

architecture b of dianzhen is signal count : integer range 0 to 6; signal rowt : std_logic_vector(7 downto 0); signal colt : std_logic_vector(7 downto 0);

begin p1:process(clk_in) begin if (clk_in'event and clk_in='1') then if count = 6 then count

--时钟输入 --输入音符 --点阵行向量 --点阵列向量

--用count 来记数

p2: process(count,yin) begin

if (yin = "0000001") then

case count is --点阵显示,表示“1”音符 when 0=> rowt rowt rowt rowt rowt rowt rowt rowt

elsif (yin = "0000010") then

case count is

--点阵显示,表示“2”音符

when 0=> rowt rowt rowt rowt rowt rowt rowt rowt

elsif (yin = "0000100") then case count is --点阵显示,表示“3”音符 when 0=> rowt rowt rowt rowt rowt rowt rowt rowt

elsif (yin = "0001000") then case count is --点阵显示,表示“4”音符 when 0=> rowt rowt

when 2=> rowt rowt rowt rowt rowt rowt

elsif (yin = "0010000") then

case count is --点阵显示,表示“5”音符 when 0=> rowt rowt rowt rowt rowt rowt rowt rowt

elsif (yin = "0100000") then case count is --点阵显示,表示“6”音符 when 0=> rowt rowt rowt rowt rowt rowt rowt rowt

elsif (yin = "1000000") then case count is --点阵显示,表示“7”音符 when 0=> rowt rowt rowt rowt rowt rowt rowt rowt

else

case count is --点阵显示,表示不输入音符 when 0=> rowt rowt rowt rowt rowt rowt rowt rowt

end process p2;

row

4. 选择音符及自动播放源程序

library ieee;

use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all;

entity auto is

port( 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 is signal count : integer range 0 to 35; signal n: integer range 0 to 6; signal yin : std_logic_vector(6 downto 0); begin

p1:process(clk_in,clear) variable i : integer range 0 to 63000000; begin

if clear ='1' then count

elsif (clk_in'event and clk_in='1') then

14

if (i=20000000) then i:=0; if count =15 then count

n

else n

p2:process(count,auto,yin_in,clear)

begin if clear='1' then

yin yin

when 2 => yin yin yin yin yin yin yin yin yin yin yin yin yin

when others => yin yin

15

--自动播放count 记数

--试音播放n 记数

--音符清零 --自动播放歌曲 --3 --7 --3 --6 --5 --6 --1 --3 --5 --3 --3 --4 --2 --4

--试音播放 --1

when 1 => yin yin yin yin yin yin yin

yin

5. 预置分频系数模块源代码

library ieee;

use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all;

entity seletone is port( 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 is signal tone0 :integer range 0 to 2000000; begin

process(highlow,yin)

begin if highlow ="00" then

case yin is when "0000001"=> tone0 tone0 tone0 tone0 tone0 tone0 tone0

when others => tone0

--2 --3 --4 --5 --6 --7

--动手演奏 高低音 --要演奏的音符预置分频系数--中音部分

-- --

end case;

elsif highlow ="10" then case yin is when "0000001"=> tone0 tone0 tone0 tone0 tone0 tone0 tone0 tone0

elsif highlow ="01" then case yin is when "0000001"=> tone0 tone0 tone0 tone0 tone0 tone0 tone0 tone0

6. 分频发音模块源代码

library ieee;

use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all;

entity div is port(clk_in : in std_logic;

tone : in integer range 0 to 2000000; clk_out : out std_logic); end div;

architecture a of div is signal clk_tmp0 : std_logic;

--高音部分

--低音部分

--输入时钟 --预置频率 --输出时钟

signal clk_tmp1: std_logic; begin

p0:process(clk_in, tone) variable cnt : integer range 0 to 49999999; begin if (clk_in'event and clk_in='1') then if cnt

cnt:=cnt+1; clk_tmp0

p1:process(clk_tmp0) variable count :std_logic; begin if(clk_tmp0'event and clk_tmp0='1') then count:=not count; if count ='1' then clk_tmp1

else clk_tmp1

7. 电子琴顶层设计

library ieee;

use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all;

entity dianzq is port(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 is

component 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 0); highlow : in std_logic_vector(1 downto 0); auto : in std_logic; auto1 : in std_logic; duan : out std_logic_vector(7 downto 0); cat : out std_logic_vector(5 downto 0) ); end component;

component auto is port( clk_in : in std_logic; auto : in std_logic; auto1 : in std_logic; yin_in : i n std_logic_vector(6 downto 0); clear : in std_logic; yin_out: out std_logic_vector(6 downto 0)); end component;

19

--自动播放 --试音播放 --复位 --输出时钟 --点阵显示 --点阵显示 --数码管显示 --数码管显示

--分频模块

--点阵显示模块

--数码管显示模块 --自动播放模块

component seletone is --预置分频系数模块 port( highlow: in std_logic_vector(1 downto 0); yin : in std_logic_vector(6 downto 0); tone : out integer range 0 to 2000000); end component;

component div is --分频发音模块 port(clk_in : in std_logic; tone : in integer range 0 to 2000000; clk_out : out std_logic); end component;

signal yin_tmp : std_logic_vector(6 downto 0); signal tone_tmp : integer range 0 to 2000000; signal clk_tmp : std_logic;

begin u1: auto port map

(clk_in=>clk,auto=>auto_in,yin_in=>yin,yin_out=>yin_tmp,clear=>clear,auto1=>auto1);

u2: seletone port map (highlow=>highlow,yin=>yin_tmp,tone=>tone_tmp); u3: shuma port map

(duan=>duan,cat=>cat,yin=>yin_tmp,clk_in=>clk_tmp,highlow=>highlow,auto=>auto_in,auto1=>auto1);

u4: dianzhen port map (row=>row,col=>col,yin=>yin_tmp,clk_in=>clk_tmp); u5: div port map (clk_in=>clk,tone=>tone_tmp,clk_out=>clk_out); u6: div0 port map (clk_in=>clk,clk_tmp=>clk_tmp); end a;

五、 功能说明

初始状态,8×8点阵显示“1 2 3 4 5 6 7”七个音符构成的电子琴键盘,其中点阵的第一列用一个LED 点亮表示音符“1”,第二列用二个LED 点亮表示音符“2”,依此类推,

用BTN1~BTN7七个按键模拟电子琴手动演奏时的“1 2 3 4 5 6 7”七个音符。

当某个按键按下时,数码管显示相应的音符,点阵上与之对应的音符显示列全灭,同时蜂鸣器演奏相应的声音;当按键弹开时数码管显示的音符灭掉,点阵显示恢复,蜂鸣器停止声音的输出。

由拨码开关切换选择高、中、低音,并用数码管进行相应的显示

可通过一个拨码开关进行手动/自动演奏的切换,并与点阵显示配合增加自动演奏乐曲的功能。

20

六、 元器件清单及资源利用情况

1. 元器件清单

基本功能:Altra 公司MaxII 系列EPM71270T144C5开发板一块及电源线、下载线各一根。开发板内元器件利用情况如下:

2. 管脚设置

3. 资源利用情况

七、 故障及问题分析

在编写各个元件程序的时候,我通过仿真查找问题,然后编程修改。单个元件的仿真都通过了。但是整个系统配合起来就不行了。这可能是我各个元件之间的线没连好。于是我重新思考了整个系统的设计,重新画了系统框图。修改程序之后可以下载到板子上面运行,不过效果不太理想。有些问题仿真看不出来, 实际运行时会出问题。就像老师说的, 要设计一个电路总要先用仿真成功之后才实际接线的,但是最后的成品却不一定与仿真时完全一样。所以调试的工作很重要。

下载完后,一开始音不好听,我调整了分频系数后,音调准了。但是音还是不够稳,不够饱满,而且老是有尾音。后来老师说因为这个实验板的按键没有进行防抖,所以波形不太稳。然后我去网上找了防抖的代码。但多数代码都只对单个按键进行防抖的。而电子琴使用按键太多太频繁。后来我又找了对波形进行整形的代码。它对每个输出的音调波形都进行了整形,均衡了占空比。加了整形的进程后,声音一下子好听了许多。效果令人满意。

对手动演奏音符的储存和播放,我想了很久,还是没想明白。我想通过定义一个数组,数组里面的元素是手动演奏输入的7位的向量。通过数组的下标访问数组元素,进行储存或者播放。但是因为储存和读取的速度不一样,所以不能简单的通过计数器来实现。而且我写的程序里面对数组元素的访问是通过下标进行的,而下标的自增在VHDL 里面实现起来有些麻烦。然后我去网上查找资料,网上有用VHDL 语言实现FIFO 队列的结构。

我想用队列来储

存和播放音符应该可以,不过研究了半天还是没明白代码的原理。总之后来因为时间紧张,还是没有完成好这个功能。

八、 总结和结论

本次数字电路与逻辑设计实验,我在MaxII 系列EPM71270T144C5开发板上完成了简易电子琴电路的设计,并在QUARTUS П上仿真实现了电子琴电路的功能。

通过这个实验,我体会很多,对硬件编程有了更多的理解。之前我习惯用C 语言编程,感觉编的很顺利。而VHDL 语言用的很别扭,很多功能在C 语言里面很容易实现,但VHDL 语言里面实现某些功能却很费劲。特别是在VHDL 语言里面,各个进程间的执行是并行的。而且在不同的进程里面不能对同一信号进行赋值。这给我编程带来了一些麻烦。之前我不理解信号和变量的区别。后来慢慢明白,变量是在进程里面定义,只在进程里面有效。而信号可以在不同元件之间传递,相当于导线的功能。我对VHDL 语言里面的信号这个概念有了更深入的理解。

通过这个实验,我学会了使用自顶向下的设计方法来设计系统。先编好底层的元件,然后对底层元件进行连接,互相配合来完成整个系统的功能,

总之这次课程设计顺利完成了。在设计中遇到的一些问题,通过仔细的分析、查找网上资料、与小组同学讨论,最终解决了这些问题,从中学习到了很多。

最后给实验室的老师一些建议,希望实验课的时间能安排好一点。因为这个实验板子不能带回去自己做,所以效率有些低。特别是最后几天的时候,信通院上数电实验的人都把实验室挤满了。

VHDL 硬件描述语言程序设计

简易电子琴演奏器

姓名:chi

目录

一、

设计课题的任务要求 ............................................................................................ 3 系统设计 . ............................................................................................................... 4

二、 三、 四、 五、 六、 七、 八、

仿真波形及波形分析 ............................................................................................ 7

源程序 . ................................................................................................................... 9

功能说明 . ............................................................................................................. 20

元器件清单及资源利用情况 .............................................................................. 21

故障及问题分析 .................................................................................................. 22

总结和结论 .......................................................................................................... 23

一、 设计课题的任务要求

基本要求:

1、 用8×8点阵显示“1 2 3 4 5 6 7”七个音符构成的电子琴键盘。其中点阵的第一列

用一个LED 点亮表示音符“1”,第二列用二个LED 点亮表示音符“2”,依此类推,如下图所示。

图1 点阵显示的电子琴键盘

2、 用BTN1~BTN7七个按键模拟电子琴手动演奏时的“1 2 3 4 5 6 7”七个音符。当某

个按键按下时,数码管显示相应的音符,点阵上与之对应的音符显示列全灭,同时蜂鸣器演奏相应的声音;当按键弹开时数码管显示的音符灭掉,点阵显示恢复,蜂鸣器停止声音的输出。下图所示为按下BTN3按键时点阵的显示情况。

图2 按键按下后的点阵显示

3、 由拨码开关切换选择高、中、低音,并用数码管进行相应的显示。 4、 通过按键BTN0进行复位,控制点阵显示图1的初始状态。 提高要求:

1、 可通过一个拨码开关进行手动/自动演奏的切换,并与点阵显示配合增加自动演奏

乐曲的功能。

2、 增加手动演奏的音符存储、播放功能。

二、 系统设计

1. 设计思路

简易电子琴的制作主要是利用不同频率的波来驱动蜂鸣器发出声响。通过输入不同的音符来设置不同的分频系数,使得50MHz 的主频分频出不同频率的波。同时,演奏的音符还可以通过数码管和8*8点阵来动态显示。

根据系统设计要求,该电子琴设计采用自顶向下的设计方法。整体的功能通过不同的底层模块配合来完成电子琴的功能。底层模块主要包括乐曲自动演奏模块、分频预置值产生模块和数控分频模块,数码管显示模块,8*8点阵显示模块五部分组成。用这种设计思路把整个系统分为了若干个模块,然后再在顶层文件中将各个模块组合在一起,从而体现出超、高速硬件描述语言VHDL 的优势,

关于提高要求中通过一个拨码开关进行手动/自动演奏的切换,并与点阵显示配合增加自动演奏乐曲的功能,我打算将一首曲子的音符储存在自动播放的数组里面,然后通过计数器来顺序播放储存的音符。

关于提高要求中的手动演奏的音符存储、播放功能,我打算通过编程实现类似数据结构中队列的模块,来储存手动输入的音符,然后在要播放的时候,队列里面的音符依次出队,从而实现音符储存播放的功能。

2. 总体框图

图3

简易电子琴总体结构框图

图4

简易电子琴逻辑流程图

图5

简易电子琴VHDL 电路原理图

3. 分块设计

(1) 分频模块div0

由于实验电路板的主频是50Mhz, 为了数码管和点阵的刷新显示,我们必须将50Mhz 的频率进行分频。分频的程序来自电路中心的网站上面。在这个模块里,我设置分频系数为cnt=2499。从实验结果看,这个分频对数码管和点阵的显示有很好的效果

(2) 数码管显示模块shuma 我使用了2个数码管,第一个数码管显示1~7的音符,第二个数码管显示相关的信息,比如高音用H 表示,低音用L 表示,自动播放用A 表示。两个数码管分别刷新,但由于刷新频率太快,人眼不能察觉,以为是两个数码管是同时亮的。在程序中我们通过duan : out std_logic_vector(7 downto 0)和 cat : out std_logic_vector(5 downto 0)来控制数码管的显示。当输入不同的音符和不同的控制信息时,duan 和cat 向量都有不同的值与之对应。

(3) 8*8点阵显示模块dianzhen

8*8点阵的显示和数码管的显示运用了同样的原理,在程序中我们通过row : out

std_logic_vector(7 downto 0)和col : out std_logic_vector(7 downto 0)这两个向量来控制点阵的显示。当输入不同的音符时,点阵显示相应的形状。

(4) 音符产生模块auto 。

这个模块的功能是,选择的不同模式来产生不同的音符。当选择自动播放模式时,随着计数器count 的值增加,即地址值递增时,程序自动读取出事先储存的音符,并把这个音符输出。当选择手动演奏模式时,直接将通过BTN1~BTN7输入的向量当做音符输出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 组合,数码管显示不同的字符。duan[0]~duan[7]对应着数码管的a 段到h 段,cat[0]~cat[5]控制不同的数码管

2. 点阵显示模块仿真波形

波形分析:不同的yin 输入,点阵的col 和row 会有不同的波形,利用clk_in的上升沿来动态扫描点阵。从而得显示出指定的图形。

3. 自动播放模块仿真波形

波形分析:当auto 置1,clear 置0,yin_out输出储存在程序里面的曲子音符,这时候相当于自动播放。当auto 置0,clear 置0,yin_out输出从BTN1~BTN7读取的手动输入的信号,这时候相当于手动演奏。

4. 分频预置值产生模块仿真波形

波形分析:输入不同的高低音highlow, 和音符yin ,输出不同的tone 。而tone 将作为发声模块的分频预置值

四、 源程序

1. 分频模块源程序

library ieee;

use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all;

entity div0 is port(clk_in : in std_logic; clk_tmp : out std_logic); end;

architecture b of div0 is signal clk : std_logic; begin p0:process(clk_in) variable cnt : integer range 0 to 2499; begin if (clk_in'event and clk_in='1') then

cnt 加1

--输入时钟 --输出时钟

if cnt=2499 then cnt:=0; clk

--分频系数为2499

--每个输入时钟上升沿到来时

end if; end process p0; clk_tmp

2. 数码管显示源程序

library ieee;

use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all;

entity shuma is

port(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 is signal duant : std_logic_vector(7 downto 0); signal catt : std_logic_vector(5 downto 0);

begin p1: process(clk_in,yin,highlow,auto,auto1) begin if auto ='1' then catt

catt

when "0000001" => catt catt catt catt catt catt catt catt

elsif (clk_in ='1') then case highlow is when "10" => catt catt catt

cat

3. 点阵显示源程序

library ieee;

use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all;

entity 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;

architecture b of dianzhen is signal count : integer range 0 to 6; signal rowt : std_logic_vector(7 downto 0); signal colt : std_logic_vector(7 downto 0);

begin p1:process(clk_in) begin if (clk_in'event and clk_in='1') then if count = 6 then count

--时钟输入 --输入音符 --点阵行向量 --点阵列向量

--用count 来记数

p2: process(count,yin) begin

if (yin = "0000001") then

case count is --点阵显示,表示“1”音符 when 0=> rowt rowt rowt rowt rowt rowt rowt rowt

elsif (yin = "0000010") then

case count is

--点阵显示,表示“2”音符

when 0=> rowt rowt rowt rowt rowt rowt rowt rowt

elsif (yin = "0000100") then case count is --点阵显示,表示“3”音符 when 0=> rowt rowt rowt rowt rowt rowt rowt rowt

elsif (yin = "0001000") then case count is --点阵显示,表示“4”音符 when 0=> rowt rowt

when 2=> rowt rowt rowt rowt rowt rowt

elsif (yin = "0010000") then

case count is --点阵显示,表示“5”音符 when 0=> rowt rowt rowt rowt rowt rowt rowt rowt

elsif (yin = "0100000") then case count is --点阵显示,表示“6”音符 when 0=> rowt rowt rowt rowt rowt rowt rowt rowt

elsif (yin = "1000000") then case count is --点阵显示,表示“7”音符 when 0=> rowt rowt rowt rowt rowt rowt rowt rowt

else

case count is --点阵显示,表示不输入音符 when 0=> rowt rowt rowt rowt rowt rowt rowt rowt

end process p2;

row

4. 选择音符及自动播放源程序

library ieee;

use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all;

entity auto is

port( 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 is signal count : integer range 0 to 35; signal n: integer range 0 to 6; signal yin : std_logic_vector(6 downto 0); begin

p1:process(clk_in,clear) variable i : integer range 0 to 63000000; begin

if clear ='1' then count

elsif (clk_in'event and clk_in='1') then

14

if (i=20000000) then i:=0; if count =15 then count

n

else n

p2:process(count,auto,yin_in,clear)

begin if clear='1' then

yin yin

when 2 => yin yin yin yin yin yin yin yin yin yin yin yin yin

when others => yin yin

15

--自动播放count 记数

--试音播放n 记数

--音符清零 --自动播放歌曲 --3 --7 --3 --6 --5 --6 --1 --3 --5 --3 --3 --4 --2 --4

--试音播放 --1

when 1 => yin yin yin yin yin yin yin

yin

5. 预置分频系数模块源代码

library ieee;

use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all;

entity seletone is port( 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 is signal tone0 :integer range 0 to 2000000; begin

process(highlow,yin)

begin if highlow ="00" then

case yin is when "0000001"=> tone0 tone0 tone0 tone0 tone0 tone0 tone0

when others => tone0

--2 --3 --4 --5 --6 --7

--动手演奏 高低音 --要演奏的音符预置分频系数--中音部分

-- --

end case;

elsif highlow ="10" then case yin is when "0000001"=> tone0 tone0 tone0 tone0 tone0 tone0 tone0 tone0

elsif highlow ="01" then case yin is when "0000001"=> tone0 tone0 tone0 tone0 tone0 tone0 tone0 tone0

6. 分频发音模块源代码

library ieee;

use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all;

entity div is port(clk_in : in std_logic;

tone : in integer range 0 to 2000000; clk_out : out std_logic); end div;

architecture a of div is signal clk_tmp0 : std_logic;

--高音部分

--低音部分

--输入时钟 --预置频率 --输出时钟

signal clk_tmp1: std_logic; begin

p0:process(clk_in, tone) variable cnt : integer range 0 to 49999999; begin if (clk_in'event and clk_in='1') then if cnt

cnt:=cnt+1; clk_tmp0

p1:process(clk_tmp0) variable count :std_logic; begin if(clk_tmp0'event and clk_tmp0='1') then count:=not count; if count ='1' then clk_tmp1

else clk_tmp1

7. 电子琴顶层设计

library ieee;

use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all;

entity dianzq is port(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 is

component 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 0); highlow : in std_logic_vector(1 downto 0); auto : in std_logic; auto1 : in std_logic; duan : out std_logic_vector(7 downto 0); cat : out std_logic_vector(5 downto 0) ); end component;

component auto is port( clk_in : in std_logic; auto : in std_logic; auto1 : in std_logic; yin_in : i n std_logic_vector(6 downto 0); clear : in std_logic; yin_out: out std_logic_vector(6 downto 0)); end component;

19

--自动播放 --试音播放 --复位 --输出时钟 --点阵显示 --点阵显示 --数码管显示 --数码管显示

--分频模块

--点阵显示模块

--数码管显示模块 --自动播放模块

component seletone is --预置分频系数模块 port( highlow: in std_logic_vector(1 downto 0); yin : in std_logic_vector(6 downto 0); tone : out integer range 0 to 2000000); end component;

component div is --分频发音模块 port(clk_in : in std_logic; tone : in integer range 0 to 2000000; clk_out : out std_logic); end component;

signal yin_tmp : std_logic_vector(6 downto 0); signal tone_tmp : integer range 0 to 2000000; signal clk_tmp : std_logic;

begin u1: auto port map

(clk_in=>clk,auto=>auto_in,yin_in=>yin,yin_out=>yin_tmp,clear=>clear,auto1=>auto1);

u2: seletone port map (highlow=>highlow,yin=>yin_tmp,tone=>tone_tmp); u3: shuma port map

(duan=>duan,cat=>cat,yin=>yin_tmp,clk_in=>clk_tmp,highlow=>highlow,auto=>auto_in,auto1=>auto1);

u4: dianzhen port map (row=>row,col=>col,yin=>yin_tmp,clk_in=>clk_tmp); u5: div port map (clk_in=>clk,tone=>tone_tmp,clk_out=>clk_out); u6: div0 port map (clk_in=>clk,clk_tmp=>clk_tmp); end a;

五、 功能说明

初始状态,8×8点阵显示“1 2 3 4 5 6 7”七个音符构成的电子琴键盘,其中点阵的第一列用一个LED 点亮表示音符“1”,第二列用二个LED 点亮表示音符“2”,依此类推,

用BTN1~BTN7七个按键模拟电子琴手动演奏时的“1 2 3 4 5 6 7”七个音符。

当某个按键按下时,数码管显示相应的音符,点阵上与之对应的音符显示列全灭,同时蜂鸣器演奏相应的声音;当按键弹开时数码管显示的音符灭掉,点阵显示恢复,蜂鸣器停止声音的输出。

由拨码开关切换选择高、中、低音,并用数码管进行相应的显示

可通过一个拨码开关进行手动/自动演奏的切换,并与点阵显示配合增加自动演奏乐曲的功能。

20

六、 元器件清单及资源利用情况

1. 元器件清单

基本功能:Altra 公司MaxII 系列EPM71270T144C5开发板一块及电源线、下载线各一根。开发板内元器件利用情况如下:

2. 管脚设置

3. 资源利用情况

七、 故障及问题分析

在编写各个元件程序的时候,我通过仿真查找问题,然后编程修改。单个元件的仿真都通过了。但是整个系统配合起来就不行了。这可能是我各个元件之间的线没连好。于是我重新思考了整个系统的设计,重新画了系统框图。修改程序之后可以下载到板子上面运行,不过效果不太理想。有些问题仿真看不出来, 实际运行时会出问题。就像老师说的, 要设计一个电路总要先用仿真成功之后才实际接线的,但是最后的成品却不一定与仿真时完全一样。所以调试的工作很重要。

下载完后,一开始音不好听,我调整了分频系数后,音调准了。但是音还是不够稳,不够饱满,而且老是有尾音。后来老师说因为这个实验板的按键没有进行防抖,所以波形不太稳。然后我去网上找了防抖的代码。但多数代码都只对单个按键进行防抖的。而电子琴使用按键太多太频繁。后来我又找了对波形进行整形的代码。它对每个输出的音调波形都进行了整形,均衡了占空比。加了整形的进程后,声音一下子好听了许多。效果令人满意。

对手动演奏音符的储存和播放,我想了很久,还是没想明白。我想通过定义一个数组,数组里面的元素是手动演奏输入的7位的向量。通过数组的下标访问数组元素,进行储存或者播放。但是因为储存和读取的速度不一样,所以不能简单的通过计数器来实现。而且我写的程序里面对数组元素的访问是通过下标进行的,而下标的自增在VHDL 里面实现起来有些麻烦。然后我去网上查找资料,网上有用VHDL 语言实现FIFO 队列的结构。

我想用队列来储

存和播放音符应该可以,不过研究了半天还是没明白代码的原理。总之后来因为时间紧张,还是没有完成好这个功能。

八、 总结和结论

本次数字电路与逻辑设计实验,我在MaxII 系列EPM71270T144C5开发板上完成了简易电子琴电路的设计,并在QUARTUS П上仿真实现了电子琴电路的功能。

通过这个实验,我体会很多,对硬件编程有了更多的理解。之前我习惯用C 语言编程,感觉编的很顺利。而VHDL 语言用的很别扭,很多功能在C 语言里面很容易实现,但VHDL 语言里面实现某些功能却很费劲。特别是在VHDL 语言里面,各个进程间的执行是并行的。而且在不同的进程里面不能对同一信号进行赋值。这给我编程带来了一些麻烦。之前我不理解信号和变量的区别。后来慢慢明白,变量是在进程里面定义,只在进程里面有效。而信号可以在不同元件之间传递,相当于导线的功能。我对VHDL 语言里面的信号这个概念有了更深入的理解。

通过这个实验,我学会了使用自顶向下的设计方法来设计系统。先编好底层的元件,然后对底层元件进行连接,互相配合来完成整个系统的功能,

总之这次课程设计顺利完成了。在设计中遇到的一些问题,通过仔细的分析、查找网上资料、与小组同学讨论,最终解决了这些问题,从中学习到了很多。

最后给实验室的老师一些建议,希望实验课的时间能安排好一点。因为这个实验板子不能带回去自己做,所以效率有些低。特别是最后几天的时候,信通院上数电实验的人都把实验室挤满了。


相关内容

  • 全国电子专业大学排名 ---仅供参考
  • 全国电子专业大学排名 ---仅供参考 这里还有其它两个和电磁,微波有关的国家重点实验室(由高能电子研究所管).并说,(在电子通信领域)成电以成为中国会下金蛋的母鸡.----似乎没有提到北邮哟!... 每个学校都有每个学校的优势.清华当然很不错,尤以数字通信,图象处理显长.但它也有不足的地方,如通信网 ...

  • 北邮电子电路综合设计实验报告
  • 北京邮电大学 电子电路综合设计实验报告 课题名称:函数信号发生器的设计 学院:信息与通信工程学院 班级:2013211123 姓名:周亮 学号:2013211123 班内序号:9 一. 摘要 方波与三角波发生器由集成运放电路构成,包括比较器与RC积分器组成. 方波发生器的基本电路由带正反馈的比较器及 ...

  • 北京邮电大学-通信工程导论 (2)
  • 2012‐10‐10 提纲 通信工程专业导论 一. 关于通信:从认知开始 二. 通信发展历史及趋势:历史照亮未来 二 通信发展历史及趋势 历史照亮未来 三. 科研机构和学术团体:成就与责任同在 1 2 三.科研机构和学术团体 贝尔实验室  贝尔实验室(Bell Laboratories), 原是美 ...

  • 在2014级研究生开学典礼上的演讲
  • 同学们,大家好! 为了一个不很方便又不很便宜的研究生公寓,让我们不得不推迟一个月开学,还得举行第二次开学典礼.用bbs上批评家们的话说,"都是扩招惹的祸".(笑声) 其实,研究生的开学典礼单独举行可能更为合理.毕竟,四年的高等教育,使你们与本科新生有着很大的区别.认真论述这种区别 ...

  • 在研究生开学典礼上的讲话:站到科学前沿
  • 同学们,大家好! 为了一个不很方便又不很便宜的研究生公寓,让我们不得不推迟一个月开学,还得举行第二次开学典礼.用 bbs 上批评家们的话说,"都是扩招惹的祸". ( 笑声 ) 其实,研究生的开学典礼单独举行可能更为合理.毕竟,四年的高等教育,使你们与本科新生有着很大的区别.认真论 ...

  • 在研究生毕业典礼上的讲话:建功立业 要趁年轻
  • 当北邮走进 21 世纪,她带着三个明显的特征:一,非常高的学生质量:二,非常高的就业比率:三,非常高的就业年薪. 国务院学位委员会刚刚传来信息,在全国百篇优秀博士论文评选中,我校于建军博士榜上有名.他是 96 年在理学院获硕士学位, 99 年在电信工程学院获博士学位.在攻读博士学位期间和获得学位的一 ...

  • 北邮电子工艺实习实验报告_智能循迹小车
  • 北京邮电大学实习报告 智能循迹小车 摘 要: 本设计是一种基于单片机控制的简易自动寻迹小车系统,包括小车系统构成软硬件设计方法.小车以AT90C52为控制核心, 用单片机控制小车速度.利用红外光电传感器对路面黑色轨迹进行检测,并将路面检测信号反馈给单片机.单片机对采集到的信号予以分析判断,及时控制驱 ...

  • 北邮2012级电磁场与微波技术实验报告
  • 北京邮电大学 电磁场与微波技术测量与仿真实验报告 学院:电子工程学院 班级:2012211204 组员: 执笔人: 实验目的 (1)通过实地测量校园内室内外的无线电信号场强值,掌握室内外电波传播的规律. (2)熟悉并掌握无线电中的传输损耗,路径损耗,穿透损耗,衰落等概念. (3)熟练使用无线电场强仪 ...

  • 北邮电子-数电综合实验报告
  • 数字电路综合实验设计 简易出租车计价器的设计与实现 学院: 电子工程学院 班级: 2011211203 学号: 2011210876 姓名: 孙月鹏 班内序号: 04 摘要 本文介绍了利用Quartus II综合性PLD/FPGA开发软件,在MAXII 数字逻辑实验开发板上实现简易出租车计价器功能的 ...