FPGA蜂鸣器


前言

  本系列文章来源于FPGA学习开源网站fpga4fun,网站由浅入深的介绍了26个FPGA工程,通过工程的学习,可以了解什么是FPGA以及它是如何工作的。本人仅用于学习和记录,如有侵权,速删。


一、工程介绍

  工程使用25MHz晶振的FPGA驱动蜂鸣器,具体连接电路如下:
实际电路连接图

  示意图如下:
电路示意图

  晶振给FPGA提供固定的频率,FPGA通过分频以驱动IO口,IO通过1KΩ电阻连接蜂鸣器,通过改变IO口的频率,使FPGA产生不同的声音。
根据代码的复杂程度,可以分成以下两个功能:
  1.简单蜂鸣器
  2.呼叫器

二、简单蜂鸣器

2.1 16位计数器的蜂鸣器

  16位计数器范围为0-65535,使用25MHz晶振可以实现16位自由运行计数器。其中,计数器最高位MSB的开关频率为25_000_000 ÷ 65536 = 381Hz,381Hz可以看成是蜂鸣器的音调,计数器在0-32767之间,counter[15]为0,在32768-65535之间,conter[15]值为1,具体verilog代码如下:

module music(
	input  clk_i,
	output speaker_o
);
	reg [15:0] counter;
	
	always@(posedge clk_i)begin
		counter <= counter + 1;
	end

	//use the most significant bit(MSB) of the counter to drive the speaker.
	assign speaker_o = counter[15];
	
endmodule

2.2 特定字符蜂鸣器

  2.1节设计的蜂鸣器,计数器的值是人为设定的,如果想让蜂鸣器发出特定的音调,比如‘A’字符,音调为440Hz,那么计数器的值应为25_000_000 ÷ 440 = 56818,具体verilog代码如下:

module music(
	input clk_i,
	output speaker_o
);

	reg [15:0] counter;

	always@(posedge clk_i)begin
		if(counter == 56817)begin
			counter <= 0;
		end else begin
			counter <= counter + 1;
		end
	end

	assign speaker_o = counter[15];

endmodule

2.3 调整蜂鸣器占空比为50%

  从2.1和2.2节得知,我们是将16位计数器的最高位counter[15]做为蜂鸣器的开关,2.1节计数器从0计数到65535期间,counter[15]的值在0-32767时的值为0,在32768-65535时的值为1,可见发出381Hz音调的占空比为50%;而2.2节计数器从0计数到56817期间,counter[15]的值在0-32767时的值为0,在32768-56817时的值为1,可见发出440Hz音调的占空比为42%。现在调整蜂鸣器占空比为50%,只需将计数器的值除以2,并且在计数器满值的时候翻转输出状态,具体verilog代码如下:

module music(
	input      clk_i,
	output reg speaker_o
);

	reg [14:0] counter;

	always@(posedge clk_i)begin
		if(counter == 28408)begin
			counter <= 0;
		end else begin
			counter <= counter + 1;
		end
	end

	always@(posedge clk_i)begin
		if(counter == 28408) begin
			speaker_o <= ~ speaker_o;
		end
	end

endmodule

2.4 参数化蜂鸣器

  对2.3节代码进行参数化设计,并使用倒计时计数器,具体verilog代码如下:

module music(
	input      clk_i,
	output reg speaker_o
);

	parameter  CLK_DIV = 25_000_000/440/2;
	
	reg [14:0] counter;

	always@(posedge clk_i)begin
		if(counter == 0)begin
			counter <= CLK_DIV - 1;
		end else begin
			counter <= counter - 1;
		end
	end

	always@(posedge clk_i)begin
		if(counter == 0) begin
			speaker_o <= ~ speaker_o;
		end
	end

endmodule

三、呼叫器

3.1 救护车

  救护车的声音通过切换两种不同的音调即可实现,我们使用24位计数器的最高位tone[23]作为蜂鸣器的开关,先给tone[23]一个计数值,然后将tone[23]里的值切换到另一个计数值,即可实现不同音调的切换,注意tone[23]切换频率约为1.5Hz,具体verilog代码如下所示:

module music(
	input      clk_i,
	output reg speaker_o
);

	parameter  CLK_DIV = 25_000_000/440/2;
	
	reg [23:0] tone;
	reg [14:0] counter;

	always@(posedge clk_i) begin
		tone <= tone + 1;
	end
	
	always@(posedge clk_i)begin
		if(counter == 0)begin
			counter <= (tone[23] ? CLK_DIV-1 : CLK_DIV/2-1);
		end else begin
			counter <= counter - 1;
		end
	end

	always@(posedge clk_i)begin
		if(counter == 0) begin
			speaker_o <= ~ speaker_o;
		end
	end

endmodule

3.2 警车

  相比于救护车,警车的音调切换的频率比较快,我们使用23位计数器的最高位tone[22]作为蜂鸣器开关,其切换频率为3Hz,为救护车频率的2倍。简单警车的声音是从低到高 再 从高到低这样一个循环的声音。因此要设计一个从低到高的计数器和一个从高到低的计数器。这里有一个思想是从23位计数器中取7位作为从低到高计数器,再取7位作为从高到低计数器。先设计从低到高计数器,取计数器的[15-21]位,计数器从0计数到127后,重新从0计数;再设计从高到低计数器,取计数器的[15-21]位取反即可实现127计数到0;最后使用最高位tone[22]作为音调切换开关,当从低到高计数器计数到127,切换到从高到低计数器,计数到0,再回到从低到高计数器,如此循环,具体代码片段如下所示:

wire [6:0] ramp = (tone[23] ? tone[21:15] : ~tone[21:15]);

  上述得到的只是一个从低到高和从高到低的计数器,并不能实际使用,需要设计一个有用的计数值去产生声音,设计一个15位的计数值,高两位为01,中间7位计数器ramp,后面6位为0,这样做是为了产生一个765Hz-1525Hz的报警器,当然你也可以设定你自己想要的值,具体代码片段如下所示:

wire [14:0] ramp = {2'b01, ramp, 6'b000_000};

  整体verilog代码如下所示:

module music(
	input      clk_i,
	output reg speaker_o
);

	reg  [22:0] tone;
	reg  [14:0] counter;
	wire [ 6:0] ramp = (tone[22] ? tone[21:15] : ~tone[21:15]);
	wire [14:0] clkdivider = {2'b01, ramp, 6'b000_000};

	always@(posedge clk_i) begin
		tone <= tone + 1;
	end
	
	always@(posedge clk_i)begin
		if(counter == 0)begin
			counter <= clkdivider;
		end else begin
			counter <= counter - 1;
		end
	end

	always@(posedge clk_i)begin
		if(counter == 0) begin
			speaker_o <= ~ speaker_o;
		end
	end

endmodule

3.3 追击警报

  在实现3.2警车的基础上,我们再实现一个追击警报,让警报声时快时慢,取tone[21:15]作为快音调,tone[24:18]作为慢音调,具体代码片段如下所示:

wire [ 6:0] fastsweep = (tone[22] ? tone[21:15] : ~tone[21:15]);
wire [ 6:0] slowsweep = (tone[25] ? tone[24:18] : ~tone[24:18]);
wire [14:0] clkdivider = {2'b01, (tone[27] ? slowsweep : fastsweep), 6'b000_000};

  整体verilog代码如下所示:

module music(
	input      clk_i,
	output reg speaker_o
);

	reg  [27:0] tone;
	reg  [14:0] counter;
	wire [ 6:0] fastsweep = (tone[22] ? tone[21:15] : ~tone[21:15]);
    wire [ 6:0] slowsweep = (tone[25] ? tone[24:18] : ~tone[24:18]);
	wire [14:0] clkdivider = {2'b01, (tone[27] ? slowsweep : fastsweep), 6'b000_000};

	always@(posedge clk_i) begin
		tone <= tone + 1;
	end
	
	always@(posedge clk_i)begin
		if(counter == 0)begin
			counter <= clkdivider;
		end else begin
			counter <= counter - 1;
		end
	end

	always@(posedge clk_i)begin
		if(counter == 0) begin
			speaker_o <= ~ speaker_o;
		end
	end

endmodule

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值