EDA实验-----4*4矩阵键盘模拟音符测试(Quartus II )
本实验根据蜂鸣器工作频率不同,从而发出不同的音符的声音。故本实验是将主时钟进行分频,使其分别产生 7 种不同分频因子。通过4*4键盘选择不同的频率输出驱动蜂鸣器。音调低/Hz中/Hz高/HzD02625231046RE2945781175MI3306981397FA3496981397SO3927841568LA4408801760SI4949881967完整的顶层模块原理图如图所示6. 将 ke
目录
1、实验目的
- 了解蜂鸣器的工作原理;
- 掌握4*4矩阵键盘的编程方法。
- 学会用于Verilog语言进行程序设计。
2、实验仪器设备
- PC机一台
- FPGA实验开发系统一套。
3、实验原理
本实验根据蜂鸣器工作频率不同,从而发出不同的音符的声音。故本实验是将主时钟进行分频,使其分别产生 7 种不同分频因子。通过4*4键盘选择不同的频率输出驱动蜂鸣器。
|
音调 |
低/Hz |
中/Hz |
高/Hz |
|
D0 |
262 |
523 |
1046 |
|
RE |
294 |
578 |
1175 |
|
MI |
330 |
698 |
1397 |
|
FA |
349 |
698 |
1397 |
|
SO |
392 |
784 |
1568 |
|
LA |
440 |
880 |
1760 |
|
SI |
494 |
988 |
1967 |
4、实验要求
- 预习教材中的相关内容。
- 阅读并熟悉本次实验的内容。
- 完成实验内容。
5、实验步骤
- 启动 Quartus II 建立一个空白工程,选择的器件为 Altera 公司的 Cyclone 系列的 EP2C8Q240C8 芯片,命名为 keyarray.qpf;
- 新建一个 Schematic File 文件,命名为 keyarray.bdf;
- 分别新建两个 Verilog HDL File 文件,分别命名为 keyarraycontrol.v,note.v。输入程序代码并保存,然后进行综合编译。若在编译过程中发现错误,则找出错误并更正错误,直至编译成功为止。
- 从设计文件创建模块(FileàCreat UpdateàCreat Symbol Files for Current File),keyarraycontrol.v 生成名为 keyarraycontrol.bsf;note.v 生成名为 note.bsf;
- 在 keyarray.bdf 文件中,在空白处双击鼠标左键,添加锁相环模块(altpll)其配置参数为输入 50MHz,输出 10MHz。在 Symbol 对话框左上角的 libraries 中,分别将 Project 下的 keyarraycontrol、note 模块放在图形文件 keyarray.bdf 中,加入输入、输出引脚,双击每个引脚,进行引脚命名,并锁定管脚,将未使用的引脚设置为三态输入(一定要设置,否则可能会损坏芯片);

完整的顶层模块原理图如图所示

6. 将 keyarray.bdf 设置为顶层实体。对该工程文件进行全程编译处理,若在编译过程中出现 错误,则找出错误并更正,直至编译通过为止;
7. 将 USB-Blaster 下载电缆的两端分别连接到 PC 机的 USB 接口和 EDA 实验箱上的 JTAG 下载口上,打开电源,执行下载命令,把程序下载到 FPGA 器件中,此时,即可在 EDA 实 验箱上通过按下相应的按键使蜂鸣器发出对应的音符声响。
6、实验报告
- 总结Verilog设计多路选择器使用的最基本与核心的语法知识。
- 对仿真的结果进行分析。
- 讨论自己在设计过程中遇到的问题、解决的过程以及收获体会。
7、实验过程
基本流程为创建项目、创建Verilog文件、写代码、进行波形仿真、画出电路图、设置管脚和三态、烧录文件。这里我就不去做演示了,下面主要去讲原理。
(1)矩阵按键原理

矩阵按键模块是先按行选取到某一行,然后再选列,跟矩阵选择某一个点的原理是一样的,如果按下这个按键的时候,此时两边的开关是接通的,这时候就会返回到一个矩阵按键回馈的信息,我们只需要去读取到这个信息,然后再根据行列的相关位置,把这个信息转换为相对于的数字返回即可。
Verilog代码(读取到矩阵按键按下的位置,输出相对于的数字):
module keyarraycontrol(clk,rst,row,col,keydata);
input clk;
input rst;
input [3:0] row;
output reg[3:0] col;
output reg[3:0] keydata;
reg keyint;
reg [19:0] cnt;
//分频获得键盘扫描频率
always @ (posedge clk, negedge rst)
if (!rst)
cnt <= 0;
else
cnt <= cnt + 1'b1;
//将计数的最高位赋给key_clk
wire key_clk = cnt[19]; // (2^20/50M = 21)ms
//设定扫描状态判断参数
parameter NO_KEY_PRESSED = 6'b000_001; // 如果没有按键按下的时候
parameter SCAN_COL0 = 6'b000_010; // 按下第一行按键
parameter SCAN_COL1 = 6'b000_100; // 按下第二行按键
parameter SCAN_COL2 = 6'b001_000; // 按下第三行按键
parameter SCAN_COL3 = 6'b010_000; // 按下第四行按键
parameter KEY_PRESSED = 6'b100_000; // 有按键按下状态
reg [5:0] current_state, next_state; // 当前状态,,,下一个状态
always @ (posedge key_clk, negedge rst)
if (!rst)
current_state <= NO_KEY_PRESSED;
else
current_state <= next_state;
//
always @ *
case (current_state)
NO_KEY_PRESSED : //
if (row != 4'hF)
next_state = SCAN_COL0;
else
next_state = NO_KEY_PRESSED;
SCAN_COL0 : //
if (row != 4'hF)
next_state = KEY_PRESSED;
else
next_state = SCAN_COL1;
SCAN_COL1 : //
if (row != 4'hF)
next_state = KEY_PRESSED;
else
next_state = SCAN_COL2;
SCAN_COL2 : //
if (row != 4'hF)
next_state = KEY_PRESSED;
else
next_state = SCAN_COL3;
SCAN_COL3 : //
if (row != 4'hF)
next_state = KEY_PRESSED;
else
next_state = NO_KEY_PRESSED;
KEY_PRESSED : //
if (row != 4'hF)
next_state = KEY_PRESSED;
else
next_state = NO_KEY_PRESSED;
endcase
reg [3:0] col_val, row_val; //
always @ (posedge key_clk, negedge rst)
if (!rst)
begin
col<= 4'h0;
keyint<=0;
end
else
case (next_state)
NO_KEY_PRESSED : //
begin
col <= 4'h0;
keyint <= 0; //
end
SCAN_COL0 : //
col <= 4'b1110;
SCAN_COL1 :
col <= 4'b1101;
SCAN_COL2 : //
col <= 4'b1011;
SCAN_COL3 : //
col <= 4'b0111;
KEY_PRESSED : //
begin
col_val<= col; // 得到列的值
row_val<= row; // 得到行的值
keyint <= 1; //
end
endcase
always @ (posedge key_clk, negedge rst)
if (!rst)
keydata <= 16'h0000;
else
if (keyint)
case ({col_val, row_val})
8'b1110_1110 : keydata <= 8'd0;
8'b1110_1101 : keydata <= 8'd4;
8'b1110_1011 : keydata <= 8'd8;
8'b1110_0111 : keydata <= 8'd12;
8'b1101_1110 : keydata <= 8'd1;
8'b1101_1101 : keydata <= 8'd5;
8'b1101_1011 : keydata <= 8'd9;
8'b1101_0111 : keydata <= 8'd13;
8'b1011_1110 : keydata <= 8'd2;
8'b1011_1101 : keydata <= 8'd6;
8'b1011_1011 : keydata <= 8'd10;
8'b1011_0111 : keydata <= 8'd14;
8'b0111_1110 : keydata <= 8'd3;
8'b0111_1101 : keydata <= 8'd7;
8'b0111_1011 : keydata <= 8'd11;
8'b0111_0111 : keydata <= 8'd15;
default: keydata <= keydata;
endcase
else
keydata <= keydata;
endmodule
(2)音符发音原理
以下是不同音符发音的频率:
|
音调 |
低/Hz |
中/Hz |
高/Hz |
|
D0 |
262 |
523 |
1046 |
|
RE |
294 |
578 |
1175 |
|
MI |
330 |
698 |
1397 |
|
FA |
349 |
698 |
1397 |
|
SO |
392 |
784 |
1568 |
|
LA |
440 |
880 |
1760 |
|
SI |
494 |
988 |
1967 |
前面我们创建了一个获取矩阵按键返回值的代码,这里我们需要去创建一个verilog文件来去对返回值进行分频操作,代码如下:
module note(clk, divcnt, out);
input clk;
input[3:0] divcnt;
output out;
reg out;
reg[15:0] cnt_0, cnt_4, cnt_8, cnt_12, cnt_1, cnt_5, cnt_9;
always@(posedge clk)
begin
if (divcnt == 8'd0)//do√
begin
if (cnt_0 == 4780)
begin
out = out + 1;
cnt_0 = 0;
end
else
begin
cnt_0 = cnt_0 + 1;
end
end
else if (divcnt == 8'd4)//re√
begin
if (cnt_4 == 4255)
begin
out = out + 1;
cnt_4 = 0;
end
else
begin
cnt_4 = cnt_4 + 1;
end
end
else if (divcnt == 8'd8)//mi√
begin
if (cnt_8 == 3794)
begin
out = out + 1;
cnt_8 = 0;
end
else
begin
cnt_8 = cnt_8 + 1;
end
end
else if (divcnt == 8'd12)//fa√
begin
if (cnt_12 == 3579)
begin
out = out + 1;
cnt_12 = 0;
end
else
begin
cnt_12 = cnt_12 + 1;
end
end
else if (divcnt == 8'd1)//so√
begin
if (cnt_1 == 3189)
begin
out = out + 1;
cnt_1 = 0;
end
else
begin
cnt_1 = cnt_1 + 1;
end
end
else if (divcnt == 8'd5)//la√
begin
if (cnt_5 == 2841)
begin
out = out + 1;
cnt_5 = 0;
end
else
begin
cnt_5 = cnt_5 + 1;
end
end
else if (divcnt == 8'd9)//si√
begin
if (cnt_9 == 2542)
begin
out = out + 1;
cnt_9 = 0;
end
else
begin
cnt_9 = cnt_9 + 1;
end
end
end
endmodule
(3)电路连接
写好了Verilog代码,就进行分析错误,分析无误后,我们就对这些代码生成子模块文件。然后就创建block文件开始连接电路图,电路图以及管脚配置如下:

(4)文件烧录
弄好了之后就是最后一步操作了,把没用到的管脚设置三态,然后烧录文件。
点击Assignment, Device

然后点击这里,设置管脚。
选择第一个就行了,就是把多余的管脚设置三态。

点击此处,烧录文件。 
这里我们会看到,下面有一个芯片,这个也就是我们写好了的sof文件,然后就是通过你的电脑接口去连接到开发板,如果你看到上面有一个No Hardware的时候,你点击旁边的按钮进行接口设置,设置为USB接口即可(USB线连接了你的开发板就会自动显示出来的)。最后点击start就可以进行烧录了。
以上就是本期的全部内容了,我们下次见!
分享一张壁纸:
更多推荐



所有评论(0)