FPGA 学习 03 蜂鸣器音乐播放器

该文描述了一个基于FPGA的蜂鸣器音乐播放器设计,通过Verilog代码实现。设计中包含了定时器模块、音乐音调lut表以及控制逻辑,以定时器生成特定频率的信号驱动蜂鸣器播放不同音调的音乐。同时,文章提供了测试激励来验证设计的功能。
摘要由CSDN通过智能技术生成

FPGA 学习 03 蜂鸣器音乐播放器

music_test.v 文件

module music_test(
	Clk,		//时钟信号
	Rst_n,	//复位信号
	
	CNT_ENABLE,	//Cnt_Enable=1 时,开启定时
					//Cnt_Enable=0 时低电平时,不使能定时
	beep 
);

input Clk ;
input	Rst_n ;
input CNT_ENABLE ;
output beep ;


wire [31:0]CNT_NOW ;
wire Full_Flag1;
wire [31:0]CNT_ARR;
wire [31:0]Compare_cnt;

//这里的频率是: 50_000_000/50_000 = 1000Hz 的频率
	timer beep0(
		.Clk(Clk) ,  					//时钟
		.Rst_n(Rst_n) , 				//复位信号
		.CNT_ARR(CNT_ARR) , 			//定时器重装载值 24999(类似与32的重装载值),相当于定时
		.MODE(1'b1)  ,					// Mode : 1  循环定时   Mode : 0 单次定时
		.CNT_ENABLE(~CNT_ENABLE) , 	//CNT_ENABLE : 1 定时器计数器使能,开始计数 //CNT_ENABLE : 0 定时器计数不使能,停止计数			
		// ~CNT_ENABLE 这里给定的是取反的信号,主要是太吵,不需要可以更改
		.CNT_NOW(CNT_NOW) , 			//实时定时器计数器
		.Full_Flag()   				//计数完成标志位,这里没有用到,且Full_Flag是输出信号,所以不用的时候可以不连接信号
	);

	sound_lut sound_lut0(
		.Clk(Full_Flag1),
		.Rst_n(Rst_n),
		.ARR(CNT_ARR),	//  CNT_ARR 传递给 beep0 的 CNT_ARR 使用
		.Compare_cnt(Compare_cnt)
	);

	//250ms固定定时,用于切换音调,Full_Flag1信号用于传递给 sound_lut0 模块使用
	timer timer_250ms(

		.Clk(Clk),
		.Rst_n(Rst_n),
		
		.CNT_ARR(6250000),
		.MODE(1'b1),	
		.CNT_ENABLE(1'b1),
		
		.CNT_NOW(),
		.Full_Flag(Full_Flag1)	
	);


assign  beep =(CNT_NOW >=Compare_cnt)? 1'b1:1'b0 ;  //只有一个时钟周期是高电平,CNT_NOW是在 0-24999+1之间徘徊

endmodule


sound_lut.v文件


module sound_lut(
	Clk,
	Rst_n,
	ARR ,
	Compare_cnt
);

	input Clk;
	input Rst_n;
	output reg[31:0]ARR;
	output reg[31:0]Compare_cnt;
	reg [4:0]index;
	
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		index <= 0;
	else if(index >= 5'd20)
		index <= 0;
	else
		index <= index + 1'b1;
		
	always@(*)begin
		case(index)
			0 : ARR = 191130;
			1 : ARR = 170241;     
			2 : ARR = 151689;     
			3 : ARR = 143183;     
			4 : ARR = 127550;     
			5 : ARR = 113635;     
			6 : ARR = 101234;     
			7 : ARR = 95546 ;     
			8 : ARR = 85134 ;     
			9 : ARR = 75837 ;     
			10: ARR = 71581 ;     
			11: ARR = 63775 ;     
			12: ARR = 56817 ;     
			13: ARR = 50617 ;     
			14: ARR = 47823 ;     
			15: ARR = 42563 ;     
			16: ARR = 37921 ;     
			17: ARR = 35793 ;     
			18: ARR = 31887 ;     
			19: ARR = 28408 ;     
			20: ARR = 25309 ;     
			default: ARR = 191130;
		endcase			
	end
	
	
		always@(*)begin
		case(index)
			0 :  Compare_cnt= 95565 ;
			1 :  Compare_cnt= 85120 ;     
			2 :  Compare_cnt= 75844 ;     
			3 :  Compare_cnt= 71591 ;     
			4 :  Compare_cnt= 63775 ;     
			5 :  Compare_cnt= 56817 ;     
			6 :  Compare_cnt= 50617 ;     
			7 :  Compare_cnt= 47773 ;     
			8 :  Compare_cnt= 42567 ;     
			9 :  Compare_cnt= 37939 ;     
			10:  Compare_cnt= 35792 ;     
			11:  Compare_cnt= 31887 ;     
			12:  Compare_cnt= 28408 ;     
			13:  Compare_cnt= 25308 ;     
			14:  Compare_cnt= 23912 ;     
			15:  Compare_cnt= 21282 ;     
			16:  Compare_cnt= 18960 ;     
			17:  Compare_cnt= 17896 ;     
			18:  Compare_cnt= 15943 ;     
			19:  Compare_cnt= 14204 ;     
			20:  Compare_cnt= 12655 ;     
			default: Compare_cnt = 24999;
		endcase			
	end
	

endmodule


timer.v 文件

//通用定时器模块
//描述:
//通过定义 timer 里面的 input 的数值
// 得到 output 里面的数值,这样就可以得到我们想要的输出(2个 分别是 CNT_NOW 和 Full_Flag 两个输出信号)
// 时钟和复位信号是输入信号,一般由系统给定
// CNT_ARR、MODE、CNT_ENABLE 可以自己给定固定值或者通过外部的输入给定一个值(信号)
module timer(
	Clk ,  	//时钟
	Rst_n , 	//复位信号
	
	CNT_ARR , //定时器重装载值(类似与32的重装载值)
	MODE  ,	// Mode : 1  循环定时   Mode : 0 单次定时
	
	CNT_ENABLE , //CNT_ENABLE : 1 定时器计数器使能,开始计数
					 //CNT_ENABLE : 0 定时器计数不使能,停止计数
 	
	CNT_NOW , 	//实时定时器计数器
	
	Full_Flag   //计数完成标志位
);

//  定义:
	input Clk ;
	input Rst_n;
	
	input [31:0]CNT_ARR ;  //通用32位定时计数器
	input MODE 	;
	input	CNT_ENABLE;
	
	output [31:0]CNT_NOW ; //当前32位定时计数器的值
	output reg Full_Flag ; //计数完成标志位
	
	
	//自己模块定义的信号
	reg [31:0]cnt; 	//定义一个32位的计数器
	reg oneshot ; 		//定义一个 单次计数值计满 触发信号
	wire Full_Flag_r; //Full_Flag_r 用于延时Full_Flag 的一个周期
	
	assign CNT_NOW = cnt;  //定时器的值
	assign Full_Flag_r = (CNT_NOW == CNT_ARR - 1)?1'b1:1'b0;

	//第1个 always 块
	always@(posedge Clk)
		Full_Flag <= Full_Flag_r;
		
	//第2个 always 块
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)   //复位信号到来,数据清零(qingling)
		cnt <=0;
	else if(MODE == 1'b1)begin	//判断是否是循环模式
		if((CNT_ENABLE == 1'b1) && (cnt < CNT_ARR)) //判断CNT_ENABLE 是否启动,且计数值是否小于重装载值
			cnt <= cnt + 1'b1;		//cnt++
		else								//否则数据清零
			cnt <= 0;
	end
	else if(!MODE)begin	//判断是否是单次计数模式
		if(oneshot)			//oneshot信号为1,表示满足一次计数条件
			cnt <= cnt + 1'b1;
		else
			cnt <= 0;
	end
	//第3个 always 块
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)	//复位信号清零
		oneshot <= 1'b0;
	else if(!MODE)
	begin			//判断是在一次计数模式下
		if(CNT_ENABLE == 1'b1)	//判断CNT_ENABLE 是否启动
			oneshot <= 1'b1;
		else if(CNT_NOW == CNT_ARR - 1) //判断计数值是否等于重转载值
			oneshot <= 1'b0;
			else	//保持不变
			oneshot <= oneshot;
	end
	else//在循环模式下,oneshot =0 ;
		oneshot <= 1'b0;
		
		
endmodule


timer_tb.v 文件


`timescale 1ns/1ns
`define clk_period 20
// 定时器定时模块测试源文件
module timer_tb; // 定义的测试模块的名称,一般命名 是后面加 _tb,表示这是测试模块

//  测试模块的输入输出 定义: input 类型 -->  reg 类型替换
//  output 类型 -->  wire 类型替换
	reg Clk ;
	reg Rst_n;

	reg [31:0]CNT_ARR ;  //通用32位定时计数器
	reg MODE 	;
	reg CNT_ENABLE;
	
	wire [31:0]CNT_NOW ; //当前32位定时计数器的值
	wire Full_Flag ; //计数完成标志位
	
timer timer0(
	.Clk(Clk) ,  					//时钟
	.Rst_n(Rst_n) , 				//复位信号
	.CNT_ARR(CNT_ARR) , 			//定时器重装载值(类似与32的重装载值)
	.MODE(MODE)  ,					// Mode : 1  循环定时   Mode : 0 单次定时
	.CNT_ENABLE(CNT_ENABLE) , 	//CNT_ENABLE : 1 定时器计数器使能,开始计数 //CNT_ENABLE : 0 定时器计数不使能,停止计数			
	.CNT_NOW(CNT_NOW) , 			//实时定时器计数器
	.Full_Flag(Full_Flag)   	//计数完成标志位
);

/*********** 定义初始化测试信号 ************/
	initial Clk = 1;	//定义时钟信号
	always #(`clk_period/2) Clk = ~Clk;
	
	initial begin  //执行一次,叫 initial  加入 begin
		//初始化所有输入信号
		Rst_n = 0;	
		CNT_ARR = 0;
		MODE = 0;
		CNT_ENABLE = 0;
		#(`clk_period*20 + 1);
		//复位信号 		Rst_n = 1; 模块正式开始工作
		Rst_n = 1;
		#(`clk_period*20);
		
		
		//设置预设值为1000,模式为循环定时模式
		CNT_ARR = 1000;
		MODE = 1;
		#(`clk_period*20); //设置为循环模式,延时20个时钟周期 
		CNT_ENABLE = 1;	 //开始启动计数定时器
		#(`clk_period*12000);
		CNT_ENABLE = 0;	//关闭计数定时器
		#(`clk_period*20);
		
		//设置预设值为600,模式为循环定时模式
		CNT_ARR = 600;
		MODE = 1;
		#(`clk_period*20);
		CNT_ENABLE = 1;
		#(`clk_period*8000);
		CNT_ENABLE = 0;
		#(`clk_period*20);	
		
		//设置预设值为1000,模式为单次定时模式
		CNT_ARR = 1000;
		MODE = 0;
		#(`clk_period*20); // CNT_ENABLE = 1; #(`clk_period)	CNT_ENABLE = 0; 表示给定的是一个脉冲信号,进入的是单次计数模式
		CNT_ENABLE = 1;
		#(`clk_period);
		CNT_ENABLE = 0;
		#(`clk_period*1200);
		
		//设置预设值为600,模式为单次定时模式
		CNT_ARR = 600;
		MODE = 0;
		#(`clk_period*20); // CNT_ENABLE = 1; #(`clk_period)	CNT_ENABLE = 0; 表示给定的是一个脉冲信号,进入的是单次计数模式
		CNT_ENABLE = 1;
		#(`clk_period);
		CNT_ENABLE = 0;
		#(`clk_period*1200);
		
		$stop;
	
	end

endmodule



assigment.tcl 文件

set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to Clk
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to Rst_n
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to CNT_ENABLE
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to beep


set_location_assignment PIN_E1 -to Clk
set_location_assignment PIN_E16 -to Rst_n 
set_location_assignment PIN_M16 -to CNT_ENABLE
set_location_assignment PIN_L16 -to beep



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值