进阶项目(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按键的通码8’h70,再发9按键的通码8’h7d,接着发0按键的断码8’hf0 8’h70,接着再发9按键的断码8’hf0 8’h7d,发送数据的顺序如下: 8’h70→8’h7d→8’hf0→8’h70→8’hf0→8’h7d。

系统架构

当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,采样深度为64K。将ps2_clk和ps2_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 //当寄存器里面是1,ps2_clk为0的时候

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都会出现一个时钟周期的尖峰脉冲。

原文地址:https://www.cnblogs.com/mengyi1989/p/11521092.html

时间: 2024-10-01 06:39:44

进阶项目(12)PS2键盘驱动程序设计讲解的相关文章

进阶项目(4)蜂鸣器程序设计讲解

写在前面的话 经过前面内容的学习,梦翼师兄相信大家的基础知识水平一定已经很扎实.那么本节,我们就一起来庆祝一下,用播放器奏响一曲<欢乐颂>,奏响我们凯旋的乐章. 什么是蜂鸣器? 蜂鸣器是一种一体化结构的电子讯响器,采用直流电压供电,广泛应用于计算机.打印机.电子玩具.定时器等电子产品中作为发声器件.蜂鸣器分为有源蜂鸣器和无源蜂鸣器两种,在电路中用字母“H”或“HA”(旧标准用“FM”.“ZZG”.“LB”.“JD”等)表示.那么,怎么区分有源蜂鸣器和无源蜂鸣器呢?有源蜂鸣器内部带震荡源,所以只

12.2440串口驱动程序设计

12.2440串口驱动程序设计 串口的功能就是接受数据跟发送数据的,在上一节已经了解串口的引脚信号.但是数据的收发需要一定的条件,也就是串口的初始化.所以所以今天的内容就会被划分为三个部分: 1.串口的初始化: 创建一个uart.c来对串口进行处理,然后把它加入到Makefile工程文件里: 接着就是串口处理程序的实现了. 程序的刚开始是对串口进行初始化,初始化的步骤: 配置引脚功能 设置数据模式 设置工作模式 设置波特率. 1)配置引脚功能: 在2440里的串口底板原理图: 上面的11 TXD

基础项目(3)三态门程序设计讲解

写在前面的话 我们所接触到的IO都是单纯的输入(input)或者输出(output)类型,而我们的一些总线协议如IIC等,要求信号为三态类型,也就是我们所说的输入输出(inout)类型.那么,本节梦翼师兄将和大家一起来探讨三态门的用法. 项目需求 设计一个三态门电路,可以实现数据的输出和总线“挂起”. 系统架构 模块功能介绍 模块名 功能描述 three_state 控制三态总线Sda是否处于挂起状态 顶层模块端口描述 端口名 端口说明 Clk 系统时钟 Rst_n 系统低电平复位 Data_b

每周一书《Oracle 12 c PL(SQL)程序设计终极指南》

本周为大家送出的书是<Oracle 12 c PL(SQL)程序设计终极指南>,此书由机械工业出版社出版, 孙风栋,王澜,郭晓惠 著. 内容简介: <Oracle 12c PL/SQL程序设计终极指南>志在打造PL/SQL领域最为系统.全面.实战.权威的著作,通过一系列非常突出的优势在大量的同类书中脱颖而出,成为该领域的标准读物. PL/SQL本身涉及的知识点浩瀚.庞杂,初学者根本无法依靠自身能力理清头绪,学习成本极高.本书对知识点进行了体系化的梳理,化繁杂为有序,突出重点,直指核

虚拟网卡TUN/TAP 驱动程序设计原理

昨天韦哥写了<Linux下Tun/Tap设备通信原理>一文,只提到了两个使用Tun的用户进程之间的通信路径,并没有说明Tun虚拟网卡驱动是如何实现的,而正好看到了这里的一篇讲解这方面的文章,果断转载了,感谢作者,原文在这里:虚拟网卡TUN/TAP 驱动程序设计原理 简介 虚拟网卡Tun/tap驱动是一个开源项目,支持很多的类UNIX平台,OpenVPN和Vtun都是基于它实现隧道包封装.本文将介绍tun/tap驱动的使用并分析虚拟网卡tun/tap驱动程序在linux环境下的设计思路. tun

【思库教育】2017PHP项目实战基础+进阶+项目之基础篇

下载链接: [思库教育]2017PHP项目实战基础+进阶+项目之基础篇 小白变大牛,您的专属资源库! 小白变大牛,您的专属资源库! 内容非常充实,可以看目录,设计的面多,项目多,技能多,如果掌握好,找一份PHP的工作,易如反掌!学完后可以到PHP小白变大牛精华区查找更加符合你的资源或者项目! [思库教育]2017PHP项目实战基础+进阶+项目之基础篇[思库教育]2017PHP项目实战基础+进阶+项目之进阶篇[思库教育]2017PHP项目实战基础+进阶+项目之项目篇小白变大牛!Python小白,J

MSM8909+Android5.1.1键盘驱动------概述

采用SN7326带智能指扫描的键盘扩展芯片,通过I2C接口来读取其状态寄存器的值就可知道是单按键还是多按键按下,可知道具体是哪个按键按下.然后键盘驱动调用input_event()上报linux的扫描码,比如KEY_RIGHT,然后传递给android框架层,流程如下图: 图1 下面介绍要实现键盘驱动所涉及的主要方方面面 1.     Input子系统 Linux输入设备总类繁杂,常见的包括有按键.键盘.触摸屏.鼠标.摇杆等等,他们本身就是字符设备,而linux内核将这些设备的共同性抽象出来,简

实验报告 实验4 外设驱动程序设计

北京电子科技学院(BESTI) 实     验    报     告 课程: 密码系统设计基础                                                               班级: 1352班.1353班 姓名:王玥.刘浩晨                                                                    学号:20135318.20135232 成绩:                      

基于MCP2515的Linux CAN总线驱动程序设计

MCP2515简介 MCP2515是一种独立的CAN总线通信控制器,是Microchip公司首批独立CAN解决方案的升级器件,其传输能力较Microchip公司原有CAN控制器(MCP2510)高两倍,最高通信速率可达到1Mbps.MCP2515能够接收和发送标准数据帧和扩展数据帧以及远程帧,通过两个接收屏蔽寄存器和六个接收过滤寄存器滤除无关报文,从而减轻CPU负担. MCP2515主要功能参数及电气特性如下: (1)支持CAN技术规范2.0A/B, 最高传输速率达到1Mbps: (2)支持标准