**这是本文档旧的修订版!**

基于STEP FPGA的PS2键盘驱动

本节将和大家一起使用FPGA驱动底板上的PS2接口的键盘外设。

====硬件说明====

我们的STEP-BaseBoard底板上集成了PS2键盘的接口,可以供大家连接PS2键盘或PS2鼠标完成相应设计,接下来我们来了解PS2接口的硬件连接及PS2键盘的驱动方法。
PS2接口连线非常简单,只需接4根线:

  • 4号引脚VCC接供电电源,一般为5V供电,后经测试3.3V也可以
  • 3号引脚GND接地即可
  • 5号引脚时钟线和1号引脚数据线为两条双向的信号线
  • 2号引脚和6号引脚为保留引脚,不需要连接

当PS2键盘上有按键按动或操作的时候,键盘会发信号给主机,PS2接口的时钟信号和数据信号的时序如下图: FPGA或主机接收键盘发回的数据,通过键盘的编码规则判定键盘当前的操作,扫描码有两种不同的类型:通码(make code)和断码(break code)。当一个键被按下或持续按住时,键盘会将该键的通码发送给主机;而当一个键被释放时,键盘会将该键的断码发送给主机。
根据键盘按键扫描码的不同,在此可将按键分为如下几类:

  • 第一类按键,通码为1字节,断码为0xF0+通码形式。如A键,其通码为0x1C,断码为0xF0 0x1C。
  • 第二类按键,通码为2字节0xE0+0xXX形式,断码为0xE0+0xF0+0xXX形式。如right ctrl键,其通码为0xE0 0x14,断码为0xE0 0xF0 0x14。
  • 第三类特殊按键有两个,print screen键通码为0xE0 0x12 0xE0 0x7C,断码为0xE0 0xF0 0x7C 0xE0 0xF0 0x12; pause键通码为0x E1 0x14 0x77 0xE1 0xF0 0x14 0xF0 0x77,断码为空。

组合按键的扫描码发送按照按键发生的次序,如以下面顺序按左SHIFT+A键:1按下左SHIFT键,2按下A键,3释放A键,4释放左SHIFT键,那么计算机上接收到的一串数据为0x12 0x1C 0xF0 0x1C 0xF0 0x12。
在驱动程序设计中,就是根据这样的分类来对不同的按键进行不同处理的,当前简单程序只支持第一类按键的操作。
键盘中不同按键的编码如下:

====Verilog代码====

// --------------------------------------------------------------------
// >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<<
// --------------------------------------------------------------------
// Module: Array_KeyBoard
// 
// Author: Step
// 
// Description: Array_KeyBoard
// 
// Web: www.stepfapga.com
//
// --------------------------------------------------------------------
// Code Revision History :
// --------------------------------------------------------------------
// Version: |Mod. Date:   |Changes Made:
// V1.0     |2015/11/11   |Initial ver
// --------------------------------------------------------------------
module Array_KeyBoard #
(
	parameter			NUM_FOR_200HZ = 60000	//定义计数器cnt的计数范围,例化时可更改
)
(
	input					clk_in,			//系统时钟
	input					rst_n_in,		//系统复位,低有效
	input			[3:0]	col,			//矩阵按键列接口
	output	reg		[3:0]	row,			//矩阵按键行接口
	output	reg		[15:0]	key_out			//消抖后的信号
);
/*
因使用4x4矩阵按键,通过扫描方法实现,所以这里使用状态机实现,共分为4种状态
在其中的某一状态时间里,对应的4个按键相当于独立按键,可按独立按键的周期采样法采样
周期采样时每隔20ms采样一次,对应这里状态机每隔20ms循环一次,每个状态对应5ms时间
对矩阵按键实现原理不明白的,请去了解矩阵按键实现原理
*/	
	localparam			STATE0 = 2'b00;
	localparam			STATE1 = 2'b01;
	localparam			STATE2 = 2'b10;
	localparam			STATE3 = 2'b11;
 
	//计数器计数分频实现5ms周期信号clk_200hz
	reg		[15:0]		cnt;
	reg					clk_200hz;
	always@(posedge clk_in or negedge rst_n_in) begin
		if(!rst_n_in) begin		//复位时计数器cnt清零,clk_200hz信号起始电平为低电平
			cnt <= 16'd0;
			clk_200hz <= 1'b0;
		end else begin
			if(cnt >= ((NUM_FOR_200HZ>>1) - 1)) begin	//数字逻辑中右移1位相当于除2
				cnt <= 16'd0;
				clk_200hz <= ~clk_200hz;	//clk_200hz信号取反
			end else begin
				cnt <= cnt + 1'b1;
				clk_200hz <= clk_200hz;
			end
		end
	end
 
	reg		[1:0]		c_state;
	//状态机根据clk_200hz信号在4个状态间循环,每个状态对矩阵按键的行接口单行有效
	always@(posedge clk_200hz or negedge rst_n_in) begin
		if(!rst_n_in) begin
			c_state <= STATE0;
			row <= 4'b1110;
		end else begin
			case(c_state)
				STATE0: begin c_state <= STATE1; row <= 4'b1101; end	//状态c_state跳转及对应状态下矩阵按键的row输出
				STATE1: begin c_state <= STATE2; row <= 4'b1011; end
				STATE2: begin c_state <= STATE3; row <= 4'b0111; end
				STATE3: begin c_state <= STATE0; row <= 4'b1110; end
				default:begin c_state <= STATE0; row <= 4'b1110; end
			endcase
		end
	end
 
	//因为每个状态中单行有效,通过对列接口的电平状态采样得到对应4个按键的状态,依次循环
	always@(negedge clk_200hz or negedge rst_n_in) begin
		if(!rst_n_in) begin
			key_out <= 16'hffff;
		end else begin
			case(c_state)
				STATE0:key_out[3:0] <= col;		//采集当前状态的列数据赋值给对应的寄存器位
				STATE1:key_out[7:4] <= col;
				STATE2:key_out[11:8] <= col;
				STATE3:key_out[15:12] <= col;
				default:key_out <= 16'hffff;
			endcase
		end
	end
 
endmodule



====小结====

本节主要为大家讲解了矩阵按键的工作原理及软件设计,需要大家掌握的同时自己创建工程,通过整个设计流程,生成FPGA配置文件加载测试。
如果你对Diamond软件的使用不了解,请参考这里:Diamond的使用

====相关资料====


使用STEP-MXO2第二代的矩阵按键程序: 后续会有下载连接 待更新
使用STEP-MAX10的矩阵按键程序: 后续会有下载连接 待更新