|
作者: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
|
|