一、PS/2鼠标接口
PS/2接口用于许多现代的鼠标和键盘,由IBM最初开发和使用。物理上的PS/2接口有两种类型的连接器:5脚的DIN和6脚的MINI-DIN。
二、鼠标的接口协议原理
PS/2鼠标接口采用一种双向同步串行协议。即每在时钟线上发一个脉冲,就在数据线上发送一位数据。但是在相互传输中 ,主机是拥有总线控制权的,即它可以在任何时候抑制鼠标的发送。方法是把时钟线一直拉低,鼠标就不能产生时钟信号和发送数据。
在两个方向的传输中,时钟信号都是由鼠标产生,即主机不产生通信时钟信号。
如果主机要发送数据,它必须控制鼠标产生时钟信号。方法如下:主机首先下拉时钟线至少100μs抑制通信,然后再下拉数据线,最后释放时钟线。通过这一时序控制鼠标产生时钟信号。当鼠标检测到这个时序状态,会在10ms内产生时钟信号。
图1、鼠标到主机传输协议
图2、主机到鼠标的传输协议
三、鼠标的工作模式
Reset模式:当鼠标上电或主机发复位命令(0xFF)给它时进入这种模式;
Stream模式: 鼠标的默认模式,当鼠标上电或复位完成后,自动进入此模式,鼠标基本上以此模式工作;
Remote模式:只有在主机发送了模式设置命令(0xF0)后,鼠标才进入这种模式;
Wrap模式:该模式只用于测试鼠标与主机连接是否正确。
四、鼠标发送的数据格式
Byte1中的Bit0、Bit1、Bit2分别表示左、右、中键的状态,用状态值0表示释放 ,用状态值1表示按下。Byte2和Byte3分别表示X轴和Y轴方向的移动计量值,是二进制补码值。Byte4的低四位表示滚轮的移动计量值,也是二进制补码值,高四位作为扩展符号位。这种数据包由带滚轮的三键三维鼠标产生。若是不带滚轮的三键鼠标,产生的数据包没有Byte4其余的相同。
五、Verilog代码
PS2驱动
odule ps2(
iSTART, //press the button for transmitting instrucions to device;
iRST_n, //FSM reset signal;
iCLK_50, //clock source;
PS2_CLK, //ps2_clock signal inout;
PS2_DAT, //ps2_data signal inout;
oLEFBUT, //left button press display;
oRIGBUT, //right button press display;
oMIDBUT, //middle button press display;
oX_MOV1, //lower SEG of mouse displacement display for X axis.
oX_MOV2, //higher SEG of mouse displacement display for X axis.
oY_MOV1, //lower SEG of mouse displacement display for Y axis.
oY_MOV2 //higher SEG of mouse displacement display for Y axis.
);
//interface;
//=======================================================
// PORT declarations
//=======================================================
input iSTART;
input iRST_n;
input iCLK_50;
inout PS2_CLK;
inout PS2_DAT;
output oLEFBUT;
output oRIGBUT;
output oMIDBUT;
output [6:0] oX_MOV1;
output [6:0] oX_MOV2;
output [6:0] oY_MOV1;
output [6:0] oY_MOV2;
//instantiation
SEG7_LUT U1(.oSEG(oX_MOV1),.iDIG(x_latch[3:0]));
SEG7_LUT U2(.oSEG(oX_MOV2),.iDIG(x_latch[7:4]));
SEG7_LUT U3(.oSEG(oY_MOV1),.iDIG(y_latch[3:0]));
SEG7_LUT U4(.oSEG(oY_MOV2),.iDIG(y_latch[7:4]));
//instruction define, users can charge the instruction byte here for other purpose according to ps/2 mouse datasheet.
//the MSB is of parity check bit, that's when there are odd number of 1's with data bits, it's value is '0',otherwise it's '1' instead.
parameter enable_byte =9'b011110100;
//=======================================================
// REG/WIRE declarations
//=======================================================
reg [1:0] cur_state,nex_state;
reg ce,de;
reg [3:0] byte_cnt,delay;
reg [5:0] ct;
reg [7:0] x_latch,y_latch,cnt;
reg [8:0] clk_div;
reg [9:0] dout_reg;
reg [32:0] shift_reg;
reg leflatch,riglatch,midlatch;
reg ps2_clk_in,ps2_clk_syn1,ps2_dat_in,ps2_dat_syn1;
wire clk,ps2_dat_syn0,ps2_clk_syn0,ps2_dat_out,ps2_clk_out,flag;
//=======================================================
// PARAMETER declarations
//=======================================================
//state define
parameter listen =2'b00,
pullclk=2'b01,
pulldat=2'b10,
trans =2'b11;
//=======================================================
// Structural coding
//=======================================================
//clk division, derive a 97.65625KHz clock from the 50MHz source;
always@(posedge iCLK_50)
begin
clk_div <= clk_div+1;
end
assign clk = clk_div[8];
//tristate output control for PS2_DAT and PS2_CLK;
assign PS2_CLK = ce?ps2_clk_out:1'bZ;
assign PS2_DAT = de?ps2_dat_out:1'bZ;
assign ps2_clk_out = 1'b0;
assign ps2_dat_out = dout_reg[0];
assign ps2_clk_syn0 = ce?1'b1:PS2_CLK;
assign ps2_dat_syn0 = de?1'b1:PS2_DAT;
//
assign oLEFBUT = leflatch;
assign oRIGBUT = riglatch;
assign oMIDBUT = midlatch;
//multi-clock region simple synchronization
always@(posedge clk)
begin
ps2_clk_syn1 <= ps2_clk_syn0;
ps2_clk_in <= ps2_clk_syn1;
ps2_dat_syn1 <= ps2_dat_syn0;
ps2_dat_in <= ps2_dat_syn1;
end
//FSM shift
always@(*)
begin
case(cur_state)
listen :begin
if ((!iSTART) && (cnt == 8'b11111111))
nex_state = pullclk;
else
nex_state = listen;
ce = 1'b0;
de = 1'b0;
end
pullclk :begin
if (delay == 4'b1100)
nex_state = pulldat;
else
nex_state = pullclk;
ce = 1'b1;
de = 1'b0;
end
pulldat :begin
nex_state = trans;
ce = 1'b1;
de = 1'b1;
end
trans :begin
if (byte_cnt == 4'b1010)
nex_state = listen;
else
nex_state = trans;
ce = 1'b0;
de = 1'b1;
end
default : nex_state = listen;
endcase
end
//idle counter
always@(posedge clk)
begin
if ({ps2_clk_in,ps2_dat_in} == 2'b11)
begin
cnt <= cnt+1;
end
else begin
cnt <= 8'd0;
end
end
//periodically reset ct; ct counts the received data length;
assign flag = (cnt == 8'hff)?1:0;
always@(posedge ps2_clk_in,posedge flag)
begin
if (flag)
ct <= 6'b000000;
else
ct <= ct+1;
end
//latch data from shift_reg;outputs is of 2's complement;
//Please treat the cnt value here with caution, otherwise wrong data will be latched.
always@(posedge clk,negedge iRST_n)
begin
if (!iRST_n)
begin
leflatch <= 1'b0;
riglatch <= 1'b0;
midlatch <= 1'b0;
x_latch <= 8'd0;
y_latch <= 8'd0;
end
else if (cnt == 8'b00011110 && (ct[5] == 1'b1 || ct[4] == 1'b1))
begin
leflatch <= shift_reg[1];
riglatch <= shift_reg[2];
midlatch <= shift_reg[3];
x_latch <= x_latch+shift_reg[19 : 12];
y_latch <= y_latch+shift_reg[30 : 23];
end
end
//pull ps2_clk low for 100us before transmit starts;
always@(posedge clk)
begin
if (cur_state == pullclk)
delay <= delay+1;
else
delay <= 4'b0000;
end
//transmit data to ps2 device;eg. 0xF4
always@(negedge ps2_clk_in)
begin
if (cur_state == trans)
dout_reg <= {1'b0,dout_reg[9:1]};
else
dout_reg <= {enable_byte,1'b0};
end
//transmit byte length counter
always@(negedge ps2_clk_in)
begin
if (cur_state == trans)
byte_cnt <= byte_cnt+1;
else
byte_cnt <= 4'b0000;
end
//receive data from ps2 device;
always@(negedge ps2_clk_in)
begin
if (cur_state == listen)
shift_reg <= {ps2_dat_in,shift_reg[32:1]};
end
//FSM movement
always@(posedge clk,negedge iRST_n)
begin
if (!iRST_n)
cur_state <= listen;
else
cur_state <= nex_state;
end
endmodule