天天看點

進階項目(12)PS2鍵盤驅動程式設計講解

寫在前面的話

我們從小就開始接觸電腦,曾經多麼羨慕那些在鍵盤上洋洋灑灑的人,手指輕柔的飛舞,刻畫出一章章美麗的篇幅…那麼作為工程師的我們,同樣擁有着屬于我們的情懷。如果曾經的向往變成我們喜歡的玩具;如果曾經的神秘變成我們夜以繼日的癡迷。那麼,一切又将如何?夢翼師兄攜手大家一起來欣賞、來品味。

項目需求

設計一個ps2鍵盤的接口驅動電路。

原理分析

ps2的接口如下圖所示:

進階項目(12)PS2鍵盤驅動程式設計講解

其中,1是資料線DATA;

2是預留N/C;

3是GND;

4是VCC(+5V);

5是時鐘信号線CLK;

6是預留N/C;

資料傳輸的時序圖如下圖所示:

進階項目(12)PS2鍵盤驅動程式設計講解

一般的ps2接口,都是ps2産生時鐘信号,而且是在上升沿的時候把資料發送出去,而在下降沿的時候資料被采樣,大多數的ps2裝置發送資料的時鐘頻率是15Khz-20Khz。每一幀的資料有11位或者12位資料,其中包括:

1位起始位:總為邏輯0;

8位資料位:低位在前;

1位奇偶校驗位;

1位停止位:總為邏輯1;

1位答應位:僅用于主機對裝置通信中(在本次鍵盤接口設計中不用)

當鍵盤的某一個按鍵被按下的時候,鍵盤會向外發送那一個按鍵的通碼,當按鍵松開的時候,鍵盤就會向外發送那一個按鍵的斷碼,需要注意的是,如果按着一個按鍵不放的話,鍵盤會以一定的頻率發送那一個按鍵的通碼。

右側小鍵盤的0-9的通碼與斷碼如下圖所示:

進階項目(12)PS2鍵盤驅動程式設計講解

現在我們具體舉一個例子來說明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可用于同其他級聯子產品的握手)。

進階項目(12)PS2鍵盤驅動程式設計講解

子產品功能介紹

子產品名 功能描述
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檔案下載下傳到開發闆中。

進階項目(12)PS2鍵盤驅動程式設計講解

按下數字鍵“1”(數字小鍵盤),波形圖上出現如下波形:

進階項目(12)PS2鍵盤驅動程式設計講解

在ps2_clk每個下降沿,我們進行讀資料,分别是:“01001011011”。第一位是起始位“0”,後面連續的8位是低位在前的有效資料:“10010110”,改成高位在前就是“01101001”,也就是我們的8‘h69(1的通碼就是8’h69)。第十位“1”為奇偶校驗位,第十一位“1”是停止位,這兩位不需要我們關心。

放開按鍵“1”,出現了如下的波形:

進階項目(12)PS2鍵盤驅動程式設計講解

在ps2_clk每個下降沿,我們進行讀資料,分别是:“00000111111”。第一位是起始位“0”,後面連續的8位是低位在前的有效資料:“00001111”,改成高位在前就是“11110000”,也就是我們的8‘hf0(1的斷碼就是8‘hf0和8’h69 ,8’h69由于采樣深度的原因顯示不出來波形,不過我們也能分析出來它是怎麼來的)。第十位“1”為奇偶校驗位,第十一位“1”是停止位,這兩位不需要我們關心。

根據ps2接口的原理和下闆實測,我們得知了ps2接口傳輸資料的方式,那麼就可以很容易地編寫出我們的代碼。

 代碼解釋

Ps2_scan子產品代碼

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”。

測試代碼

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“的按下和擡起。

 仿真分析

進階項目(12)PS2鍵盤驅動程式設計講解

在仿真中,測試了數字 “1”的通、斷碼的接收和發送,每當檢測出一個八位有效資料的時候,valid都會出現一個時鐘周期的尖峰脈沖。

轉載于:https://www.cnblogs.com/mengyi1989/p/11521092.html

繼續閱讀