差别

这里会显示出您选择的修订版和当前版本之间的差别。

到此差别页面的链接

后一修订版
前一修订版
ps2键盘模块 [2017/06/09 14:15]
anran 创建
ps2键盘模块 [2017/06/09 17:13] (当前版本)
anran [相关资料]
行 1: 行 1:
-======基于STEP FPGA的矩阵按键驱动======+======基于STEP FPGA的PS2驱动======
  
-本节将和大家一起使用FPGA驱动底板上的4x4矩阵键盘。+本节将和大家一起使用FPGA驱动底板上的PS2接口的键盘外设
  
  
 ====硬件说明==== ====硬件说明====
 ------- -------
-键盘中按键数量较多时,为了减少I/​O口占用,通常将按键排列成矩阵形式,使用行线和列线分别连到按键开关的两端这样我们就可以通过4根行线和4根列线(共8个I/​O口)连接16个按键,而且按键数量越多优势越明显。 +我们的[[STEP-BaseBoard]]底板上集成了PS2键盘的接,可以供大家连接PS2盘或PS2鼠标完成相应设计接下来我们来了解PS2接口的硬件连接及PS2键盘的驱动方法。
- +
-FPGA驱动矩阵按键模块,首先我们来了解矩阵按键的硬件连接+
 \\ \\
-{{ :矩阵按键.jpg?​800 |}}+PS2接口连线非常简单,只需接4根线: 
 +  * 4号引脚VCC接供电电源,一般为5V供电,后经测试3.3V也可以 
 +  * 3号引脚GND接地即可 
 +  * 5号引脚时钟线和1号引脚数据线为两条双向的信号线 
 +  * 2号引脚和6号引脚为保留引脚,不需要连接 
 +{{ :ps2接口电路.jpg?​600 |}} 
 +当PS2键盘上有按键按动或操作的时候,键盘会发信号给主机,PS2接口的时钟信号和数据信号的时序如下图: 
 +{{ :​ps2键盘时序.jpg?800 |}} 
 +FPGA或主机接收键盘发回的数据,通过键盘的编码规则判定键盘当前的操作,扫描码有两种不同的类型:通码(make code)和断码(break code)。当一个键被按下或持续按住时,​键盘会将该键的通码发送给主机;​而当一个键被释放时,​键盘会将该键的断码发送给主机。
 \\ \\
-上图为4x4矩阵按键的硬件电路图,以看到4根行线(ROW1、ROW2、ROW3、ROW4)和4根列线(COL1、COL2、COL3、COL4),同时列线通过上拉电阻连接到VCC电压(3.3V),对于矩阵按键来讲: +根据键盘按键扫描码不同,​在此按键分为如下几类: 
-  ​- 4根行线是输入的,是由FPGA控制拉高或拉低, +  ​* 第一类按键,通码为1字节,​断码为0xF0+通码形式。如A键,​其通码为0x1C,​断码为0xF0 0x1C。 
-  - 4根列线数输出的,是由4根行线的输入及按键的状态决定,输出给FPGA +  * 第二类按键,​通码为2字节0xE0+0xXX形式,​断码为0xE0+0xF0+0xXX形式。如right ctrl键,​其通码为0xE0 0x14,​断码为0xE0 0xF0 0x14。 
-当某一时刻,FPGA控制4根行线分别ROW1=0、ROW2=1、ROW3=1、ROW4=1时, +  * 第三类特殊按键有两个,​print screen通码为0xE0 0x12 0xE0 0x7C,​断码为0xE0 0xF0 0x7C 0xE0 0xF0 0x12; pause通码为0x E1 0x14 0x77 0xE1 0xF0 0x14 0xF0 0x77,​断码为空。 
-  *   对于K1、K2、K3、K4按键:按下时对应4根列线输出COL1=0、COL2=0、COL3=0、COL4=0,不按时对应4根列线输出COL1=1、COL2=1、COL3=1、COL4=1, +组合键的扫描码发送按照按键发生,如下面顺序左SHIFT+A:1下左SHIFT键,​2按下A,​3释放A键,​4释放左SHIFT键,​那么计算机上接收到的串数据为0x12 0x1C 0xF0 0x1C 0xF0 0x12
-  *   对于K5~~~K16之间的按键:无论按下与否,对应4根列线输出COL1=1、COL2=1、COL3=1、COL4=1, +
-通过上面的描述:在这一时刻只K1、K2、K3、K4按被按下,才会导致4根列线输出COL1=0、COL2=0、COL3=0、COL4=0,否则COL1=1、COL2=1、COL3=1、COL4=1,反之当FPGA检测到列线(COL1、COL2、COL3、COL4)中有低电平信号时,对应的K1、K2、K3、K4按应该是被按下了。 +
- +
-扫描的方式,一共分为4个时刻,分别对应4根行线中的一根拉低,4个时刻依次循环,这样就完成了矩阵按键的全部扫描检测,我们在程这4个时刻对应状态机的4个状态。 +
-至于循环的周期,根据我们基础教程里可知,按键抖动的不稳定时间在10ms以内,所以对同一个按键采样的周期大于10ms,这同样取20ms时间。20ms时间对应4个状态,每5分钟进行次状态转换+
 \\ \\
-{{ :矩阵按键程序框图.jpg?​800 |}}+在驱动程序设计中,​就是根据这样的分类来对不同的按键进行不同处理的,当前简单程序只支持第一类按键的操作。
 \\ \\
-{{ :矩阵按键扫描法原理.jpg |}}+键盘中不同按键的编码如下: 
 +{{ :键盘按键通码.jpg?800 |}}
 \\ \\
 +
 ====Verilog代码==== ====Verilog代码====
 ------ ------
行 34: 行 37:
 // >>>>>>>>>>>>>>>>>>>>>>>>>​ COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<<​ // >>>>>>>>>>>>>>>>>>>>>>>>>​ COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<<​
 // -------------------------------------------------------------------- // --------------------------------------------------------------------
-// Module: ​Array_KeyBoard+// Module: ​Keyboard_PS2
 //  // 
 // Author: Step // Author: Step
 //  // 
-// Description: ​Array_KeyBoard+// Description: ​PS2 keyboard driver
 //  // 
 // Web: www.stepfapga.com // Web: www.stepfapga.com
-//+// 
 // -------------------------------------------------------------------- // --------------------------------------------------------------------
 // Code Revision History : // Code Revision History :
 // -------------------------------------------------------------------- // --------------------------------------------------------------------
 // Version: |Mod. Date:   ​|Changes Made: // Version: |Mod. Date:   ​|Changes Made:
-// V1.0     |2015/11/11   |Initial ver+// V1.0     |2016/04/20   |Initial ver
 // -------------------------------------------------------------------- // --------------------------------------------------------------------
-module ​Array_KeyBoard #+module ​Keyboard_PS2
 ( (
- parameter NUM_FOR_200HZ = 60000 //​定义计数器cnt的计数范围,例化时可更改 +input clk_in,​ //​系统时钟 
-+input rst_n_in,​ //​系统复位,低有效 
-+input key_clk,​ //​PS2键盘时钟输入 
- input clk_in,​ //​系统时钟 +input key_data, //PS2盘数据输入 
- input rst_n_in,​ //​系统复位,低有效 +output reg key_state, //键盘的下状态,按下为1,松开为0 
- input [3:0] col, //矩阵按列接口 +output reg [7:0] key_ascii //按键键值对应ASCII编码
- output reg [3:0] row, //矩阵键行接口 +
- output reg [15:0] key_out //消抖后信号+
 ); );
 +
 /* /*
-因使用4x4矩阵按键,通过扫描方法实现,所以里使用状态机实现,共分4种状态 +个模块FPGA驱动PS2键盘简单程序只能支持盘中第一类按键的单键不支持多个按键同时 
-在其中某一状态时间里对应的4个按相当于独立按键,可独立按键的周期采样法采样 +*/
-周期采样时每隔20ms采样一次对应这里状态机每隔20ms循环一次,每状态对应5ms时间 +
-对矩阵按键实现原理不明白的,请去了解矩阵键实现原理 +
-*/  +
- localparam STATE0 = 2'​b00;​ +
- localparam STATE1 = 2'​b01;​ +
- localparam STATE2 = 2'​b10;​ +
- localparam STATE3 = 2'b11;+
  
- //器计数分频实现5ms周期信号clk_200hz +reg key_clk_r0 = 1'​b1,​key_clk_r1 = 1'b1;  
- reg [15:​0] cnt;​ +reg key_data_r0 = 1'​b1,​key_data_r1 = 1'​b1;​ 
- reg clk_200hz;​ +//对键盘时钟信号进行延时锁存 
- always@(posedge clk_in or negedge rst_n_in) begin +always @ (posedge clk_in or negedge rst_n_in) begin 
- if(!rst_n_in) begin //​复位时计数器cnt清零,clk_200hz信号起始电平为低电平 + if(!rst_n_in) begin 
- cnt ​<= 16'd0+ key_clk_r0 ​<= 1'b1
- clk_200hz ​<= 1'b0+ key_clk_r1 ​<= 1'b1
- end else begin + key_data_r0 <= 1'b1
- if(cnt >((NUM_FOR_200HZ>>​1) - 1)) begin //​数字逻辑中右移1位相当于除2 + key_data_r1 ​<= 1'b1
- cnt <= 16'd0+ end else begin 
- clk_200hz ​<= ~clk_200hz; //​clk_200hz信号取反 + key_clk_r0 ​<= key_clk
- end else begin + key_clk_r1 ​<= key_clk_r0
- cnt ​<= cnt + 1'b1+ key_data_r0 <= key_data; 
- clk_200hz ​<= clk_200hz+ key_data_r1 <= key_data_r0;​
- end +
- end+
  end  end
 +end
  
- reg [1:0] c_state+//​键盘时钟信号下降沿检测 
- //状态机根据clk_200hz信号在4个状态间循环每个状态对矩阵按键的行接口单行有效 +wire key_clk_neg = key_clk_r1 & (~key_clk_r0);​  
- always@(posedge ​clk_200hz ​or negedge rst_n_in) begin + 
- if(!rst_n_in) begin +reg [3:0] cnt;  
- c_state ​<= STATE0+reg [7:​0] temp_data
- row <= 4'b1110+//根据键盘的时钟信号的下降沿读取数据详细参考PS2盘数据传输格式及时序 
- end else begin +always @ (posedge ​clk_in ​or negedge rst_n_in) begin 
- case(c_state) + if(!rst_n_in) begin 
- STATE0: begin c_state ​<= STATE1row <= 4'b1101end //状态c_state跳转及对应状态下矩阵按键的row输出 + cnt <= 4'd0
- STATE1begin c_state ​<= STATE2row <= 4'b1011; end + temp_data ​<= 8'd0
- STATE2begin c_state ​<= STATE3row <= 4'b0111end + end else if(key_clk_neg) ​begin  
- STATE3begin c_state ​<= STATE0row <= 4'b1110end + if(cnt >= 4'd10cnt <= 4'd0; 
- default:begin c_state ​<= STATE0row <= 4'b1110end + else cnt <= cnt + 1'​b1;​ 
- endcase + case (cnt) 
- end+ 4'd0: ; //起始位 
 + 4'd1temp_data[0] ​<= key_data_r1 //​数据位bit0 
 + 4'd2temp_data[1] ​<= key_data_r1 //​数据位bit1 
 + 4'd3: temp_data[2] <= key_data_r1 //​数据位bit2 
 + 4'd4temp_data[3] ​<= key_data_r1 //​数据位bit3 
 + 4'​d5:​ temp_data[4] ​<= key_data_r1; ​ //​数据位bit4 
 + 4'd6: temp_data[5] <= key_data_r1 //​数据位bit5 
 + 4'd7temp_data[6] ​<= key_data_r1 //​数据位bit6 
 + 4'​d8:​ temp_data[7] ​<= key_data_r1; ​ //​数据位bit7 
 + 4'd9: ; //校验位 
 + 4'​d10:;​ //​结束位 
 + default: ; 
 + endcase
  end  end
-  +end 
- //因为每个状态中单行有效,过对列接口的电平状态采样得到对应4个按键的状态,依次循环 + 
- always@(negedge clk_200hz ​or negedge rst_n_in) begin +reg key_break = 1'​b0; ​   
- if(!rst_n_in) begin +reg [7:​0] key_byte = 1'b0; 
- key_out ​<= 16'hffff+//根据码和断码判定按键的当前是按下还是松开 
- end else begin +always @ (posedge clk_in ​or negedge rst_n_in) begin  
- case(c_state) + if(!rst_n_in) begin 
- STATE0:​key_out[3:​0] ​<= col; //采集当前状态的列数据给对应的寄存器位 + key_break ​<= 1'b0
- STATE1:​key_out[7:​4] ​<= col+ key_state <= 1'​b0;​ 
- STATE2:​key_out[11:​8] ​<= col+ key_byte <= 1'​b0;​ 
- STATE3:​key_out[15:​12] ​<= col+ end else if(cnt==4'​d10 && key_clk_neg) ​begin  
- default:​key_out ​<= 16'hffff; + if(temp_data == 8'hf0key_break ​<= 1'b1; //​收到段码(8'​hf0)表示按键松开,设置断码标示为1 
- endcase+ else if(!key_break) begin  //当断码标示为0时,表示当前数据为按下数据,输出键并设置按下标示为1 
 + key_state ​<= 1'b1
 + key_byte ​<= temp_data;  
 + end else begin //​当断码标示为1时,标示当前数据为松开数据,断码标示和按下标示都清零 
 + key_state ​<= 1'b0
 + key_break ​<= 1'b0;
  end  end
  end  end
 +end
  
 +//​将键盘返回的有效键值转换为按键字母对应的ASCII码值
 +always @ (key_byte) begin
 + case (key_byte) ​   //translate key_byte to key_ascii
 + 8'​h15:​ key_ascii = "​Q";//​8'​h51; ​  //Q
 + 8'​h1d:​ key_ascii = "​W";//​8'​h57; ​  //W
 + 8'​h24:​ key_ascii = "​E";//​8'​h45; ​  //E
 + 8'​h2d:​ key_ascii = "​R";//​8'​h52; ​  //R
 + 8'​h2c:​ key_ascii = "​T";//​8'​h54; ​  //T
 + 8'​h35:​ key_ascii = "​Y";//​8'​h59; ​  //Y
 + 8'​h3c:​ key_ascii = "​U";//​8'​h55; ​  //U
 + 8'​h43:​ key_ascii = "​I";//​8'​h49; ​  //I
 + 8'​h44:​ key_ascii = "​O";//​8'​h4f; ​  //O
 + 8'​h4d:​ key_ascii = "​P";//​8'​h50; ​  //P
 + 8'​h1c:​ key_ascii = "​A";//​8'​h41; ​  //A
 + 8'​h1b:​ key_ascii = "​S";//​8'​h53; ​  //S
 + 8'​h23:​ key_ascii = "​D";//​8'​h44; ​  //D
 + 8'​h2b:​ key_ascii = "​F";//​8'​h46; ​  //F
 + 8'​h34:​ key_ascii = "​G";//​8'​h47; ​  //G
 + 8'​h33:​ key_ascii = "​H";//​8'​h48; ​  //H
 + 8'​h3b:​ key_ascii = "​J";//​8'​h4a; ​  //J
 + 8'​h42:​ key_ascii = "​K";//​8'​h4b; ​  //K
 + 8'​h4b:​ key_ascii = "​L";//​8'​h4c; ​  //L
 + 8'​h1a:​ key_ascii = "​Z";//​8'​h5a; ​  //Z
 + 8'​h22:​ key_ascii = "​X";//​8'​h58; ​  //X
 + 8'​h21:​ key_ascii = "​C";//​8'​h43; ​  //C
 + 8'​h2a:​ key_ascii = "​V";//​8'​h56; ​  //V
 + 8'​h32:​ key_ascii = "​B";//​8'​h42; ​  //B
 + 8'​h31:​ key_ascii = "​N";//​8'​h4e; ​  //N
 + 8'​h3a:​ key_ascii = "​M";//​8'​h4d; ​  //M
 + default: ;
 + endcase
 +end
 +
 endmodule endmodule
  
行 130: 行 175:
 ====小结==== ====小结====
 ------ ------
-本节主要为大家讲解了矩阵按的工作原理软件设计,需要大家掌握的同时自己创建工程,通过整个设计流程,生成FPGA配置文件加载测试。+本节主要为大家讲解了PS2接口电路、PS2盘编码规则使用FPGA简单驱动PS2键盘的方法,需要大家掌握的同时自己创建工程,通过整个设计流程,生成FPGA配置文件加载测试。
 \\ \\
 如果你对Diamond软件的使用不了解,请参考这里:[[lattice_diamond的使用|Diamond的使用]]。 如果你对Diamond软件的使用不了解,请参考这里:[[lattice_diamond的使用|Diamond的使用]]。
行 137: 行 182:
 ------ ------
 \\ \\
-使用[[STEP-MXO2第二代]]的矩阵按键程序: ​ 后续会有下载连接 ​ 待更新+使用[[STEP-MXO2第二代]]的PS2盘驱动程序: ​ 后续会有下载连接 ​ 待更新
 \\ \\
-使用[[STEP-MAX10]]的矩阵按键程序: ​ 后续会有下载连接 ​ 待更新+使用[[STEP-MAX10]]的PS2盘驱动程序: ​ 后续会有下载连接 ​ 待更新
 \\ \\