如果用单片机来写这个程序,我们会这么写,先输出col,再检测row行值。同样上面的Verilog代码目的也是要实现这个时序,但恰恰是检测 row在输出col之前。所以对这个代码进行修改的话就是让它满足,先输出col,再检测row的时序。我们可以通过插入额外的状态来实现,修改后的代码 如下:
/*************************************************/
parameter NO_KEY_PRESSED=4'b0000;//没有键按下
parameter SET_COL0 =4'b0001;
parameter SCAN_COL0 =4'b0010;//扫描第 0 列
parameter SET_COL1 =4'b0011;
parameter SCAN_COL1 =4'b0100;//扫描第 1 列
parameter SET_COL2 =4'b0101;
parameter SCAN_COL2 =4'b0110;//扫描第 2 列
parameter SET_COL3 =4'b0111;
parameter SCAN_COL3 =4'b1000;//扫描第 3 列
parameter SET_ALL =4'b1001;
parameter KEY_PRESSED =4'b1010;//有键按下
/*************************************************/
reg [5:0]current_state,next_state; //现态和次态
/*************************************************/
always @(posedge key_clk or negedge rst_n)
if(!rst_n)
current_state<=SET_ALL;
else
current_state<=next_state;
/*************************************************/
always @ (current_state) //根据条件转移状态
case (current_state)
SET_ALL :
next_state=NO_KEY_PRESSED;
NO_KEY_PRESSED: //没有键按下
if(row!=4'hf)
next_state=SET_COL0;
else
next_state=SET_ALL;
SET_COL0:
next_state=SCAN_COL0;
SCAN_COL0: //扫描第 0 列
if(row!=4'hF)
next_state=KEY_PRESSED;
else
next_state=SET_COL1;
SET_COL1:
next_state=SCAN_COL1;
SCAN_COL1: //扫描第 1 列
if(row!=4'hF)
next_state=KEY_PRESSED;
else
next_state=SET_COL2;
SET_COL2:
next_state=SCAN_COL2;
SCAN_COL2: //扫描第 2 列
if(row!=4'hF)
next_state=KEY_PRESSED;
else
next_state=SET_COL3;
SET_COL3:
next_state=SCAN_COL3;
SCAN_COL3: //扫描第 3 列
if(row!=4'hF)
next_state=KEY_PRESSED;
else
next_state=NO_KEY_PRESSED;
KEY_PRESSED: //有按键按下
if(row!=4'hf)
next_state=KEY_PRESSED;
else
next_state=SET_ALL;
endcase
/*************************************************/
reg key_pressed_flag; //按键按下标志
reg [3:0]col_val; //列值
reg [3:0]row_val; //行值
/*************************************************/
//根据次态,给相应的寄存器赋值
/*************************************************/
always @(posedge key_clk or negedge rst_n)
if(!rst_n) //复位
begin
col<=4'h0;
key_pressed_flag<=0;
end
else begin
case (next_state)
SET_ALL : col<=4'b0000 ;
NO_KEY_PRESSED:
begin
key_pressed_flag<=0;
end
SET_COL0 : col<=4'b1110 ;
SCAN_COL0: ; //扫描第 0 列
SET_COL1 : col<=4'b1101;
SCAN_COL1: ; //扫描第 1 列
SET_COL2 : col<=4'b1011;
SCAN_COL2: ; //扫描第 2 列
SET_COL3 : col<=4'b0111;
SCAN_COL3: ; //扫描第 3 列
KEY_PRESSED: //有按键按下
begin
col_val<=col; // 锁存列值
row_val<=row; // 锁存行值
key_pressed_flag<=1; // 置键盘按下标
end
end
endcase
话说终于可以松口气了…喝口雪碧…
等等!好像有什么不对,为什么以前写状态机没遇到过这种情况呢?
我左想友想上想下想,跟以前的状态机一比较,还真发现问题了。
比如说典型的售货机,投进去多少钱(当然要是它能认的,不认的它可不收),币值是多少,顺序咋样,谁说了算?当然是我们说了算,售货机只管钱够不够,够了就吐东西,不够继续等待投币。也就是说输出跟输入没有半毛关系。
再看这个按键检测,输入的数据,不仅受按键是否按下、哪个键按下有关,还跟此时Col的输出有关,而输出又要受状态控制,这就是症结所在了。
哎 ,算是彻底的解脱了,但以后碰到这种输入与输出相关的状态机,还得多加注意时序啊!
用户401816 2012-5-24 14:51
用户403664 2012-5-24 08:32