基于VHDL的FPGA简易电子琴(实现三音阶切换与弹奏)

目录

一、设计内容简介

二、系统框图

1、简易框图

 2、总电路图(quarters软件自动生成)

三、代码说明 

1、总体代码

2、代码思路

四、仿真实现结果与分析总结

1、仿真思路

2、仿真建立过程

3、仿真结果检验

(1)静音功能检验

(2)琴键功能检验

(3)模式选择功能检验

五、心得体会

六、开发板管脚介绍及管脚对应

1、开发板管脚介绍

2、管脚对应 

七、程序下载到实验板的步骤


一、设计内容简介

        本项目中介绍了电子琴系统的整体的设计,并基于超高速硬件描述语言VHDL在相关的芯片上编程实现的。本设计是采用EDA技术设计的一个简易的十三音符电子琴,该系统基于计算机中时钟分频器的原理,可以通过对时钟脉冲的分频,并根据按键输入设置分频系数,进而控制无源蜂鸣器的发声频率,实现一个简易电子琴的设计。

      基于Quartus Ⅱ软件平台,本小组运用VHDL语言对简易电子琴进行了基础设计,程序仿真以及波形验证。该电子琴的代码中主要实现了分频,消抖,模式选择,琴键发声等功能,超速硬件描述语言VHDL编程实现,能够实现高中低三个音阶的切换,每个音阶对应八个不同的琴键。系统实现是用硬件描述语言VHDL按照模块化方式进行设计,然后进行编程、时序仿真、总体整合。本系统的功能比较齐全,对于实际的电子琴功能仿真程度较高,具有一定的实用价值。

     在实现功能上,该电子琴可以实现三个音阶的按键切换,并设置有钢琴黑键功能,最大程度上模拟了实际使用的电子琴功能,不具备自动弹奏功能,仅能实现手动控制发声。

二、系统框图

1、简易框图

 2、总电路图(quarters软件自动生成)

三、代码说明 

1、总体代码

library IEEE;

use IEEE.STD_LOGIC_1164.ALL;

use IEEE.STD_LOGIC_unsigned.ALL;

use IEEE.STD_LOGIC_arith.ALL;

entity piano3 is

    Port ( CLK : in  STD_LOGIC;

  BB1,BB2: IN STD_LOGIC;--高低八度开关

  BB3: IN STD_LOGIC;--黑键开关

              SS1,SS2,SS3,SS4,SS5,SS6,SS7,SS8: in  STD_LOGIC; --开关1-8

  LED:    out   STD_LOGIC_VECTOR (6 downto 0); --数码管

  EN_LED: OUT STD_LOGIC_VECTOR (7 downto 0);--数码管选择

  RSST: in  STD_LOGIC;--复位/静音

  SND : out STD_LOGIC);--输出声音

end piano3;



architecture Behavioral of piano3 is



SIGNAL   CP0:INTEGER RANGE 0 TO 500000;  --分频计数

SIGNAL CP:STD_LOGIC;  --分频时钟50HZ用以消抖

signal      fm:INTEGER range 0 to 50000000;--声音时长

signal      CT:INTEGER;  --CT为计数最大值决定音阶

signal     CT0:INTEGER range 0 to 50000000;--CT0计数

signal   sound:std_logic;  --输出声音

signal   flag:std_logic;

SIGNAL S1,S2,S3,S4,S5,S6,S7,S8,B1,B2,B3,RST:STD_LOGIC;

SIGNAL MODE:STD_LOGIC_VECTOR(1 DOWNTO 0);

SIGNAL MUL:INTEGER;

signal       HEX:std_logic_vector(3 downto 0);--七段信号

signal       HEXX:std_logic_vector(5 downto 0);

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

begin

SND <= sound;

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

-----------------数码管模块------------------------

HEXX<=HEX&B1&B2;

WITH HEXX SELECT

   LED<= ----------默认

"1111001" when "000000",   --1

         "0100100" when "000100",   --2

         "0110000" when "001000",   --3

         "0011001" when "001100",   --4

         "0010010" when "010000",   --5

         "0000010" when "010100",   --6

         "1111000" when "011000",   --7

         "0000000" when "011100",   --8

      "0111111" when "111100",   ---

"1111001" when "000011",   --1

         "0100100" when "000111",   --2

         "0110000" when "001011",   --3

         "0011001" when "001111",   --4

         "0010010" when "010011",   --5

         "0000010" when "010111",   --6

         "1111000" when "011011",   --7

         "0000000" when "011111",   --8

      "0111111" when "111111",   ---

----------LOW

"1111001" when "000010",   --1

         "0100100" when "000110",   --2

         "0110000" when "001010",   --3

         "0011001" when "001110",   --4

         "0010010" when "010010",   --5

         "0000010" when "010110",   --6

         "1111000" when "011010",   --7

         "0000000" when "011110",   --8

      "1000111" when "111110",   --L

----------HIGH

"1111001" when "000001",   --1

         "0100100" when "000101",   --2

         "0110000" when "001001",   --3

         "0011001" when "001101",   --4

         "0010010" when "010001",   --5

         "0000010" when "010101",   --6

         "1111000" when "011001",   --7

         "0000000" when "011101",   --8

      "0001001" when "111101",   --H

"0111111" when others;

WITH HEX SELECT

   EN_LED<= "11111110" when "0000",   --1

            "11111101" when "0001",   --2

            "11111011" when "0010",   --3

            "11110111" when "0011",   --4

            "11101111" when "0100",   --5

"11011111" when "0101",   --6

"10111111" when "0110",   --7

"01111111" when "0111",   --8

"00000000" when others;   ---

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

----------------消抖模块---------------------------

PROCESS(CLK)

BEGIN

IF RISING_EDGE(CLK) THEN

IF CP0 = 500000 THEN

CP0<=0;

CP <= NOT CP;

ELSE CP0 <=CP0+1;

END IF;

END IF;

END PROCESS;



PROCESS(CP)

BEGIN

IF RISING_EDGE(CP) THEN

S1 <= SS1;

S2 <= SS2;

S3 <= SS3;

S4 <= SS4;

S5 <= SS5;

S6 <= SS6;

S7 <= SS7;

S8 <= SS8;

B1 <= BB1;

B2 <= BB2;

B3 <= BB3;

RST <= RSST;

END IF;

END PROCESS;

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

-----------------高低八度模式选择-------------------

MODE<=B1&B2;

PROCESS(MODE)--S1设置低八度,S2设置高八度,S1和S2相同时为中八度(默认)

BEGIN

IF MODE="00" OR MODE="11" THEN

MUL<=2;

ELSIF MODE="10" THEN

MUL<=4;

ELSIF MODE="01" THEN

MUL<=1;

END IF;

END PROCESS;

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

------------------频率设置模块---------------------

PROCESS(RST,CLK)--根据模式设置声音频率

BEGIN

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

IF B3 = '0' THEN

IF S1='0'    THEN

CT<=47777*MUL;

HEX<="0000";

flag<='1';

ELSIF S2='0' THEN

CT <=42567*MUL;

HEX<="0001";

flag<='1';

ELSIF S3='0' THEN

CT <=37922*MUL;

HEX<="0010";

flag<='1';

ELSIF S4='0' THEN

CT <=35793*MUL;

HEX<="0011";

flag<='1';

ELSIF S5='0' THEN

CT <=31888*MUL;

HEX<="0100";

flag<='1';

ELSIF S6='0' THEN

CT <=28409*MUL;

HEX<="0101";

flag<='1';

ELSIF S7='0' THEN

CT <=25310*MUL;

HEX<="0110";

flag<='1';

ELSIF S8='0' THEN

CT <=23888*MUL;

HEX<="0111";

flag<='1';

ELSE

CT <=0;

HEX<="1111";

flag<='0';

END IF;

ELSIF B3 = '1' THEN  --黑键

IF S1='0'    THEN

CT<=45126*MUL;

HEX<="0000";

flag<='1';

ELSIF S2='0' THEN

CT <=40193*MUL;

HEX<="0001";

flag<='1';

ELSIF S3='0' THEN

CT <=0;-----

HEX<="0010";

flag<='1';

ELSIF S4='0' THEN

CT <=33783*MUL;

HEX<="0011";

flag<='1';

ELSIF S5='0' THEN

CT <=30120*MUL;

HEX<="0100";

flag<='1';

ELSIF S6='0' THEN

CT <=26824*MUL;

HEX<="0101";

flag<='1';

ELSIF S7='0' THEN

CT <=0;-----

HEX<="0110";

flag<='1';

ELSIF S8='0' THEN

CT <=22563*MUL;

HEX<="0111";

flag<='1';

ELSE

CT <=0;

HEX<="1111";

flag<='0';

END IF;

END IF;

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

---------------------核心分频模块------------------

IF RST ='1'  THEN --因为接开关S8所以RST为1时静音,为0时允许发出声音

      sound <='1';

ELSE

IF rising_edge(clk) then

  if flag='1' then

IF CT0 = CT THEN

CT0 <= 0;

SOUND <=NOT SOUND;

ELSIF CT0 /= CT  THEN

CT0 <= CT0 + 1;

ELSE

sound <= '1';

CT0 <= 0;

END IF;

else

   sound <= '1';

CT0 <= 0;

END IF;

end if;

END IF;

end process;

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

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

end Behavioral;

2、代码思路

(1)单个按键发声功能,即基本分频功能实现

    首先编写核心分频模块,实现最简单的按一个键出一个波形的功能,输出声音时长取决于按键时长,可以设置变量CT0来对时钟脉冲进行计数,CT为对应音阶相对应的脉冲计数上限值,CT0每达到该上限值对声音SOUND信号进行翻转,CT0未达到该上限值CT则进行加1。如果RST开关在有效位置,则输出声音SOUND始终为1。

对应的代码如下:

IF RST ='1'  THEN

      sound <='1';

ELSE

IF rising_edge(clk) then

  if flag='1' then

IF CT0 = CT THEN

CT0 <= 0;

SOUND <=NOT SOUND;

ELSIF CT0 /= CT  THEN

CT0 <= CT0 + 1;

ELSE

sound <= '1';

CT0 <= 0;

END IF;

else

   sound <= '1';

CT0 <= 0;

END IF;

end if;

END IF;

end process;

    在仿真时可以先将CT设置为一个较小的数字,便于查看仿真时查看波形,验证基本功能。而在实际上板烧录时,则需要根据实际的不同的音阶及音调频率计算CT值,下图是小组组员根据实际频率计算的CT值。

(2)根据不同频率扩展到8个按键S1,S2,S3,S4,S5,S6,S7,S8

    在核心的分频实现单个按键发声功能后,类似的可以根据计算的计数值表设置好同音阶的另外几个计数值,原理与单个按键类似。这里我们考虑到了烧录到FPGA板子时管脚设置的便利性问题,并没有将八个按键设置为一个长度为8的向量,而是设置了8个不同的变量S1,S2,S3,S4,S5,S6,S7,S8,这样在设置管脚时,只需要一一对应的设置管脚,更加方便快捷。这八个按键作为输入,为反逻辑(0有效),当Si(i=1...8)='0'时,则视为按下按键,发出该按键对应频率的琴音。

IF S1='0'    THEN

CT<=47777*MUL;

HEX<="0000";

flag<='1';

ELSIF S2='0' THEN

CT <=42567*MUL;

HEX<="0001";

flag<='1';

ELSIF S3='0' THEN

CT <=37922*MUL;

HEX<="0010";

flag<='1';

ELSIF S4='0' THEN

CT <=35793*MUL;

HEX<="0011";

flag<='1';

ELSIF S5='0' THEN

CT <=31888*MUL;

HEX<="0100";

flag<='1';

ELSIF S6='0' THEN

CT <=28409*MUL;

HEX<="0101";

flag<='1';

ELSIF S7='0' THEN

CT <=25310*MUL;

HEX<="0110";

flag<='1';

ELSIF S8='0' THEN

CT <=23888*MUL;

HEX<="0111";

flag<='1';

ELSE

CT <=0;

HEX<="1111";

flag<='0';

END IF;

(3)根据高中低三个音阶及黑键功能设置模式选择开关B1,B2,B3

根据三个不同的音阶设定计数初值。为了简化代码,提高运行效率,我们设置了模式选择的中间变量MODE与频率倍数中间变量MUL。其中,我们将B1和B2两个开关的外部输入视为一个两位向量MODE,B1,B2改变时会频率倍数变量MUL赋给不同值(1,2,4),而MUL变量则作为按键发声部分代码后的一个系数变量,改变发声的频率值(详见上2部分代码)。

-----------------高低八度模式选择-------------------

MODE<=B1&B2;

PROCESS(MODE)--S1设置低八度,S2设置高八度,S1和S2相同时为中八度(默认)

BEGIN

IF MODE="00" OR MODE="11" THEN

MUL<=2;

ELSIF MODE="10" THEN

MUL<=4;

ELSIF MODE="01" THEN

MUL<=1;

END IF;

END PROCESS;



将B3单独设置,用于模拟实际钢琴中“黑键”的功能,即B3=1或0时分别算出两种不同的频率计数初值赋予CT。

(4)根据按下琴键的不同设置对应显示的七段码显示管

    本设计中共设计了8个不同的按键,我们根据八个按键由低到高的顺序排序,当按下某一琴键时,显示译码管上就会显示此时按下的按键序号,以及当前对应发声模式:高八度——H,低八度——L,中八度(默认)——不显示。

-----------------数码管模块------------------------

HEXX<=HEX&B1&B2;

WITH HEXX SELECT

   LED<= ----------默认

"1111001" when "000000",   --1

         "0100100" when "000100",   --2

         "0110000" when "001000",   --3

         "0011001" when "001100",   --4

         "0010010" when "010000",   --5

         "0000010" when "010100",   --6

         "1111000" when "011000",   --7

         "0000000" when "011100",   --8

      "0111111" when "111100",   ---

"1111001" when "000011",   --1

         "0100100" when "000111",   --2

         "0110000" when "001011",   --3

         "0011001" when "001111",   --4

         "0010010" when "010011",   --5

         "0000010" when "010111",   --6

         "1111000" when "011011",   --7

         "0000000" when "011111",   --8

      "0111111" when "111111",   ---

----------LOW

"1111001" when "000010",   --1

         "0100100" when "000110",   --2

         "0110000" when "001010",   --3

         "0011001" when "001110",   --4

         "0010010" when "010010",   --5

         "0000010" when "010110",   --6

         "1111000" when "011010",   --7

         "0000000" when "011110",   --8

      "1000111" when "111110",   --L

----------HIGH

"1111001" when "000001",   --1

         "0100100" when "000101",   --2

         "0110000" when "001001",   --3

         "0011001" when "001101",   --4

         "0010010" when "010001",   --5

         "0000010" when "010101",   --6

         "1111000" when "011001",   --7

         "0000000" when "011101",   --8

      "0001001" when "111101",   --H

"0111111" when others;

WITH HEX SELECT

   EN_LED<= "11111110" when "0000",   --1

            "11111101" when "0001",   --2

            "11111011" when "0010",   --3

            "11110111" when "0011",   --4

            "11101111" when "0100",   --5

"11011111" when "0101",   --6

"10111111" when "0110",   --7

"01111111" when "0111",   --8

"00000000" when others;   ---

 

5.实现按键消抖

在代码的基本部分全部实现完成后,我们对8个琴键按键,3个模式选择开关,和一个复位开关加入了消抖代码。其思路为将输入时钟信号CLK进行分频,得到低频中间信号CP0,以所得的CP0

----------------消抖模块---------------------------

PROCESS(CLK)

BEGIN

IF RISING_EDGE(CLK) THEN

IF CP0 = 500000 THEN

CP0<=0;

CP <= NOT CP;

ELSE CP0 <=CP0+1;

END IF;

END IF;

END PROCESS;



PROCESS(CP)

BEGIN

IF RISING_EDGE(CP) THEN

S1 <= SS1;

S2 <= SS2;

S3 <= SS3;

S4 <= SS4;

S5 <= SS5;

S6 <= SS6;

S7 <= SS7;

S8 <= SS8;

B1 <= BB1;

B2 <= BB2;

B3 <= BB3;

RST <= RSST;

END IF;

END PROCESS;

四、仿真实现结果与分析总结

1、仿真思路

  下载程序到开发板前需要仿真查错,由于QURTUS仿真功能时间很短,不能实现真实的音阶频率仿真(50M晶振的频率分频需要很久)于是在填入不同音节对应分频系数前,新建专门用于仿真的工程文件,把分频系数调小到仿真可清晰看出变化的程度。更改后仿真分频系数如下:

PROCESS(CLK)

BEGIN

IF B3 = '0' THEN

IF S1='0'    THEN

CT<=1*MUL;

HEX<="0000";

flag<='1';

ELSIF S2='0' THEN

CT <=2*MUL;

HEX<="0001";

flag<='1';

ELSIF S3='0' THEN

CT <=3*MUL;

HEX<="0010";

flag<='1';

ELSIF S4='0' THEN

CT <=4*MUL;

HEX<="0011";

flag<='1';

ELSIF S5='0' THEN

CT <=5*MUL;

HEX<="0100";

flag<='1';

ELSIF S6='0' THEN

CT <=6*MUL;

HEX<="0101";

flag<='1';

ELSIF S7='0' THEN

CT <=7*MUL;

HEX<="0110";

flag<='1';

ELSIF S8='0' THEN

CT <=8*MUL;

HEX<="0111";

flag<='1';

ELSE

CT <=0;

HEX<="1111";

flag<='0';

END IF;

ELSIF B3 = '1' THEN

IF S1='0'    THEN

CT<=1*MUL;

HEX<="0000";

flag<='1';

ELSIF S2='0' THEN

CT <=2*MUL;

HEX<="0001";

flag<='1';

ELSIF S3='0' THEN

CT <=0;-----

HEX<="0010";

flag<='1';

ELSIF S4='0' THEN

CT <=3*MUL;

HEX<="0011";

flag<='1';

ELSIF S5='0' THEN

CT <=4*MUL;

HEX<="0100";

flag<='1';

ELSIF S6='0' THEN

CT <=5*MUL;

HEX<="0101";

flag<='1';

ELSIF S7='0' THEN

CT <=0;-----

HEX<="0110";

flag<='1';

ELSIF S8='0' THEN

CT <=6*MUL;

HEX<="0111";

flag<='1';

ELSE

CT <=0;

HEX<="1111";

flag<='0';

END IF;

END IF;

2、仿真建立过程

(1)设置ENDTIME为 10us

(2)为了仿真结果更明显,仿真时间内有更多CLK时钟信号,将其设置为周期20ns的方波。

(3)RST为复位键‘1’有效,赋初值,同时RST为1时应实现静音功能。

S1 S2为琴键,1有效,S1最初设置为0检验静音功能,后再RST为‘0’时检验声音分频功能。S2错开S1有效时间有效,检验不同按键功能。

(4)B1 B2为模式选择键,设置如下:

3、仿真结果检验

(1)静音功能检验

 

如上图仿真结果,静音按键下按下琴键,只会有数码管显示,不会对蜂鸣器输出声音。

(2)琴键功能检验

 

如上图,S1和S2分别有效时,输出的频率是2倍的关系,同时具体周期也与仿真代码分频模块中设置相同,同时数码管译码前的数据正确。

(3)模式选择功能检验

如上图仿真结果,不同模式下按下不同琴键对应输出频率和仿真代码模式选择模块中设置相同,模式功能实现。

五、心得体会

电子综合设计是培养我们综合运用所学知识,提出和解决问题能力的重要学习活动。通过这次实践,我们对FPGA的工作原理与使用都有了进一步的理解,更重要的是提高了自主研究学习的能力。

刚开始,我们研读设计要求,通过网络等各种方式查询相关的资料。通过对这些资料的学习,我们大致了解了本次课程设计相关的知识,例如Quartus Ⅱ软件的使用与VHDL语言等,锻炼了我们搜集信息的能力。

开发过程中,通过学习资料,我们对DE2-115板卡的各个部分有了更深层次的了解,也熟悉了一些引脚的分配方法。在编程方面,通过Quartus Ⅱ软件的开发,使我们加深了对FPGA的工作原理的理解,熟悉了VHDL语言的结构和编程规则。通过DE2-115板与外部设备的配合实现我们所需的相关功能。

此次课程设计中,我们遇到了一系列的问题,也看到了自身的不足。代码的编写部分我们花费了大量时间。由于上学期数电中接触过ISE软件,因此一开始我们小组考虑采用ISE进行代码编写与仿真,而实际上板时直接迁移至Quartus Ⅱ软件中,完成课程设计。代码思路也与现在的最终成果思路不太一致,当时我们考虑到板卡中按键数量不够无法满足实际需要,便按照拨动开关作为琴键设计,而非最终的按键开关设计,且触发源我们采用上升沿和下降沿均可以触发,即对于拨动开关,我们在上拨和下拨时都会让琴发出声音,并设定声音延时时长为0.25s,较为符合电子琴的实际效果。

当时在ise软件中,这一思路的代码已经通过了软件编译和波形仿真,能够实现我们想要的初步功能,只差加入显示译码管和消抖功能上板了。但是后来在第一次集中烧录时,我们满怀信心的将原本ISE中的代码迁移至Quartus Ⅱ软件进行编译烧录时,却遇到了很大的问题——代码根本无法通过调试!我们尝试进行修改,但无奈报错实在太多,让我们非常苦恼。后来我们无奈的向老师求助,老师在看了我们的代码后,很确定的告诉我们,我们这种边沿触发的方式是完全错误的,FPGA根本无法执行这样的指令,且由于时钟速度过快,边沿触发也很难进行消抖处理,我们当时就如同晴空霹雳一般,心里想着三个人三个周末的努力居然就这么白白浪费了,真是心有不甘。但后来组员们也镇静下来,最终商讨后决定使用Quartus Ⅱ软件重新编写代码,由一开始的单个按键,重新走一遍设计流程。有了第一次的失败经验,大家的信心有些受挫,甚至有组员考虑过临时更换题目,但后来也都坚持了下来。最终经历了两个周末,第二版代码成功编译并完成仿真,上板烧录,实现了这一实践设计。

从开始设计到最终完成代码编写,我们小组花费了一个月左右的时间,代码的最终实现离不开三位组员共同努力相互帮助,也离不开老师的耐心指导。最后,感谢组员的相互帮助,以及老师的精心指导,让我们小组得以完成这次课程设计。

六、开发板管脚介绍及管脚对应

1、开发板管脚介绍

 

 

 

 

2、管脚对应 

 

 

七、程序下载到实验板的步骤

 1.将设计下载在FPGA 中。点击菜单项Tools->Programmer。打开程序下载环境。

 2.点击Hardware Setup 按钮,选择USB。点击 Close 确认设置。

3.下载程序。在Programmer界面中,将文件列表中Program/Configure 属性勾上,点击Start,精度条显示100%时完成下载。

 

  • 18
    点赞
  • 200
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鸽呜顾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值