FPGA 08 基础 蜂鸣器实验音乐谱播放器实验

在这里插入图片描述

FPGA 08 基础 蜂鸣器实验音乐谱播放器实验

在这里插入图片描述

模块名称: music_test

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r39UW650-1627394271087)(img/blog_img/fpga/image-20210719144108895.png)]

主要功能 :每隔一段时间,输出的音调由低到高,且每个音调的发出的声音是固定的。

实现(设计)流程: 1、内部一共由3个部分(2个定时器+1个查找表)组成,自左向右分别是: 音频切换循环定时器、音频查找表、音频生成器

​ ①音频切换循环定时器 : 通过设置内部循环定时器的计数值,保持当前的音调持续一个固定的时间。当每次计数器计数到设置的值以后,产生一个Full_flag 脉冲信号。随后将脉冲信号给音频查找表,作为其Clk信号,随后切换下一个信号。另外,循环定时计数器开始重新计数。

​ ②音频查找表模块,由于Clk信号是:循环定时器的输出脉冲信号。所以,信号周期是定时器设定的周期。在该模块中,每当时钟上升沿到来的时候,音频信号进行切换一次(查找表值 +1),在内部通过自己设置查找表输出对应的信号,切换音频信号主要输出的是:计数器的计数值CNT_ARR[31:0]和带比较值Compare_NOW[31:0]。

​ ③音频生成器模块,音频查找表输出了一个循环定时的计数器的计数值信号,这个信号给了音频生成器模块(本质就是一个定时器),CNT_ENABLE =1 的时候,循环定时器开始工作。在输出端口我们可以看到有两个输出: Full_flag 信号(计数器计满输出标志信号),和 ARR[31:0]当前(实时)计数器输出信号。

​ 在整个模块中,音频查找表的比较值Compare_NOW和音频生成器的实时输出计数器值ARR[31:0]进行比较,计数器值 >= 比较值,输出高电平,否则输出低电平。


各个音调对应的频率(在sound_lut 模块中需要用到):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IH0mOFtV-1627394271092)(img/blog_img/fpga/image-20210329133120430.png)]

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 文件

// 音频查找表文件
// 里面的定时器之和比较值是 50%的占空比数值
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

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值