本次实验使用硬件描述语言 Verilog HDL,针对以 Altera 公司的 MAX II 系列可编程器 件 EPM1270T144C5 为核心芯片的可编程器件实验板设计实验简易电子演奏琴,其基本功能 包括如下:
(1)通过用 8×8 点阵显示“1 2 3 4 5 6 7”七个音符构成的电子琴键盘。其中点阵的第 一列用一个 LED 点亮表示音符“1”,第二列用二个 LED 点亮表示音符“2”,依此类推,如图 1 所示。当音符为低音 1~7 时,点阵显示为绿色;当音符为中音 1~7 时,点阵显示为红色; 当音符为高音 1~7 时,点阵显示为黄色。
(2)用 BTN7~BTN1 七个按键模拟电子琴手动演奏时的“1 2 3 4 5 6 7”七个音符。当 某个按键按下时,数码管 DISP7 显示相应的音符,点阵上与之对应的音符显示列全灭,同 时蜂鸣器演奏相应的声音;当按键放开时数码管显示的音符灭掉,点阵显示恢复,蜂鸣器停 止声音的输出。
(3)由拨码开关切换选择高、中、低音,点阵颜色进行相应变化。
(4)可通过拨码开关 SW0 进行手动/自动演奏的切换,自动演奏时,点阵根据乐曲进 行颜 色和亮灭的变化。 该实验设计过程中,将整个门铃分为 8 个模块:分频模块,演奏模块,点阵显示模块, 数码管显示模块。
最后通过对程序的调试、相应部分功能的仿真以及在电路板的烧写,验证了整个系统的原理和本设计方案的正确性。
完整工程文件: https://download.csdn.net/download/weixin_45817309/14968048
根据声乐知识,产生音乐的两个因素是音乐频率的持续时间,音乐的十二平均率规定, 每两个八音度之间的频率相差一倍,在两个八音度之间,又可分为 12 个半音。每两个半音 的频率比为 4。另外,音名 A(乐谱中的低音 6)的频率为 440HZ,音名 B 到 C 之间,E 到 F 之间为半音,其余为全音。由此可以计算出乐谱中从低音 1 到高音 1 之间每个音名的频率如表1所示
用 8×8 点阵显示“1 2 3 4 5 6 7”七个音符构成的电子琴键盘。其中点阵的第一列用一个 LED 点亮表示音符“1”,第二列用二个 LED 点亮表示音符“2”,依此类推,如图 1 所示。当音符为低音 1~7 时,点阵显示为绿色;当音符为中音 1~7 时,点阵显示为红色;当音符为高音 1~7 时,点阵显示为黄色;如图1所示。
用 BTN7~BTN1 七个按键模拟电子琴手动演奏时的“1 2 3 4 5 6 7”七个音符。当 某个按键按下时,数码管 DISP7 显示相应的音符,点阵上与之对应的音符显示列全灭,同时蜂鸣器演奏相应的声音;当按键放开时数码管显示的音符灭掉,点阵显示恢复,蜂鸣器停止声音的输出。图 2 为演奏中音 3(BTN5 按下)时点阵的显示情况。
由拨码开关切换选择高、中、低音,点阵颜色进行相应变化。
可通过一个拨码开关进行手动/自动演奏的切换,自动演奏时,点阵根据乐曲进行颜色和亮灭的变化。
Verilog HDL是硬件描述语言的一种,用于数字电子系统设计。它允许设计者用它来进行各种级别的逻辑设计,可以用它进行数字逻辑系统的仿真验证、时序分析、逻辑综合。它是目前应用最广泛的一种硬件描述语言之一。
针对本实验所需要实现的基本要求,笔者将设计任务拆分为点阵扫描模块、数码管显示模块、音乐播放模块三大模块进行设计。其中,该设计的顶层模块与音乐播放模块直接相连,即将输入和输出与音乐播放模块直接关联。在音乐播放模块中,根据得到的输入信号的不同决定所播放的音调和传递给点阵扫描模块、数码管显示模块的参数,从而完成实验题目所需要满足的基本要求。
点阵模块在可编程实验板上使用的是 8×8 红绿双色点阵,由8行8列共 64 个红色发光二极管和 8 行 8 列 64 个绿色发光二极管封装在一元器件上面构成的。元器件对外引出 24 条控制线,分别为行信号(发光二极管公共端)ROW7-ROW0、红色发光二极管列信号 R_COL7-R_COL0 和绿色发光二极管列信号 G_COL7-G_COL0。点阵模块的构造示意图如图 3 所示。
由图3可以得出:
(1)点阵上某个点显示红色的条件:控制该点行的引脚输出低电平,控制该点红色发光二极管列信号引脚输出高电平,控制该点绿色发光二极管列信号引脚输出低电平。
(2)点阵上某个点显示绿色的条件:控制该点行的引脚输出低电平,控制该点红色发光二极管列信号引脚输出低电平,控制该点绿色发光二极管列信号引脚输出 高电平。
(3)点阵上某个点显示黄色的条件:控制该点行的引脚输出低电平,控制该点红色发光二极管列信号引脚输出高电平,控制该点绿色发光二极管列信号引脚输出高电平。
由于该可编程实验班的点阵中,绿色发出的光强明显低于红色发出的光强,因此在产生黄色的时候,需要对红光和绿光进行分频。经过试验发现,当红光和绿光的占空比为1:3时,产生的效果较为明显。
基于上述知识,再利用人眼的视觉暂留现象,使用1MHZ的系统时钟,对点阵进行行扫描,即可轻松地显示所需要的图像。具体的实现方法笔者将在3.2节中详细介绍。
7 段数码管模块由8个7段数码管组成,数码管编号分别为DISP7-DISP0。在数码管右边电路板上有各段的编号图示。本开发板上8个7段数码管的段码输入端是并联在一起的,8个位码相互独立。当 AA-AP 控制引脚输出高电平,同时需要显示的数码管的共阴极端 CAT为低电平时,该数码管相应的端就点亮。
根据该可编程实验板的特点,当使用数码管DS6和DS7时应将拨码开关SW10的第3位和第4位拨到上边。当拨码开关SW10的第3位和第4位拨到下边时数码管DS6和DS7的位码控制端CAT6和CAT7不受EPM1270T144C5 芯片的引脚PIN_30和PIN_31控制,此时EPM1270T144C5芯片的引脚PIN_30和PIN_31作为外部扩展接口使用。
数码管显示的原理图如图4所示。根据原理图和可编程实验板的说明书,点亮相应位置的段码即可在数码管上呈现出所需要的数字。
频率的高低决定了音调的高低。音乐的十二平均率规定:每两个 8 度音(如简谱中的中音 1 与高音 1)之间的频率相差一倍。在两个 8度音之间,又可分为 12个半音,每两个半音的频率比为 12 2 12\sqrt{2} 122。 另外,音名 A(简谱中的低音 6)的频率为 440Hz,音名 B到 C之间、E到 F之间为半音,其余为全音。简谱中音名与频率的关系如1.1节中表1所示。
所有不同频率的信号都是从同一个基准频率分频得到的。由于音阶频率多为非整数,而分频系数又不能为小数,故必须将计算得到的分频数四舍五入取整。若基准频率过低,则由于分频比太小,四舍五入取整后的误差较大;若基准频率过高,虽然误差变小,但分频数将变大。实际的设计综合考虑这两方面的因素,在尽量减小频率误差的前提下取合适的基准频率。基于点阵显示模块对于系统时钟的要求,本实验中笔者对1MHZ的时钟进行分频,进而输出到发声器件。实际上,只要各个音名间的相对频率关系不变,演奏出的乐曲听起来都不会 “走调”。
本实验需要演奏从低音1到高音7跨越三个八度的音符,因此通过简单的计算可以得出,在基准频率为1MHZ时,简谱中各音阶相应的分频系数如表2所示。
此外,对于乐曲中的休止符,只需要将输出到发声器件的变量赋值为0,发声器件便不会发出声音。
音符的持续时间必须根据乐曲的速度及每个音符的节拍数来确定。该实验中音长的控制主要应用于自动演奏模块中。本实验演奏的《天空之城》片段,最短的音符为4分音符,假设一个全音符持续的时间为1秒,则4分音符所需要持续的时间为0.25秒,即需要通过分频提供一个4HZ的时钟信号,从而控制音长。
控制音长是通过控制计数器预置数的停留时间来实现的,预置数停留的时间越长,则该音符演奏的时间越长。每个音符的演奏时间都是0.25秒的整数倍,对于节拍较长的音符,如 2 分音符,在自动播放的时候连续输出两次该音符即可。
该实验设计时的总体技术框图如图5所示。
clk 表示输入时钟信号,本实验中选用的是 1MHZ 的系统时钟。
auto_sw 表示自动播放开关,当 auto_sw 为 1 时,开始自动播放乐曲《天空之城》;当 auto_sw 为 0 时,为手动演奏状态。
SW 表示手动演奏的音域切换。当 SW=3’b100 时,演奏低音音域;当 SW=3’b010 时, 演奏中音音域;当 SW=3’b001 时,演奏高音音域。
BTN 表示手动演奏的音符。BTN[0]至 BTN[6]分别表示音符 1、2、3、4、5、6、7,按 下按键时蜂鸣器即产生响应的音符。
beep 表示输出给蜂鸣器的信号。
row, col_r, col_g 分别表示输出给点阵列的行、列信号。
seg 表示控制八段数码管的输出信号,CAT 为控制八段数码管的共阴极端信号。
由于该模块为程序的顶层模块,因此主要功能为定义输入输出信号和调用功能模块。具体的Verilog HDL语言实现如下:
1. //电子琴顶层模块
2. module electronic_organ(clk, auto_sw, SW, BTN,
3. beep, row, col_r, col_g, seg, CAT);
4. input [2:0]SW;
5. input [6:0]BTN;
6. input clk, auto_sw;
7. output beep; //蜂鸣器初始化
8. output [7:0]row, col_r, col_g, seg;
9. output reg [7:0]CAT = 7'b0111_1111; //仅7号数码管亮
10.
11. //输出音调
12. play p1(.clk(clk),
13. .auto_sw(auto_sw),
14. .key(BTN),
15. .SW(SW),
16. .beep(beep),
17. .row(row),
18. .col_r(col_r),
19. .col_g(col_g),
20. .seg(seg));
21.
22.
23. endmodule
由于本实验中需要发出不同音调的声音,分频模块是必不可少的一个模块。笔者使用的是计数器对时钟信号进行分频。其中,clk表示输入的系统时钟信号,clk_out表示分频后输出的时钟信号。
在该模块设计中,笔者首先定义一个用于分频输出的时钟信号clk_out,计数器随着系统时钟工作,计数器计数达到预置位前,clk_out始终保持原值。当计数达到预置位时,将clk_out翻转一次,从而可以通过改变预置位得到需要频率的时钟信号。
该模块具体的Verilog HDL语言实现如下:
1. /*
2. * Name: division.v
3. * Author: lgc0208
4. * Date: 2020-11-22
5. * Function: 时钟分频,对时钟信号进行分频,输出信号为输入信号频率的1/2DIV_NUM倍
6. * Input: 时钟信号clk
7. * Output: 分频后的信号clk_out
8. */
9. module division(clk, clk_out);
10.
11. parameter DIV_NUM = 1; //对时钟进行分频
12.
13. input clk;
14. output reg clk_out;
15. reg [19:0]count;
16.
17. always@(posedge clk)
18. begin
19. if(count == DIV_NUM)
20. begin
21. clk_out <= !clk_out;
22. count <= 0;
23. end
24. else
25. count <= count + 1;
26. end
27.
28. endmodule
在本实验中,点阵列的显示需要随着音调的变化而产生相应的变化,因此需要点阵列扫描模块来实现相应的功能。在点阵列的显示中,常用的扫描方式有行扫描和列扫描。使用列扫描时,容易出现同一列的灯光颜色亮度有较大差异,因此笔者此处选择了行扫描的方式扫描点阵。
同时,由于该实验所使用的可编程实验板点阵列中绿光的强度要明显低于红光,因此在产生黄光时需要对绿光和红光进行占空比的调整。在实验中发现,红光和绿光占空比为1:3时产生的效果较好。
该模块中,clk表示输入的时钟信号,SW表示颜色显示的切换信号,key表示所获得的按键信号,row,col_r,col_g分别表示控制点阵的行信号、控制红色发光二极管的列信号和控制绿色发光二极管的列信号。
该模块具体的Verilog HDL语言实现如下:
1. /*
2. * Name: show_LED.v
3. * Author: lgc0208
4. * Date: 2020-11-21
5. * Function:在点阵列上显示不同颜色
6. * Input: 时钟信号clk,颜色显示切换[2:0]SW,按键信号key
7. * Output: 控制点阵的行信号row,红色列信号col_r,绿色列信号col_g
8. *
9. * Update: 2020-11-22
10. * Author: LIN Guocheng
11. * Function: 按下按键后,对应列灭。
12. */
13.
14. module show_LED(clk, SW, key, row,col_r, col_g);
15. input clk;
16. input [2:0]SW;
17. input [6:0]key;
18. output reg [7:0]row,col_r, col_g;
19. reg [2:0] count;
20.
21. wire clk_3r, clk_1g;
22. reg [2:0] count_r, count_g;
23.
24. //分频,红绿光占空比1:3,显示黄色
25. division #(1/8) d1r(.clk(clk), .clk_out(clk_3r));
26. division #(3/8) d2g(.clk(clk), .clk_out(clk_1g));
27.
28. //模8计数器
29. always @(posedge clk)
30. begin
31. if(count == 3'b111)
32. count <= 3'b000;
33. else
34. count <= count + 1'b1;
35. end
36.
37. always @(posedge clk_3r)
38. begin
39. if(count_r == 3'b111)
40. count_r <= 3'b000;
41. else
42. count_r <= count_r + 1'b1;
43. end
44. always @(posedge clk_1g)
45. begin
46. if(count_g == 3'b111)
47. count_g <= 3'b000;
48. else
49. count_g <= count_g + 1'b1;
50. end
51.
52.
53.
54. //点阵显示部分
55. always@(count or count_r or count_g)
56. begin
57. //高音部分,显示黄色
58. if(SW == 3'b001)
59. begin
60. case(count_r)
61. 3'b000:begin row <= 8'b1111_1111; col_r <= 8'b0000_0000; end
62. 3'b001:begin row <= 8'b1011_1111; col_r <= 8'b0100_0000; end
63. 3'b010:begin row <= 8'b1101_1111; col_r <= 8'b0110_0000; end
64. 3'b011:begin row <= 8'b1110_1111; col_r <= 8'b0111_0000; end
65. 3'b100:begin row <= 8'b1111_0111; col_r <= 8'b0111_1000; end
66. 3'b101:begin row <= 8'b1111_1011; col_r <= 8'b0111_1100; end
67. 3'b110:begin row <= 8'b1111_1101; col_r <= 8'b0111_1110; end
68. 3'b111:begin row <= 8'b1111_1110; col_r <= 8'b0111_1111; end
69. endcase
70. case(count_g)
71. 3'b000:begin row <= 8'b1111_1111; col_g <= 8'b0000_0000; end
72. 3'b001:begin row <= 8'b1011_1111; col_g <= 8'b0100_0000; end
73. 3'b010:begin row <= 8'b1101_1111; col_g <= 8'b0110_0000; end
74. 3'b011:begin row <= 8'b1110_1111; col_g <= 8'b0111_0000; end
75. 3'b100:begin row <= 8'b1111_0111; col_g <= 8'b0111_1000; end
76. 3'b101:begin row <= 8'b1111_1011; col_g <= 8'b0111_1100; end
77. 3'b110:begin row <= 8'b1111_1101; col_g <= 8'b0111_1110; end
78. 3'b111:begin row <= 8'b1111_1110; col_g <= 8'b0111_1111; end
79. endcase
80.
81. end
82. //中音部分,显示红色
83. else if(SW == 3'b010)
84. begin
85. case(count)
86. 3'b000:begin row <= 8'b1111_1111; col_r <= 8'b0000_0000; col_g <= 8'b0000_0000; end
87. 3'b001:begin row <= 8'b1011_1111; col_r <= 8'b0100_0000; col_g <= 8'b0000_0000; end
88. 3'b010:begin row <= 8'b1101_1111; col_r <= 8'b0110_0000; col_g <= 8'b0000_0000; end
89. 3'b011:begin row <= 8'b1110_1111; col_r <= 8'b0111_0000; col_g <= 8'b0000_0000; end
90. 3'b100:begin row <= 8'b1111_0111; col_r <= 8'b0111_1000; col_g <= 8'b0000_0000; end
91. 3'b101:begin row <= 8'b1111_1011; col_r <= 8'b0111_1100; col_g <= 8'b0000_0000; end
92. 3'b110:begin row <= 8'b1111_1101; col_r <= 8'b0111_1110; col_g <= 8'b0000_0000; end
93. 3'b111:begin row <= 8'b1111_1110; col_r <= 8'b0111_1111; col_g <= 8'b0000_0000; end
94. endcase
95. end
96. //低音部分,显示绿色
97. else if(SW == 3'b100)
98. begin
99. case(count)
100. 3'b000:begin row <= 8'b1111_1111; col_r <= 8'b0000_0000; col_g <= 8'b0000_0000; end
101. 3'b001:begin row <= 8'b1011_1111; col_r <= 8'b0000_0000; col_g <= 8'b0100_0000; end
102. 3'b010:begin row <= 8'b1101_1111; col_r <= 8'b0000_0000; col_g <= 8'b0110_0000; end
103. 3'b011:begin row <= 8'b1110_1111; col_r <= 8'b0000_0000; col_g <= 8'b0111_0000; end
104. 3'b100:begin row <= 8'b1111_0111; col_r <= 8'b0000_0000; col_g <= 8'b0111_1000; end
105. 3'b101:begin row <= 8'b1111_1011; col_r <= 8'b0000_0000; col_g <= 8'b0111_1100; end
106. 3'b110:begin row <= 8'b1111_1101; col_r <= 8'b0000_0000; col_g <= 8'b0111_1110; end
107. 3'b111:begin row <= 8'b1111_1110; col_r <= 8'b0000_0000; col_g <= 8'b0111_1111; end
108. endcase
109. end
110. else
111. begin
112. case(count)
113. 3'b000:begin row <= 8'b0000_0000; col_r <= 8'b0000_0000; col_g <= 8'b0000_0000; end
114. 3'b001:begin row <= 8'b0000_0000; col_r <= 8'b0000_0000; col_g <= 8'b0000_0000; end
115. 3'b010:begin row <= 8'b0000_0000; col_r <= 8'b0000_0000; col_g <= 8'b0000_0000; end
116. 3'b011:begin row <= 8'b0000_0000; col_r <= 8'b0000_0000; col_g <= 8'b0000_0000; end
117. 3'b100:begin row <= 8'b0000_0000; col_r <= 8'b0000_0000; col_g <= 8'b0000_0000; end
118. 3'b101:begin row <= 8'b0000_0000; col_r <= 8'b0000_0000; col_g <= 8'b0000_0000; end
119. 3'b110:begin row <= 8'b0000_0000; col_r <= 8'b0000_0000; col_g <= 8'b0000_0000; end
120. 3'b111:begin row <= 8'b0000_0000; col_r <= 8'b0000_0000; col_g <= 8'b0000_0000; end
121. endcase
122. end
123.
124. if(key[0] == 1) begin col_r[0] <= 0; col_g[0] <= 0; end
125. if(key[1] == 1) begin col_r[1] <= 0; col_g[1] <= 0; end
126. if(key[2] == 1) begin col_r[2] <= 0; col_g[2] <= 0; end
127. if(key[3] == 1) begin col_r[3] <= 0; col_g[3] <= 0; end
128. if(key[4] == 1) begin col_r[4] <= 0; col_g[4] <= 0; end
129. if(key[5] == 1) begin col_r[5] <= 0; col_g[5] <= 0; end
130. if(key[6] == 1) begin col_r[6] <= 0; col_g[6] <= 0; end
131.
132. end
133. endmodule
7 段数码管模块由 8 个 7 段数码管组成,每个数码管的结构如图4所示,在2.1.2节中笔者也对数码管的显示原理进行了详细的说明。该模块中,key表示输入的按键信号,控制数码管所显示的数字;clk表示系统时钟信号,用于扫描并呈现所需要展现的数字;seg表示数码管显示信号,为该模块中输出到数码管所用的信号。
该模块具体的Verilog HDL语言实现如下:
1. /*
2. * Name: show_seg_tube.v
3. * Author: lgc0208
4. * Date: 2020-11-23
5. * Function: 根据按键显示不同数字
6. * Input: 按键信号key,时钟信号clk
7. * Output: 数码管显示信号seg
8. */
9.
10. module show_seg_tube(key, clk, seg);
11.
12. input [6:0] key;
13. input clk;
14. output reg [7:0]seg;
15.
16. always@(clk)
17. begin
18. if(key[0] == 1) begin seg <= 8'b0000_0110; end //显示1
19. else if(key[1] == 1) begin seg <= 8'b0101_1011; end //显示2
20. else if(key[2] == 1) begin seg <= 8'b0100_1111; end //显示3
21. else if(key[3] == 1) begin seg <= 8'b0110_0110; end //显示4
22. else if(key[4] == 1) begin seg <= 8'b0110_1101; end //显示5
23. else if(key[5] == 1) begin seg <= 8'b0111_1101; end //显示6
24. else if(key[6] == 1) begin seg <= 8'b0000_0111; end //显示7
25. else begin seg <= 8'b0000_0000; end
26. end
27.
28. endmodule
音乐演奏模块是该实验中所有功能模块的核心。首先,笔者将先对该部分的输入输出信号进行说明。
clk表示输入时钟信号,本实验中选用的是1MHZ的系统时钟。
auto_sw表示自动播放开关,当auto_sw为1时,开始自动播放乐曲《天空之城》;当auto_sw为0时,为手动演奏状态。
SW表示手动演奏的音域切换。当SW=3’b100时,演奏低音音域;当SW=3’b010时,演奏中音音域;当SW=3’b001时,演奏高音音域。
BTN表示手动演奏的音符。BTN[0]至BTN[6]分别表示音符1、2、3、4、5、6、7,按下按键时蜂鸣器即产生响应的音符。
beep表示输出给蜂鸣器的信号。
row, col_r, col_g分别表示输出给点阵列的行、列信号。
seg表示控制八段数码管的输出信号。
在该模块中,首先调用分频器模块,产生低音、中音、高音三个音域共21种不同频率的音符。并调用分频器模块产生一个4HZ的时钟信号,用于控制自动播放每个音符单元的持续时间。当自动播放控制信号auto_sw为1时,用于自动播放的计数器开始工作,循环播放事先存在程序中的《天空之城》音乐。当自动播放控制信号auto_sw为0时,为手动播放模式。此时,通过SW信号检测用户所选择的音域,通过BTN信号检测用户所选择要演奏的音符,并将事先完成分频的对应时钟信号赋值给信号beep,并输出到蜂鸣器,产生对应的音调。同时,根据SW信号和BTN信号的检测结果,将用户所选择的音域和音符信息传递给点阵列扫描模块和数码管显示模块,在点阵列和数码管上出现对应的图案或数字。
该模块具体的Verilog HDL语言实现如下:
1. /*
2. * Name: play.v
3. * Author: lgc0208
4. * Date: 2020-11-29
5. * Function: 根据按键演奏不同音符
6. * Input: 时钟信号clk,自动播放信号auto_sw, 按键信号key, 音域切换信号SW
7. * Output: 蜂鸣器信号beep, 点阵列行信号row, 点阵列列信号col_r, col_g,数码管显示信号seg
8. */
9.
10.
11. module play(clk, auto_sw, key, SW, beep, row, col_r, col_g, seg);
12.
13. input clk; //输入时钟1MHZ
14. input [6:0] key;
15. input [2:0] SW;
16. input auto_sw; //自动演奏开关
17. output reg beep = 0; //蜂鸣器
18. output [7:0]row, col_r, col_g; //点阵控制信号
19. output [7:0]seg; //数码管显示信号
20. wire [6:0] high, middle, low; //定义低中高音
21.
22. reg [2:0] pitch; //定义音高
23. reg [6:0] tone; //定义音调
24.
25. //调用分频模块
26. //低音1~7(分别为low[0]~low[7])
27. division #(3882/2) d_l1(.clk(clk),
28. .clk_out(low[0]));
29. division #(3405/2) d_l2(.clk(clk),
30. .clk_out(low[1]));
31. division #(3033/2) d_l3(.clk(clk),
32. .clk_out(low[2]));
33. division #(2863/2) d_l4(.clk(clk),
34. .clk_out(low[3]));
35. division #(2551/2) d_l5(.clk(clk),
36. .clk_out(low[4]));
37. division #(2272/2) d_l6(.clk(clk),
38. .clk_out(low[5]));
39. division #(2025/2) d_l7(.clk(clk),
40. .clk_out(low[6]));
41.
42. //中音1~7(分别为middle[0]~middle[7])
43. division #(1911/2) d_m1(.clk(clk),
44. .clk_out(middle[0]));
45. division #(1702/2) d_m2(.clk(clk),
46. .clk_out(middle[1]));
47. division #(1516/2) d_m3(.clk(clk),
48. .clk_out(middle[2]));
49. division #(1431/2) d_m4(.clk(clk),
50. .clk_out(middle[3]));
51. division #(1275/2) d_m5(.clk(clk),
52. .clk_out(middle[4]));
53. division #(1136/2) d_m6(.clk(clk),
54. .clk_out(middle[5]));
55. division #(1012/2) d_m7(.clk(clk),
56. .clk_out(middle[6]));
57.
58. //高音1~7(分别为high[0]~high[7])
59. division #(956/2) d_h1(.clk(clk),
60. .clk_out(high[0]));
61. division #(851/2) d_h2(.clk(clk),
62. .clk_out(high[1]));
63. division #(758/2) d_h3(.clk(clk),
64. .clk_out(high[2]));
65. division #(715/2) d_h4(.clk(clk),
66. .clk_out(high[3]));
67. division #(637/2) d_h5(.clk(clk),
68. .clk_out(high[4]));
69. division #(568/2) d_h6(.clk(clk),
70. .clk_out(high[5]));
71. division #(506/2) d_h7(.clk(clk),
72. .clk_out(high[6]));
73.
74. //4HZ分频,用于自动演奏
75. division #(250000/2) d_4hz(.clk(clk),
76. .clk_out(clk_4HZ));
77. //《天空之城》选用 126 个音符,需要7位2进制数表示
78. reg [6:0]count; //用于轮流播放不同音符
79.
80. //开始计数
81. always@(posedge clk_4HZ)
82. begin
83. //若按键按下,则开启自动播放计数,否则不工作
84. if(auto_sw)
85. begin
86. //计数器
87. if(count == 130)
88. count <= 0;
89. else
90. count <= count + 1'b1;
91. end
92. end
93.
94. //开始演奏
95. always@(posedge clk)
96. begin
97.
98. //手动演奏
99. if(!auto_sw)
100. begin
101. pitch <= SW;
102. tone <= key;
103. end
104.
105. //自动演奏 天空之城
106. else
107. begin
108. case(count)
109. 1: begin pitch<=3'b100; tone<=7'b010_0000; end //低音6
110. 2: begin pitch<=3'b100; tone<=7'b100_0000; end //低音7
111. 3: begin pitch<=3'b010; tone<=7'b000_0001; end //中音1
112. 4: begin pitch<=3'b010; tone<=7'b000_0001; end //延音
113. 5: begin pitch<=3'b010; tone<=7'b000_0001; end //延音
114. 6: begin pitch<=3'b100; tone<=7'b100_0000; end //低音7
115. 7: begin pitch<=3'b010; tone<=7'b000_0001; end //中音1
116. 8: begin pitch<=3'b010; tone<=7'b000_0001; end //延音
117. 9: begin pitch<=3'b010; tone<=7'b000_0100; end //中音3
118. 10: begin pitch<=3'b010; tone<=7'b000_0100; end //延音
119. 11: begin pitch<=3'b100; tone<=7'b100_0000; end //低音7
120. 12: begin pitch<=3'b100; tone<=7'b100_0000; end //延音
121. 13: begin pitch<=3'b100; tone<=7'b100_0000; end //延音
122. 14: begin pitch<=3'b100; tone<=7'b100_0000; end //延音
123. 15: begin pitch<=3'b100; tone<=7'b100_0000; end //延音
124. 16: begin pitch<=3'b100; tone<=7'b100_0000; end //延音
125. 17: begin pitch<=3'b100; tone<=7'b000_0100; end //低音3
126. 18: begin pitch<=3'b100; tone<=7'b000_0100; end //延音
127. 19: begin pitch<=3'b100; tone<=7'b010_0000; end //低音6
128. 20: begin pitch<=3'b100; tone<=7'b010_0000; end //延音
129. 21: begin pitch<=3'b100; tone<=7'b010_0000; end //延音
130. 22: begin pitch<=3'b100; tone<=7'b001_0000; end //低音5
131. 23: begin pitch<=3'b100; tone<=7'b010_0000; end //低音6
132. 24: begin pitch<=3'b100; tone<=7'b010_0000; end //延音
133. 25: begin pitch<=3'b010; tone<=7'b000_0001; end //中音1
134. 26: begin pitch<=3'b010; tone<=7'b000_0001; end //延音
135. 27: begin pitch<=3'b100; tone<=7'b001_0000; end //低音5
136. 28: begin pitch<=3'b100; tone<=7'b001_0000; end //延音
137. 29: begin pitch<=3'b100; tone<=7'b001_0000; end //延音
138. 30: begin pitch<=3'b100; tone<=7'b001_0000; end //延音
139. 31: begin pitch<=3'b100; tone<=7'b001_0000; end //延音
140. 32: begin pitch<=3'b100; tone<=7'b001_0000; end //延音
141. 33: begin pitch<=3'b100; tone<=7'b000_0100; end //低音3
142. 34: begin pitch<=3'b100; tone<=7'b000_0100; end //延音
143. 35: begin pitch<=3'b100; tone<=7'b000_1000; end //低音4
144. 36: begin pitch<=3'b100; tone<=7'b000_1000; end //延音
145. 37: begin pitch<=3'b100; tone<=7'b000_1000; end //延音
146. 38: begin pitch<=3'b100; tone<=7'b000_0100; end //低音3
147. 39: begin pitch<=3'b100; tone<=7'b000_1000; end //低音4
148. 40: begin pitch<=3'b010; tone<=7'b000_0001; end //中音1
149. 41: begin pitch<=3'b010; tone<=7'b000_0001; end //延音
150. 42: begin pitch<=3'b010; tone<=7'b000_0001; end //延音
151. 43: begin pitch<=3'b100; tone<=7'b000_0100; end //低音3
152. 44: begin pitch<=3'b100; tone<=7'b000_0100; end //延音
153. 45: begin pitch<=3'b100; tone<=7'b000_0100; end //延音
154. 46: begin pitch<=3'b100; tone<=7'b000_0100; end //延音
155. 47: begin pitch<=3'b100; tone<=7'b000_0100; end //延音
156. 48: begin pitch<=3'b100; tone<=7'b000_0100; end //延音
157. 49: begin pitch<=3'b010; tone<=7'b000_0001; end //中音1
158. 50: begin pitch<=3'b010; tone<=7'b000_0001; end //延音
159. 51: begin pitch<=3'b100; tone<=7'b100_0000; end //低音7
160. 52: begin pitch<=3'b100; tone<=7'b100_0000; end //延音
161. 53: begin pitch<=3'b100; tone<=7'b100_0000; end //延音
162. 54: begin pitch<=3'b100; tone<=7'b000_1000; end //低音4
163. 55: begin pitch<=3'b100; tone<=7'b000_1000; end //延音
164. 56: begin pitch<=3'b100; tone<=7'b000_1000; end //延音
165. 57: begin pitch<=3'b100; tone<=7'b000_1000; end //低音4
166. 58: begin pitch<=3'b100; tone<=7'b100_0000; end //低音7
167. 59: begin pitch<=3'b100; tone<=7'b100_0000; end //延音
168. 60: begin pitch<=3'b100; tone<=7'b100_0000; end //延音
169. 61: begin pitch<=3'b100; tone<=7'b100_0000; end //延音
170. 62: begin pitch<=3'b100; tone<=7'b100_0000; end //延音
171. 63: begin pitch<=3'b100; tone<=7'b100_0000; end //延音
172. 64: begin pitch<=3'b000; tone<=7'b000_0000; end //休止符
173. 65: begin pitch<=3'b000; tone<=7'b000_0000; end //休止符
174. 66: begin pitch<=3'b100; tone<=7'b010_0000; end //低音6
175. 67: begin pitch<=3'b100; tone<=7'b100_0000; end //低音7
176. 68: begin pitch<=3'b010; tone<=7'b000_0001; end //中音1
177. 69: begin pitch<=3'b010; tone<=7'b000_0001; end //延音
178. 70: begin pitch<=3'b010; tone<=7'b000_0001; end //延音
179. 71: begin pitch<=3'b100; tone<=7'b100_0000; end //低音7
180. 72: begin pitch<=3'b010; tone<=7'b000_0001; end //中音1
181. 73: begin pitch<=3'b010; tone<=7'b000_0001; end //延音
182. 74: begin pitch<=3'b010; tone<=7'b000_0100; end //中音3
183. 75: begin pitch<=3'b010; tone<=7'b000_0100; end //延音
184. 76: begin pitch<=3'b100; tone<=7'b100_0000; end //低音7
185. 77: begin pitch<=3'b100; tone<=7'b100_0000; end //延音
186. 78: begin pitch<=3'b100; tone<=7'b100_0000; end //延音
187. 79: begin pitch<=3'b100; tone<=7'b100_0000; end //延音
188. 80: begin pitch<=3'b100; tone<=7'b100_0000; end //延音
189. 81: begin pitch<=3'b100; tone<=7'b100_0000; end //延音
190. 82: begin pitch<=3'b100; tone<=7'b000_0100; end //低音3
191. 83: begin pitch<=3'b100; tone<=7'b000_0100; end //延音
192. 84: begin pitch<=3'b100; tone<=7'b010_0000; end //低音6
193. 85: begin pitch<=3'b100; tone<=7'b010_0000; end //延音
194. 86: begin pitch<=3'b100; tone<=7'b010_0000; end //延音
195. 87: begin pitch<=3'b100; tone<=7'b001_0000; end //低音5
196. 88: begin pitch<=3'b100; tone<=7'b010_0000; end //低音6
197. 89: begin pitch<=3'b100; tone<=7'b010_0000; end //延音
198. 90: begin pitch<=3'b010; tone<=7'b000_0001; end //中音1
199. 91: begin pitch<=3'b010; tone<=7'b000_0001; end //延音
200. 92: begin pitch<=3'b100; tone<=7'b001_0000; end //低音5
201. 93: begin pitch<=3'b100; tone<=7'b001_0000; end //延音
202. 94: begin pitch<=3'b100; tone<=7'b001_0000; end //延音
203. 95: begin pitch<=3'b100; tone<=7'b001_0000; end //延音
204. 96: begin pitch<=3'b100; tone<=7'b001_0000; end //延音
205. 97: begin pitch<=3'b100; tone<=7'b001_0000; end //延音
206. 98: begin pitch<=3'b100; tone<=7'b000_0100; end //低音3
207. 99: begin pitch<=3'b100; tone<=7'b000_1000; end //低音4
208. 100: begin pitch<=3'b010; tone<=7'b000_0001; end //中音1
209. 101: begin pitch<=3'b100; tone<=7'b100_0000; end //低音7
210. 102: begin pitch<=3'b100; tone<=7'b100_0000; end //延音
211. 103: begin pitch<=3'b100; tone<=7'b100_0000; end //延音
212. 104: begin pitch<=3'b010; tone<=7'b000_0001; end //中音1
213. 105: begin pitch<=3'b010; tone<=7'b000_0001; end //延音
214. 106: begin pitch<=3'b010; tone<=7'b000_0010; end //中音2
215. 107: begin pitch<=3'b010; tone<=7'b000_0010; end //延音
216. 108: begin pitch<=3'b010; tone<=7'b000_0100; end //中音3
217. 109: begin pitch<=3'b010; tone<=7'b000_0001; end //中音1
218. 110: begin pitch<=3'b010; tone<=7'b000_0001; end //延音
219. 111: begin pitch<=3'b010; tone<=7'b000_0001; end //延音
220. 112: begin pitch<=3'b010; tone<=7'b000_0001; end //延音
221. 113: begin pitch<=3'b000; tone<=7'b000_0000; end //休止符
222. 114: begin pitch<=3'b010; tone<=7'b000_0001; end //中音1
223. 115: begin pitch<=3'b100; tone<=7'b100_0000; end //低音7
224. 116: begin pitch<=3'b100; tone<=7'b010_0000; end //低音6
225. 117: begin pitch<=3'b100; tone<=7'b010_0000; end //延音
226. 118: begin pitch<=3'b100; tone<=7'b100_0000; end //低音7
227. 119: begin pitch<=3'b100; tone<=7'b100_0000; end //延音
228. 120: begin pitch<=3'b100; tone<=7'b001_0000; end //低音5
229. 121: begin pitch<=3'b100; tone<=7'b001_0000; end //延音
230. 122: begin pitch<=3'b100; tone<=7'b010_0000; end //低音6
231. 123: begin pitch<=3'b100; tone<=7'b010_0000; end //延音
232. 124: begin pitch<=3'b100; tone<=7'b010_0000; end //延音
233. 125: begin pitch<=3'b100; tone<=7'b010_0000; end //延音
234. 126: begin pitch<=3'b000; tone<=7'b000_0000; end //休止符
235.
236. endcase
237. end
238.
239.
240. //高音部分
241. if(pitch == 3'b001)
242. begin
243. case(tone)
244. 7'b000_0000:begin beep<=0; end
245. 7'b000_0001:begin beep<=high[0]; end
246. 7'b000_0010:begin beep<=high[1]; end
247. 7'b000_0100:begin beep<=high[2]; end
248. 7'b000_1000:begin beep<=high[3]; end
249. 7'b001_0000:begin beep<=high[4]; end
250. 7'b010_0000:begin beep<=high[5]; end
251. 7'b100_0000:begin beep<=high[6]; end
252. endcase
253. end
254.
255. //中音部分
256. else if(pitch == 3'b010)
257. begin
258. case(tone)
259. 7'b000_0000:begin beep<=0; end
260. 7'b000_0001:begin beep<=middle[0]; end
261. 7'b000_0010:begin beep<=middle[1]; end
262. 7'b000_0100:begin beep<=middle[2]; end
263. 7'b000_1000:begin beep<=middle[3]; end
264. 7'b001_0000:begin beep<=middle[4]; end
265. 7'b010_0000:begin beep<=middle[5]; end
266. 7'b100_0000:begin beep<=middle[6]; end
267. endcase
268. end
269.
270. //低音部分
271. else if(pitch == 3'b100)
272. begin
273. case(tone)
274. 7'b000_0000:begin beep<=0; end
275. 7'b000_0001:begin beep<=low[0]; end
276. 7'b000_0010:begin beep<=low[1]; end
277. 7'b000_0100:begin beep<=low[2]; end
278. 7'b000_1000:begin beep<=low[3]; end
279. 7'b001_0000:begin beep<=low[4]; end
280. 7'b010_0000:begin beep<=low[5]; end
281. 7'b100_0000:begin beep<=low[6]; end
282. endcase
283. end
284.
285. end
286.
287.
288.
289.
290. //显示点阵
291. show_LED sL1(.clk(clk),
292. .SW(pitch),
293. .key(tone),
294. .row(row),
295. .col_r(col_r),
296. .col_g(col_g));
297.
298. //数码管显示
299. show_seg_tube(.key(tone),
300. .clk(clk),
301. .seg(seg));
302. endmodule
在该模块的仿真中,笔者通过改变不同时间对应的key值,模拟用户在使用简易电子琴演奏器时按下按键的行为。仿真波形图如图6所示。
从图6中可以看出,随着用户按下按键的变化,数码管的输出信号产生了不同形式的变化。数码管的输出信号seg[0]至seg[7]分别对应了八段数码管的a,b,c,d,e,f,g七个显示段。与数码管的结构进行对照可以发现,key[0]至key[6]信号分别为高电平时,数码管上显示的数字为1,2,3,4,5,6,7。
在本实验中,数码管显示所需要的行信号在顶层模块中直接赋值以确保只有7号数码管正常工作,因此在该模块仿真中并未体现。
在该模块的仿真中,笔者给予输入的信号clk 1MHZ的时钟信号,尝试改变分频系数对时钟信号进行仿真,期望得到原时钟四分之一的频率。仿真的波形图如图7所示。
从图7可以明显的看出,分频后的时钟信号的周期是原时钟信号周期的四倍。由此可知,该分频模块可以得到所需要的分频效果。
在该模块的仿真中,笔者分别改变输入的音调和按下按键的情况,得到仿真波形如图8所示。
由图8可以看出,该模块中,当SW[2]=1代表播放低音时,只有绿色点阵亮起。同时,当按下key[3]时,列信号中的col_g[3]置0,即对应列全灭。当SW[1]=1代表播放中音时,只有红色点阵亮起,同时,当按下key[4]时,对应列全灭。当SW[0]=1代表播放高音时,红色绿色点阵同时亮起,显示黄色,同时,当按下key[1]时,对应列全灭。由此可知,该模块可以实现预设中所要实现的效果。
该模块的仿真中,笔者将分别对手动演奏和自动演奏进行仿真。由于音符和系统时钟的频率相差较大,因此手动演奏和自动演奏的部分笔者都将以不同分度值的两幅仿真图展示,分别展示输出到蜂鸣器的频率和点阵、数码管的显示。
手动演奏的仿真波形图如图9和图10所示。自动演奏的仿真波形图如图11、图12和图13所示。由于演奏部分所需要仿真的时间更长,远大于Quartus II中自带的仿真上限时间100us,故笔者在此编写testbench文件并使用ModelSim软件进行Gate Level Simulation操作。
由图9可以看出,当BTN[5]置为1,其他BTN置为0,SW为3’b100时,蜂鸣器输出信号beep变为低音6所对应的频率。同时,由图10可以知道,此时点阵列只出现绿色,且col_g[5]所表示的第6列不亮,数码管显示数字6。由此可知,该模块手动演奏部分可以正常实现所需要的功能。
由图11可以看出,在自动演奏信号auto_sw为1时,随着时间的推移每0.25秒会产生一次点阵列和数码管的变换,音符演奏所引起的行阵列和数码管的持续时间和切换效果符合预期设计。由图12可以看出,在切换音符时输出到蜂鸣器的信号频率也会相应改变,音符的切换效果符合预期设计。由图13可以看出,在自动演奏播放不同音符时,行阵列和数码管显示的效果(即点阵列上音符对应列全灭,数码管上显示音符数字)符合预期设计。
至此,仿真结果表示该模块设计完全符合预期效果。
1.用 8×8 点阵显示“1 2 3 4 5 6 7”七个音符构成的电子琴键盘。其中点阵的第一列用一个 LED 点亮表示音符“1”,第二列用二个 LED 点亮表示音符“2”,依此类推。当音符为低音 1~7 时,点阵显示为绿色;当音符为中音 1~7 时,点阵显示为红色;当音符为高音 1~7 时,点阵显示为黄色;
2.用 BTN7~BTN1 七个按键模拟电子琴手动演奏时的“1 2 3 4 5 6 7”七个音符。当某个按键按下时,数码管 DISP7 显示相应的音符,点阵上与之对应的音符显示列全灭,同时蜂鸣器演奏相应的声音;当按键放开时数码管显示的音符灭掉,点阵显示恢复,蜂鸣器停止声音的输出。
3.由拨码开关SW7,SW6,SW5切换选择高、中、低音,点阵颜色进行相应变化。
4.可通过一个拨码开关进行手动/自动演奏的切换,自动演奏时,点阵根据乐曲进行颜 色和亮灭的变化。
本次实验共使用了1150个逻辑单元,占电路板总逻辑单元数的91%;共使用了53个管脚,占电路板总管脚数的46%,如图14所示。
管脚分配情况如图15和图16所示。
故障描述:调用按键消抖后蜂鸣器只产生“噗”的一声,而无法正常发出音调。
问题分析:经过分析按键消抖的方式和蜂鸣器发声的条件,发现按键消抖主要是靠产生的脉冲信号实现功能,而电子琴发声需要按键产生持续的高电平。同时,在该情况下按键不消抖并不会影响到电子琴功能的输出和用户的体验,因此取消按键消抖模块,该问题得以解决。
故障描述:在使用Quartus自带仿真时,点击运行仿真,蜂鸣器信号保持为一条直线。
问题分析:Quartus自带的仿真时间上限为100us,而需要输出的信号频率导致了其需要更长的时间才能展现出来,因此编写testbench文件,调用ModelSim软件进行仿真,问题得以解决。
故障描述:使用Quartus自带仿真对自动播放功能进行仿真时,无法显示自动播放所产生的各种波形变换。
问题分析:Quartus自带的仿真时间上限为100us,而一个四分音符播放所需要的时间至少为0.25秒,远远大于Quartus自带的仿真时间上限,因此通过编写testbench文件,调用ModelSim软件进行仿真,问题得以解决。
故障描述:烧录成功后发现蜂鸣器发声错误,点阵数码管亮得十分混乱。
问题分析:电路实验板上的电路拨码开关设置的状态不正确,时钟信号不是1MHz。通过调整拨码开关,将第2位和第4位调至高电平,时钟信号变为1MHz,蜂鸣器正常发声,点阵和数码管正常显示。
故障描述:调用按键消抖后蜂鸣器只产生“噗”的一声,而无法正常发出音调。
问题分析:经过分析按键消抖的方式和蜂鸣器发声的条件,发现按键消抖主要是靠产生的脉冲信号实现功能,而电子琴发声需要按键产生持续的高电平。同时,在该情况下按键不消抖并不会影响到电子琴功能的输出和用户的体验,因此取消按键消抖模块,该问题得以解决。
故障描述:左边两个数码管始终亮,且LED半边不亮。
问题分析:当拨码开关SW10的第3位和第4位拨到下边时,数码管DS6和DS7的位码 控制端CAT6和CAT7不受EPM1270T144C5芯片的引脚控制。因而需要把使能控制SW10的最右两位拨码成’11’,即可正常显示。
故障描述:点阵列显示的图案和预计的图案发生了反转。
问题分析:经过查对,发现可编程实验板上点阵列的序号是从下往下、从左往右递增的,和笔者预设的高低位值相反,导致了图案发生反转。经过更改管脚使点阵正确显示。
故障描述:将仿真通过的程序烧录到可编程实验板后,一些按键按下后有正常效果出现,而一些按键按下后没有反应。
问题分析:由于部分按键按下后有正常效果,更换可编程实验板后也有正常效果,因此排除程序上的问题。将可编程实验板没有反应的按键的引脚对应引脚接入示波器,按下按键后发现没有波形。再将正常功能按键的引脚接入示波器后,按下按键后可以检测到高电平,因此排除了程序上的故障,问题应是由于按键损坏等原因所引起的。