• verilog实验1:基于FPGA蜂鸣器演奏乐曲并数码管显示


    一、实验任务

           利用FPGA进行代码开发,使蜂鸣器演奏出乐曲《生日快乐》,将音调显示在数码管。原理为蜂鸣器为交流源蜂鸣器,在引脚上加一定频率的方波就可以发声,而且发声的频率由所加方波决定。这样我们就可以根据无源蜂鸣器的原理进行发声练习了。

    二、代码实现

           由于需要蜂鸣器发声且数码管显示音调,所以我们将代码分为两部分。

           第一部分用于产生音调的方波。第二部分为数码管显示。

           (一)产生音调

           (1)PreDiv 预置分频数模块

           将48M晶振分频12M,再计算得出各个音调的频率,公式为12M÷音调频率÷2,所得即为预置分频数。程序中只编写了低音和中音的14个音。

    module prediv(
        input [3:0]Index,
        input clk,
        input Reset_n,
        output reg[15:0] PreDiv
        );
            
        always @ (negedge Reset_n or posedge clk)
                if(!Reset_n)
                begin
                PreDiv<=16'h5997;
                end
            else
                begin
                case(Index)
                4'd1:PreDiv<=16'h5997;
                4'd2:PreDiv<=16'h4FCD;
                4'd3:PreDiv<=16'h471B;
                4'd4:PreDiv<=16'h431E;
                4'd5:PreDiv<=16'h3BCA;
                4'd6:PreDiv<=16'h3544;
                4'd7:PreDiv<=16'h2F74;
                4'd8:PreDiv<=16'h2CCA;
                4'd9:PreDiv<=16'h27E8;
                4'd10:PreDiv<=16'h238D;
                4'd11:PreDiv<=16'h218E;
                4'd12:PreDiv<=16'h1DE5;
                4'd13:PreDiv<=16'h1AA2;
                4'd14:PreDiv<=16'h17BA;
            endcase
            end
    endmodule

           (2)Index 索引数模块

            为了便于代码书写,需要引用索引数(其实加一个ROM更为方便),即使用“5“为低音”so",如简谱一样,可以更为方便的编写乐曲。毕竟乐曲有很多音符,如果每次都用预置数编写程序,程序书写和查错会非常不方便。当然这也是verilog语言的魅力之处。

    module index(
        input clk,
        input reset_n,
        output reg[3:0]index
        );
        
        reg[5:0]cnt;
        wire clk2m;
        wire clk2000;
        wire clk2;
        
        defparam clk_rhythm1.divdWIDTH=3,clk_rhythm1.divdFACTOR=12;//24分频2M
        div clk_rhythm1(
                .reset(reset_n),
                .clkin(clk),
                .clkout(clk2m)
                );
        defparam clk_rhythm2.divdWIDTH=8,clk_rhythm2.divdFACTOR=500;//1000分频2000hz
        div clk_rhythm2(
                .reset(reset_n),
                .clkin(clk2m),
                .clkout(clk2000)
                );
        defparam clk_rhythm3.divdWIDTH=8,clk_rhythm3.divdFACTOR=500;//1000分频2hz
        div clk_rhythm3(
                .reset(reset_n),
                .clkin(clk2000),
                .clkout(clk2)
                );
        always @ (negedge reset_n or posedge clk2)
            if(!reset_n)
                begin
                index<=4'd0;
                cnt<=6'h0;
                end
            else
                begin
                if(cnt==6'd42)
                    cnt<=6'h0;
                else
                cnt<=cnt+1'b1;
                case(cnt)
                6'd1:index<=4'd5;
                6'd2:index<=4'd5;
                6'd3:index<=4'd6;
                6'd4:index<=4'd6;
                6'd5:index<=4'd5;
                6'd6:index<=4'd5;
                6'd7:index<=4'd8;
                6'd8:index<=4'd8;
                6'd9:index<=4'd7;
                6'd10:index<=4'd7;
                6'd11:index<=4'd5;
                6'd12:index<=4'd5;
                6'd13:index<=4'd6;
                6'd14:index<=4'd6;
                6'd15:index<=4'd5;
                6'd16:index<=4'd5;
                6'd17:index<=4'd9;
                6'd18:index<=4'd9;
                6'd19:index<=4'd8;
                6'd20:index<=4'd8;
                6'd21:index<=4'd5;
                6'd22:index<=4'd5;
                6'd23:index<=4'd12;
                6'd24:index<=4'd12;
                6'd25:index<=4'd10;
                6'd26:index<=4'd10;
                6'd27:index<=4'd8;
                6'd28:index<=4'd8;
                6'd29:index<=4'd7;
                6'd30:index<=4'd7;
                6'd31:index<=4'd6;
                6'd32:index<=4'd6;
                6'd33:index<=4'd11;
                6'd34:index<=4'd11;
                6'd35:index<=4'd10;
                6'd36:index<=4'd10;
                6'd37:index<=4'd8;
                6'd38:index<=4'd8;
                6'd39:index<=4'd9;
                6'd40:index<=4'd9;
                6'd41:index<=4'd8;
                6'd42:index<=4'd8;
            endcase
            end
    endmodule    

             (3)节拍

               歌曲中不仅有音调还要有快慢,这个快慢即为节拍。所以我们还是分频分出节拍为0.5s一拍。具体代码见上一部分。

              (二)数码管显示

              数码管显示的原理是利用人眼的视觉暂留0.05s,数码管动态扫描时间小于等于0.05s,人眼看到数码管就是一直在显示,根据这个原理,我们将音调显示在数码管上。音调即为上文提到的索引数,所以我们编写代码的思路就为将索引数输入,进行一些运算,在数码管上显示。道理十分简单。

    module smdisplay(clk,rst,index,dataout,en);
    
    input clk,rst;
    input [3:0]index;
    output[7:0] dataout;
    output[3:0] en;//COM使能输出
    
    reg[7:0] dataout;//各段数据输出
    reg[3:0] en;
    
    reg[15:0] cnt_scan;//扫描频率计数器
    reg[3:0] dataout_buf;
    
    always@(posedge clk or negedge  rst)
    begin
        if(!rst) 
            begin //低电平复位
                cnt_scan<=0;
             end
        else 
            begin
                cnt_scan<=cnt_scan+1;
            end
    end
    
    always @(cnt_scan)//段码扫描频率
    begin 
      case(cnt_scan[15:14])
          2'b00 :
              en = 4'b1110;
          2'b01 :
              en = 4'b1101;
          2'b10 :
              en = 4'b1011;
          2'b11 :
              en = 4'b0111;
          default :
              en = 4'b1110;
        endcase
    end
    
    always@(en or index) //对应COM信号给出各段数据,段码
    begin
        if(index>=1 && index<=7)
        case(en)
            4'b1110:
                dataout_buf<=index;
            4'b1101:
                dataout_buf<=0;
            4'b1011:
                dataout_buf<=0;
            4'b0111:
                dataout_buf<=0;   
            default:
                dataout_buf<=8;
         endcase
        else if(index >=8 && index <=14)
         case(en)
            4'b1110:
                dataout_buf<=0;
            4'b1101:
                dataout_buf<=index - 4'd7;
            4'b1011:
                dataout_buf<=0;
            4'b0111:
                dataout_buf<=0;   
            default:
                dataout_buf<=8;
         endcase
        else
         case(en)
            4'b1110:
                dataout_buf<=0;
            4'b1101:
                dataout_buf<=0;
            4'b1011:
                dataout_buf<=index - 4'd14;
            4'b0111:
                dataout_buf<=0;   
            default:
                dataout_buf<=8;
         endcase
    end
    
    always@(dataout_buf)
    begin
        case(dataout_buf)  //将要显示的数字译成段码
            4'b0000://0
                dataout=8'b0000_0011;
            4'b0001://1
                dataout=8'b1001_1111;
            4'b0010://2
                dataout=8'b0010_0101;
            4'b0011://3
                dataout=8'b0000_1101;
            4'b0100://4
                dataout=8'b1001_1001;
            4'b0101://5
                dataout=8'b0100_1001;
            4'b0110://6
                dataout=8'b0100_0001;
            4'b0111://7
                dataout=8'b0001_1111;
            4'b1000://8
                dataout=8'b0000_0001;
            4'b1001://9
                dataout=8'b0000_1001;
           default://这里仅编译了0-9这几个数字
                dataout=8'b1111_1111;//全灭
         endcase
    end
    
    endmodule

          之后,我们在顶层编写,将各个模块连接起来。输入输出分清楚。再进行管脚的绑定,我们就可以进行程序的烧写了!烧写如我们所料,可以演奏音乐,并且在数码管上显示音调。

    三、感悟

          这是在一年前大学里学了EDA课程后,再一次拾起。年轻时候由于EDA是选修课程,尽管很感兴趣也很喜欢讲授课程的老师,但是还是没有很重视起来,觉得这些课程像高数大物之类般看看书完成完成课后作业便可以了,可是在每一次完成老师布置的大作业时的吃力,至今想想还是不容小觑的。那时的我不会查资料,不会从网上看别人发的经验帖,更是看代码也十分粗糙,也不会用仿真软件。总结起来就是不知道这些与时俱进的技术怎么学。现在很高兴的是,我又一次的拾起来这些大概还没忘记的知识。由于实习,我又一次接触了FPGA,并且将其作为我的毕设题目。这次,我一定要吸取经验,努力提高自己。如果不知道该努力什么,那么就把自己现在所面临的每一件事情做好。加油。

  • 相关阅读:
    mybatis学习笔记
    markdownPad常用功能示例
    2018-2019-2 《Java程序设计》第3周学习总结
    2018-2019-2 《Java程序设计》第2周学习总结
    2018-2019-2 《Java程序设计》第1周学习总结
    Djnago models 一对多、多对多
    Superset 安装
    lvm 添加分区
    partprobe 重新检测Linux系统分区
    Docker 、Docker Compose 安装
  • 原文地址:https://www.cnblogs.com/amberwang2018/p/8433650.html
Copyright © 2020-2023  润新知