1 设计要求
以蜂鸣器演奏《世上只有妈妈好》的片段为例,用FPGA设计一个乐曲演奏系统。
2 设计原理
2.1 蜂鸣器的结构原理
蜂鸣器是一种一体化结构的电子讯响器,采用直流电压供电,广泛应用于计算机、打印机、复印机、报警器、电子玩具、汽车电子设备、电话机、定时器等电子产品中作发声器件。蜂鸣器主要分为压电式蜂鸣器和电磁式蜂鸣器两种类型。
按照内部有无振荡源可以为有源蜂鸣器和无源蜂鸣器。有源蜂鸣器内部带振荡源,所以只要一通电就会发生声音;而无源内部不带振荡源,所以如果用直流信号无法令其鸣叫。必须用一定频率的方波去驱动它,如下图所示。
蜂鸣器“鸣叫”需要的电流较大,故而采用三极管进行驱动, FPGA 控制三极管是否导通。
2.2 简谱分析
乐曲能连续演奏所需要的两个基本数据是:组成乐曲的每个音符的频率值(音调)和每个音符持续的时间(音长)。要演奏乐曲必须考虑两个方面,一个音符或音调,另一个是音长或节拍。本项目研究如何利用蜂鸣器演奏歌曲《世上只有妈妈好》。下图为《世上只有妈妈好》的简谱。
简谱用来记录、传播音乐的工具。在简谱中,用以表示音的高低及相互关系的基本符号为七个阿拉伯数字,即1、2、3、4、5、6、7,唱作do、re、mi、fa、sol、la、si,称为唱名。
(1)音的高低(音调)
单用以上七个音是无法表现众多的音乐形象的。在实际作品中,还有一些更高或更低的音,如在基本音符上方加记一个"·",表示该音升高一个八度,称为高音;加记两个" :",则表示该音升高两个八度,称为倍高音。在基本音符下方加记一个"·",表示该音降低一个八度,称为低音;加记两个" :",则表示该音降低两个八度,称为倍低音。在一般歌曲中,无论是在基本音符上方或下方加记两个以上的"·"的音符都是很少见的。
(2)音的长短(音长)
在简谱中,1、2、3、4、5、6、7这七个基本音符,不仅表示音的高低,而且还是表示时值长短的基本单位,称为四分音符,其他音符均是在四分音符的基础上,用加记短横线"-"和附点"·"表示。简谱中用线(减时线、增时线、连音线)、点(符点音符)休止符来表示音的长短。
在基本音符右侧加记一条短横线,表示增长一个四分音符的时值。这类加记在音符右侧、使音符时值增长的短横线,称为增时线。增时线越多,音符的时值越长。
在基本音符下方加记一条短横线,表示缩短原音符时值的一半。这类加记在音符下方、使音符时值缩短的短横线,称为减时线。减时线越多,音符的时值越短。
在简谱中,加记在单纯音符的右侧的、使音符时值增长的小圆点"·",称为附点。加记附点的音符称为附点音符。附点本身并无一定的长短,其长短由前面的单纯音符来决定。附点的意义在于增长原音符时值的一半,常用于四分音符和小于四分音符的各种音符之后。
休止符,用来表示音乐停顿的符号。
(3)音的强弱
曲谱中有规则的竖线,称小节线。作用是划分小节内的拍数,明确节拍音的强弱规律。 拍子是节拍的时值单位,如2/4、3/4、3/8、6/8 等,表示每小节有几拍/几分音符为一拍。如二拍子: 强、弱 | 强、弱 |……,三拍子: (强、弱、弱 | 强、弱、弱|),四拍子: (强、弱、次强、弱);等等。
(4)调号与定调
1234567是do、re、mi、fa、so、la、xi。这是音的唱名。那么音名是,1是C,2是D,3是E,4是F,5是G,6是A,7是B。比如说D调,就会表明,1=D,而D是do re mi的re的音名。所以1=D的意思就是这个调的do就是re的位置。以此类推,所有的音符都会随着do的变化而变化。C调就是没有升降号 12345671。 那G调为一个升号升4 为567123#45 1=G. D调两个#号为23#4567#12 1=D 其它调类似推出。
(5)各个音符对应的频率
在音乐中有十二平均律的规定:每两个八度音之间的频率相差一倍,在两个八度音之间又分为十二个半音,
2.3 架构设计
此设计共分4各模块:
addr_ctrl模块(地址控制模块):每1/4秒让地址进行加1,共有64个音符,故而输出地址采用6位即可。本模块中首先设计1/4秒的计时器。当到1/4秒时,让输出的addr进行变化:小于63时,进行加1操作;等于63时,进行清零操作。此时蜂鸣器将不断的重复播放这个音乐。
music_mem模块(音符存储模块):根据简谱将64个音符存储起来,然后根据外部的地址,将储存的音符进行输出。
music_freq模块(音符转换频率模块):根据输入的音符以及不同音符所对应的频率,输出对应的频率值。
wave_gen模块(产生对应频率的方波):根据输入的频率值,产生对应频率的方波。产生方波的方法采用计时器计时半个周期,然后进行取反。利用时钟的频率(50MHz)除以想要的波形的频率,得出分频比,将分频比除以2,得到半个周期的计数值。
|
信号 |
说明 |
端口/连线 |
FPGA引脚 |
|
clk |
系统时钟,50MHz; |
输入端口 |
|
|
rst_n |
复位信号,低电平有效; |
输入端口 |
|
|
Addr[5:0] |
查找音符的地址,存储器存有64个字符,地址线为2^6; |
内部连线 |
|
|
Music[8:0] |
音符,[8:6]为高音,[5:3]为中音,[2:0]为低音;如中音1,编码为000_001_000; |
内部连线 |
|
|
Freq[10:0] |
音符所对应的频率 |
内部连线 |
|
|
beep |
对应频率的方波 |
输出端口 |
|
3 设计与实现
3.1 地址控制模块
1 module addr_ctrl( 2 input wire clk, 3 input wire rst_n, 4 output reg [5:0] addr 5 ); 6 7 parameter T_1S = 26\'d50_000_000; 8 localparam SEC_1_4 = T_1S/4; 9 10 reg [25:0] cnt; 11 12 always@(posedge clk or negedge rst_n)begin 13 if(rst_n == 1\'b0) 14 cnt <= 26\'d0; 15 else begin 16 if(cnt < SEC_1_4 - 1\'b1) 17 cnt <= cnt + 1\'b1; 18 else 19 cnt <= 26\'d0; 20 end 21 end 22 23 always@(posedge clk or negedge rst_n)begin 24 if(rst_n == 1\'b0) 25 addr <= 6\'d0; 26 else if(cnt == SEC_1_4 - 1\'b1)begin 27 if(addr < 6\'d63) 28 addr <= addr + 1\'b1; 29 else 30 addr <= 6\'d0; 31 end 32 else 33 addr <= addr; 34 end 35 36 endmodule
3.2 音符存储模块
1 module music_mem( 2 input wire clk, 3 input wire rst_n, 4 input wire [5:0] addr, 5 output reg [8:0] music 6 ); 7 8 always@(posedge clk or negedge rst_n)begin 9 if(rst_n == 1\'b0) 10 music <= 9\'b000_000_000; 11 else 12 case(addr) 13 6\'d0: music <= 9\'b000_110_000; 14 6\'d1: music <= 9\'b000_110_000; 15 6\'d2: music <= 9\'b000_110_000; 16 6\'d3: music <= 9\'b000_101_000; 17 6\'d4: music <= 9\'b000_011_000; 18 6\'d5: music <= 9\'b000_011_000; 19 6\'d6: music <= 9\'b000_101_000; 20 6\'d7: music <= 9\'b000_101_000; 21 22 6\'d8: music <= 9\'b001_000_000; 23 6\'d9: music <= 9\'b001_000_000; 24 6\'d10: music <= 9\'b000_110_000; 25 6\'d11: music <= 9\'b000_101_000; 26 6\'d12: music <= 9\'b000_110_000; 27 6\'d13: music <= 9\'b000_110_000; 28 6\'d14: music <= 9\'b000_110_000; 29 6\'d15: music <= 9\'b000_110_000; 30 31 6\'d16: music <= 9\'b000_011_000; 32 6\'d17: music <= 9\'b000_011_000; 33 6\'d18: music <= 9\'b000_101_000; 34 6\'d19: music <= 9\'b000_110_000; 35 6\'d20: music <= 9\'b000_101_000; 36 6\'d21: music <= 9\'b000_101_000; 37 6\'d22: music <= 9\'b000_011_000; 38 6\'d23: music <= 9\'b000_011_000; 39 40 6\'d24: music <= 9\'b000_001_000; 41 6\'d25: music <= 9\'b000_000_110; 42 6\'d26: music <= 9\'b000_101_000; 43 6\'d27: music <= 9\'b000_011_000; 44 6\'d28: music <= 9\'b000_010_000; 45 6\'d29: music <= 9\'b000_010_000; 46 6\'d30: music <= 9\'b000_010_000; 47 6\'d31: music <= 9\'b000_010_000; 48 49 6\'d32: music <= 9\'b000_010_000; 50 6\'d33: music <= 9\'b000_010_000; 51 6\'d34: music <= 9\'b000_010_000; 52 6\'d35: music <= 9\'b000_101_000; 53 6\'d36: music <= 9\'b000_110_000; 54 6\'d37: music <= 9\'b000_110_000; 55 6\'d38: music <= 9\'b000_110_000; 56 6\'d39: music <= 9\'b000_110_000; 57 58 6\'d40: music <= 9\'b000_011_000; 59 6\'d41: music <= 9\'b000_011_000; 60 6\'d42: music <= 9\'b000_011_000; 61 6\'d43: music <= 9\'b000_010_000; 62 6\'d44: music <= 9\'b000_001_000; 63 6\'d45: music <= 9\'b000_001_000; 64 6\'d46: music <= 9\'b000_001_000; 65 6\'d47: music <= 9\'b000_001_000; 66 67 6\'d48: music <= 9\'b000_101_000; 68 6\'d49: music <= 9\'b000_101_000; 69 6\'d50: music <= 9\'b000_101_000; 70 6\'d51: music <= 9\'b000_011_000; 71 6\'d52: music <= 9\'b000_010_000; 72 6\'d53: music <= 9\'b000_001_000; 73 6\'d54: music <= 9\'b000_000_110; 74 6\'d55: music <= 9\'b000_001_000; 75 76 6\'d56: music <= 9\'b000_000_101; 77 6\'d57: music <= 9\'b000_000_101; 78 6\'d58: music <= 9\'b000_000_101; 79 6\'d59: music <= 9\'b000_000_101; 80 6\'d60: music <= 9\'b000_000_101; 81 6\'d61: music <= 9\'b000_000_101; 82 6\'d62: music <= 9\'b000_000_101; 83 6\'d63: music <= 9\'b000_000_101; 84 default: music <= 9\'b000_000_101; 85 endcase 86 end 87 88 endmodule
3.3 音符转换频率模块
1 module music_freq( 2 input wire clk, 3 input wire rst_n, 4 input wire [8:0] music, 5 output reg [10:0] freq 6 ); 7 8 always@(posedge clk or negedge rst_n)begin 9 if(rst_n == 1\'b0) 10 freq <= 11\'d1; //div_freq=sys_clk/(2*freq),freq != 0 11 else 12 case(music) 13 9\'b000_000_001 : freq <= 11\'d262; 14 9\'b000_000_010 : freq <= 11\'d294; 15 9\'b000_000_011 : freq <= 11\'d330; 16 9\'b000_000_100 : freq <= 11\'d349; 17 9\'b000_000_101 : freq <= 11\'d392; 18 9\'b000_000_110 : freq <= 11\'d440; 19 9\'b000_000_111 : freq <= 11\'d494; 20 21 9\'b000_001_000 : freq <= 11\'d523; 22 9\'b000_010_000 : freq <= 11\'d587; 23 9\'b000_011_000 : freq <= 11\'d659; 24 9\'b000_100_000 : freq <= 11\'d699; 25 9\'b000_101_000 : freq <= 11\'d784; 26 9\'b000_110_000 : freq <= 11\'d880; 27 9\'b000_111_000 : freq <= 11\'d988; 28 29 9\'b001_000_000 : freq <= 11\'d1050; 30 9\'b010_000_000 : freq <= 11\'d1175; 31 9\'b011_000_000 : freq <= 11\'d1319; 32 9\'b100_000_000 : freq <= 11\'d1397; 33 9\'b101_000_000 : freq <= 11\'d1568; 34 9\'b110_000_000 : freq <= 11\'d1760; 35 9\'b111_000_000 : freq <= 11\'d1976; 36 default: freq <= 11\'d1; 37 endcase 38 end 39 40 endmodule
3.4 波形产生模块
1 module wave_gen( 2 input wire clk, 3 input wire rst_n, 4 input wire [10:0] freq, 5 output reg beep 6 ); 7 8 parameter F_CLK = 50_000_000; 9 10 wire [25:0] half; 11 reg [25:0] cnt; 12 13 assign half = F_CLK /(2*freq); 14 15 always@(posedge clk or negedge rst_n)begin 16 if(rst_n == 1\'b0) 17 cnt <= 26\'d0; 18 else 19 if(cnt < half - 1\'b1) 20 cnt <= cnt + 1\'b1; 21 else 22 cnt <= 26\'d0; 23 end 24 25 always@(posedge clk or negedge rst_n)begin 26 if(rst_n == 1\'b0) 27 beep <= 1\'b0; 28 else 29 if(cnt == half - 1\'b1) 30 beep <= ~beep; 31 else 32 beep <= beep; 33 end 34 35 endmodule
3.5 顶层模块
1 module music_beep( 2 input wire clk, 3 input wire rst_n, 4 output wire beep 5 ); 6 7 wire [5:0] addr; 8 wire [8:0] music; 9 wire [10:0] freq; 10 11 addr_ctrl addr_ctrl_inst( 12 .clk (clk), 13 .rst_n (rst_n), 14 .addr (addr) 15 ); 16 17 music_mem music_mem_inst( 18 .clk (clk), 19 .rst_n (rst_n), 20 .addr (addr), 21 .music (music) 22 ); 23 24 music_freq music_freq_inst( 25 .clk (clk), 26 .rst_n (rst_n), 27 .music (music), 28 .freq (freq) 29 ); 30 31 wave_gen wave_gen_inst( 32 .clk (clk), 33 .rst_n (rst_n), 34 .freq (freq), 35 .beep (beep) 36 ); 37 38 endmodule
4 仿真与验证
4.1 地址控制模块的仿真
1 `timescale 1ns/1ps 2 3 module addr_ctrl_tb(); 4 5 reg clk; 6 reg rst_n; 7 wire [5:0] addr; 8 9 addr_ctrl addr_ctrl_inst( 10 .clk (clk), 11 .rst_n (rst_n), 12 .addr (addr) 13 ); 14 15 defparam addr_ctrl_inst.T_1S = 20; 16 17 initial clk = 1\'b0; 18 always #10 clk = ~clk; 19 20 initial begin 21 rst_n = 1\'b0; #51; 22 rst_n = 1\'b1; 23 #(20*4*200); 24 $stop; 25 end 26 27 endmodule
4.2 顶层模块仿真测试
1 `timescale 1ns/1ps 2 3 module music_beep_tb(); 4 5 reg clk; 6 reg rst_n; 7 wire beep; 8 9 music_beep music_beep_inst( 10 .clk (clk), 11 .rst_n (rst_n), 12 .beep (beep) 13 ); 14 15 initial clk = 1\'b0; 16 always #10 clk = ~clk; 17 18 initial begin 19 rst_n = 1\'b0; 20 #21; rst_n = 1\'b1; 21 #1_000_000_000; 22 $stop; 23 end 24 25 endmodule
5 参考文献