按键,流水灯这些以前觉得比较简单的程序现在踏踏实实的做一遍,还是收获很多。
拿按键消都来说,看特权的例程开始觉得有点问题,后来想了想还是起到了按键消抖的作用。
//-------------------------------------------------
//按键按下检测,即下降沿检测
reg[1:0] button_get;//button_get是按键的最新状态
always @ (posedge clk or negedge rst_n)
if(!rst_n) button_get <= 2'b11;
else button_get <= {button_2_n,button_1_n};
reg[1:0] button_get_r;//button_get_r是前一个时钟按键的状态
always @ (posedge clk or negedge rst_n)
if(!rst_n) button_get_r <= 2'b11;
else button_get_r <= button_get;
//前一个状态的值和当前状态的非做与运算就可以判断下降沿即有键被按下
wire[1:0] button_an = button_get_r[1:0] & (~button_get[1:0]);/*
当button_an中某一位为则该位被按下
button_get 111 111 110 110
button_get_r 111 111 110 110
~button_get 000 000 001 001
button_an 000 001 000
可以看到当有键被按下时,即使是长按?
琤utton_an中对应为会置一,
但它只会在一个时钟周期有效
,下一个时钟周期变为0,尽管你还按着键。
*///--------------------------------------------------
//20ms计数器
reg[19:0] cnt;always @ (posedge clk or negedge rst_n)
if(!rst_n) cnt <= 20'd0;
else if(button_an) cnt <= 20'd0;
else cnt <= cnt + 1'b1;
//-------------------------------------------------
reg[1:0] low_sw;//low_sw是每隔20ms才能才一次按键的值
always @ (posedge clk or negedge rst_n)
if(!rst_n) low_sw <= 2'b11;
else if(cnt == 20'hffff)
low_sw <= {button_2_n,button_1_n};
reg[1:0] low_sw_r;/*
它虽然只滞后low_sw一个时钟周期
但low_sw是每隔20ms才改变一次
简单的说low_sw_r就是20ms以前的值
low_sw是当前的值,这样就完成了消抖。
*/
always @ (posedge clk or negedge rst_n)
if(!rst_n) low_sw_r <= 2'b11;
else low_sw_r <= low_sw;
wire[1:0] led_ctrl = low_sw_r & (~low_sw);//--------------------------------------------------
前面的下降沿检测只是起一个计数器清零的作用,要是加上一个是能信号就会跟完美,前面的检测到下降沿之后,才启动计数器开始计数,然后20ms之后如果还是低,与20ms前的运算就可以消除抖动。
关于流水灯,想起原来用的用计数器到了以后状态加一,再用case语句就觉得幼稚,我开始也想过用移位来写,但我没想出来循环移位怎么个写法,后来可看了一下特权同学的程序,才知道没我想的那么复杂。
led_r <= {led_r[0],led_r[3:1]};
把最低位放到最高位就实现了循环右移。
总结:基础一定要打扎实,好高骛远是不行的,决定把基础实验认认真真做一遍,完完整整的啃几本书。
文章评论(0条评论)
登录后参与讨论