Demo3 主要演示FPGA+无源蜂鸣器实现音乐的播放。
1.实验说明
这个实验需要接无源蜂鸣器模块,引脚连接查看adc文件这里。LOCATION这一列就是指定的管脚编号。
set_pin_assignment { buzzer } { LOCATION = P4; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 20; }
set_pin_assignment { clk_ref } { LOCATION = P11; IOSTANDARD = LVCMOS33; }
set_pin_assignment { i_rst } { LOCATION = P84; IOSTANDARD = LVCMOS33; }
set_pin_assignment { key_in } { LOCATION = P83; IOSTANDARD = LVCMOS33; }
蜂鸣器连接:
- buzzer, 信号控制脚,即P60。
- 蜂鸣器电源+连接板子上的3V3
- 蜂鸣器电源地连GND。
其它管脚的说明:
- clk_ref 时钟参考脚
- i_rst 是复位脚,也即板子上的按键SW2,
- key_in 是切换音乐的按键SW1,
- led[0:3] 是LED灯在教程(2)中介绍过。
黑色的蜂鸣器上S即为buzzer脚,-为电源3V3,中间脚为GND
蓝色蜂鸣器上有明确的信号标识,VCC接板子电源3V3,GND为地,I/O接buzzer管脚,即P60。
按SW1可以切换音乐(由于demo防抖处理不是太好,可能要多按两下),SW2可以复位。
2.无源蜂鸣器播放音乐原理
FPGA用无源蜂鸣器实现音乐播放的原理其实也是利用PWM波,不同的音阶对应不PWM占空比,每个音阶持续的时长就代表了节拍。
音阶控制
下表是每个音符所对应的频率及半周期。
音符 | 对应频率(Hz) | 时钟周期数 |
---|---|---|
1 | 523 | 95600 |
2 | 587 | 85150 |
3 | 659 | 75850 |
4 | 698 | 71600 |
5 | 784 | 63750 |
6 | 880 | 56800 |
7 | 988 | 50600 |
音符对应的时钟周期数越多,那么它的一个周期就越长,从下图可以看出来,音符从 1 到 7,它的一个周期逐次递减。
节拍控制
当输出一个周期的脉冲给蜂鸣器,它肯定会响,但是有一点,一个周期的脉冲时长只有几百毫秒,我们能听到吗?当然听不到,所以需要不断地重复一个周期的脉冲,使它连续输出一个音符的持续时长,那么我们就能听到了。
这里以音符 1 为基准,让它的一个周期重复 250 次,那么它总时长为 95600 × 250 = 23 , 900 , 000 95600×250=23,900,000 95600×250=23,900,000 个时序周期,其它的音符总时长也为 23 , 900 , 000 23,900,000 23,900,000 个时序周期,
所以不难得出
歌谱
3. 程序说明
工程里面跟本实验内容相关的只有两个模块,一个是key_fliter,另一个是pwm_buzzer。key_fliter在demo2中有说明,不重复介绍。
蜂鸣器模块 pwm_buzzer.v
模块定义
定义音符时序周期数
判断播放哪首歌
曲谱定义
向蜂鸣器输出脉冲
工程源码中的注释有更详细的说明,就不一一列举了,其中要说明的一点是,曲谱中有一个 “-” 的符号,我将它定位 M0,可以在向蜂鸣器输出波形的时序逻辑中,可以看到当音符为 M0 时,它输出高电平,也就是让它此时不发声。还有一点时,我设置了一个时间占空比为 4/5,也就是前 4/5 发声,后 1/5 的时间不发声,如果不设置时间占空比也可以,只不过它会连续不断的播放曲谱,感觉就不好听了,所以这里设置了个时间占空比。、
附录
更多简谱
让AI也来写一代verilog代码实现的蜂鸣器播放音乐吧!
下面是一个简单的Verilog代码示例,用于实现蜂鸣器播放音乐的功能:
module Buzzer_Music;
reg [11:0] tone; // 定义一个12位寄存器存储音调值
reg clk;
always @(posedge clk) begin
case (tone)
12'd0: // 音调为0时,关闭蜂鸣器
// 控制蜂鸣器关闭的逻辑
12'd1: // 音调为1时,播放音乐1
// 播放音乐1的逻辑
12'd2: // 音调为2时,播放音乐2
// 播放音乐2的逻辑
// 添加更多音调的逻辑
default:
// 默认逻辑,可以选择关闭蜂鸣器或播放其他音乐
endcase
end
initial begin
clk = 0;
tone = 0;
#10; // 延时10个时间单位来改变音调值
while (1) begin
clk = ~clk; // 翻转时钟状态
#5; // 延时5个时间单位
end
end
// 添加对应的输出或蜂鸣器控制逻辑
// 这里假设有一个名为buzzer_control的模块用于控制蜂鸣器
// 你可以根据具体平台的需要修改这部分逻辑
// 如何连接到实际的蜂鸣器硬件上取决于您的平台或开发板
endmodule
在上述代码中,我们使用一个12位寄存器`tone`来存储音调值。根据`tone`的值,使用`case`语句来选择播放不同的音乐或关闭蜂鸣器。在每个时钟上升沿触发时,根据`tone`的值执行相应的逻辑。
请注意,上述代码仅作为示例,您可能需要根据实际情况进行适当的修改以适应您的平台和硬件配置,以及添加适当的输出或蜂鸣器控制逻辑。
就这???还是再详细点吧。
下面是一个更完善的Verilog代码示例,用于实现蜂鸣器播放音乐的功能:
module Buzzer_Music #(
parameter FREQUENCY = 50000000, // 系统时钟频率
parameter TEMPO = 120, // 音乐的速度
parameter SAMPLE_RATE = 8000, // 采样率
parameter MUSIC_LENGTH = 32 // 音乐的长度(以64分音符为单位)
)(
input wire clk, // 时钟信号
output wire buzzer // 蜂鸣器控制信号
);
reg [11:0] tone_counter; // 音调计数器
reg [6:0] note_counter; // 音符计数器
reg [1:0] note_duration; // 音符时长
reg [11:0] note_index; // 音符索引
reg [23:0] music[MUSIC_LENGTH-1:0] = { // 定义音乐数据数组
// 插入音乐数据,每个元素表示一个音符的频率
// 数组长度需要与MUSIC_LENGTH参数匹配
};
reg [6:0] duration_counter; // 时长计数器
reg [11:0] current_note; // 当前音符
always @(posedge clk) begin
// 音符计数器每个时钟周期加1
note_counter <= note_counter + 1;
// 音符计数器大于等于当前音符时长时,切换到下一个音符
if (note_counter >= note_duration) begin
note_counter <= 0;
note_index <= note_index + 1;
// 若达到音乐的结束位置,则重置回音乐的开头
if (note_index >= MUSIC_LENGTH)
note_index <= 0;
current_note <= music[note_index];
// 计算当前音符的持续时间
if (current_note > 0) begin
note_duration <= TEMPO * SAMPLE_RATE / (current_note * 64);
end
else
note_duration <= 0;
end
// 根据当前音符的频率计算音调计数器的值
if (current_note > 0)
tone_counter <= (FREQUENCY / (current_note * 2 * SAMPLE_RATE));
end
// 在模块的最后添加对应的输出或蜂鸣器控制逻辑
// 这里假设buzzer信号用于控制蜂鸣器的开关
// 根据当前音调计数器的值控制蜂鸣器输出
// 这里只是一个简单的控制逻辑示例,实际上可能需要更精确的控制
always @(posedge clk) begin
if (tone_counter > 0)
buzzer <= 1;
else
buzzer <= 0;
end
endmodule
在以上代码中,我们使用了一些参数,如系统时钟频率`FREQUENCY`、音乐的速度`TEMPO`、采样率`SAMPLE_RATE`和音乐的长度`MUSIC_LENGTH`。根据这些参数和音符的频率,我们可以计算出每个音符的时长。使用音符和时长的计数器来切换音符,并根据音符频率计算音调计数器的值。根据音调计数器的值,控制蜂鸣器输出来实现音乐播放。
请注意,上述代码仅供参考,您需要根据实际情况进行适当的修改,包括提供音乐数据、调整参数以及添加适当的蜂鸣器控制逻辑。还要根据硬件平台的要求,将蜂鸣器的控制信号与实际的蜂鸣器硬件进行连接。
逼问了半天就这,然后提示他无法处理了。。。。
没有回复内容