差别
这里会显示出您选择的修订版和当前版本之间的差别。
| 两侧同时换到之前的修订记录 前一修订版 后一修订版 | 前一修订版 | ||
|
蜂鸣器模块 [2017/06/05 10:19] anran [硬件说明] |
蜂鸣器模块 [2022/01/26 21:07] (当前版本) gongyu [### Verilog代码] |
||
|---|---|---|---|
| 行 1: | 行 1: | ||
| - | ======STEP FPGA驱动无源蜂鸣器模块====== | + | ## STEP FPGA驱动无源蜂鸣器模块 |
| 本节将和大家一起使用FPGA驱动底板上的无源蜂鸣器模块实现不同音节的输出。 | 本节将和大家一起使用FPGA驱动底板上的无源蜂鸣器模块实现不同音节的输出。 | ||
| - | ====硬件说明==== | + | ### 硬件说明 |
| ------- | ------- | ||
| + | |||
| 蜂鸣器的分类: | 蜂鸣器的分类: | ||
| \\ | \\ | ||
| 行 30: | 行 32: | ||
| 我们使用PWM的方法(关于PWM的说明,快速入门中的[[脉冲发生器]]章节有详细的介绍),使用计数器对系统时钟进行分频,改变计数器的计数终值从而实现调节PWM信号频率的目的,使用PWM信号控制蜂鸣器电路。 | 我们使用PWM的方法(关于PWM的说明,快速入门中的[[脉冲发生器]]章节有详细的介绍),使用计数器对系统时钟进行分频,改变计数器的计数终值从而实现调节PWM信号频率的目的,使用PWM信号控制蜂鸣器电路。 | ||
| \\ | \\ | ||
| - | ====Verilog代码==== | + | |
| + | ### Verilog代码 | ||
| ------ | ------ | ||
| + | |||
| <code verilog> | <code verilog> | ||
| 行 37: | 行 42: | ||
| // >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<< | // >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<< | ||
| // -------------------------------------------------------------------- | // -------------------------------------------------------------------- | ||
| - | // Module:Segment_scan | + | // Module: Beeper |
| // | // | ||
| // Author: Step | // Author: Step | ||
| // | // | ||
| - | // Description: Display with Segment tube | + | // Description: Beeper |
| // | // | ||
| - | // Web: www.stepfpga.com | + | // Web: www.stepfapga.com |
| // | // | ||
| // -------------------------------------------------------------------- | // -------------------------------------------------------------------- | ||
| 行 49: | 行 54: | ||
| // -------------------------------------------------------------------- | // -------------------------------------------------------------------- | ||
| // Version: |Mod. Date: |Changes Made: | // Version: |Mod. Date: |Changes Made: | ||
| - | // V1.0 |2015/11/11 |Initial ver | + | // V1.0 |2016/04/20 |Initial ver |
| // -------------------------------------------------------------------- | // -------------------------------------------------------------------- | ||
| - | module Segment_scan | + | module Beeper |
| ( | ( | ||
| - | input clk_in, //系统时钟 | + | input clk_in, //系统时钟 |
| - | input rst_n_in, //系统复位,低有效 | + | input rst_n_in, //系统复位,低有效 |
| - | input [3:0] seg_data_1, //SEG1 数码管要显示的数据 | + | input tone_en, //蜂鸣器使能信号 |
| - | input [3:0] seg_data_2, //SEG2 数码管要显示的数据 | + | input [4:0] tone, //蜂鸣器音节控制 |
| - | input [3:0] seg_data_3, //SEG3 数码管要显示的数据 | + | output reg piano_out //蜂鸣器控制输出 |
| - | input [3:0] seg_data_4, //SEG4 数码管要显示的数据 | + | |
| - | input [3:0] seg_data_5, //SEG5 数码管要显示的数据 | + | |
| - | input [3:0] seg_data_6, //SEG6 数码管要显示的数据 | + | |
| - | input [5:0] seg_data_en, //各位数码管数据显示使能,[MSB~LSB]=[SEG6~SEG1] | + | |
| - | input [5:0] seg_dot_en, //各位数码管小数点显示使能,[MSB~LSB]=[SEG6~SEG1] | + | |
| - | output reg rclk_out, //74HC595的RCK管脚 | + | |
| - | output reg sclk_out, //74HC595的SCK管脚 | + | |
| - | output reg sdio_out //74HC595的SER管脚 | + | |
| ); | ); | ||
| + | /* | ||
| + | 无源蜂鸣器可以发出不同的音节,与蜂鸣器震动的频率(等于蜂鸣器控制信号的频率)相关, | ||
| + | 为了让蜂鸣器控制信号产生不同的频率,我们使用计数器计数(分频)实现,不同的音节控制对应不同的计数终值(分频系数) | ||
| + | 计数器根据计数终值计数并分频,产生蜂鸣器控制信号 | ||
| + | */ | ||
| + | reg [15:0] time_end; | ||
| + | //根据不同的音节控制,选择对应的计数终值(分频系数) | ||
| + | //低音1的频率为261.6Hz,蜂鸣器控制信号周期应为12MHz/261.6Hz = 45871.5, | ||
| + | //因为本设计中蜂鸣器控制信号是按计数器周期翻转的,所以几种终值 = 45871.5/2 = 22936 | ||
| + | //需要计数22936个,计数范围为0 ~ (22936-1),所以time_end = 22935 | ||
| + | always@(tone) begin | ||
| + | case(tone) | ||
| + | 5'd1: time_end = 16'd22935; //L1, | ||
| + | 5'd2: time_end = 16'd20428; //L2, | ||
| + | 5'd3: time_end = 16'd18203; //L3, | ||
| + | 5'd4: time_end = 16'd17181; //L4, | ||
| + | 5'd5: time_end = 16'd15305; //L5, | ||
| + | 5'd6: time_end = 16'd13635; //L6, | ||
| + | 5'd7: time_end = 16'd12147; //L7, | ||
| + | 5'd8: time_end = 16'd11464; //M1, | ||
| + | 5'd9: time_end = 16'd10215; //M2, | ||
| + | 5'd10: time_end = 16'd9100; //M3, | ||
| + | 5'd11: time_end = 16'd8589; //M4, | ||
| + | 5'd12: time_end = 16'd7652; //M5, | ||
| + | 5'd13: time_end = 16'd6817; //M6, | ||
| + | 5'd14: time_end = 16'd6073; //M7, | ||
| + | 5'd15: time_end = 16'd5740; //H1, | ||
| + | 5'd16: time_end = 16'd5107; //H2, | ||
| + | 5'd17: time_end = 16'd4549; //H3, | ||
| + | 5'd18: time_end = 16'd4294; //H4, | ||
| + | 5'd19: time_end = 16'd3825; //H5, | ||
| + | 5'd20: time_end = 16'd3408; //H6, | ||
| + | 5'd21: time_end = 16'd3036; //H7, | ||
| + | default:time_end = 16'd65535; | ||
| + | endcase | ||
| + | end | ||
| - | parameter CLK_DIV_PERIOD = 600; //分频系数 | + | reg [17:0] time_cnt; |
| - | + | //当蜂鸣器使能时,计数器按照计数终值(分频系数)计数 | |
| - | localparam IDLE = 3'd0; | + | |
| - | localparam MAIN = 3'd1; | + | |
| - | localparam WRITE = 3'd2; | + | |
| - | + | ||
| - | localparam LOW = 1'b0; | + | |
| - | localparam HIGH = 1'b1; | + | |
| - | + | ||
| - | //创建数码管的字库,字库数据依段码顺序有关 | + | |
| - | //这里字库数据[MSB~LSB]={DP,G,F,E,D,C,B,A} | + | |
| - | reg[7:0] seg [15:0]; | + | |
| - | initial begin | + | |
| - | seg[0] = 8'h3f; // 0 | + | |
| - | seg[1] = 8'h06; // 1 | + | |
| - | seg[2] = 8'h5b; // 2 | + | |
| - | seg[3] = 8'h4f; // 3 | + | |
| - | seg[4] = 8'h66; // 4 | + | |
| - | seg[5] = 8'h6d; // 5 | + | |
| - | seg[6] = 8'h7d; // 6 | + | |
| - | seg[7] = 8'h07; // 7 | + | |
| - | seg[8] = 8'h7f; // 8 | + | |
| - | seg[9] = 8'h6f; // 9 | + | |
| - | seg[10] = 8'h77; // A | + | |
| - | seg[11] = 8'h7c; // b | + | |
| - | seg[12] = 8'h39; // C | + | |
| - | seg[13] = 8'h5e; // d | + | |
| - | seg[14] = 8'h79; // E | + | |
| - | seg[15] = 8'h71; // F | + | |
| - | end | + | |
| - | + | ||
| - | //计数器对系统时钟信号进行计数 | + | |
| - | reg[9:0] cnt=0; | + | |
| always@(posedge clk_in or negedge rst_n_in) begin | always@(posedge clk_in or negedge rst_n_in) begin | ||
| if(!rst_n_in) begin | if(!rst_n_in) begin | ||
| - | cnt <= 1'b0; | + | time_cnt <= 1'b0; |
| + | end else if(!tone_en) begin | ||
| + | time_cnt <= 1'b0; | ||
| + | end else if(time_cnt>=time_end) begin | ||
| + | time_cnt <= 1'b0; | ||
| end else begin | end else begin | ||
| - | if(cnt>=(CLK_DIV_PERIOD-1)) cnt <= 1'b0; | + | time_cnt <= time_cnt + 1'b1; |
| - | else cnt <= cnt + 1'b1; | + | |
| end | end | ||
| end | end | ||
| - | + | ||
| - | //根据计数器计数的周期产生分频的脉冲信号 | + | //根据计数器的周期,翻转蜂鸣器控制信号 |
| - | reg clk_div; | + | |
| always@(posedge clk_in or negedge rst_n_in) begin | always@(posedge clk_in or negedge rst_n_in) begin | ||
| if(!rst_n_in) begin | if(!rst_n_in) begin | ||
| - | clk_div <= 1'b0; | + | piano_out <= 1'b0; |
| + | end else if(time_cnt==time_end) begin | ||
| + | piano_out <= ~piano_out; //蜂鸣器控制输出翻转,两次翻转为1Hz | ||
| end else begin | end else begin | ||
| - | if(cnt==(CLK_DIV_PERIOD-1)) clk_div <= 1'b1; | + | piano_out <= piano_out; |
| - | else clk_div <= 1'b0; | + | |
| end | end | ||
| end | end | ||
| - | + | ||
| - | //使用状态机完成数码管的扫描和74HC595时序的实现 | + | |
| - | reg [15:0] data_reg; | + | |
| - | reg [2:0] cnt_main; | + | |
| - | reg [5:0] cnt_write; | + | |
| - | reg [2:0] state = IDLE; | + | |
| - | always@(posedge clk_in or negedge rst_n_in) begin | + | |
| - | if(!rst_n_in) begin //复位状态下,各寄存器置初值 | + | |
| - | state <= IDLE; | + | |
| - | cnt_main <= 3'd0; | + | |
| - | cnt_write <= 6'd0; | + | |
| - | sdio_out <= 1'b0; | + | |
| - | sclk_out <= LOW; | + | |
| - | rclk_out <= LOW; | + | |
| - | end else begin | + | |
| - | case(state) | + | |
| - | IDLE:begin //IDLE作为第一个状态,相当于软复位 | + | |
| - | state <= MAIN; | + | |
| - | cnt_main <= 3'd0; | + | |
| - | cnt_write <= 6'd0; | + | |
| - | sdio_out <= 1'b0; | + | |
| - | sclk_out <= LOW; | + | |
| - | rclk_out <= LOW; | + | |
| - | end | + | |
| - | MAIN:begin | + | |
| - | if(cnt_main >= 3'd5) cnt_main <= 1'b0; | + | |
| - | else cnt_main <= cnt_main + 1'b1; | + | |
| - | case(cnt_main) | + | |
| - | //对6位数码管逐位扫描 | + | |
| - | 3'd0: begin | + | |
| - | state <= WRITE; //在配置完发给74HC595的数据同时跳转至WRITE状态,完成串行时序 | + | |
| - | data_reg <= {seg[seg_data_1]|(seg_dot_en[0]?8'h80:8'h00),seg_data_en[0]?8'hfe:8'hff}; | + | |
| - | //data_reg[15:8]为段选,data_reg[7:0]为位选 | + | |
| - | //seg[seg_data_1] 是根据端口的输入获取相应字库数据 | + | |
| - | //seg_dot_en[0]?8'h80:8'h00 是根据小数点显示使能信号 控制SEG1数码管的小数点DP段的电平 | + | |
| - | //seg_data_en[0]?8'hfe:8'hff 是根据数据显示使能信号 控制SEG1数码管的位选引脚的电平 | + | |
| - | end | + | |
| - | 3'd1: begin | + | |
| - | state <= WRITE; | + | |
| - | data_reg <= {seg[seg_data_2]|(seg_dot_en[1]?8'h80:8'h00),seg_data_en[1]?8'hfd:8'hff}; | + | |
| - | end | + | |
| - | 3'd2: begin | + | |
| - | state <= WRITE; | + | |
| - | data_reg <= {seg[seg_data_3]|(seg_dot_en[2]?8'h80:8'h00),seg_data_en[2]?8'hfb:8'hff}; | + | |
| - | end | + | |
| - | 3'd3: begin | + | |
| - | state <= WRITE; | + | |
| - | data_reg <= {seg[seg_data_4]|(seg_dot_en[3]?8'h80:8'h00),seg_data_en[3]?8'hf7:8'hff}; | + | |
| - | end | + | |
| - | 3'd4: begin | + | |
| - | state <= WRITE; | + | |
| - | data_reg <= {seg[seg_data_5]|(seg_dot_en[4]?8'h80:8'h00),seg_data_en[4]?8'hef:8'hff}; | + | |
| - | end | + | |
| - | 3'd5: begin | + | |
| - | state <= WRITE; | + | |
| - | data_reg <= {seg[seg_data_6]|(seg_dot_en[5]?8'h80:8'h00),seg_data_en[5]?8'hdf:8'hff}; | + | |
| - | end | + | |
| - | default: state <= IDLE; | + | |
| - | endcase | + | |
| - | end | + | |
| - | WRITE:begin | + | |
| - | if(clk_div) begin //74HC595的串行时钟有速度要求,需要按照分频后的节拍 | + | |
| - | if(cnt_write >= 6'd33) cnt_write <= 1'b0; | + | |
| - | else cnt_write <= cnt_write + 1'b1; | + | |
| - | case(cnt_write) | + | |
| - | //74HC595是串行转并行的芯片,3路输入可产生8路输出,而且可以级联使用 | + | |
| - | //74HC595的时序实现,参考74HC595的芯片手册 | + | |
| - | 6'd0: begin sclk_out <= LOW; sdio_out <= data_reg[15]; end //SCK下降沿时SER更新数据 | + | |
| - | 6'd1: begin sclk_out <= HIGH; end //SCK上升沿时SER数据稳定 | + | |
| - | 6'd2: begin sclk_out <= LOW; sdio_out <= data_reg[14]; end | + | |
| - | 6'd3: begin sclk_out <= HIGH; end | + | |
| - | 6'd4: begin sclk_out <= LOW; sdio_out <= data_reg[13]; end | + | |
| - | 6'd5: begin sclk_out <= HIGH; end | + | |
| - | 6'd6: begin sclk_out <= LOW; sdio_out <= data_reg[12]; end | + | |
| - | 6'd7: begin sclk_out <= HIGH; end | + | |
| - | 6'd8: begin sclk_out <= LOW; sdio_out <= data_reg[11]; end | + | |
| - | 6'd9: begin sclk_out <= HIGH; end | + | |
| - | 6'd10: begin sclk_out <= LOW; sdio_out <= data_reg[10]; end | + | |
| - | 6'd11: begin sclk_out <= HIGH; end | + | |
| - | 6'd12: begin sclk_out <= LOW; sdio_out <= data_reg[9]; end | + | |
| - | 6'd13: begin sclk_out <= HIGH; end | + | |
| - | 6'd14: begin sclk_out <= LOW; sdio_out <= data_reg[8]; end | + | |
| - | 6'd15: begin sclk_out <= HIGH; end | + | |
| - | 6'd16: begin sclk_out <= LOW; sdio_out <= data_reg[7]; end | + | |
| - | 6'd17: begin sclk_out <= HIGH; end | + | |
| - | 6'd18: begin sclk_out <= LOW; sdio_out <= data_reg[6]; end | + | |
| - | 6'd19: begin sclk_out <= HIGH; end | + | |
| - | 6'd20: begin sclk_out <= LOW; sdio_out <= data_reg[5]; end | + | |
| - | 6'd21: begin sclk_out <= HIGH; end | + | |
| - | 6'd22: begin sclk_out <= LOW; sdio_out <= data_reg[4]; end | + | |
| - | 6'd23: begin sclk_out <= HIGH; end | + | |
| - | 6'd24: begin sclk_out <= LOW; sdio_out <= data_reg[3]; end | + | |
| - | 6'd25: begin sclk_out <= HIGH; end | + | |
| - | 6'd26: begin sclk_out <= LOW; sdio_out <= data_reg[2]; end | + | |
| - | 6'd27: begin sclk_out <= HIGH; end | + | |
| - | 6'd28: begin sclk_out <= LOW; sdio_out <= data_reg[1]; end | + | |
| - | 6'd29: begin sclk_out <= HIGH; end | + | |
| - | 6'd30: begin sclk_out <= LOW; sdio_out <= data_reg[0]; end | + | |
| - | 6'd31: begin sclk_out <= HIGH; end | + | |
| - | 6'd32: begin rclk_out <= HIGH; end //当16位数据传送完成后RCK拉高,输出生效 | + | |
| - | 6'd33: begin rclk_out <= LOW; state <= MAIN; end | + | |
| - | default: state <= IDLE; | + | |
| - | endcase | + | |
| - | end else begin | + | |
| - | sclk_out <= sclk_out; | + | |
| - | sdio_out <= sdio_out; | + | |
| - | rclk_out <= rclk_out; | + | |
| - | cnt_write <= cnt_write; | + | |
| - | state <= state; | + | |
| - | end | + | |
| - | end | + | |
| - | default: state <= IDLE; | + | |
| - | endcase | + | |
| - | end | + | |
| - | end | + | |
| endmodule | endmodule | ||
| 行 243: | 行 133: | ||
| \\ | \\ | ||
| - | ====小结==== | + | |
| + | ### 小结 | ||
| ------ | ------ | ||
| - | 本节主要为大家讲解了数码管显示的相关原理及软件设计,需要大家掌握的同时自己创建工程,通过整个设计流程,生成FPGA配置文件加载测试。 | + | |
| + | 本节主要为大家讲解了蜂鸣器的不同类型及无源蜂鸣器的驱动原理,需要大家掌握的同时自己创建工程,通过整个设计流程,生成FPGA配置文件加载测试。 | ||
| \\ | \\ | ||
| 如果你对Diamond软件的使用不了解,请参考这里:[[lattice_diamond的使用|Diamond的使用]]。 | 如果你对Diamond软件的使用不了解,请参考这里:[[lattice_diamond的使用|Diamond的使用]]。 | ||
| - | ====相关资料==== | + | ### 相关资料 |
| ------ | ------ | ||
| \\ | \\ | ||
| - | 使用[[STEP-MXO2第二代]]的数码管扫描程序: 后续会有下载连接 待更新 | + | 使用[[STEP-MXO2第二代]]的蜂鸣器控制程序: 后续会有下载连接 待更新 |
| \\ | \\ | ||
| - | 使用[[STEP-MAX10]]的数码管扫描程序: 后续会有下载连接 待更新 | + | 使用[[STEP-MAX10]]的蜂鸣器控制程序: 后续会有下载连接 待更新 |
| \\ | \\ | ||