基于FPGA的PS2键盘识别


前言:
本文主要介绍了基于FPGA的PS2键盘识别+VGA显示键名的显示原理及操作过程。使用的软件是Quartus Ⅱ,使用fpga芯片为cyclone IV EP4CE115F29C7。

1. 项目内容

本实验以通用的 PS2键盘为输入,设计一个能够识别PS2键盘输入编码的电路,当按下 Shift 键时,可以实现大小写的切换显示。

2. PS2接口协议

引脚说明:

在这里插入图片描述

数据格式与发送:

与常用的串口类似,不管是上行还是下行,每帧都包含以下内容:
在这里插入图片描述
如果数据位中1的个数为偶数,校验位就为1;如果数据位中1的个数为奇数,校验位就为0;总之,数据位中1的个数加上校验位中1的个数总为奇数,因此总进行奇校验。

ps2设备的clock和data都是集电极开路的,平时都是高电平。当ps2设备等待发送数据时,它首先检查clock是否为高。如果为低,则认为PC抑制了通讯,此时它缓冲数据直到获得总线的控制权。如果clock为高电平,ps2则开始向PC发送数据。

一般都是由ps2设备产生时钟信号。发送按帧格式。数据位在clock为高电平时准备好,在clock下降沿被PC读入。

数据从键盘/鼠标发送到主机或从主机发送到键盘/鼠标,时钟都是PS2设备产生.主机对时钟控制有优先权,即主机想发送控制指令给PS2设备时,可以拉低时钟线至少100μS,然后再下拉数据线,最后释放时钟线为高。PS2设备的时钟线和数据线都是集电极开路的,容易实现拉低电平。PC在时钟的下降沿读取数据。

数据发送时序:

在这里插入图片描述

键盘返回值介绍:

键盘的处理器如果发现有键被按下或释放将发送扫描码的信息包到计算机。扫描码有两种不同的类型:通码和断码。当一个键被按下就发送通码,当一个键被释放就发送断码。每个按键被分配了唯一的通码和断码。这样主机通过查找唯一的扫描码就可以测定是哪个按键。每个键一整套的通断码组成了扫描码集。有三套标准的扫描码集:分别是第一套,第二套和第三套。所有现代的键盘默认使用第二套扫描码。
虽然多数第二套通码都只有一个字节宽,但也有少数扩展按键的通码是两字节或四字节宽。这类的通码第一个字节总是为E0。
正如键按下通码就被发往计算机一样,只要键一释放断码就会被发送。每个键都有它自己唯一的通码和断码。在通码和断码之间存在着必然的联系。多数第二套断码有两字节长。它们的第一个字节是F0 ,第二个字节是这个键的通码。扩展按键的断码通常有三个字节,它们前两个字节是E0h,F0h ,最后一个字节是这个按键通码的最后一个字节。
下面列出了几个按键的第二套通码和断码:
在这里插入图片描述
一个键盘发送值的例子:
通码和断码是以什么样的序列发送到你的计算机从而使得字符G 出现在你的字处理软件里的呢?因为这是一个大写字母,需要发生这样的事件次序:按下Shift 键-按下G键-释放G 键-释放Shift 键。与这些时间相关的扫描码如下:Shift 键的通码12h,G 键的通码34h ,G 键的断码F0h 34h ,Shift 键的断码F0h 12h 。因此发送到你的计算机的数据应该是:

12h 34h F0h 34h F0h 12h

第二套扫描码:
在这里插入图片描述
在这里插入图片描述

3.代码实现

3.1 PS/2键盘信号采集代码实现

PS2_DAT , //键盘传入pc的数据
在这里插入图片描述
这块代码是个状态机,根据ps2协议,用于接收起始位,8位数据位,奇偶校验位,停止位共11位数据,并对接收的8位数据位进行判断,如果接收的是8‘hf0,则需要进行断码接收,否则的话停止接收,并准备接收下一位。
在这里插入图片描述这块代码是对shift键的功能设计一开始没有任何操作的时候,定义一个变量shift置为0,每按一下shift键时,即ps2_key_data == 8’h12时,变量shift的值进行一次翻转,0的时候,字母小写,1的时候,字母大写。
在这里插入图片描述
根据ps2协议键盘按键的通码和相应字母的ASCII码,编写如上代码,以便将接收的按键信号传输给其他模块。

3.2 总体代码

module ps2_scan
(   
    input   wire        clk         ,
    input   wire        rst_n       ,
    input   wire        PS2_CLK     ,
    input   wire        PS2_DAT     ,  //键盘传入pc的数据
    
    output  reg     ps2_state   ,       //接收到按键信息,产生一个时钟高电平
    output [7:0]    ps2_byte    
);

reg ps2_clk_r0,ps2_clk_r1;
wire neg_ps2_clk;

//检测PS2_CLK的下降沿
always @(posedge clk or negedge rst_n) begin
if (!rst_n)  begin
        ps2_clk_r0 <= 1'b0;
        ps2_clk_r1 <= 1'b0;
    end
    else begin
        ps2_clk_r0 <= PS2_CLK;
        ps2_clk_r1 <= ps2_clk_r0;
    end    
end 
    
assign neg_ps2_clk = ~ps2_clk_r0 & ps2_clk_r1;

reg [7:0] ps2_key_data;        // 来自PS/2的数据寄存器
reg [7:0] temp_data;            // 当前接受数据寄存器
reg [4:0] num;        

always @(posedge clk or negedge rst_n)begin
    if (!rst_n) begin
        ps2_key_data <= 8'd0;
        num <= 5'd0;
        temp_data <= 8'd0;
    end
    else if (neg_ps2_clk) begin
        case (num)
            5'd0: begin
                num <= num + 1'b1; //起始位
            end
            5'd1: begin
                num <= num + 1'b1;
                ps2_key_data[0] <= PS2_DAT;
            end
            5'd2: begin
                num <= num + 1'b1;
                ps2_key_data[1] <= PS2_DAT;
            end
            5'd3: begin
                num <= num + 1'b1;
                ps2_key_data[2] <= PS2_DAT;
            end
            5'd4: begin
                num <= num + 1'b1;
                ps2_key_data[3] <= PS2_DAT;
            end
            5'd5: begin
                num <= num + 1'b1;
                ps2_key_data[4] <= PS2_DAT;
            end
            5'd6: begin
                num <= num + 1'b1;
                ps2_key_data[5] <= PS2_DAT;
            end
            5'd7: begin
                num <= num + 1'b1;
                ps2_key_data[6] <= PS2_DAT;
            end
            5'd8: begin
                num <= num + 1'b1;
                ps2_key_data[7] <= PS2_DAT;//8位有效信号
            end
            5'd9: begin
                num <= num + 1'b1;//奇偶校验位
            end
            5'd10: begin
                if(ps2_key_data == 8'hf0)
                    num <= num + 1'b1;//接收断码
                else
                    num <= 5'd0;//停止位
            end
            5'd11: begin
                num <= num + 1'b1;
            end
            5'd12: begin
                num <= num + 1'b1;
                temp_data[0] <= PS2_DAT;
            end
            5'd13: begin
                num <= num + 1'b1;
                temp_data[1] <= PS2_DAT;
            end
            5'd14: begin
                num <= num + 1'b1;
                temp_data[2] <= PS2_DAT;
            end
            5'd15: begin
                num <= num + 1'b1;
                temp_data[3] <= PS2_DAT;
            end
            5'd16: begin
                num <= num + 1'b1;
                temp_data[4] <= PS2_DAT;
            end
            5'd17: begin
                num <= num + 1'b1;
                temp_data[5] <= PS2_DAT;
            end
            5'd18: begin
                num <= num + 1'b1;
                temp_data[6] <= PS2_DAT;
            end
            5'd19: begin
                num <= num + 1'b1;
                temp_data[7] <= PS2_DAT;
            end
            5'd20: begin
                num <= num + 1'b1;
            end
            5'd21: begin
                num <= 5'd0;
            end
            default: begin 
                num <= 5'd0;
            end
        endcase
    end
end

reg shift;
always @(posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            shift <= 1'b0;
        else if(ps2_key_data == 8'h12)
            shift <= ~shift;
        /* else if(temp_data == 8'h12)
            shift <= 1'b0; */
    end
    
reg key_valid;
reg [7:0] ps2_temp_data;
always @(posedge clk or negedge rst_n)
    begin 
        if(!rst_n)
            begin 
                ps2_state <= 1'b0;
                key_valid <= 1'b0;
            end 
        else if (num == 5'd10)
            begin 
                if ((ps2_key_data == 8'hf0) || (ps2_key_data == 8'h12)||(ps2_key_data == 8'he0))
                    key_valid <= 1'b1;
                else 
                    begin 
                        if (!key_valid)
                            begin 
                                ps2_temp_data <= ps2_key_data;
                                ps2_state <= 1'b1;
                            end 
                        else 
                            begin 
                                key_valid <= 1'b0;
                                ps2_state <= 1'b0;
                            end 
                    end 
            end 
        else if (num == 5'd0)
            begin 
                ps2_state <= 1'b0;
            end 
    end

reg [7:0] ps2_ascii;
always @({shift,ps2_temp_data} )
    begin 
        if(shift == 0)
            begin
                case(ps2_temp_data)
                    8'h15:ps2_ascii<= 8'h71;//q
                    8'h1d:ps2_ascii<= 8'h77;//w
                    8'h24:ps2_ascii<= 8'h65;//e
                    8'h2d:ps2_ascii<= 8'h72;//r
                    8'h2c:ps2_ascii<= 8'h74;//t
                    8'h35:ps2_ascii<= 8'h79;//y
                    8'h3c:ps2_ascii<= 8'h75;//u
                    8'h43:ps2_ascii<= 8'h69;//i
                    8'h44:ps2_ascii<= 8'h6f;//0
                    8'h4d:ps2_ascii<= 8'h70;//p
                    8'h1c:ps2_ascii<= 8'h61;//a
                    8'h1b:ps2_ascii<= 8'h73;//s
                    8'h23:ps2_ascii<= 8'h64;//d
                    8'h2b:ps2_ascii<= 8'h66;//f
                    8'h34:ps2_ascii<= 8'h67;//g
                    8'h33:ps2_ascii<= 8'h68;//h
                    8'h3b:ps2_ascii<= 8'h6a;//j
                    8'h42:ps2_ascii<= 8'h6b;//k
                    8'h4b:ps2_ascii<= 8'h6c;//l
                    8'h1a:ps2_ascii<= 8'h7a;//z
                    8'h22:ps2_ascii<= 8'h78;//z
                    8'h21:ps2_ascii<= 8'h63;//z
                    8'h2a:ps2_ascii<= 8'h76;//z
                    8'h32:ps2_ascii<= 8'h62;//z
                    8'h31:ps2_ascii<= 8'h6e;//z
                    8'h3a:ps2_ascii<= 8'h6d;//z
                    8'h16:ps2_ascii<= 8'h31;//z
                    8'h1e:ps2_ascii<= 8'h32;//z
                    8'h26:ps2_ascii<= 8'h33;//z
                    8'h25:ps2_ascii<= 8'h34;//z
                    8'h2e:ps2_ascii<= 8'h35;//z
                    8'h36:ps2_ascii<= 8'h36;//z
                    8'h3d:ps2_ascii<= 8'h37;//z
                    8'h3e:ps2_ascii<= 8'h38;//z
                    8'h46:ps2_ascii<= 8'h39;//z
                    8'h45:ps2_ascii<= 8'h30;//z
                    8'h4e:ps2_ascii<= 8'h2d;//z
                    8'h55:ps2_ascii<= 8'h3d;//z
                    8'h54:ps2_ascii<= 8'h5b;//z
                    8'h5b:ps2_ascii<= 8'h5d;//z
                    8'h5d:ps2_ascii<= 8'h5c;//z
                    8'h4c:ps2_ascii<= 8'h3b;//z
                    8'h52:ps2_ascii<= 8'h27;//z
                    8'h41:ps2_ascii<= 8'h2c;//z
                    8'h49:ps2_ascii<= 8'h2e;//z
                    8'h4a:ps2_ascii<= 8'h2f;//z
                    8'h0e:ps2_ascii<= 8'h60;//z
                    8'h66:ps2_ascii<= 8'h08;//backspace
                    8'h5a:ps2_ascii<= 8'h0d;//enter
                    8'h75:ps2_ascii<= 8'h80;//u
                    8'h6b:ps2_ascii<= 8'h81;//l
                    8'h72:ps2_ascii<= 8'h82;//d
                    8'h74:ps2_ascii<= 8'h83;//r
                    default:ps2_ascii<= 8'h00;
                endcase
            end
            
        else if (shift == 1)  //大写
            begin 
                case(ps2_temp_data)
                    8'h15:ps2_ascii<= 8'h51;//Q//shift和相应
                    8'h1d:ps2_ascii<= 8'h57;//W
                    8'h24:ps2_ascii<= 8'h45;//E
                    8'h2d:ps2_ascii<= 8'h52;//R
                    8'h2c:ps2_ascii<= 8'h54;//T
                    8'h35:ps2_ascii<= 8'h59;//Y
                    8'h3c:ps2_ascii<= 8'h55;//U
                    8'h43:ps2_ascii<= 8'h49;//I
                    8'h44:ps2_ascii<= 8'h4f;//0
                    8'h4d:ps2_ascii<= 8'h50;//P
                    8'h1c:ps2_ascii<= 8'h41;//A
                    8'h1b:ps2_ascii<= 8'h53;//S
                    8'h23:ps2_ascii<= 8'h44;//D
                    8'h2b:ps2_ascii<= 8'h46;//F
                    8'h34:ps2_ascii<= 8'h47;//G 
                    8'h33:ps2_ascii<= 8'h48;//H
                    8'h3b:ps2_ascii<= 8'h4a;//J
                    8'h42:ps2_ascii<= 8'h4b;//K
                    8'h4b:ps2_ascii<= 8'h4c;//L
                    8'h1a:ps2_ascii<= 8'h5a;//Z
                    8'h22:ps2_ascii<= 8'h58;//X
                    8'h21:ps2_ascii<= 8'h43;//C
                    8'h2a:ps2_ascii<= 8'h56;//V
                    8'h32:ps2_ascii<= 8'h42;//B
                    8'h31:ps2_ascii<= 8'h4e;//N
                    8'h3a:ps2_ascii<= 8'h4d;//M
                    8'h16:ps2_ascii<= 8'h21;//z
                    8'h1e:ps2_ascii<= 8'h40;//z
                    8'h26:ps2_ascii<= 8'h23;//z
                    8'h25:ps2_ascii<= 8'h24;//z
                    8'h2e:ps2_ascii<= 8'h25;//z
                    8'h36:ps2_ascii<= 8'h5e;//z
                    8'h3d:ps2_ascii<= 8'h26;//z
                    8'h3e:ps2_ascii<= 8'h2a;//z
                    8'h46:ps2_ascii<= 8'h28;//z
                    8'h45:ps2_ascii<= 8'h29;//z
                    8'h4e:ps2_ascii<= 8'h5f;//z
                    8'h55:ps2_ascii<= 8'h2b;//z
                    8'h54:ps2_ascii<= 8'h7b;//z
                    8'h5b:ps2_ascii<= 8'h7d;//z
                    8'h5d:ps2_ascii<= 8'h7c;//z
                    8'h4c:ps2_ascii<= 8'h3a;//z
                    8'h52:ps2_ascii<= 8'h22;//z
                    8'h41:ps2_ascii<= 8'h3c;//z
                    8'h49:ps2_ascii<= 8'h3e;//z
                    8'h4a:ps2_ascii<= 8'h3f;//z
                    8'h0e:ps2_ascii<= 8'h7e;//z
                    default:ps2_ascii<= 8'h00;
                endcase
            end 
    end 
    
assign ps2_byte = ps2_ascii;

endmodule
           
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值