zoukankan      html  css  js  c++  java
  • 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,并且将其作为我的毕设题目。这次,我一定要吸取经验,努力提高自己。如果不知道该努力什么,那么就把自己现在所面临的每一件事情做好。加油。

  • 相关阅读:
    java.lang.NoSuchMethodError:antlr.collections.AST.getLine() I
    T7 java Web day01 标签HTML
    T6 s1 day19
    T5 s5 Day18
    T5 s4 Day 17
    T5 s3 day16
    T5 s2 Day 15
    T5 s1 day14
    T4 S03 day 12
    T4 S01 day1
  • 原文地址:https://www.cnblogs.com/amberwang2018/p/8433650.html
Copyright © 2011-2022 走看看