实验任务:
使用板载4位独立按键,进行4位按键消抖检测,当没有按键按下时不响,按下则发出声响,分别为 DO RE MI FA。
实验思路
还是有两个part,一个part用来检测按键是否按下,输入key_in,输出key_out,为第几位。
第二个part用来运行蜂鸣器,根据按键值case,未按下的,不响,按下就DO RE MI FA四个输出。
4位按键消抖
关于这部分,已经在之前一篇博客中写过了,
Verilog实现4位按键消抖,分别控制一个LED
所以这里重点在于怎么利用按键控制蜂鸣器。
蜂鸣器控制
[3:0] beep_ctrl里面就的4位分别对应一位按键,为1即为该按键按下。
如果只是像下面这么改,其实是没有用的,因为beep_ctrl中是一个脉冲信号,只能在一个clk的时间让freq等于四个值中的一个,其他时间都是等于0,根本不能响,所以需要将这个脉冲信号转换为电平信号。
//音阶切换,根据cnt_500ms来切换freq_data的值
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
freq_data <= 0;
else case(beep_ctrl)
4'd0: freq_data <= 0 ;
4'd1: freq_data <= RE;
4'd2: freq_data <= MI;
4'd4: freq_data <= FA;
4'd8: freq_data <= SO;
// 4'd5: freq_data <= LA;
// 4'd6: freq_data <= XI;
default:freq_data <= 0;
endcase
end
笔者确实不会了,附上现在的代码。写崩溃了,这么简单的程序都不会,我。。。。。请路过的大佬指导
module key_beep
#(
parameter KEY_20MS = 999_999,
parameter TIME_500MS = 25'd24_999_999, //0.5s 计数值
parameter DO = 18'd190839 , //"哆"音调分频计数值(频率 262)
parameter RE = 18'd170067 , //"来"音调分频计数值(频率 294)
parameter MI = 18'd151514 , //"咪"音调分频计数值(频率 330)
parameter FA = 18'd143265 , //"发"音调分频计数值(频率 349)
parameter SO = 18'd127550 , //"梭"音调分频计数值(频率 392)
parameter LA = 18'd113635 , //"拉"音调分频计数值(频率 440)
parameter XI = 18'd101214 //"西"音调分频计数值(频率 494)
)
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire [3:0] key_in ,
output reg beep
);
reg key_rst;//检测是否有按键按下,并且同步到时钟下
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
key_rst <= 1'b1;
else
key_rst <= key_in[3] & key_in[2] & key_in[1] & key_in[0];//将四个按键的值存进,只要有一位被按下就会发生跳变
end
reg key_rst_reg;//对key_rst进行延一拍
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
key_rst_reg <= 1'b1;
else
key_rst_reg <= key_rst;
end
//检测下降沿操作
wire key_en = (~key_rst) & key_rst_reg;
reg [19:0] cnt_20ms;//20ms计数器
//cnt_20ms 计数器
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
cnt_20ms <= 20'd0;
else if(key_en) //检测到抖动
cnt_20ms <= 20'd0;
//else if(cnt_20ms == CNT_MAX )
// cnt_20ms <= CNT_MAX; //计数到最大值就保持
else
cnt_20ms <= cnt_20ms + 20'd1;
end
reg [3:0] key_flag;//作为按键检测标志位
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
key_flag <= 4'b1111;
else if(cnt_20ms == KEY_20MS)//每20ms保存一次键值
key_flag <= key_in;
end
reg [3:0] key_flag_reg;//延一拍
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
key_flag_reg <= 4'b1111;
else
key_flag_reg <= key_flag;
end
//保存按键值
wire [3:0] beep_ctrl = (~key_flag) & key_flag_reg;
//蜂鸣器部分代码
reg [24:0] cnt ;//0.5s计数
reg [17:0] freq_cnt ;//音调计数器
reg [2:0] cnt_500ms ;//0.5s个数计数器
reg [17:0] freq_data ;//音调分频计数值
wire [16:0] duty_data ;//占空比计数值
//设置占空比 50%
assign duty_data = freq_data >> 1;//使用移位来实现除2
//0.5s计数器
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
cnt <= 25'b0;
else if(cnt == TIME_500MS)
cnt <= 25'b0;
else
cnt <= cnt + 1'b1;
end
reg [3:0] beep_ctrl_dp;
//将脉冲信号转换为电平信号
always @(posedge sys_clk or negedge sys_rst_n ) begin
if(!sys_rst_n)
beep_ctrl_dp <= 4'b0000;
else if(cnt == TIME_500MS)
beep_ctrl_dp <= 4'b0000;
else if(beep_ctrl != beep_ctrl_dp)
beep_ctrl_dp <= beep_ctrl;
end
//音阶切换,根据cnt_500ms来切换freq_data的值
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
freq_data <= 0;
else case(beep_ctrl_dp)
4'd0: freq_data <= 0 ;
4'd1: freq_data <= RE;
4'd2: freq_data <= MI;
4'd4: freq_data <= FA;
4'd8: freq_data <= SO;
// 4'd5: freq_data <= LA;
// 4'd6: freq_data <= XI;
default:freq_data <= 0;
endcase
end
//freq_cnt:当计数到音阶计数值或跳转到下一音阶时,开始重新计数
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
freq_cnt <= 18'd0;
else if(freq_cnt == freq_data || cnt == TIME_500MS)
freq_cnt <= 18'd0;
else
freq_cnt <= freq_cnt+ 1'b1;
end
//beep:输出蜂鸣器波形
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
beep <= 1'b0;
else if(freq_cnt >= duty_data)
beep <= 1'b1;
else
beep <= 1'b0;
end
endmodule
疑问
//将脉冲信号转换为电平信号
always @(posedge beep_ctrl or posedge cnt or negedge sys_rst_n ) begin
if(!sys_rst_n)
beep_ctrl_dp <= 4'b0000;
else if(beep_ctrl != beep_ctrl_dp)//只响0.5s
beep_ctrl_dp <= 4'b0000;
else if(cnt == TIME_500MS)
beep_ctrl_dp <= beep_ctrl;
end
Error (10200): Verilog HDL Conditional Statement error at keep_beep.v(121): cannot match operand(s) in the condition to the corresponding edges in the enclosing event control of the always construct
报错如下,也没搞懂什么意思。
一篇介绍这个错误的。没看懂