天天看點

verilog語言的ps2鍵盤驅動設計

PS/2接口是目前最常見的滑鼠接口,最初是IBM公司的專利,俗稱“小口”。這是一種滑鼠和鍵盤的專用接口,是一種6針的圓型接口。本設計完成了ps2鍵盤驅動,并将鍵盤對應的16進制ascii碼值在數位管輸出。

一、ps2接口協定

協定參考文檔連結:http://wenku.baidu.com/link?url=HBkLHCwo2zwdScmz2zPfig4T5eJZhcpj4qC65zTjQBE47GiubPOfg0QOKxflxTMs9F_vzyEtTNjX4fq1qsiMciBprAFxoeEkc3hpFaWnMZ3

主要内容:常見的PS/2端口有兩類:一種5腳的DIN或6腳的mini-DIN,這兩種連接配接器在電氣特性上是十分類似的,兩者隻有一點不同那就是管腳的排列,具有6 腳mini-DIN 的鍵盤

通常被叫做PS/2 鍵盤,而那些有5 腳DIN 叫做 AT 裝置。常見ps2鍵盤接口如下所示:

verilog語言的ps2鍵盤驅動設計

 由于電源和地都是主機接口提供,主要用于驅動設計的管腳隻有兩個,data和clock,該clock時鐘是鍵盤産生的,最大的時鐘頻率是33kHz 而且大多數裝置工作在10 20kHz。

PS/2 滑鼠和鍵盤履行一種雙向同步串行協定, 換句話說每次資料線上發送一位資料并且每在時鐘線上發一個脈沖就被讀入,鍵盤/ 滑鼠可以發送資料到主機,而主機也可以發送資料到裝置。

 從鍵盤/ 滑鼠發送到主機的資料在時鐘信号的下降沿, 當時鐘從高變到低的時候 被讀取 。從主機發送到鍵盤/滑鼠的資料在上升沿,當時鐘從低變到高的時候被讀取。

 不管通訊的方向怎樣鍵盤/滑鼠總是産生時鐘信号

該同步串行協定的幀結構如下:(從鍵盤到主機的幀長度為11bit)

verilog語言的ps2鍵盤驅動設計

注意校驗位是對資料位的校驗,且是奇校驗,當資料位為偶數個1時該位置為1,為奇數個1時該位為0.總是保證1的個數為奇數個。

每一位都是在時鐘的下降沿被讀取:

verilog語言的ps2鍵盤驅動設計

二、電氣接口協定(掃描碼):鍵盤的處理器花費很多的時間來掃描或監視按鍵矩陣,如果它發現有鍵被按下釋放或按住,鍵盤将發送掃描碼的資訊包到計算機。

     掃描碼有兩種不同的類型: 通碼和斷碼

     當一個鍵被按下或按住就發送通碼,當一個鍵被釋放就發送斷碼。每個按鍵被配置設定了唯一的通碼和斷碼,這樣主機通過查找唯一的掃描碼就可以測定是哪個按鍵。 每個鍵一整套的通斷碼組成了 掃描碼集, 有三套标準的掃描碼集分别是第一套、 第二套和第三套, 所有現代的鍵盤預設使用第二套掃描碼。

隻要一個鍵被按下這個鍵的通碼就被發送到計算機,記住通碼隻表示鍵盤上的一個按鍵,它不表示印刷在按鍵上的那個字元。這就意味着在通碼和ASCII 碼之間沒有已定義的關聯,直到主機把掃描碼翻譯成一個字元或指令。在verilog 語句中需要将通碼用case語句轉換層所需要的ascii碼。

多數通碼長度為一個位元組,少數擴充碼為2或4個位元組,這種碼都含有E0H。

同理,隻要鍵一釋放斷碼就會被發送每個鍵都有它自己唯一的通碼

它們也都有唯一的斷碼,不用總是通過查表來找出按鍵的斷碼。在通碼和斷碼之間存在着必然的聯系,多數第二套斷碼有兩位元組長。它們的第一個位元組是F0h ,第二個位元組是這個鍵的通碼擴

展按鍵的斷碼,通常有三個位元組。它們前兩個位元組是E0h,F0h 。最後一個位元組是這個按鍵通碼的最後一個位元組,一些例子如下。

verilog語言的ps2鍵盤驅動設計

三、設計過程。

主要包括鍵盤驅動子產品,ascii碼轉換,數位管顯示等子產品構成,其中數位管顯示部分可以參考我的前一篇博文:http://blog.csdn.net/baijingdong/article/details/20220363

主要程式如下:主要思路将在注釋中闡述

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-器件連接配接

verilog語言的ps2鍵盤驅動設計

圖2-測試

verilog語言的ps2鍵盤驅動設計

繼續閱讀