【FPGA实例】FPGA矩阵键盘驱动设计及Testbench验证

FPGA矩阵键盘驱动设计及Testbench验证

FPGA矩阵键盘驱动设计

设计思路

采用20ms的延时电路产生使能时钟去扫描,可以直接省略掉按键的消抖延时;虽然每个状态都要进行一次延时消抖,但RTL设计上却更加简单了。

工作过程

  1. ROW[3:0]作为处理器的输入,COL[3:0]作为处理器的输出;
  2. 闲置状态下,COL[3:0]输出低电平;ROW[3:0]由于上拉电阻的原因输出高电平;
  3. 当某一按键被按下时,相应的行被拉低,ROW[3:0]中的某一个引脚检测到低电平,此时确定了按键所在的行;
  4. 为了检测哪一列被按下,人为将COL[3:0]逐个设置为低电平,其他3个I/O为高电平,扫描顺序为由高位到低位,即从左到右,进行扫描。如果检测到此时的ROW[3:0]的值与先前的一样,则确定了闭合按键的所在列。最多扫描4次,便能确定按键位置。

矩阵键盘的Verilog HDL实现

 * All rights Reserved, Designed By https://blog.csdn.net/zhaogoudan
 * @description   :Matrix key scan design  
 * @author 			:ZhaoGoudan     
 * @date   			:21.01.14
 * @version 		:V1.0.0
 * @copyright ${YEAR} https://blog.csdn.net/zhaogoudan
*/
//
module matrix_key_scan(
	input					clk,
	input					rst_n,
	input	[3:0]			row_data,
	
	output reg [3:0]		col_data,
	output reg [3:0]		key_value,
	output reg				key_flag
);

//----------------------------
localparam	SCAN_IDLE		= 3'd0;
localparam	SCAN_JITTER1	= 3'd1;
localparam	SCAN_COL1		= 3'd2;
localparam	SCAN_COL2		= 3'd3;
localparam	SCAN_COL3		= 3'd4;
localparam	SCAN_COL4		= 3'd5;
localparam	SCAN_READ		= 3'd6;
localparam	SCAN_JITTER2	= 3'd7;

//----------------------------
//Delay for 20ms
reg	[19:0]	delay_cnt;
//localparam	DELAY_TOP = 20'd1000_000;
localparam	DELAY_TOP = 20'd1000;			//just for test
always @(posedge clk or negedge rst_n) begin
	if(!rst_n) 
		delay_cnt <= 20'd0;
	else if(delay_cnt < DELAY_TOP - 1'b1)		//产生一个时钟周期的信号
		delay_cnt <= delay_cnt + 1'b1;
	else	
		delay_cnt <= 20'd0;
end
wire delay_done = (delay_cnt == DELAY_TOP - 1'b1) ? 1'b1 : 1'b0;

//-------------------------------
//FSM : always1
reg	[2:0]	current_state,next_state;
always @(posedge clk or negedge rst_n) begin
	if(!rst_n)
		current_state <= SCAN_IDLE;
	else if(delay_done)						//每个状态都进行了消抖延时
		current_state <= next_state;
	else
		current_state <= current_state;
end

//---------------------------------
//FSM : always2
always @(*) begin
	case(current_state)
	
		SCAN_IDLE : begin
			if(row_data != 4'b1111)		next_state = SCAN_JITTER1;
			else								next_state = SCAN_IDLE;
		end
		
		SCAN_JITTER1 : begin
			if(row_data != 4'b1111)		next_state = SCAN_COL1;
			else								next_state = SCAN_IDLE;
		end
		
		SCAN_COL1 : begin
			if(row_data != 4'b1111)		next_state = SCAN_READ;
			else								next_state = SCAN_COL2;
		end
		
		SCAN_COL2 : begin
			if(row_data != 4'b1111)		next_state = SCAN_READ;
			else								next_state = SCAN_COL3;
		end
		
		SCAN_COL3 : begin
			if(row_data != 4'b1111)		next_state = SCAN_READ;
			else								next_state = SCAN_COL4;
		end
		
		SCAN_COL4 : begin
			if(row_data != 4'b1111)		next_state = SCAN_READ;
			else								next_state = SCAN_IDLE;
		end
		
		SCAN_READ : begin
			if(row_data != 4'b1111)		next_state = SCAN_JITTER2;
			else								next_state = SCAN_IDLE;
		end
		
		SCAN_JITTER2 : begin
			if(row_data != 4'b1111)		next_state = SCAN_JITTER2;
			else								next_state = SCAN_IDLE;
		end
		
		endcase
end

//------------------------------
//FSM : always3
reg	[3:0]	row_data_r;
reg	[3:0]	col_data_r;
always @(posedge clk or negedge rst_n) begin
	if(!rst_n) begin
		col_data <= 4'b0000;
		row_data_r <= 4'b0000;
		col_data_r <= 4'b0000;
	end
	else if(delay_done) begin
		case(next_state)
			SCAN_IDLE : begin
				col_data <= 4'b0000;		//ensure current can flow when key is down
				row_data_r <= row_data_r;
				col_data_r <= col_data_r;
			end
			
			//SCAN JITTER1:
			SCAN_COL1 : col_data <= 4'b0111;
			SCAN_COL2 : col_data <= 4'b1011;
			SCAN_COL3 : col_data <= 4'b1101;
			SCAN_COL4 : col_data <= 4'b1110;
			
			SCAN_READ : begin
				row_data_r <= row_data;
				col_data_r <= col_data;
			end
			
			//SCAN JITTER2:
			default : ;
		endcase
	end
	else begin
		col_data <= col_data;
		col_data_r <= col_data_r;
		row_data_r <= row_data_r;
	end
end

//------------------------------
//键值的解码触发信号
wire key_trigger = (current_state == SCAN_JITTER2 && next_state == SCAN_IDLE && delay_done == 1'b1) ? 1'b1 : 1'b0;

//------------------------------
//detect of matrix key value
always @(posedge clk or negedge rst_n) begin
	if(!rst_n)
		key_value <= 1'b0;
	else if(key_trigger) begin
		case({row_data_r,col_data_r})
			8'b0111_0111 : key_value <= 4'h0;
			8'b0111_1011 : key_value <= 4'h1;
			8'b0111_1101 : key_value <= 4'h2;
			8'b0111_1110 : key_value <= 4'h3;
			
			8'b1011_0111 : key_value <= 4'h4;
			8'b1011_1011 : key_value <= 4'h5;
			8'b1011_1101 : key_value <= 4'h6;
			8'b1011_1110 : key_value <= 4'h7;
			
			8'b1101_0111 : key_value <= 4'h8;
			8'b1101_1011 : key_value <= 4'h9;
			8'b1101_1101 : key_value <= 4'ha;
			8'b1101_1110 : key_value <= 4'hb;
			
			8'b1110_0111 : key_value <= 4'hc;
			8'b1110_1011 : key_value <= 4'hd;
			8'b1110_1101 : key_value <= 4'he;
			8'b1110_1110 : key_value <= 4'hf;
			
			default : key_value <= key_value;
		endcase
	end
	else
		key_value <= key_value;
end

//----------------------------
//Lag 1 clock for vaild read enable
always @(posedge clk or negedge rst_n) begin
	if(!rst_n)
		key_flag <= 1'b0;
	else
		key_flag <= key_trigger;
end
endmodule

在这里插入图片描述


版权声明:本文为zhaogoudan原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。