• 进阶项目(12)PS2键盘驱动程序设计讲解


    写在前面的话

    我们从小就开始接触电脑,曾经多么羡慕那些在键盘上洋洋洒洒的人,手指轻柔的飞舞刻画出一章章美丽的篇幅那么作为工程师的我们同样拥有着属于我们的情怀。如果曾经的向往变成我们喜欢的玩具;如果曾经的神秘变成我们夜以继日的痴迷。那么,一切又将如何?梦翼师兄携手大家一起来欣赏、来品味。

    项目需求

    设计一个ps2键盘的接口驱动电路。

    原理分析

    ps2的接口如下图所示:

    其中,1是数据线DATA;

    2是预留N/C;

    3是GND;

    4是VCC(+5V);

    5是时钟信号线CLK;

    6是预留N/C;

    数据传输的时序图如下图所示:

    一般的ps2接口,都是ps2产生时钟信号,而且是在上升沿的时候把数据发送出去,而在下降沿的时候数据被采样,大多数的ps2设备发送数据的时钟频率是15Khz-20Khz。每一帧的数据有11位或者12位数据,其中包括:

    1位起始位:总为逻辑0;

    8位数据位:低位在前;

    1位奇偶校验位;

    1位停止位:总为逻辑1;

    1位答应位:仅用于主机对设备通信中(在本次键盘接口设计中不用)

    当键盘的某一个按键被按下的时候,键盘会向外发送那一个按键的通码,当按键松开的时候,键盘就会向外发送那一个按键的断码,需要注意的是,如果按着一个按键不放的话,键盘会以一定的频率发送那一个按键的通码。

    右侧小键盘的0-9的通码与断码如下图所示:

     

    现在我们具体举一个例子来说明ps2接口的工作原理,假设我现在按下小键盘中的0键,再按下按键9,然后把按键0松开,最后再松开按键9,ps2往FPGA发送的数据就会如下,先发0按键的通码8h70,再发9按键的通码8h7d,接着发0按键的断码8hf0 8h70,接着再发9按键的断码8hf0 8h7d,发送数据的顺序如下: 8h70→8h7d→8hf0→8h70→8hf0→8h7d

    系统架构

    ps2_data_out信号有效的时候,valid会拉高一个周期(valid可用于同其他级联模块的握手)。

     

    模块功能介绍

    模块名

    功能描述

    ps2_scan

    将ps2接口传输过来数据转成通码或者断码

    顶层模块端口描述

    端口名

    端口说明

    clk

    系统时钟输入

    rst_n

    系统复位

    Ps2_clk

    时钟信号线

    Ps2_data_in

    数据线

    valid

    通、断码有效信号(高电平有效)

    Ps2_data_out

    通、断码信号

     用signaltap ii 分析波形

    打开signaltap ii ,将采样时钟设置为clk,采样深度为64Kps2_clkps2_data_in两个输入信号引出来,并将ps2_clk的的触发条件改为下降沿(我们是在ps2_clk为下降沿的时候采集数据),之后进行全编译,并将编译好的sof文件下载到开发板中。

    按下数字键“1”(数字小键盘),波形图上出现如下波形:

    ps2_clk每个下降沿,我们进行读数据,分别是:“01001011011”。第一位是起始位“0”,后面连续的8位是低位在前的有效数据:“10010110”,改成高位在前就是“01101001”,也就是我们的8‘h69(1的通码就是8’h69)。第十位“1”为奇偶校验位,第十一位“1”是停止位,这两位不需要我们关心。

    放开按键“1”,出现了如下的波形:

    ps2_clk每个下降沿,我们进行读数据,分别是:“00000111111”。第一位是起始位“0”,后面连续的8位是低位在前的有效数据:“00001111”,改成高位在前就是“11110000”,也就是我们的8‘hf0(1的断码就是8‘hf0和8’h69 ,8’h69由于采样深度的原因显示不出来波形,不过我们也能分析出来它是怎么来的)。第十位“1”为奇偶校验位,第十一位“1”是停止位,这两位不需要我们关心。

    根据ps2接口的原理和下板实测,我们得知了ps2接口传输数据的方式,那么就可以很容易地编写出我们的代码。

     代码解释

    Ps2_scan模块代码

    /****************************************************          

     *   Engineer      :   梦翼师兄

     *   QQ             :   761664056

     *   The module function: 将ps2接口传输过来的数据译成通、断码

    *****************************************************/

    000 module ps2_scan (

    001 clk, //系统输入时钟

    002 rst_n,//系统复位

    003 ps2_data_in,//ps2的数据

    004 ps2_data_out,//按键的通、断码

    005 ps2_clk,//ps2的时钟

    006 valid//通、断码有效信号

    007 );

    008 //系统输入

    009 input clk;//系统输入时钟

    010 input rst_n;//系统复位

    011 input ps2_clk;//ps2的时钟

    012 input ps2_data_in;//ps2的数据

    013 //系统输出

    014 output reg [7:0] ps2_data_out;//按键的通、断码

    015 output reg valid;//通、断码有效信号

    016

    017 wire neg;//下降沿标志线

    018 reg ps2_clk_temp;//时钟寄存器

    019

    020 always @ (posedge clk or negedge rst_n)

    021 begin

    022 if (!rst_n)

    023 ps2_clk_temp <= 0;

    024 else

    025         ps2_clk_temp <= ps2_clk;//时钟寄存器中的值永远比ps2_clk晚一拍

    026 end

    027

    028

    029 //当寄存器里面是1ps2_clk0的时候

    030 assign neg = ps2_clk_temp && (~ps2_clk);//正好就是ps2_clk为下降沿。

    031

    032 reg [3:0] num;//接收到数据线上数据的个数

    033

    034 always @ (posedge clk or negedge rst_n)

    035 begin

    036 if (!rst_n)

    037 begin

    038 num <= 0;

    039 valid <= 0;

    040 ps2_data_out <= 0;

    041 end

    042 else

    043 begin

    044 if (neg)

    045 begin

    046 case (num)

    047 0 : num <= num + 1;//起始位

    048

    049 1 : begin//有效数据的第0

    050 num <= num + 1;

    051 ps2_data_out[0] <= ps2_data_in;

    052 end

    053

    054 2 : begin//有效数据的第1

    055 num <= num + 1;

    056 ps2_data_out[1] <= ps2_data_in;

    057 end

    058

    059 3 : begin//有效数据的第2

    060 num <= num + 1;

    061 ps2_data_out[2] <= ps2_data_in;

    062 end

    063

    064 4 : begin//有效数据的第3

    065 num <= num + 1;

    066 ps2_data_out[3] <= ps2_data_in;

    067 end

    068

    069 5 : begin//有效数据的第4

    070 num <= num + 1;

    071 ps2_data_out[4] <= ps2_data_in;

    072 end

    073

    074 6 : begin//有效数据的第5

    075 num <= num + 1;

    076 ps2_data_out[5] <= ps2_data_in;

    077 end

    078

    079 7 : begin//有效数据的第6

    080 num <= num + 1;

    081 ps2_data_out[6] <= ps2_data_in;

    082 end

    083

    084 8 : begin//有效数据的第7

    085 num <= num + 1;

    086 ps2_data_out[7] <= ps2_data_in;

    087 end

    088

    089 9 : begin//奇偶校验位

    090 num <= num + 1;

    091 valid <= 1;//拉高通、断码有效标志

    092 end

    093

    094 10 : num <= 0;//停止位

    095

    096 default : ;

    097 endcase

    098 end

    099 else

    100 begin

    101 valid <= 0;//拉低通、断码有效标志

    102 end

    103 end

    104 end

    105

    106 endmodule

    代码中第47,我们知道第一个是起始位是”0“,故并没有判断,而是直接跳转到下一个状态。

    代码中第89行ps2_data_in发送的是奇偶校验位,在此我们并没有做出判断,而是直接跳转到下一个状态。

    代码中第91行,直接拉高通、断码的有效标志信号。

    代码中第101行,直接拉低通、断码的有效标志信号。

    代码中第94行ps2_data_in发送的是停止位“0”

    测试代码

    /****************************************************          

     *   Engineer      :   梦翼师兄

     *   QQ             :   761664056

     *   The module function: 测试ps2_scan模块

    *****************************************************/

    000 `timescale 1ns/1ps //定义时间单位和精度

    001

    002 module ps2_scan_tb;

    003     //系统输入

    004     reg clk;//系统输入时钟

    005     reg rst_n;//系统复位

    006     reg ps2_clk;//ps2的时钟

    007     reg ps2_data_in;//ps2的数据

    008     //系统输出

    009     wire [7:0] ps2_data_out;//按键的通、断码

    010     wire valid;//通、断码有效信号

    011     

    012     initial begin

    013         clk = 1;

    014         rst_n = 0;

    015         ps2_clk = 1;

    016         ps2_data_in = 1; 

    017         #200.1

    018         rst_n = 1;

    019         //数字“1”的通码

    020         ps2_data_in = 0;//起始位“0”

    021         #60

    022         ps2_clk = 0;

    023         #120

    024         ps2_clk = 1;

    025         #60

    026         ps2_data_in = 1;//“1”

    027         #60

    028         ps2_clk = 0;

    029         #120

    030         ps2_clk = 1;

    031         #60

    032         ps2_data_in = 0;//“0”

    033         #60

    034         ps2_clk = 0;

    035         #120

    036         ps2_clk = 1;

    037         #60

    038         ps2_data_in = 0;//“0”

    039         #60

    040         ps2_clk = 0;

    041         #120

    042         ps2_clk = 1;

    043         #60

    044         ps2_data_in = 1;//“1”

    045         #60

    046         ps2_clk = 0;

    047         #120

    048         ps2_clk = 1;

    049         #60

    050         ps2_data_in = 0;//“0”

    051         #60

    052         ps2_clk = 0;

    053         #120

    054         ps2_clk = 1;

    055         #60

    056         ps2_data_in = 1;//“1”

    057         #60

    058         ps2_clk = 0;

    059         #120

    060         ps2_clk = 1;

    061         #60

    062         ps2_data_in = 1;//“1”

    063         #60

    064         ps2_clk = 0;

    065         #120

    066         ps2_clk = 1;

    067         #60

    068         ps2_data_in = 0;//“0”

    069         #60

    070         ps2_clk = 0;

    071         #120

    072         ps2_clk = 1;

    073         #60

    074         ps2_data_in = 1;//奇偶校验位“1”

    075         #60

    076         ps2_clk = 0;

    077         #120

    078         ps2_clk = 1;

    079         #60

    080         ps2_data_in = 1;//停止位“1”

    081         #60

    082         ps2_clk = 0;

    083         #120

    084         ps2_clk = 1;

    085         #2000

    086         //断码中“f0”

    087         ps2_data_in = 0;//起始位“0”

    088         #60

    089         ps2_clk = 0;

    090         #120

    091         ps2_clk = 1;

    092         #60

    093         ps2_data_in = 0;//“0”

    094         #60

    095         ps2_clk = 0;

    096         #120

    097         ps2_clk = 1;

    098         #60

    099         ps2_data_in = 0;//“0”

    100         #60

    101         ps2_clk = 0;

    102         #120

    103         ps2_clk = 1;

    104         #60

    105         ps2_data_in = 0;//“0”

    106         #60

    107         ps2_clk = 0;

    108         #120

    109         ps2_clk = 1;

    110         #60

    111         ps2_data_in = 0;//“0”

    112         #60

    113         ps2_clk = 0;

    114         #120

    115         ps2_clk = 1;

    116         #60

    117         ps2_data_in = 1;//“1”

    118         #60

    119         ps2_clk = 0;

    120         #120

    121         ps2_clk = 1;

    122         #60

    123         ps2_data_in = 1;//“1”

    124         #60

    125         ps2_clk = 0;

    126         #120

    127         ps2_clk = 1;

    128         #60

    129         ps2_data_in = 1;//“1”

    130         #60

    131         ps2_clk = 0;

    132         #120

    133         ps2_clk = 1;

    134         #60

    135         ps2_data_in = 1;//“1”

    136         #60

    137         ps2_clk = 0;

    138         #120

    139         ps2_clk = 1;

    140         #60

    141         ps2_data_in = 1;//奇偶校验位“1”

    142         #60

    143         ps2_clk = 0;

    144         #120

    145         ps2_clk = 1;

    146         #60

    147         ps2_data_in = 1;//停止位“1”

    148         #60

    149         ps2_clk = 0;

    150         #120

    151         ps2_clk = 1;

    152         #2000

    153         //数字“1”的通码

    154         ps2_data_in = 0;//起始位“0”

    155         #60

    156         ps2_clk = 0;

    157         #120

    158         ps2_clk = 1;

    159         #60

    160         ps2_data_in = 1;//“1”

    161         #60

    162         ps2_clk = 0;

    163         #120

    164         ps2_clk = 1;

    165         #60

    166         ps2_data_in = 0;//“0”

    167         #60

    168         ps2_clk = 0;

    169         #120

    170         ps2_clk = 1;

    171         #60

    172         ps2_data_in = 0;//“0”

    173         #60

    174         ps2_clk = 0;

    175         #120

    176         ps2_clk = 1;

    177         #60

    178         ps2_data_in = 1;//“1”

    179         #60

    180         ps2_clk = 0;

    181         #120

    182         ps2_clk = 1;

    183         #60

    184         ps2_data_in = 0;//“0”

    185         #60

    186         ps2_clk = 0;

    187         #120

    188         ps2_clk = 1;

    189         #60

    190         ps2_data_in = 1;//“1”

    191         #60

    192         ps2_clk = 0;

    193         #120

    194         ps2_clk = 1;

    195         #60

    196         ps2_data_in = 1;//“1”

    197         #60

    198         ps2_clk = 0;

    199         #120

    200         ps2_clk = 1;

    201         #60

    202         ps2_data_in = 0;//“0”

    203         #60

    204         ps2_clk = 0;

    205         #120

    206         ps2_clk = 1;

    207         #60

    208         ps2_data_in = 1;//奇偶校验位“1”

    209         #60

    210         ps2_clk = 0;

    211         #120

    212         ps2_clk = 1;

    213         #60

    214         ps2_data_in = 1;//停止位“1”

    215         #60

    216         ps2_clk = 0;

    217         #120

    218         ps2_clk = 1;

    219     end

    220     

    221     always # 10 clk = ~clk;//50M的时钟

    222     

    223      ps2_scan  ps2_scan (

    224                     .clk(clk), //系统输入时钟

    225                     .rst_n(rst_n),//系统复位

    226                     .ps2_data_in(ps2_data_in),//ps2的数据

    227                     .ps2_data_out(ps2_data_out),//按键的通、断码

    228                     .ps2_clk(ps2_clk),//ps2的时钟

    229                     .valid(valid)//通、断码有效信号

    230                 );

    231

    232 endmodule 

    测试中,我们发送了数字”1“的通码,断码,模仿了数字键”1“的按下和抬起。

     仿真分析

    在仿真中,测试了数字 “1”的通、断码的接收和发送,每当检测出一个八位有效数据的时候,valid都会出现一个时钟周期的尖峰脉冲。

  • 相关阅读:
    使用snv命令来更新项目
    linux下实现开机启动应用程序
    Qt Designer 打开失败,报Run time error错误的解决办法。
    <<Effective c++>>读书笔记---条款19:设计class犹如设计type
    字符串的格式化方式
    最新民政部行政区划代码,省市区三级
    DigitalOcean
    (译)Windsor入门教程---第五部分 添加日志功能
    (译)Windsor入门教程---第四部分 整合
    (译)Windsor入门教程---第三部分 编写第一个Installer
  • 原文地址:https://www.cnblogs.com/mengyi1989/p/11521092.html
Copyright © 2020-2023  润新知