蜂鸣器播放《粉刷匠》

一.蜂鸣器硬件原理

在这里插入图片描述

  • 由上图可知,蜂鸣器原理是低电平有效

二.音频原理

在这里插入图片描述

  • 音频周期频率公式为T(周期)=1/f(频率)。我们需要得到20ns震动的次数,例如高音Do的震动次数计算方式如下:
    在这里插入图片描述

三.乐谱

  • 我们想播放《粉刷匠》,需要得到它的乐谱如下
    在这里插入图片描述

  • 于是我们可以对应每一个音符应该是DO,RE,MI,FA,SO,LA,SI中的哪一个。

  • 每两个“ | ”之间设定为1s,那么每一拍设定为250ms,有一些空拍,我们将空拍设定为保持上一个音符的频率。

四.设计思路

  • 1.每一个音符计250ms
  • 2.计震动的频率
  • 3.计音符总个数。例如《粉刷匠》设定64个音符(包括空拍)
  • 4.考察音符个数计数器的值,确定每个音符震动频率应该是多少
  • 5.震动频率计数器和总频率的一半(以实现占空比50%)比较,来控制蜂鸣器响或不响。

五.代码

/**************************************功能介绍***********************************
Date	: 2023年8月3日15:25:24
Author	: Alegg xy.
Version	: 14.0
Description: 《粉刷匠》
*********************************************************************************/
    
//---------<模块及端口声名>------------------------------------------------------
module beep_music( 
    input				clk		,
    input				rst_n	,
    output     reg      beep        
);								 
//---------<参数定义>--------------------------------------------------------- 
    parameter MAX_300ms = 25'd15_000_000;//300ms
    parameter   HIGH_DO =   18'd47750,//震动频率
                HIGH_RE =   18'd42250,
                HIGH_MI =   18'd37900,
                HIGH_FA =   18'd37550,
                HIGH_SO =   18'd31850,
                HIGH_LA =   18'd28400,
                HIGH_XI =   18'd25400;
    reg [18:0]  freq_r;//寄存频率值
    parameter NUM = 7'd64;//共64个音符
//---------<内部信号定义>-----------------------------------------------------
    reg			[24:0]	cnt_300ms	   	;//一个音符占300ms计数器
    wire				add_cnt_300ms	;
    wire				end_cnt_300ms	;
    
    reg			[15:0]	cnt_freq	   	;//震动频率计数器
    wire				add_cnt_freq	;
    wire				end_cnt_freq	;

    reg			[5:0]	cnt_num	   	;//音符个数计数器
    wire				add_cnt_num	;
    wire				end_cnt_num	;
    
    //计一个音符占250ms
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_300ms <= 'd0;
        end 
        else if(add_cnt_300ms)begin 
            if(end_cnt_300ms)begin 
                cnt_300ms <= 'd0;
            end
            else begin 
                cnt_300ms <= cnt_300ms + 1'd1;
            end 
        end
    end 
    
    assign add_cnt_300ms = 1'b1;
    assign end_cnt_300ms = add_cnt_300ms && cnt_300ms == MAX_300ms - 1'd1;
    
    //震动频率计数器
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_freq <= 'd0;
        end 
        else if(add_cnt_freq)begin 
            if(end_cnt_freq)begin 
                cnt_freq <= 'd0;
            end
            else begin 
                cnt_freq <= cnt_freq + 1'd1;
            end 
        end
    end 
    
    assign add_cnt_freq = 1'b1;
    assign end_cnt_freq = add_cnt_freq && ((cnt_freq == freq_r - 1'd1) || end_cnt_300ms);
    
    //音符个数计数器
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_num <= 'd0;
        end 
        else if(add_cnt_num)begin 
            if(end_cnt_num)begin 
                cnt_num <= 'd0;
            end
            else begin 
                cnt_num <= cnt_num + 1'd1;
            end 
        end
    end 
    
    assign add_cnt_num = end_cnt_300ms;
    assign end_cnt_num = add_cnt_num && cnt_num == NUM - 1'd1 && cnt_300ms == MAX_300ms - 1'd1;
    
    //组合逻辑考察每个音符对应的震动频率
    always @(*)begin 
        case (cnt_num)
            6'd0:	freq_r = HIGH_SO ;
			6'd1:	freq_r = HIGH_MI ;
			6'd2:	freq_r = HIGH_SO ;						
			6'd3:	freq_r = HIGH_MI ;					
			6'd4:	freq_r = HIGH_SO ;					
			6'd5:	freq_r = HIGH_MI ;					
			6'd6:	freq_r = HIGH_DO ;					
			6'd7:	freq_r = HIGH_DO ;					
			6'd8:	freq_r = HIGH_RE ;					
			6'd9:	freq_r = HIGH_FA ;					
			6'd10:	freq_r = HIGH_MI ;
            6'd11:  freq_r = HIGH_RE ;
            6'd12:	freq_r = HIGH_SO ;					
			6'd13:	freq_r = HIGH_SO ;					
			6'd14:	freq_r = HIGH_SO ;
            6'd15:  freq_r = HIGH_SO ;

			6'd16:	freq_r = HIGH_SO ;
			6'd17:	freq_r = HIGH_MI ;
			6'd18:	freq_r = HIGH_SO ;
			6'd19:	freq_r = HIGH_MI ;
			6'd20:	freq_r = HIGH_SO ;
			6'd21:	freq_r = HIGH_MI ;
			6'd22:	freq_r = HIGH_DO ;
			6'd23:	freq_r = HIGH_DO ;
			6'd24:	freq_r = HIGH_RE ;
			6'd25:	freq_r = HIGH_FA ;
			6'd26:	freq_r = HIGH_MI ;
			6'd27:	freq_r = HIGH_RE ;
			6'd28:	freq_r = HIGH_DO ;
			6'd29:	freq_r = HIGH_DO ;
			6'd30:	freq_r = HIGH_DO ;
			6'd31:	freq_r = HIGH_DO ; 

            6'd32:	freq_r = HIGH_RE ;
            6'd33:	freq_r = HIGH_RE ;
            6'd34:	freq_r = HIGH_FA ;
            6'd35:	freq_r = HIGH_FA ;
            6'd36:	freq_r = HIGH_MI ;
            6'd37:	freq_r = HIGH_DO ;
            6'd38:	freq_r = HIGH_SO ;
            6'd39:	freq_r = HIGH_SO ;
            6'd40:	freq_r = HIGH_RE ;
            6'd41:	freq_r = HIGH_FA ;
            6'd42:	freq_r = HIGH_MI ;
            6'd43:	freq_r = HIGH_RE ;
            6'd44:	freq_r = HIGH_SO ;
            6'd45:	freq_r = HIGH_SO ;
            6'd46:	freq_r = HIGH_SO ;
            6'd47:	freq_r = HIGH_SO ;
            
            6'd48:	freq_r = HIGH_SO ;
            6'd49:	freq_r = HIGH_MI ;
            6'd50:	freq_r = HIGH_SO ;
            6'd51:	freq_r = HIGH_MI ;
            6'd52:	freq_r = HIGH_SO ;
            6'd53:	freq_r = HIGH_MI ;
            6'd54:	freq_r = HIGH_DO ;
            6'd55:	freq_r = HIGH_DO ;
            6'd56:	freq_r = HIGH_RE ;
            6'd57:	freq_r = HIGH_FA ;
            6'd58:	freq_r = HIGH_MI ;
            6'd59:	freq_r = HIGH_RE ;
            6'd60:	freq_r = HIGH_DO ;
            6'd61:	freq_r = HIGH_DO ;
            6'd62:	freq_r = HIGH_DO ;
            6'd63:	freq_r = HIGH_DO ;
			default:freq_r = HIGH_DO;
        endcase
    end

    //震动频率计数器和对应震动频率总值的一半比较生成flag信号
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            beep <= 1'd1;
        end 
        else if(cnt_freq < (freq_r >> 1))begin 
            beep <= 1'b0;
        end 
        else begin 
            beep <= 1'b1;
        end 
    end
endmodule

六.引脚配置

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值