基于Basys2开发板的简易电子琴和音乐播放器设计
背景:华中科技大学 电测综合实验
主要功能:Basys2开发板外接一个扬声器(或无源蜂鸣器也可)实现电子琴和音乐播放器的功能。其中由于开发板上只有4个按键,所以电子琴功能只做了4个音调,分别对应于4个轻触开关。音乐播放器功能需要根据挑选出来的乐谱,人工翻译为代码中对应的音调代码,然后输入到代码的状态机之中。两个功能分别采用两个不同的声道输出,因此两个功能互不干扰,可以同时进行。实现了一个符合综合实验要求的简单项目。因为没有队友,是一个人不到2天的时间赶出来的成果,所以比较简易,给需要的人做一个参考。
ISE工程文件(github链接)(包含实验报告):https://github.com/W-yt/YuTian_Pro/tree/master/piano%26palyer
以下直接粘贴课设报告的主要内容。
如有问题欢迎指正交流。
一、实验目的
1.熟练掌握Basys2的开发流程;
2.提高FPGA综合调试的能力;
二、实验选题及设计目标
实验选题:音乐播放器+电子琴(双声道)
项目目标:本项目要设计实现一个基于BASYS2的多功能电子琴,音乐播放和电子琴各占用一个声道的双声道电子琴。计划采用两个扬声器作为双声道的输出设备,利用拨码开关实现音乐播放器的控制,利用轻触开关实现电子琴的琴键(这里由于BASYS2开发板上只有4个轻触开关,因此电子琴部分仅选用了四个音,仅用来演示电子琴的实现原理,也可以外接按键,或者矩阵键盘拓展)。另外在程序中写定一个音乐的乐谱,实现音乐播放器的演示功能,如果时间允许,会在程序中存储多段音乐,使用拨码开关选择播放曲目。
三、硬件部分
- 实验整体电路
2.扬声器简易驱动电路:
四、软件部分
1.系统主频
获取系统主频由开发板主频经过10分频得到:50MHz分频为5MHz
代码(digital_piano.v):
/* 10分频:50MHz到5Mhz分频 */
always@(posedge inclk)
begin
if(cnt<3'd5)
cnt <= cnt + 3'b1;
else
begin
cnt <= 3'b0;
clk_5MHz <= ~clk_5MHz;
end
end
2.电子琴子模块
读取输入的轻触按键的状态,决定声音频率,再通过计数器实现定频率方波的输出,作为扬声器的驱动信号。其中origin的值为根据各个音调对应的声音频率换算确定的。
代码(piano.v):
/* piano子模块 */
module piano(inclk, clk_5MHz, key_in, beep);
input inclk; //开发板主频
input clk_5MHz; //系统时钟
input [3:0] key_in; //轻触按键输入
output beep; //扬声器输出
wire carry;
reg beep_r;
assign beep = beep_r; //输出音乐
/* 按键转换音调输出 */
always @ (posedge clk_5MHz)
begin
case (key_in[3:0])
/* 消除不按时的噪声 */
4'b0000: origin<=0000;
4'b0001: origin<=6826; // Hdo
4'b0010: origin<=7871; // Hre
4'b0100: origin<=8798; // Hmi
4'b1000: origin<=9224; // Hfa
default: origin<=0000;
endcase
end
assign carry=(count == 16383);
/* 获取驱动信号 */
always @(posedge clk_5MHz)
begin
if(key_in[3:0] == 4'b0000)
begin
end
else
if(carry)
count = origin;
else
count = count + 1;
end
/* 扬声器驱动 */
always @(posedge carry)
begin
beep_r<=~beep_r;
end
endmodule
3.音乐播放器子模块
音乐播放器子模块首先通过分频实现了一个低频时钟(这里采用5Hz),作为音乐的节拍器。然后调用按照程序翻译出来的曲谱函数(这里由于时间原因仅翻译了天空之城一首音乐的一部分)。
代码(song.v):
module song(clk_5MHz, select, beep);
input clk_5MHz;
input select; //音乐播放暂停选项
output beep; //蜂鸣器输出
reg [25:0] cnt2; //计数器
reg clk_5Hz;
wire beep_r;
wire out3; //曲谱的输出
wire clk;
assign beep = beep_r;
/* 调用曲谱模块——模拟有限状态机实现 */
/* 《天空之城》 */
song3 m3(.clk_5MHz(clk_5MHz),.clk_4Hz(clk),.select(select),.beep(out3));
assign beep_r = out3;
assign clk = clk_5Hz;
/* 5hz分频 */
always@(posedge clk_5MHz)
begin
// if(select)
// begin
if(cnt2<25'd400000)
cnt2 <= cnt2+25'b1;
else
begin
cnt2<=25'b0;
clk_5Hz <= ~clk_5Hz;
end
// end
end
endmodule
4.曲谱子模块
这里对天空之城曲谱的一部分进行了翻译,由于钢琴谱比较复杂,且一般为双手谱,这里选择了一个较为简单的吉他谱,按照吉他谱中的简谱进行逐拍翻译。
曲谱如下:
代码(song3.v):
/* 《天空之城》 */
module song3(clk_5MHz,clk_4Hz,select,beep);
input clk_5MHz,clk_4Hz,select; //系统时钟,节拍时钟,播放暂停选项
output beep; //扬声器输出
reg [3:0] high,med,low;
reg [15:0] origin;
reg beep_r;
reg [7:0] state;
reg [15:0] count;
assign beep = beep_r;
/* 扬声器基本驱动 */
always @(posedge clk_5MHz)
begin
/* 计数器 */
count <= count + 1'b1;
If(count == origin)
begin
/* 计数器清零 */
count <= 16'h0;
/* 输出取反 */
beep_r <= !beep_r;
end
end
/* 音调输出转换 */
always@(posedge clk_4Hz)
begin
if(select)
begin
case({high,med,low})
/* 24音转换为方波频率 */
'b000000000001:origin=22900; //低1
'b000000000010:origin=20408; //低2
'b000000000011:origin=18181; //低3
'b000000000100:origin=17142; //低4
'b000000000101:origin=15267; //低5
'b000000000110:origin=13605; //低6
'b000000000111:origin=12121; //低7
'b000000010000:origin=11472; //中1
'b000000100000:origin=10216; //中2
'b000000110000:origin=9101; //中3
'b000000111000:origin=8571; //中4
'b000001010000:origin=7653; //中5
'b000001100000:origin=6818; //中6
'b000010000000:origin=6060; //中7
'b000100000000:origin=5733; //高1
'b001000000000:origin=5108; //高2
'b001100000000:origin=4551; //高3
'b001010000000:origin=4294; //高4
'b010000000000:origin=3826; //高5
'b011000000000:origin=3409; //高6
'b010100000000:origin=3050; //高7
endcase
end
else
/* 消除杂音 */
origin=0000;
end
/* 《天空之城》乐谱翻译 */
always @(posedge clk_4Hz)
begin
if(select)
begin
/* 全曲总节拍数 */
if(state ==193)
/* 自动重放 */
state = 0;
else
/* 节拍计数 */
state = state + 1'b1;
case(state)
/* 按小节间隔 */
/* 1 */
0: {high,med,low}='b000001100000;//中6
1: {high,med,low}='b000010000000;//中7
/* 2 */
2,3,4: {high,med,low}='b000100000000;//高1
5: {high,med,low}='b000010000000;//中7
6,7: {high,med,low}='b000100000000;//高1
8,9: {high,med,low}='b001100000000;//高3
/* 3 */
10,11,12,13,14,15: {high,med,low}='b000010000000;//中7
16,17: high,med,low}='b000000110000;//中3
/* 4 */
18,19,20: {high,med,low}='b000001100000;//中6
21: {high,med,low}='b000001010000;//中5
22,23: {high,med,low}='b000001100000;//中6
24,25: {high,med,low}='b000100000000;//高1
………………………………..
/* 25 */
186,187,188,189,190,191,192,193: {high,med,low}='b001100000000;//高3
endcase
end
end
endmodule
5.管脚定义
代码(digital_piano_ucf.ucf):
NET "inclk" LOC = "B8"; //开发板主频
NET "select" LOC = "N3"; //拨码开关作为播放器暂停选项
/* 双声道输出:电子琴&音乐播放器,互不干扰
NET "out_r" LOC = "D12"; //右声道输出
NET "out_l" LOC = "C9"; //左声道输出
/* 电子琴演示琴键:轻触开关 */
NET "key_in[3]" LOC = "G12";
NET "key_in[2]" LOC = "C11";
NET "key_in[1]" LOC = "M4";
NET "key_in[0]" LOC = "A7";
NET "select" CLOCK_DEDICATED_ROUTE = FALSE;
如有问题欢迎指正交流。
——cloud over sky
——2020/1/18