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

 

posted @ 2019-09-15 08:34  梦翼师兄  阅读(2265)  评论(0编辑  收藏  举报