这里我们将让我们的FPGA播放声音和音乐。我们从产生一个单频音开始。然后,逐步让它实现一些更加有趣的功能,例如播放警笛和曲子。
这个工程中用到的硬件器件包括:一块Pluto板、一个扬声器(speaker)以及一个1千欧姆的电阻(resistor)。
关于此硬件系统的一个更加正式的表示方法如下图所示:
振荡器(oscillator)产生一个固定频率输入到FPGA,FPGA将此频率分频后驱动一个I/O口。这个I/O口通过一个1千欧姆的电阻连接到一个扬声器。通过改变这个I/O口的输出频率,就可以使扬声器发出各种声音。
HDL(硬件描述语言)设计
insidious这里将分三部分来描述它:
∙ 第一部分 - 简单的哔哔声
∙ 第二部分 - 警笛声
∙ 第三部分 - 曲调
简单的哔哔声
FPGA 可以很容易就实现二进制的计数。让我们从一个16位的计数器开始。首先从25MHz的时钟开始,对于这个时钟信号,我们可以简单的应用计数器来实现“分频”remedy。一个16位的计数器从0计到65535(一共65536个不同的值)。计数器的最高位将以25000000/65536=
381Hz的频率翻转。
对应的Verilog HDLhm是什么东西语言如下所示:
代码
1. module music(clk, speaker);
2. therebe就近原则input clk;
3. output speaker;
4.
5. // 16位的2进制计数器
6. reg [15:0] counter;
7. always @(podge clk) counter <= counter+1;
8.
9. // 使用计数器的最高有效位驱动扬声器
10. assign speaker = counter[15];
11.
12. endmodule
计数器的最低有效位(澳门留学counter[0])以12.5MHz的频率翻转,类似的counter[1]以6.125MHz的频率翻转,以此类推。我们使用最高有效位(counter[15])来驱动扬声器。这样就可以给扬声器输出一个很好的381Hz的方波。
"A" 调 (440Hz)
好了,与其产生一个随机的频率,为何不试试得到一个440Hz的频率。这个频率就是“A”调的频率。这样一来,我们需要将25MHz的信号56818分频,下面是对应的Verilog HDL代码。
代码
1. module music(clk, speaker);
2. input clk;
3. output speaker;
4.
5. reg [15:0] counter;
6. always @(podge clk) if(counter==56817) counter <= 0; el counter <= counter+1;
7.
8. assign speaker = counter[15];
9.
10. endmodule
2020年英语四级真题卷
问题来了,输出信号的频率虽然是希望的440Hz,但是其占空比不再是50%。因为低电平从0一直维持到32767(期间counter[15]等于0),而高电平则从32768维持到56817。这样输出信号中,高电平的占空比仅为42%。
最简单的得到50%占空比的办法是添加一个状态,使输出信号先28409分频(56818的一半),然后再2分频。以下是修改后的Verilog HDL代码。
代码
1. module music(clk, speaker);
2. input clk;
3. output speaker;
4.
5. reg [14:0] counter;
6. always @(podge clk) if(counter==28408) counter <= 0; el counter <= counter+1;
7.
8. reg speaker;
9. always @(podge clk) if(counter==28408) speaker <= ~speaker;
10.
11. endmodule
添加一个参数
下面的代码跟上面的代码效果完全一样,一个名为“clkdivider”的参数被添加到代码中,而计数器则变为向下技术(这个只是个人爱好问题).
代码
1. module music(clk, speaker);
2. input clk;
3. output speaker;
4. parameter clkdivider = 25000000/440/2;
5.
6. reg [14:0] counter;
7. always @(podge clk) if(counter==0) counter <= clkdivider-1; el counter <= counter-1;
8.
9. reg speaker;
10. always @(podge clk) if(counter==0) speaker <= ~speaker;
11.
12. endmodule
救护车笛声
让我们交替发出两个音调。首先我们使用一个24位的计数器“tone”来产生一个低频的方波。其最高有效位(tone[23])以大约1.5Hz的频率翻转。
我们使用这一位(tone[23])来控制主计数器产生在两个频率之间切换的输出波形,这样一来就可以交替发出两个音调。下面是对应的Verilog HDL代码。
代码
1. module music(clk, speaker);
2. input clk;
3. output speaker;
4. parameter clkdivider = 25000000/440/2;
5.
6. reg [23:0] tone;
7. angryalways @(podge clk) tone <= tone+1;
8.
9. reg [14:0] counter;
10. always @(podge clk) if(counter==0) counter <= (tone[23] ? clkdivider-1 : clkdivider/2-1); el counter <= counter-1;
11.
12. reg speaker;
13. always @(podge clk) if(counter==0) speaker <= ~speaker;
14.
15. endmodule
警车笛声
问题现在变得复杂起来。我们需要产生一个音调的变化,使之听起来像是警车的笛声。
仍然从“tone”计数器开始。我们仅使用23位,这样便可以得到两倍与前面的频率(最高有效位大约以3Hz的频率翻转)
下面是如何产生变化的音调的技巧。使用一个寄存器“ramp”来表征当前的音调,则要求ramp的值在某一区间来回变化,例如...---。考虑“tone”计数器的15到21位(tone[21:15]), 这是一个在培训学校如何招生0到127之间循环递增的值,-127-0-。再考虑这几位的反转,即~tone[21:15],这是一个在127-0之间循环递减的值。如果能控制ramp在这两个值之间来回切换,即可得到一个形如...--的计数器。而这个变化规律正好符合警车笛声的音调变化规律。为了让ramp在这两个值之间来回切换,我们使用tone[22]来控制。可以这样考虑,tone[22: 15]从0计数,对于前128个值(0-127),tone[22]等于0,后128个值(128-255),tone[22]等于1。于是我
们就可以使用tone[22]来控制ramp的取值,当tone[22]等于0时,让ramp等于tone[21:15],当tone[22]等于1时,让ramp 等于~tone[21:15]。具体的硬件描述语言如下:
代码
1. wire [6:0] ramp = (tone[22] ? tone[21:15] : ~tone[21:15]); 汉翻英在线
2.
3. // 含义
4. // 当 tone[22]等于1 取 ramp=tone[21:15] 否则 ramp=~tone[21:15]
这样一来ramp就会在7b'0000000与7b'1111111之间来回变化. 为了得到一个对于产生声音有用的值, 我们在其前面补上两位数据"01",并且在其尾部也补上6个0,即"000000"。
代码
1. wire [14:0] clkdivider = {2'b01, ramp, 6'b000000};
通过这样的处理,"clkdivider" 就拥有了一个在15'b010000000000000 与 15'b011111111000000之间来回变化的值(或者以16进制表示在15'h2000 与 15'h3FC0,以十进制表示在8192 到 16320之间变化)。当输入频率为25MHz时,将产生频率在765Hz到1525Hz之间变化的音调,从而产生类似于警车笛声的声音。下面是整个模块的Verilog HDL语言描述。
代码
1. 感谢父母的英语作文module music(clk, speaker);
2. input clk;
3. output speaker;
4.
5. reg [22:0] tone;
6. always @(podge clk) tone <= tone+1;
7.
8. wire [6:0] ramp = (tone[22] ? tone[21:15] : ~tone[21:15]);
9. wire [14:0] clkdivider = {2'b01, ramp, 6'b000000};
10.
11. reg [14:0] counter;
12. always @(podge clk) if(counter==0) counter <= clkdivider; el counter <= counter-1;
13.
14. reg speaker;