当前位置:   article > 正文

FPGA开发:音乐播放器_fpga蜂鸣器音乐

fpga蜂鸣器音乐

相关阅读 

FPGA开发专栏icon-default.png?t=N7T8https://blog.csdn.net/weixin_45791458/category_12388695.html?spm=1001.2014.3001.5482


        FPGA开发板上的蜂鸣器可以用来播放音乐,只需要控制蜂鸣器信号的方波频率、占空比和持续时间即可。

1、简谱原理

        简谱上的4/4表示该简谱以4分音符为一拍,每小节4拍,简谱上应该也会标注每分钟多少拍。音符时值对照表如下图所示,这表示了每个音符的演奏时长。

        音符是记录音的高低和长短的符号,简谱中的音符是七个阿拉伯数字,它们是:1(Do)、2(Re)、3(Mi)、4(Fa)、5(Sol)、6(La)、7(Ti),为了标记更高或更低的音,则在基本符号的上面或下面加上小圆点。在简谱中,不带点的基本符号叫中音。记在简谱基本音符号下面的小圆点,叫低音点,它表示将基本音符降低一个音组,即降低一个纯八度。在基本符号下面加一个点叫低音,加两个点叫倍低音,加三个点叫超低音。记在简谱基本音符号上面的小圆点,叫高音点,它表示将基本音符升高一个音组,即升高一个纯八度。在基本符号上面加一个点叫高音,加两个点叫倍高音,加三个点叫超高音

        音符所对应的频率如下表所示。

音符频率
低音1261Hz
低音2293Hz
低音3329Hz
低音4349Hz
低音5392Hz
低音6440Hz
低音7499Hz
中音1523Hz
中音2587Hz
中音3659Hz
中音4698Hz
中音5784Hz
中音6880Hz
中音7998Hz
高音11046Hz
高音21174Hz
高音31318Hz
高音41396Hz
高音51568Hz
高音61760Hz
高音71976Hz

2、结构设计

2.1、按键消抖模块

        由于要是用按键控制音乐开始播放,所以需要一个按键消抖模块,具体可以在FPGA开发:按键消抖一文中找到。

  1. Debounce debounce_0
  2. (
  3. .clk (clk),
  4. .rst (rst_n),
  5. .button_in (button_in),
  6. .button_out (button_out)
  7. );

        同时我们还需要一个边沿检测的机制来保证一次按下只触发一次按键操作。

  1. always @ (posedge clk or posedge rst)begin
  2. if(rst == 1'b1)begin
  3. button_out_d0 <= 1'b1;
  4. button_negedge <= 1'b0;
  5. end
  6. else begin
  7. button_out_d0 <= button_out;
  8. button_negedge <= button_out_d0 & ~button_out;
  9. end
  10. end

2.2、ROM模块

        使用ROM保存音符时长和音调,创建ROM的过程可以根据不同的FPGA开发环境而定,如果是Quartus的话步骤如下:

        首先新建两个个MIF文件,它们是用来初始化ROM的,如下图所示。

         根据你的简谱长度,设置深度,如下图所示。

        随后根据简谱填入对应信息并保存,如下图所示。 

        接着在IP窗口搜索ROM IP,如下图所示。

         选好模块名和HDL类型并保存,这里选择Verilog HDL,如下图所示。

        在ROM创建菜单中选择创建的ROM大小(这里应该要和刚才的MIF文件一致),如下图所示。

        在初始化界面,选择使用刚才创建的MIF文件并Finish即可完成ROM的创建,如下图所示。

2.3、频率译码模块

         规定中音1使用十进制数11表示,而低音1使用01表示,中音2使用12表示。译码模块根据对应的音符频率,输出相应的周期,其中CLK_FRE根据开发板的频率而定。

  1. module music_hz(
  2. input [7:0] hz_sel,
  3. output reg [19:0] cycle
  4. );
  5. parameter CLK_FRE = 50 ;
  6. always @(*)begin
  7. case(hz_sel)
  8. 8'h01 : cycle = CLK_FRE*1000000/261 ; //low 1 261Hz
  9. 8'h02 : cycle = CLK_FRE*1000000/293 ; //low 2 293Hz
  10. 8'h03 : cycle = CLK_FRE*1000000/329 ; //low 3 329Hz
  11. 8'h04 : cycle = CLK_FRE*1000000/349 ; //low 4 349Hz
  12. 8'h05 : cycle = CLK_FRE*1000000/392 ; //low 5 392Hz
  13. 8'h06 : cycle = CLK_FRE*1000000/440 ; //low 6 440Hz
  14. 8'h07 : cycle = CLK_FRE*1000000/499 ; //low 7 499Hz
  15. 8'h11 : cycle = CLK_FRE*1000000/523 ; //middle 1 523Hz
  16. 8'h12 : cycle = CLK_FRE*1000000/587 ; //middle 2 587Hz
  17. 8'h13 : cycle = CLK_FRE*1000000/659 ; //middle 3 659Hz
  18. 8'h14 : cycle = CLK_FRE*1000000/698 ; //middle 4 698Hz
  19. 8'h15 : cycle = CLK_FRE*1000000/784 ; //middle 5 784Hz
  20. 8'h16 : cycle = CLK_FRE*1000000/880 ; //middle 6 880Hz
  21. 8'h17 : cycle = CLK_FRE*1000000/998 ; //middle 7 998Hz
  22. 8'h21 : cycle = CLK_FRE*1000000/1046 ; //high 1 1046Hz
  23. 8'h22 : cycle = CLK_FRE*1000000/1174 ; //high 2 1174Hz
  24. 8'h23 : cycle = CLK_FRE*1000000/1318 ; //high 3 1318Hz
  25. 8'h24 : cycle = CLK_FRE*1000000/1396 ; //high 4 1396Hz
  26. 8'h25 : cycle = CLK_FRE*1000000/1568 ; //high 5 1568Hz
  27. 8'h26 : cycle = CLK_FRE*1000000/1760 ; //high 6 1760Hz
  28. 8'h27 : cycle = CLK_FRE*1000000/1976 ; //high 7 1976Hz
  29. default : cycle = 20'd0 ;
  30. endcase
  31. end
  32. endmodule

2.4、状态机演奏模块

        状态机设有四个状态,IDLE,PLAY,PLAY_WAIT和PLAY_END,其中PLAY状态使用一个计数器对每个音符的演奏时长进行计数,PLAY_WAIT用于检查是否全部音符演奏完毕,如果否,则会对演奏时长计数器清零并再次进入PLAY状态。

  1. always @(*)begin
  2. case(state)
  3. IDLE:begin
  4. if (button_negedge)
  5. next_state = PLAY;
  6. else
  7. next_state = IDLE;
  8. end
  9. PLAY:begin
  10. if (play_cnt == music_time)
  11. next_state = PLAY_WAIT;
  12. else
  13. next_state = PLAY;
  14. end
  15. PLAY_WAIT:begin
  16. if (music_cnt == music_len - 1)
  17. next_state = PLAY_END;
  18. else
  19. next_state = PLAY;
  20. end
  21. PLAY_END:next_state = IDLE;
  22. default:next_state = IDLE;
  23. endcase
  24. end

        周期计数器用于对音符的每个周期进行计数,并提供计数值给输出信号模块。

  1. always @(posedge clk or negedge rst_n)begin
  2. if (~rst_n)
  3. hz_cnt <= 20'd0;
  4. else if (state == PLAY || state == PLAY_WAIT)begin
  5. if (hz_cnt == cycle - 1)
  6. hz_cnt <= 20'd0;
  7. else
  8. hz_cnt <= hz_cnt + 1'b1;
  9. end
  10. else
  11. hz_cnt <= 20'd0;
  12. end

        输出信号模块根据计数值输出信号,其中还可以控制占空比。

  1. always @(posedge clk or negedge rst_n)begin
  2. if (~rst_n)
  3. buzzer <= 1'b1;
  4. else if (state == PLAY || state == PLAY_WAIT)begin
  5. if (hz_cnt < cycle/32) //控制占空比
  6. buzzer <= 1'b0;
  7. else
  8. buzzer <= 1'b1;
  9. end
  10. else if (state == IDLE || state == PLAY_END)
  11. buzzer <= 1'b1;
  12. end

        演奏时长计数器用于对每个音符的演奏时间计数。

  1. always @(posedge clk or negedge rst_n)begin
  2. if (~rst_n)
  3. play_cnt <= 32'd0;
  4. else if (state == PLAY)
  5. play_cnt <= play_cnt + 1'b1;
  6. else
  7. play_cnt <= 32'd0;
  8. end

        演奏个数计数器用于对演奏的音符数计数。

  1. always @(posedge clk or negedge rst_n)begin
  2. if (~rst_n)
  3. music_cnt <= 32'd0;
  4. else if (state == PLAY_WAIT)
  5. music_cnt <= music_cnt + 1'b1;
  6. else if (state == IDLE || state == PLAY_END)
  7. music_cnt <= 32'd0;
  8. end

        最后实例化ROM,并且注意,这里规定演奏时长rom值以8为一拍,所以读取rom值后需要进行转换,假设一分钟85拍。

  1. music_hz hz0
  2. (
  3. .hz_sel(rom_hz_data),
  4. .cycle(cycle)
  5. ) ;
  6. music_rom hz_rom
  7. (
  8. .address(music_cnt[8:0]),
  9. .clock(clk),
  10. .q(rom_hz_data)
  11. );
  12. music_time_rom time_rom
  13. (
  14. .address(music_cnt[8:0]),
  15. .clock(clk),
  16. .q(rom_time_data)
  17. );
  18. always @(posedge clk or negedge rst_n)begin
  19. if (~rst_n)
  20. music_time <= 32'hffff_ffff;
  21. else
  22. music_time <= rom_time_data*(CLK_FRE*1000000*60/85/8);
  23. end

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/article/detail/35011
推荐阅读
相关标签
  

闽ICP备14008679号