找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 4107|回复: 1
打印 上一主题 下一主题
收起左侧

基于FPGA的硬件电子琴设计-by楠妹

[复制链接]
跳转到指定楼层
楼主
ID:77367 发表于 2015-4-19 01:25 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式


作者:13电信二楠妹

本实验通过检测电脑键盘,根据不同键值来控制数码管的段选变化以及位选变化,并且通过分频器控制蜂鸣器输出不同的音调(⊙o⊙)哦!

先来了解一些关于PS2电脑键盘的通码吧:

  
KEY
  
通码
断码
KEY
通码
断码
KEY
通码
断码
A
1C
F0 1C
9
46
F0 46
[
54
F0 54
B
32
F0 32
`
0E
F0 0E
INSERT
E0 70
E0 F0 70
C
21
F0 21
-
4E
F0 4E
HOME
E0 6C
E0 F0 6C
D
23
F0 23
=
55
F0 55
PG UP
E0 7D
E0 F0 7D
E
24
F0 24
\
5D
F0 5D
DELETE
E0 71
E0 F0 71
F
2B
F0 2B
BKSP
66
F0 66
END
E0 69
E0 F0 69
G
34
F0 34
SPACE
29
F0 29
PG DN
E0 7A
E0 F0 7A
H
33
F0 33
TAB
0D
F0 0D
U ARROW
E0 75
E0 F0 75
I
43
F0 43
CAPS
58
F0 58
L ARROW
E0 6B
E0 F0 6B
J
3B
F0 3B
L SHFT
12
F0 12
D ARROW
E0 72
E0 F0 72
K
42
F0 42
L CTRL
14
F0 14
R ARROW
E0 74
E0 F0 74
L
4B
F0 4B
L GUI
E0 1F
E0 F0 1F
NUM
77
F0   77
M
3A
F0 3A
L ALT
11
F0 11
KP /
E0 4A
E0 F0 4A
N
31
F0 31
R SHFT
59
F0 59
KP *
7C
F0   7C
O
44
F0 44
R CTRL
E0 14
E0 F0 14
KP -
7B
F0 7B
P
4D
F0 4D
R GUI
E0 27
E0 F0 27
KP +
79
F0 79
Q
15
F0 15
R ALT
E0 11
E0 F0 11
KP EN
E0 5A
E0 F0 5A
R
2D
F0 2D
APPS
E0 2F
E0 F0 2F
KP
71
F0 71
S
1B
F0 1B
ENTER
5A
F0 5A
KP 0
70
F0 70
T
2C
F0 2C
ESC
76
F0 76
KP 1
69
F0 69
U
3C
F0 3C
F1
5
F0 05
KP 2
72
F0 72
V
2A
F0 2A
F2
6
F0 06
KP 3
7A
F0 7A
W
1D
F0 1D
F3
4
F0 04
KP 4
6B
F0 6B
X
22
F0 22
F4
0C
F0 0C
KP 5
73
F0 73
Y
35
F0 35
F5
3
F0 03
KP 6
74
F0 74
Z
1A
F0 1A
F6
0B
F0 0B
KP 7
6C
F0 6C
0
45
F0 45
F7
83
F0 83
KP 8
75
F0 75
1
16
F0 16
F8
0A
F0 0A
KP 9
7D
F0 7D
2
1E
F0 1E
F9
1
F0 01
]
58
F0 58
3
26
F0 26
F10
9
F0 09
;
4C
F0 4C
4
25
F0 25
F11
78
F0 78
'
52
F0 52
5
2E
F0 2E
F12
7
F0 07
,
41
F0 41
6
36
F0 36
PRNTSCRN
E0 12 E0 7C
E0 F0 7C E0 F0 12
.
49
F0 49
7
3D
F0 3D
SCROLL
7E
F0,7E
/
4A
F0 4A
8
3E
F0 3E
PAUSE
E1 14 77
-NONE-
E1 F0 14
F0 77


以下是关于音调的知识:

频率的高低决定了音调的高低。音乐的十二平均率规定:每两个8度音(如简谱中的中音1与高音1)之间的频率相差一倍。在两个8度音之间,又可分为12个半音,每两个半音的频率比为122?另外,音名A(简谱中的低音6)的频率为440Hz,音名BC之间、EF之间为半音,其余为全音[4]。由此可以计算出简谱中从低音1至高音1之间每个音名对应的频率,如下表所示:
                              
所有不同频率的信号都是从同一个基准频率分频得到的。由于音阶频率多为非整数,而分频系数又不能为小数,故必须将计算得到的分频数四舍五入取整。若基准频率过低,则由于分频比太小,四舍五入取整后的误差较大;若基准频率过高,虽然误差变小,但分频数将变大。实际的设计综合考虑这两方面的因素,在尽量减小频率误差的前提下取合适的基准频率[4]。本例中选取500kHz为基准频率。若无500kHz的基准频率,则可以先分频得到500kHz,或换一个新的基准频率。实际上,只要各个音名间的相对频率关系不变,演奏出的乐曲听起来都不会"走调"

下面来看看顶层设计模块吧!O(∩_∩)O~~


下面给出各个模块的代码:

//     电子琴的顶层设计模块

module electronic_organ_top(
       clk,
       rst_n,
       ps2_clk,
       ps2_data,
       wela,
       dula,
       fre_out
       );

input clk;                                    //     系统时钟,50MHz
input rst_n;                                 //     复位信号,低电平有效
input ps2_clk;                             //     PS2键盘的时钟信号
input ps2_data;                            //     PS2键盘的数据信号

output [7:0] wela;                 //     位选信号,高电平有效
output [7:0] dula;                 //     段选信号
output fre_out;

wire [4:0] ps2_byte_wire;

ps2_scan ps2_scan_exam(
       .clk(clk),
       .rst_n(rst_n),
       .ps2_clk(ps2_clk),
       .ps2_data(ps2_data),
       .ps2_byte(ps2_byte_wire)
       );

shumaguan shumaguan_exam(
       .clk(clk),
       .rst_n(rst_n),
       .wela(wela),
       .dula(dula),
       .key_in(ps2_byte_wire)
);

frequency_control frequency_control_exam(
       .clk(clk),
       .rst_n(rst_n),
       .fre_out(fre_out),
       .fre_control(ps2_byte_wire)
       );

endmodule


//     PS2键盘扫描模块

module ps2_scan(
       clk,
       rst_n,
       ps2_clk,
       ps2_data,
       ps2_byte
       );

input clk;                                    //     系统时钟,50MHz
input rst_n;                                 //     复位信号,低电平有效
input ps2_clk;                             //     PS2键盘的时钟信号
input ps2_data;                            //     PS2键盘的数据信号
output reg [4:0] ps2_byte; //  PS2键盘扫描模块输出的5位数据

//-------------------------------------------------------

reg [2:0] ps2_clk_r;
wire ps2_clk_neg;

always @ (posedge clk or negedgerst_n)begin
       if(!rst_n)
              ps2_clk_r<= 3'b000;
       else
              ps2_clk_r<= {ps2_clk_r[1:0] , ps2_clk};
end

assign ps2_clk_neg = ~ps2_clk_r[1] &ps2_clk_r[2];

//-------------------------------------------------------

reg [7:0] data_temp;
reg [3:0] num;

always @ (posedge clk or negedgerst_n)begin
       if(!rst_n)begin
              num<= 4'd0;
              data_temp<= 8'd0;
       end
       elsebegin
              if(ps2_clk_neg)begin
                     num<= num + 1'b1;
                     case(num)
                            4'd1: data_temp[0] <= ps2_data;//第一位是起始位
                            4'd2: data_temp[1] <= ps2_data;
                            4'd3: data_temp[2] <= ps2_data;
                            4'd4: data_temp[3] <= ps2_data;
                            4'd5: data_temp[4] <= ps2_data;
                            4'd6: data_temp[5] <= ps2_data;
                            4'd7: data_temp[6] <= ps2_data;
                            4'd8: data_temp[7] <= ps2_data;
                            default: ;
                     endcase
              end
              else;//不做校验
              if(num== 4'd11)
                     num<= 4'd0;
              else;
              end
end

//-------------------------------------------------------

reg [8:0] ps2_byte_r;

always @ (posedge clk or negedgerst_n)begin
       if(!rst_n)
              ps2_byte_r<= 4'h00;
       elseif(num == 4'd11)begin
              if(data_temp!= 4'hf0)
                     ps2_byte_r<= data_temp;
              end
end

//-------------------------------------------------------

always @ (ps2_byte_r)begin
       case(ps2_byte_r)
              8'h16:ps2_byte <= 5'd1;       //1(键盘值)
              8'h1e:ps2_byte <= 5'd2; //2
              8'h26:ps2_byte <= 5'd3;       //3
              8'h25:ps2_byte <= 5'd4;       //4
              8'h2e:ps2_byte <= 5'd5; //5
              8'h36:ps2_byte <= 5'd6;       //6
              8'h3d:ps2_byte <= 5'd7;       //7

              8'h15:ps2_byte <= 5'd8;       //q   
              8'h1d:ps2_byte <= 5'd9;       //w
              8'h24:ps2_byte <= 5'd10;      //e
              8'h2d:ps2_byte <= 5'd11;      //r
              8'h2c:ps2_byte <= 5'd12;      //t
              8'h35:ps2_byte <= 5'd13;      //y
              8'h3c:ps2_byte <= 5'd14;      //u

              8'h1c:ps2_byte <= 5'd15;      //a   
              8'h1b:ps2_byte <= 5'd16;      //s
              8'h23:ps2_byte <= 5'd17;      //d
              8'h2b:ps2_byte <= 5'd18;      //f
              8'h34:ps2_byte <= 5'd19;      //g
              8'h33:ps2_byte <= 5'd20;      //h
              8'h3b:ps2_byte <= 5'd21;      //j

              default: ps2_byte <= 5'd22;
              endcase
end

endmodule

//     数码管驱动模块

module shumaguan (
       clk,
       rst_n,
       wela,
       dula,
       key_in
);

input clk;                                    //     系统时钟,50MHz
input rst_n;                                 //     复位信号,低电平有效
input [4:0] key_in;

//wire [3:0] key_in;
//assign key_in = 4'd4;

output reg [7:0] wela;           //     位选信号,高电平有效
output reg [7:0] dula;            //     段选信号

//-------------------------------------------------------

parameter seg0 = ~8'h3f,
                      seg1 = ~8'h06,
                      seg2 = ~8'h5b,
                      seg3 = ~8'h4f,
                      seg4 = ~8'h66,
                      seg5 = ~8'h6d,
                      seg6 = ~8'h7d,
                      seg7 = ~8'h07,
                      xiao_ying = 8'hff;

parameter T_FULL = 20'd625000; //     80Hz的动态扫描
parameter T_HALF = 20'd312500;

//-------------------------------------------------------

reg [19:0] cnt;               //用于产生动态扫描的延时

always @ (posedge clk or negedge rst_n)
       if(!rst_n)
              cnt<= 20'd0;
       elseif(cnt <= T_FULL)
              cnt<= cnt + 1'b1;
       else
              cnt<= 20'd0;

//-------------------------------------------------------

always @ (posedge clk or negedge rst_n)
begin
       if(!rst_n)
       begin
              wela<= 8'h11;
              dula<= xiao_ying;
       end
       else
       begin
              seg_dongtai(key_in);
       end
end

//-------------------------------------------------------

task seg_dongtai;                  //     动态扫描任务

       input[4:0] seg_num;
       if(seg_num== 1 || seg_num == 8 || seg_num == 15)begin
              if(cnt<= T_HALF)begin
              wela<= 8'h01;
              dula<= xiao_ying;
              end
              elseif((cnt > T_HALF)&&(cnt <= T_FULL))begin
              wela<= 8'hfe;
              dula<= seg1;
              end
       end
       if(seg_num== 2 || seg_num == 9 || seg_num == 16)begin
              if(cnt<= T_HALF)begin
              wela<= 8'h10;
              dula<= xiao_ying;
              end
              elseif((cnt > T_HALF)&&(cnt <= T_FULL))begin
              wela<= 8'hfd;
              dula<= seg2;
              end
       end
       if(seg_num== 3 || seg_num == 10 || seg_num == 17)begin
              if(cnt<= T_HALF)begin
              wela<= 8'h04;
              dula<= xiao_ying;
              end
              elseif((cnt > T_HALF)&&(cnt <= T_FULL))begin
              wela<= 8'hfb;
              dula<= seg3;
              end
       end
       if(seg_num== 4 || seg_num == 11 || seg_num == 18)begin
              if(cnt<= T_HALF)begin
              wela<= 8'h08;
              dula<= xiao_ying;
              end
              if((cnt> T_HALF)&&(cnt <= T_FULL))begin
              wela<= 8'hf7;
              dula<= seg4;
              end
       end
       if(seg_num== 5 || seg_num == 12 || seg_num == 19)begin
              if(cnt<= T_HALF)begin
              wela<= 8'h10;
              dula<= xiao_ying;
              end
              elseif((cnt > T_HALF)&&(cnt <= T_FULL))begin
              wela<= 8'hef;
              dula<= seg5;
              end
       end
       if(seg_num== 6 || seg_num == 13 || seg_num == 20)begin
              if(cnt<= T_HALF)begin
              wela<= 8'h20;
              dula<= xiao_ying;
              end
              if((cnt> T_HALF)&&(cnt <= T_FULL))begin
              wela<= 8'hdf;
              dula<= seg6;
              end
       end
       if(seg_num== 7 || seg_num == 14 || seg_num == 21)begin
              if(cnt<= T_HALF)begin
              wela<= 8'h40;
              dula<= xiao_ying;
              end
              if((cnt> T_HALF)&&(cnt <= T_FULL))begin
              wela<= 8'hbf;
              dula<= seg7;
              end
       end
       if(seg_num== 22)begin
              wela<= 8'h11;
              dula<= xiao_ying;
       end

endtask           //     注意这是任务的结尾

//-------------------------------------------------------

Endmodule


//     频率控制模块

module frequency_control(
       clk,
       rst_n,
       fre_out,
       fre_control
       );

input clk;                                    //     系统时钟,50MHz
input rst_n;                                 //     复位信号,低电平有效
input [4:0] fre_control;  //     频率控制信号

output reg fre_out;               //     频率输出信号

//-------------------------------------------------------

parameter low1               = 18'd191131,//   低音1
                      low1_half = 18'd95566,               
                      low2              = 18'd170242,//  低音2
                      low2_half = 18'd85121,
                      low3              = 18'd151699,//  低音3
                      low3_half = 18'd75850,
                      low4              = 18'd143184,//  低音4
                      low4_half = 18'd71592,
                      low5              = 18'd127551,//  低音5
                      low5_half = 18'd63776,
                      low6              = 18'd113636,//   低音6
                      low6_half = 18'd56818,
                      low7              = 18'd101235,//  低音7
                      low7_half = 18'd50618,        
                      mid1      = 18'd95547,      //     中音1
                  mid1_half = 18'd47773,
                      mid2     = 18'd85135,    //     中音2
                      mid2_half = 18'd42568,
                      mid3     = 18'd75838,    //     中音3
                      mid3_half = 18'd37919,
                      mid4     = 18'd71480,    //     中音4
                      mid4_half = 18'd35740,
                      mid5     = 18'd63776,    //     中音5
                      mid5_half = 18'd31888,      
                      mid6     = 18'd56818,    //     中音6
                      mid6_half = 18'd28409,
                      mid7     = 18'd50618,    //     中音7
                      mid7_half = 18'd23509,
                      high1      = 18'd47778,      //     高音1
                      high1_half = 18'd23889,      
                      high2      = 18'd42586,      //     高音2
                      high2_half = 18'd21293,
                      high3      = 18'd37893,      //     高音3
                      high3_half = 18'd18947,
                      high4      = 18'd35794,      //     高音4
                      high4_half = 18'd17897,
                      high5      = 18'd31888,      //     高音5
                      high5_half = 18'd15944,
                      high6      = 18'd28409,      //     高音6
                      high6_half = 18'd14205,
                      high7      = 18'd25310,      //     高音7
                      high7_half = 18'd12655;

//-------------------------------------------------------

reg [17:0] fre_reg;
reg [17:0] fre_half_reg;
reg [17:0] fre_reg_r;
reg [17:0] fre_half_reg_r;

always @ (posedge clk or negedge rst_n)
begin
       if(!rst_n)
       begin
              fre_reg_r<= 18'd0;
              fre_half_reg_r<= 18'd0;
       end
       else
       begin
              case(fre_control)
                     5'd1: begin fre_reg_r <= mid1; fre_half_reg_r <= mid1_half; end
                     5'd2: begin fre_reg_r <= mid2; fre_half_reg_r <= mid2_half; end
                     5'd3: begin fre_reg_r <= mid3; fre_half_reg_r <= mid3_half; end
                     5'd4: begin fre_reg_r <= mid4; fre_half_reg_r <= mid4_half; end
                     5'd5: begin fre_reg_r <= mid5; fre_half_reg_r <= mid5_half; end
                     5'd6: begin fre_reg_r <= mid6; fre_half_reg_r <= mid6_half; end
                     5'd7: begin fre_reg_r <= mid7; fre_half_reg_r <= mid7_half; end

                     5'd8: begin fre_reg_r <= low1; fre_half_reg_r <= low1_half; end
                     5'd9: begin fre_reg_r <= low2; fre_half_reg_r <= low2_half; end
                     5'd10: begin fre_reg_r <= low3; fre_half_reg_r <= low3_half; end
                     5'd11: begin fre_reg_r <= low4; fre_half_reg_r <= low4_half; end
                     5'd12: begin fre_reg_r <= low5; fre_half_reg_r <= low5_half; end
                     5'd13: begin fre_reg_r <= low6; fre_half_reg_r <= low6_half; end
                     5'd14: begin fre_reg_r <= low7; fre_half_reg_r <= low7_half; end                 

                     5'd15: begin fre_reg_r <= high1; fre_half_reg_r <= high1_half; end
                     5'd16: begin fre_reg_r <= high2; fre_half_reg_r <= high2_half; end
                     5'd17: begin fre_reg_r <= high3; fre_half_reg_r <= high3_half; end
                     5'd18: begin fre_reg_r <= high4; fre_half_reg_r <= high4_half; end
                     5'd19: begin fre_reg_r <= high5; fre_half_reg_r <= high5_half; end
                     5'd20: begin fre_reg_r <= high6; fre_half_reg_r <= high6_half; end
                     5'd21: begin fre_reg_r <= high7; fre_half_reg_r <= high7_half; end
                     5'd22       : begin fre_reg_r <= 17'd0;fre_half_reg_r <= 17'd0;       end
                     default: ;
                     endcase
       end
end

always @ (fre_control)
begin
       fre_reg<= fre_reg_r;
       fre_half_reg<= fre_half_reg_r;
end

//-------------------------------------------------------

reg [17:0] cnt;

always @ (posedge clk or negedge rst_n)
begin
       if(!rst_n)
       begin
              cnt<= 18'd0;
       end
       elseif(cnt == fre_reg)
       begin
              cnt<= 18'd0;
       end
       else
       begin
              cnt<= cnt + 1'b1;
       end
end

//-------------------------------------------------------

always @ (posedge clk or negedge rst_n)
begin
       if(!rst_n)
       begin
              fre_out= 1'b0;
       end
       elseif(cnt <= fre_half_reg)
       begin
              fre_out= 1'b1;
       end
       else
       begin
              fre_out= 1'b0;
       end
end

//-------------------------------------------------------

endmodule


分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶1 踩
回复

使用道具 举报

沙发
ID:620580 发表于 2019-10-8 20:47 | 只看该作者
管脚分配能看一下吗
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表