|
作者:13电信二楠妹
本实验通过检测电脑键盘,根据不同键值来控制数码管的段选变化以及位选变化,并且通过分频器控制蜂鸣器输出不同的音调(⊙o⊙)哦!
先来了解一些关于PS2电脑键盘的通码吧:
以下是关于音调的知识:
频率的高低决定了音调的高低。音乐的十二平均率规定:每两个8度音(如简谱中的中音1与高音1)之间的频率相差一倍。在两个8度音之间,又可分为12个半音,每两个半音的频率比为12√2。?另外,音名A(简谱中的低音6)的频率为440Hz,音名B到C之间、E到F之间为半音,其余为全音[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
|
|