verilog語言的ps2鍵盤驅動設計
PS/2介面是目前最常見的滑鼠介面,最初是IBM公司的專利,俗稱“小口”。這是一種滑鼠和鍵盤的專用介面,是一種6針的圓型介面。本設計完成了ps2鍵盤驅動,並將鍵盤對應的16進位制ascii碼值在數碼管輸出。
一、ps2介面協議
主要內容:常見的PS/2埠有兩類:一種5腳的DIN或6腳的mini-DIN,這兩種聯結器在電氣特性上是十分類似的,兩者只有一點不同那就是管腳的排列,具有6 腳mini-DIN 的鍵盤
通常被叫做PS/2 鍵盤,而那些有5 腳DIN 叫做 AT 裝置。常見ps2鍵盤介面如下所示:
由於電源和地都是主機介面提供,主要用於驅動設計的管腳只有兩個,data和clock,該clock時鐘是鍵盤產生的,最大的時鐘頻率是33kHz 而且大多數裝置工作在10 20kHz。
PS/2 滑鼠和鍵盤履行一種雙向同步序列協議, 換句話說每次資料線上傳送一位資料並且每在時鐘線上發一個脈衝就被讀入,鍵盤/ 滑鼠可以傳送資料到主機,而主機也可以傳送資料到裝置。
從鍵盤/ 滑鼠傳送到主機的資料在時鐘訊號的下降沿, 當時鍾從高變到低的時候 被讀取 。從主機發送到鍵盤/滑鼠的資料在上升沿,當時鍾從低變到高的時候被讀取。
不管通訊的方向怎樣鍵盤/滑鼠總是產生時鐘訊號
該同步序列協議的幀結構如下:(從鍵盤到主機的幀長度為11bit)
注意校驗位是對資料位的校驗,且是奇校驗,當資料位為偶數個1時該位置為1,為奇數個1時該位為0.總是保證1的個數為奇數個。
每一位都是在時鐘的下降沿被讀取:
二、電氣介面協議(掃描碼)
掃描碼有兩種不同的型別: 通碼和斷碼
當一個鍵被按下或按住就傳送通碼,當一個鍵被釋放就傳送斷碼。每個按鍵被分配了唯一的通碼和斷碼,這樣主機通過查詢唯一的掃描碼就可以測定是哪個按鍵。 每個鍵一整套的通斷碼組成了 掃描碼集, 有三套標準的掃描碼集分別是第一套、 第二套和第三套, 所有現代的鍵盤預設使用第二套掃描碼。
只要一個鍵被按下這個鍵的通碼就被髮送到計算機,記住通碼只表示鍵盤上的一個按鍵,它不表示印刷在按鍵上的那個字元。這就意味著在通碼和ASCII 碼之間沒有已定義的關聯,直到主機把掃描碼翻譯成一個字元或命令。在verilog 語句中需要將通碼用case語句轉換層所需要的ascii碼。
多數通碼長度為一個位元組,少數擴充套件碼為2或4個位元組,這種碼都含有E0H。
同理,只要鍵一釋放斷碼就會被髮送每個鍵都有它自己唯一的通碼
它們也都有唯一的斷碼,不用總是通過查表來找出按鍵的斷碼。在通碼和斷碼之間存在著必然的聯絡,多數第二套斷碼有兩位元組長。它們的第一個位元組是F0h ,第二個位元組是這個鍵的通碼擴
展按鍵的斷碼,通常有三個位元組。它們前兩個位元組是E0h,F0h 。最後一個位元組是這個按鍵通碼的最後一個位元組,一些例子如下。
三、設計過程。
主要程式如下:主要思路將在註釋中闡述
module ps2_keyboard_driver(clk,rst_n,ps2k_clk,ps2k_data,sm_bit,sm_seg,ps2_state);
input clk; //50M時鐘訊號
input rst_n; //復位訊號
input ps2k_clk; //PS2介面時鐘訊號
input ps2k_data; //PS2介面資料訊號
wire [7:0] ps2_byte; // 1byte鍵值,只做簡單的按鍵掃描
output ps2_state; //鍵盤當前狀態,ps2_state=1表示有鍵被按下
output reg [1:0] sm_bit='b01;
output reg [7:0]sm_seg;
//------------------------------------------
reg ps2k_clk_r0,ps2k_clk_r1,ps2k_clk_r2; //ps2k_clk狀態暫存器
//wire pos_ps2k_clk; // ps2k_clk上升沿標誌位
wire neg_ps2k_clk; // ps2k_clk下降沿標誌位
//裝置傳送向主機的資料在下降沿有效,首先檢測PS2k_clk的下降沿
//利用上面邏輯賦值語句可以提取得下降沿,neg_ps2k_clk為高電平時表示資料可以被採集
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
ps2k_clk_r0 <= 1'b0;
ps2k_clk_r1 <= 1'b0;
ps2k_clk_r2 <= 1'b0;
end
else begin //鎖存狀態,進行濾波
ps2k_clk_r0 <= ps2k_clk;
ps2k_clk_r1 <= ps2k_clk_r0;
ps2k_clk_r2 <= ps2k_clk_r1;
end
end
assign neg_ps2k_clk = ~ps2k_clk_r1 & ps2k_clk_r2; //下降沿
//-----------------資料採集-------------------------
/*
幀結構:裝置發往主機資料幀為11位元,(主機發送資料包為12bit)
1bit start bit ,This is always 0,
8bit data bits,
1 parity bit,(odd parity)校驗位,奇校驗,
data bits 為偶數個1時該位為1,
data bits 為奇數個1時該位為0.
1bit stop bit ,this is always 1.
num 範圍為 'h00,'h0A;
*/
reg[7:0] ps2_byte_r; //PC接收來自PS2的一個位元組資料儲存器
reg[7:0] temp_data; //當前接收資料暫存器
reg[3:0] num; //計數暫存器
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
num <= 4'd0;
temp_data <= 8'd0;
end
else if(neg_ps2k_clk) begin //檢測到ps2k_clk的下降沿
case (num)
/*
幀結構中資料位為一個位元組,且低位在前,高位在後,
這裡要定義一個buf,size is one Byte.
*/
4'd0: num <= num+1'b1;
4'd1: begin
num <= num+1'b1;
temp_data[0] <= ps2k_data; //bit0
end
4'd2: begin
num <= num+1'b1;
temp_data[1] <= ps2k_data; //bit1
end
4'd3: begin
num <= num+1'b1;
temp_data[2] <= ps2k_data; //bit2
end
4'd4: begin
num <= num+1'b1;
temp_data[3] <= ps2k_data; //bit3
end
4'd5: begin
num <= num+1'b1;
temp_data[4] <= ps2k_data; //bit4
end
4'd6: begin
num <= num+1'b1;
temp_data[5] <= ps2k_data; //bit5
end
4'd7: begin
num <= num+1'b1;
temp_data[6] <= ps2k_data; //bit6
end
4'd8: begin
num <= num+1'b1;
temp_data[7] <= ps2k_data; //bit7
end
4'd9: begin
num <= num+1'b1; //奇偶校驗位,不做處理
end
4'd10: begin
num <= 4'd0; // num清零
end
default: ;
endcase
end
end
reg key_f0; //鬆鍵標誌位,置1表示接收到資料8'hf0,再接收到下一個資料後清零
reg ps2_state_r; //鍵盤當前狀態,ps2_state_r=1表示有鍵被按下
//+++++++++++++++資料處理開始++++++++++++++++=============
always @ (posedge clk or negedge rst_n) begin //接收資料的相應處理,這裡只對1byte的鍵值進行處理
if(!rst_n) begin
key_f0 <= 1'b0;
ps2_state_r <= 1'b0;
end
else if(num==4'd10) ///一幀資料是否採集完。
begin //剛傳送完一個位元組資料
if(temp_data == 8'hf0) key_f0 <= 1'b1;//判斷該接收資料是否為斷碼
else
begin
//========================理解困難==================================
if(!key_f0)
begin //說明有鍵按下
ps2_state_r <= 1'b1;
ps2_byte_r <= temp_data; //鎖存當前鍵值
end
else
begin
ps2_state_r <= 1'b0;
key_f0 <= 1'b0;
end
//=====================================================
end
end
end
/*+++++++++++++等效寫法+++++++++++++++++++++++++++++
reg key_released;//收到碼段後是否鬆開
reg [7:0] ps2_byte;
always @(posedge clk or negedge rst)
begin
if(!rst)
key_released<='b0;
else if(cnt=='h0A)//一幀資料是否採集完。
begin
if(ps2_byte_buf==8'hF0)//資料為段碼f0
key_released<='b1;//鬆開標誌位置位
else
key_released<='b0;
end
end
always @ (posedge clk or negedge rst)
begin
if(!rst)
key_pressed<= 0;
else if (cnt == 4'hA) // 採集完一個位元組?
begin
if (!key_released) // 有鍵按過?
begin
ps2_byte<= ps2_byte_buf; // 鎖存當前鍵值
key_pressed <= 'b1; // 按下標誌置一
end
else
key_pressed <= 'b0; // 按下標誌清零
end
end
*/
reg[7:0] ps2_asci; //接收資料的相應ASCII碼
always @ (ps2_byte_r) begin
case (ps2_byte_r) //鍵值轉換為ASCII碼,這裡做的比較簡單,只處理字母
8'h15: ps2_asci <= 8'h51; //Q
8'h1d: ps2_asci <= 8'h57; //W
8'h24: ps2_asci <= 8'h45; //E
8'h2d: ps2_asci <= 8'h52; //R
8'h2c: ps2_asci <= 8'h54; //T
8'h35: ps2_asci <= 8'h59; //Y
8'h3c: ps2_asci <= 8'h55; //U
8'h43: ps2_asci <= 8'h49; //I
8'h44: ps2_asci <= 8'h4f; //O
8'h4d: ps2_asci <= 8'h50; //P
8'h1c: ps2_asci <= 8'h41; //A
8'h1b: ps2_asci <= 8'h53; //S
8'h23: ps2_asci <= 8'h44; //D
8'h2b: ps2_asci <= 8'h46; //F
8'h34: ps2_asci <= 8'h47; //G
8'h33: ps2_asci <= 8'h48; //H
8'h3b: ps2_asci <= 8'h4a; //J
8'h42: ps2_asci <= 8'h4b; //K
8'h4b: ps2_asci <= 8'h4c; //L
8'h1a: ps2_asci <= 8'h5a; //Z
8'h22: ps2_asci <= 8'h58; //X
8'h21: ps2_asci <= 8'h43; //C
8'h2a: ps2_asci <= 8'h56; //V
8'h32: ps2_asci <= 8'h42; //B
8'h31: ps2_asci <= 8'h4e; //N
8'h3a: ps2_asci <= 8'h4d; //M
default: ;
endcase
end
assign ps2_byte = ps2_asci;
assign ps2_state = ps2_state_r;
//==================keyboard driver part over======================
//=======================1KHz div====display part start===================
parameter N2=50000;
reg clk3=1'b0;
reg [16:0]count3=17'd0;
//assign clk_out=clk3;
always @(posedge clk or negedge rst_n)
begin
if (!rst_n)
begin
count3<=17'd0;
clk3<=1'b0;
end
else
if(count3<N2-1)
begin
count3<=count3+1'b1;
if(count3<(N2/2-1))
clk3<=1'b0;
else
clk3<=1'b1;
end
else
begin
count3<=17'd0;
clk3<=1'b0;
end
end
//==================state select================
reg[3:0] Num;
always @(posedge clk3)
begin
case (sm_bit)
'b01: begin
Num<=ps2_byte[7:4];
sm_bit<='b10;
end
'b10: begin
Num<=ps2_byte[3:0];
sm_bit<='b01;
end
default:
Num<='b0;
endcase
/*if(sm_bit=='b01)
begin
Num<=ps2_byte[3:0];
sm_bit<='b10;
end
else if(sm_bit=='b10)
begin
Num<=ps2_byte[7:4];
sm_bit<='b01;
end
*/
end
//=========================================================
always @ (Num)//
begin
case (Num)
4'h0 : sm_seg = 8'h3f; // "0"
4'h1 : sm_seg = 8'h06; // "1"
4'h2 : sm_seg = 8'h5b; // "2"
4'h3 : sm_seg = 8'h4f; // "3"
4'h4 : sm_seg = 8'h66; // "4"
4'h5 : sm_seg = 8'h6d; // "5"//共陰極數碼管表
4'h6 : sm_seg = 8'h7d; // "6"
4'h7 : sm_seg = 8'h07; // "7"
4'h8 : sm_seg = 8'h7f; // "8"
4'h9 : sm_seg = 8'h6f; // "9"
4'ha : sm_seg = 8'h77; // "a"
4'hb : sm_seg = 8'h7c; // "b"
4'hc : sm_seg = 8'h39; // "c"
4'hd : sm_seg = 8'h5e; // "d"
4'he : sm_seg = 8'h79; // "e"
4'hf : sm_seg = 8'h71; // "f"
endcase
end
//==============================================
endmodule
四、測試:
本設計直接輸出對應ASCII碼值,根據16進位制ascii碼錶;
Q------->51H
W------->57H
E-------->45H
R-------->52H
T-------->54H
圖1-器件連線
圖2-測試