抱着“不气馁、不放弃、誓不罢休、搞不定你我还能搞其他玩意吗”的心态,调试许久的PS2鼠标实验,终于在今天被我搞定了。发几张图显摆一下,嘿嘿。。。

                                 左键按下+鼠标移动

                               右键按下+鼠标移动

                                中键按下+鼠标移动

一、程序框架:

大概情况:

1、先由控制模块启动发送模块,把指令0xff发送给鼠标,进入复位模式。

2、发送完后,通知控制模块启动接收模块,接收鼠标应答数据fa、aa、00。

3、接收完后,在次给鼠标发送0xf4,鼠标进入待发数据状态。

4、发送完后,启动接收模块,接收鼠标应答数据fa。

5、回复的数据都OK后,就让程序进入一直处于接收数据状态,每当鼠标按下一次或移动,鼠标都会发送三个字节的数据,提取数据进行解析让在LCD实时显示。

二、鼠标和键盘不管是向鼠标发送数据还是接收数据,都有特定的格式,每一组数据都是一个起始位、8位有效数据、一个校验位、一个停止位。

三、主机到设备时序图:这里注意停止位发送完后,鼠标会在产生一个时钟,并向主机发送一个应答位,在时钟为高时,数据线是从高到底变化的,有点像主机接收设备发送过来的起始信号,伪起始信号,如果这步没处理好,启动接收模块时就会认为此时是一个起始信号,结果接收到的数据把真正起始信号“0”给处理了,我当时就在这里犯错了。主机在发送数据时,是在时钟的下降沿改变数据。

发送模块流程:(ps2_clk 和ps2_data 都定义双向端口)

1、拉低时钟,至少要保持100us,程序中设置的是200us,这个时间不是越大约好,从主机拉低时钟到设备真正产生时钟时时间不能大于15MS。

2、200us后,拉低数据线,给鼠标发送请求状态。

3、释放时钟线,link_clk设置为0,让FPGA把该ps2_clk管脚置为高阻态,在高阻态时,该管脚可以接收高低电平,也就成了输入管脚。若设置为1,那么就成了输出管脚。
      assign ps2_clk = link_clk ? ps2_clk_out : 1'bz;

4、在时钟的下降沿开始发送数据,8bit数据、一位校验位、一位停止位。

5、释放数据线,鼠标发送应答位,link_data设置为0,原理同link_clk。

assign ps2_data = link_data ? ps2_data_out : 1'bz;

6、等到最后一个时钟产生完,发送完标志产生send_done_sig = 1'b1;

7、马上又进入send_done_sig清零状态,为了下次在送数据做好准备。

注意:释放时钟或释放数据一定要在准确位置释放。

四、设备到主机时序图:

时序比较简单,属于单向通信,设备发,主机只接收。程序中起始位、奇偶校验、停止位都不作处理。这个就不多讲。由于接收模块只作为接收,所以ps2_clk 和ps2_data 都定输入端口。

五、发送、接收和显示都处于待命状态后,由控制模块来协调工作,到这里,对整个控制流程应该很清楚了吧。

代码实现:(由于把所有接收到数据都显示到LCD上,所以有些地方代码写的比较繁琐,暂时先这样吧)

ps2_data_control.v

 module ps2_data_control(
//input
sys_clk,
rst_n,
send_done_sig, //发送完标志
rx_done_sig, //接收完标志
data_buf, //接收到的数据 //output
rx_en, //接收使能
send_en, //发送使能
send_cmd, //要发送的命令
dis_data_low1, //要显示的数据
dis_data_hig1, dis_data_low2, //要显示的数据
dis_data_hig2, dis_data_low3, //要显示的数据
dis_data_hig3, dis_data_low4, //要显示的数据
dis_data_hig4, dis_x_low,
dis_x_hig, dis_y_low,
dis_y_hig, dis_data_btn
);
input sys_clk;
input rst_n;
input send_done_sig;
input rx_done_sig;
input [:] data_buf; output rx_en;
output send_en;
output [:] send_cmd;
output [:] dis_data_low1;
output [:] dis_data_hig1;
output [:] dis_data_low2;
output [:] dis_data_hig2;
output [:] dis_data_low3;
output [:] dis_data_hig3;
output [:] dis_data_low4;
output [:] dis_data_hig4;
output [:] dis_data_btn;
output [:] dis_x_low;
output [:] dis_x_hig;
output [:] dis_y_low;
output [:] dis_y_hig;
/**********************************************************************/
parameter T100MS = 'd4_999_999;
parameter PS2_RST = 'hff; //复位cmd
parameter PS2_EN = 'hf4; //数据报告使能cmd
parameter IDLE = 'd0,
SEND_PS2_RST = 'd1,
RX_EN1 = 'd2,
RX_ANSWER_FA = 'd3,
RX_ANSWER_AA = 'd4,
RX_ANSWER_ID = 'd5,
SEND_PS2_EN = 'd6,
RX_EN2 = 'd7,
RX_ANSWER2 = 'd8,
RX_BYTE1 = 'd9,
RX_BYTE2 = 'd10,
RX_BYTE3 = 'd11,
DELAY = 'd12,
STOP = 'd13;
/**********************************************************************/
reg [:] cnt;
always @(posedge sys_clk or negedge rst_n)
if(!rst_n)
cnt <= 'd0;
else if(!cnt_en || cnt == T100MS)
cnt <= 'd0;
else
cnt <= cnt + 'b1;
/**********************************************************************/
reg send_en;
reg rx_en;
reg [:] data_answer; //保存应答位
reg [:] x_move; //x的偏移量
reg [:] y_move; //y的偏移量
reg [:] state;
reg [:] dis_data_temp1;
reg [:] dis_data_temp2;
reg [:] dis_data_temp3;
reg [:] dis_data_temp4;
reg [:] dis_data_btn;
reg cnt_en;
reg [:] send_cmd;
always @(posedge sys_clk or negedge rst_n)
if(!rst_n) begin
send_en <= 'b0;
rx_en <= 'b0;
data_answer <= 'h00;
state <= IDLE;
dis_data_temp1 <= 'h00;
dis_data_temp2 <= 'h00;
dis_data_temp3 <= 'h00;
dis_data_temp4 <= 'h00;
cnt_en <= 'b0;
dis_data_btn <= "X";
send_cmd <= 'h00;
end
else begin
case(state)
IDLE:
begin
state <= SEND_PS2_RST;
cnt_en <= 'b1;
end SEND_PS2_RST: //发送复位 0xff
if(cnt == T100MS) begin
cnt_en <= 'b0;
send_en <= 'b1; //启动发送
send_cmd <= PS2_RST;
state <= RX_EN1;//RX_EN1;
end
else
state <= SEND_PS2_RST; RX_EN1:
if(send_done_sig) begin
rx_en <= 'b1;
send_en <= 'b0;
state <= RX_ANSWER_FA;
end
else
state <= RX_EN1; RX_ANSWER_FA: //接收鼠标发回的应答数据0xfa
if(rx_done_sig) begin
dis_data_temp1 <= data_buf;
state <= RX_ANSWER_AA;//RX_BYTE1;
end
else
state <= RX_ANSWER_FA; RX_ANSWER_AA: //接收鼠标发回的应答数据0xaa
if(rx_done_sig) begin
dis_data_temp2 <= data_buf;
state <= RX_ANSWER_ID;
end
else
state <= RX_ANSWER_AA; RX_ANSWER_ID: //接收鼠标发回的应答数据0x00
if(rx_done_sig) begin
dis_data_temp3 <= data_buf;
cnt_en <= 'b1;
rx_en <= 'b0;
state <= SEND_PS2_EN;
end
else
state <= RX_ANSWER_ID; SEND_PS2_EN: //发送0xf4
if(cnt == T100MS)begin
cnt_en <= 'b0;
send_en <= 'b1; //启动发送
send_cmd <= PS2_EN;
state <= RX_EN2;
end
else
state <= SEND_PS2_EN; RX_EN2:
if(send_done_sig) begin
rx_en <= 'b1; //启动接收
send_en <= 'b0;
state <= RX_ANSWER2;
end
else
state <= RX_EN2; RX_ANSWER2: //第二次应答位
if(rx_done_sig) begin
dis_data_temp4 <= data_buf;
state <= RX_BYTE1;
end
else
state <= RX_ANSWER2; RX_BYTE1:
if(rx_done_sig) begin
if(data_buf[] == 'b1)//左键被按下
dis_data_btn <= "L";
else if(data_buf[] == 'b1) //右键被按下
dis_data_btn <= "R";
else if(data_buf[] == 'b1) //中键被按下
dis_data_btn <= "M"; state <= RX_BYTE2;
end
else
state <= RX_BYTE1; RX_BYTE2:
if(rx_done_sig) begin //接收到第二个字节
x_move <= data_buf;
state <= RX_BYTE3;
end
else
state <= RX_BYTE2; RX_BYTE3: //接收到第三个字节
if(rx_done_sig) begin
y_move <= data_buf;
state <= STOP;
cnt_en <= 'b1;
end
else
state <= RX_BYTE3; STOP:
if(cnt == T100MS)
begin
cnt_en <= 'b0;
state <= RX_BYTE1;
end
else
state <= STOP; endcase
end reg [:] dis_data_low1;
always @(dis_data_temp1[:])
case(dis_data_temp1[:])
'h0: dis_data_low1 = "0";
'h1: dis_data_low1 = "1";
'h2: dis_data_low1 = "2";
'h3: dis_data_low1 = "3";
'h4: dis_data_low1 = "4";
'h5: dis_data_low1 = "5";
'h6: dis_data_low1 = "6";
'h7: dis_data_low1 = "7";
'h8: dis_data_low1 = "8";
'h9: dis_data_low1 = "9";
'ha: dis_data_low1 = "a";
'hb: dis_data_low1 = "b";
'hc: dis_data_low1 = "c";
'hd: dis_data_low1 = "d";
'he: dis_data_low1 = "e";
'hf: dis_data_low1 = "f";
endcase reg [:] dis_data_hig1;
always @(dis_data_temp1[:])
case(dis_data_temp1[:])
'h0: dis_data_hig1 = "0";
'h1: dis_data_hig1 = "1";
'h2: dis_data_hig1 = "2";
'h3: dis_data_hig1 = "3";
'h4: dis_data_hig1 = "4";
'h5: dis_data_hig1 = "5";
'h6: dis_data_hig1 = "6";
'h7: dis_data_hig1 = "7";
'h8: dis_data_hig1 = "8";
'h9: dis_data_hig1 = "9";
'ha: dis_data_hig1 = "a";
'hb: dis_data_hig1 = "b";
'hc: dis_data_hig1 = "c";
'hd: dis_data_hig1 = "d";
'he: dis_data_hig1 = "e";
'hf: dis_data_hig1 = "f";
endcase reg [:] dis_data_low2;
always @(dis_data_temp2[:])
case(dis_data_temp2[:])
'h0: dis_data_low2 = "0";
'h1: dis_data_low2 = "1";
'h2: dis_data_low2 = "2";
'h3: dis_data_low2 = "3";
'h4: dis_data_low2 = "4";
'h5: dis_data_low2 = "5";
'h6: dis_data_low2 = "6";
'h7: dis_data_low2 = "7";
'h8: dis_data_low2 = "8";
'h9: dis_data_low2 = "9";
'ha: dis_data_low2 = "a";
'hb: dis_data_low2 = "b";
'hc: dis_data_low2 = "c";
'hd: dis_data_low2 = "d";
'he: dis_data_low2 = "e";
'hf: dis_data_low2 = "f";
endcase reg [:] dis_data_hig2;
always @(dis_data_temp2[:])
case(dis_data_temp2[:])
'h0: dis_data_hig2 = "0";
'h1: dis_data_hig2 = "1";
'h2: dis_data_hig2 = "2";
'h3: dis_data_hig2 = "3";
'h4: dis_data_hig2 = "4";
'h5: dis_data_hig2 = "5";
'h6: dis_data_hig2 = "6";
'h7: dis_data_hig2 = "7";
'h8: dis_data_hig2 = "8";
'h9: dis_data_hig2 = "9";
'ha: dis_data_hig2 = "a";
'hb: dis_data_hig2 = "b";
'hc: dis_data_hig2 = "c";
'hd: dis_data_hig2 = "d";
'he: dis_data_hig2 = "e";
'hf: dis_data_hig2 = "f";
endcase reg [:] dis_data_low3;
always @(dis_data_temp3[:])
case(dis_data_temp3[:])
'h0: dis_data_low3 = "0";
'h1: dis_data_low3 = "1";
'h2: dis_data_low3 = "2";
'h3: dis_data_low3 = "3";
'h4: dis_data_low3 = "4";
'h5: dis_data_low3 = "5";
'h6: dis_data_low3 = "6";
'h7: dis_data_low3 = "7";
'h8: dis_data_low3 = "8";
'h9: dis_data_low3 = "9";
'ha: dis_data_low3 = "a";
'hb: dis_data_low3 = "b";
'hc: dis_data_low3 = "c";
'hd: dis_data_low3 = "d";
'he: dis_data_low3 = "e";
'hf: dis_data_low3 = "f";
endcase reg [:] dis_data_hig3;
always @(dis_data_temp3[:])
case(dis_data_temp3[:])
'h0: dis_data_hig3 = "0";
'h1: dis_data_hig3 = "1";
'h2: dis_data_hig3 = "2";
'h3: dis_data_hig3 = "3";
'h4: dis_data_hig3 = "4";
'h5: dis_data_hig3 = "5";
'h6: dis_data_hig3 = "6";
'h7: dis_data_hig3 = "7";
'h8: dis_data_hig3 = "8";
'h9: dis_data_hig3 = "9";
'ha: dis_data_hig3 = "a";
'hb: dis_data_hig3 = "b";
'hc: dis_data_hig3 = "c";
'hd: dis_data_hig3 = "d";
'he: dis_data_hig3 = "e";
'hf: dis_data_hig3 = "f";
endcase reg [:] dis_data_low4;
always @(dis_data_temp4[:])
case(dis_data_temp4[:])
'h0: dis_data_low4 = "0";
'h1: dis_data_low4 = "1";
'h2: dis_data_low4 = "2";
'h3: dis_data_low4 = "3";
'h4: dis_data_low4 = "4";
'h5: dis_data_low4 = "5";
'h6: dis_data_low4 = "6";
'h7: dis_data_low4 = "7";
'h8: dis_data_low4 = "8";
'h9: dis_data_low4 = "9";
'ha: dis_data_low4 = "a";
'hb: dis_data_low4 = "b";
'hc: dis_data_low4 = "c";
'hd: dis_data_low4 = "d";
'he: dis_data_low4 = "e";
'hf: dis_data_low4 = "f";
endcase reg [:] dis_data_hig4;
always @(dis_data_temp4[:])
case(dis_data_temp4[:])
'h0: dis_data_hig4 = "0";
'h1: dis_data_hig4 = "1";
'h2: dis_data_hig4 = "2";
'h3: dis_data_hig4 = "3";
'h4: dis_data_hig4 = "4";
'h5: dis_data_hig4 = "5";
'h6: dis_data_hig4 = "6";
'h7: dis_data_hig4 = "7";
'h8: dis_data_hig4 = "8";
'h9: dis_data_hig4 = "9";
'ha: dis_data_hig4 = "a";
'hb: dis_data_hig4 = "b";
'hc: dis_data_hig4 = "c";
'hd: dis_data_hig4 = "d";
'he: dis_data_hig4 = "e";
'hf: dis_data_hig4 = "f";
endcase
//move x
reg [:] dis_x_low;
always @(x_move[:])
case(x_move[:])
'h0: dis_x_low = "0";
'h1: dis_x_low = "1";
'h2: dis_x_low = "2";
'h3: dis_x_low = "3";
'h4: dis_x_low = "4";
'h5: dis_x_low = "5";
'h6: dis_x_low = "6";
'h7: dis_x_low = "7";
'h8: dis_x_low = "8";
'h9: dis_x_low = "9";
'ha: dis_x_low = "a";
'hb: dis_x_low = "b";
'hc: dis_x_low = "c";
'hd: dis_x_low = "d";
'he: dis_x_low = "e";
'hf: dis_x_low = "f";
endcase reg [:] dis_x_hig;
always @(x_move[:])
case(x_move[:])
'h0: dis_x_hig = "0";
'h1: dis_x_hig = "1";
'h2: dis_x_hig = "2";
'h3: dis_x_hig = "3";
'h4: dis_x_hig = "4";
'h5: dis_x_hig = "5";
'h6: dis_x_hig = "6";
'h7: dis_x_hig = "7";
'h8: dis_x_hig = "8";
'h9: dis_x_hig = "9";
'ha: dis_x_hig = "a";
'hb: dis_x_hig = "b";
'hc: dis_x_hig = "c";
'hd: dis_x_hig = "d";
'he: dis_x_hig = "e";
'hf: dis_x_hig = "f";
endcase
//move y
reg [:] dis_y_low;
always @(y_move[:])
case(y_move[:])
'h0: dis_y_low = "0";
'h1: dis_y_low = "1";
'h2: dis_y_low = "2";
'h3: dis_y_low = "3";
'h4: dis_y_low = "4";
'h5: dis_y_low = "5";
'h6: dis_y_low = "6";
'h7: dis_y_low = "7";
'h8: dis_y_low = "8";
'h9: dis_y_low = "9";
'ha: dis_y_low = "a";
'hb: dis_y_low = "b";
'hc: dis_y_low = "c";
'hd: dis_y_low = "d";
'he: dis_y_low = "e";
'hf: dis_y_low = "f";
endcase reg [:] dis_y_hig;
always @(y_move[:])
case(y_move[:])
'h0: dis_y_hig = "0";
'h1: dis_y_hig = "1";
'h2: dis_y_hig = "2";
'h3: dis_y_hig = "3";
'h4: dis_y_hig = "4";
'h5: dis_y_hig = "5";
'h6: dis_y_hig = "6";
'h7: dis_y_hig = "7";
'h8: dis_y_hig = "8";
'h9: dis_y_hig = "9";
'ha: dis_y_hig = "a";
'hb: dis_y_hig = "b";
'hc: dis_y_hig = "c";
'hd: dis_y_hig = "d";
'he: dis_y_hig = "e";
'hf: dis_y_hig = "f";
endcase
endmodule

ps2_send_control.v

 module ps2_send_control(
//input
sys_clk,
rst_n,
send_en, //发送使能
send_cmd, //要发送的数据 0xf4 //output
send_done_sig,//发送完标志 //inout
ps2_clk, //鼠标时钟
ps2_data //鼠标数据
); input sys_clk;
input rst_n;
input send_en;
input [:] send_cmd; output send_done_sig; inout ps2_clk;
inout ps2_data;
/**************************************************************/
parameter T200US = 'd9999;
parameter IDLE = 'd0,
PS2_CLK_SET0 = 'd1, //时钟拉低
PS2_DATA_SET0 = 'd2, //数据拉低
PS2_CLK_SET1 = 'd3, //释放时钟,拉高
SEND_DATA = 'd4, //发送8bit数据和校验位
ACK = 'd5, //释放数据,拉高
STOP = 'd6,
CLEAR = 'd7;
/**************************************************************/
//如果send_data中有偶数个1,那么^send_data结果为0,否则为1,在取反即为奇校验位应设置的值
wire odd_parity;
assign odd_parity = ~(^send_cmd);
//上面这句,一位网友说是下面的操作方式
// ~(odd_parity ^send_cmd[0]) -> ~(~(odd_parity ^send_cmd[0]) ^send_cmd[1])
// -> ~(~(~(odd_parity ^send_cmd[0]) ^send_cmd[1]) ^send_cmd[2])... 一次类推
/**************************************************************/
//控制鼠标时钟和数据的方向
//link_clk = 1,ps2_clk为output ,link_clk = 0,ps2_clk为input,FPGA内部得把该管脚设置为高阻态,以便接收时钟
//link_data = 1,ps2_data为output ,link_data = 0,ps2_data为input,FPGA内部得把该管脚设置为高阻态,以便接受数据
assign ps2_clk = link_clk ? ps2_clk_out : 'bz;
assign ps2_data = link_data ? ps2_data_out : 'bz;
/**************************************************************/
//200us计数器
reg [:] cnt;
always @(posedge sys_clk or negedge rst_n)
if(!rst_n)
cnt <= 'd0;
else if(!cnt_en || cnt == T200US)
cnt <= 'd0;
else
cnt <= cnt + 'b1;
/**************************************************************/
reg ps2_clk_n_1;
reg ps2_clk_n_2;
always @(posedge sys_clk or negedge rst_n)
if(!rst_n) begin
ps2_clk_n_1 <= 'b1;
ps2_clk_n_2 <= 'b1;
end
else begin
ps2_clk_n_1 <= ps2_clk;
ps2_clk_n_2 <= ps2_clk_n_1;
end wire ps2_clk_n;
assign ps2_clk_n = ps2_clk_n_2 & (~ps2_clk_n_1);
/**************************************************************/
reg link_clk;
reg link_data;
reg cnt_en;
reg ps2_clk_out;
reg ps2_data_out;
reg send_done_sig;
reg [:] state;
reg [:] i;
reg [:] s_data;
always @(posedge sys_clk or negedge rst_n)
if(!rst_n) begin
link_clk <= 'b0;
link_data <= 'b0;
cnt_en <= 'b0;
ps2_clk_out <= 'b1;
ps2_data_out <= 'b1;
send_done_sig <= 'b0;
state <= IDLE;
i <= 'd0;
s_data <= 'd0;
end
else if(send_en) begin
case(state)
IDLE:
begin
state <= PS2_CLK_SET0;
s_data <= /*{1'b0,send_cmd}*/{odd_parity,send_cmd};
end PS2_CLK_SET0:
begin
link_clk <= 'b1; //输出状态
ps2_clk_out <= 'b0;
cnt_en <= 'b1; //启动计数器
state <= PS2_DATA_SET0;
end PS2_DATA_SET0:
if(cnt == T200US) begin //200us后 拉低数据线
cnt_en <= 'b0;
link_data <= 'b1;
ps2_data_out <= 'b0;
state <= PS2_CLK_SET1;
end
else
state <= PS2_DATA_SET0; PS2_CLK_SET1: //释放时钟线
begin
link_clk <= 'b0; //输入状态
ps2_clk_out <= 'b1;
state <= SEND_DATA;
end SEND_DATA:
if(ps2_clk_n) begin //在时钟的下降沿设置数据
if(i == 'd9) begin
i <= 'd0;
link_data <= 'b1;
ps2_data_out <= 'b1; //发送一个停止位
state <= ACK;
end
else begin
link_data <= 'b1;
ps2_data_out <= s_data[i];
i <= i + 'b1;
state <= SEND_DATA;
end
end
else
state <= SEND_DATA; ACK:
begin
link_data <= 'b0; //鼠标应答
state <= STOP;
end //鼠标产生数据应答位,同时还会产生最后一个时钟,时钟产生完后,主机发送数据-设备应答 才算完整的结束
//如这里不加if(ps2_clk_n)判断条件,接收模块会把真正的起始位采进去,为什么呢,仔细看设备发回的应答数据,
//是在时钟为高时,数据拉低,也是相当产生一个起始动作,这是一个伪起始位。
//导致后面发送ff命令后,收到的是f4 54 00,发送f4命令后,收到的是f4,按左键显示 R ,右键显示 M ,中键不变化
//这里我是通过上面显示数据推断才知道,接收模块会把真正的起始位采进去。
STOP:
if(ps2_clk_n) begin
send_done_sig <= 'b1;
state <= CLEAR;
end
else
state <= STOP; CLEAR:
begin
send_done_sig <= 'b0;
state <= IDLE;
end
endcase
end endmodule

ps2_rx_control.v

 module ps2_rx_control(
//input
sys_clk,
rst_n,
ps2_clk_in, //鼠标时钟
ps2_data_in, //鼠标数据
rx_en, //接收模块使能信号 //output
rx_done_sig, //接收完标志信号
data_buf //保存接收到的数据
);
input sys_clk;
input rst_n;
input ps2_clk_in;
input ps2_data_in;
input rx_en; output rx_done_sig;
output [:] data_buf;
/**************************************************************/
reg ps2_clk_in_1;
reg ps2_clk_in_2;
wire ps2_clk_in_n;
always @(posedge sys_clk or negedge rst_n)
if(!rst_n) begin
ps2_clk_in_1 <= 'b1;
ps2_clk_in_2 <= 'b1;
end
else begin
ps2_clk_in_1 <= ps2_clk_in;
ps2_clk_in_2 <= ps2_clk_in_1;
end assign ps2_clk_in_n = ps2_clk_in_2 & (~ps2_clk_in_1);
/**************************************************************/
reg [:] i;
reg [:] data_buf;
reg rx_done_sig;
always @(posedge sys_clk or negedge rst_n)
if(!rst_n) begin
i <= 'd0;
data_buf <= 'h00;
rx_done_sig <= 'b0;
end
else if(rx_en) begin //ps2_clk_in_n不能写在这个地方,rx_done_sig置1和置0没必要等到ps2_clk_in下降沿时设置,否则会出问题
case(i)
'd0:
if(ps2_clk_in_n) begin
i <= i + 'b1;
end 'd1,4'd2,'d3,4'd4,'d5,4'd6,'d7,4'd8: //接收8位数据
if(ps2_clk_in_n) begin
i <= i + 'b1;
data_buf[i- ] <= ps2_data_in;
end 'd9:
if(ps2_clk_in_n)
i <= i + 'b1; //奇校验位不处理 'd10:
if(ps2_clk_in_n)
i <= i + 'b1; //停止位不处理 'd11:
begin
rx_done_sig <= 'b1; //标志着一帧数据接收完
i <= i + 'b1;
end 'd12:
begin
rx_done_sig <= 'b0; //置0,给下次接收做好准备
i <= 'd0;
end
endcase
end endmodule

LCD12864.v

 module LCD12864(
//input
sys_clk,
rst_n,
dis_data_low1,
dis_data_hig1, dis_data_low2,
dis_data_hig2, dis_data_low3,
dis_data_hig3, dis_data_low4,
dis_data_hig4, dis_data_btn, dis_x_low,
dis_x_hig, dis_y_low,
dis_y_hig, //output
lcd_rs,
lcd_rw,
lcd_en,
lcd_data,
lcd_psb
);
input sys_clk;// 50MHZ
input rst_n;
input [:] dis_data_low1;
input [:] dis_data_hig1;
input [:] dis_data_low2;
input [:] dis_data_hig2;
input [:] dis_data_low3;
input [:] dis_data_hig3;
input [:] dis_data_low4;
input [:] dis_data_hig4;
input [:] dis_x_low;
input [:] dis_x_hig;
input [:] dis_y_low;
input [:] dis_y_hig; input [:] dis_data_btn; output lcd_rs;//H:data L:command
output lcd_rw;//H:read module L:write module
output lcd_en;//H active
output [:] lcd_data;
output lcd_psb;//H:parallel module L:SPI module /***************************************************/
parameter T3MS = 'd149_999;
parameter IDLE = 'd0,
INIT_FUN_SET1 = 'd1,
INIT_FUN_SET2 = 'd2,
INIT_DISPLAY = 'd3,
INIT_CLEAR = 'd4,
INIT_DOT_SET = 'd5,
SET_DDRAM = 'd6,
WRITE_DATA0 = 'd7,
WRITE_DATA1 = 'd8,
WRITE_DATA2 = 'd9,
WRITE_BLANK1 = 'd10,
WRITE_DATA3 = 'd11,
WRITE_DATA4 = 'd12,
WRITE_BLANK2 = 'd13,
WRITE_DATA5 = 'd14,
WRITE_DATA6 = 'd15,
SET_DDRAM1 = 'd16,
WRITE_DATA7 = 'd17,
WRITE_DATA8 = 'd18,
WRITE_DATA9 = 'd19,
SET_DDRAM2 = 'd20,
WRITE_DATA10 = 'd21,
WRITE_DATA11 = 'd22,
WRITE_BLANK3 = 'd23,
VALUE_X_HIG = 'd24,
VALUE_X_LOW = 'd25,
WRITE_BLANK4 = 'd26,
VALUE_Y_HIG = 'd27,
VALUE_Y_LOW = 'd28,
WRITE_BLANK5 = 'd29,
WRITE_BLANK6 = 'd30; /***************************************************/
//产生周期为6MS的lcd_clk给LCD
reg [:] cnt;
reg lcd_clk;
always @(posedge sys_clk or negedge rst_n)
if(!rst_n) begin
cnt <= 'd0;
lcd_clk <= 'b0;
end
else if(cnt == T3MS)begin
cnt <= 'd0;
lcd_clk <= ~lcd_clk;
end
else
cnt <= cnt + 'b1; /***************************************************/
reg lcd_rs;
always @(posedge lcd_clk or negedge rst_n)
if(!rst_n)
lcd_rs <= 'b0;
else if( (state == WRITE_DATA1) || (state == WRITE_DATA2)
|| (state == WRITE_DATA3) || (state == WRITE_DATA4)
|| (state == WRITE_DATA5) || (state == WRITE_DATA6)
|| (state == WRITE_DATA7) || (state == WRITE_DATA8)
|| (state == WRITE_DATA9) || (state == WRITE_DATA10)
|| (state == WRITE_DATA11) || (state == WRITE_DATA0)
|| (state == VALUE_X_HIG) || (state == VALUE_X_LOW)
|| (state == VALUE_Y_HIG) || (state == VALUE_Y_LOW)
||(state == WRITE_BLANK1) || (state == WRITE_BLANK2)
||(state == WRITE_BLANK3) || (state == WRITE_BLANK4)
||(state == WRITE_BLANK5) || (state == WRITE_BLANK6))
lcd_rs <= 'b1; //写数据模式
else
lcd_rs <= 'b0; //写命令模式
/***************************************************/
reg [:] state;
reg [:] lcd_data;
reg [:] num;
reg en;
reg line_flag;
always @(posedge lcd_clk or negedge rst_n)
if(!rst_n) begin
state <= IDLE;
lcd_data <= 'h00;
en <= 'b1;
num <= 'd0;
line_flag <= 'b0;
end
else
case(state)
IDLE:
begin
state <= INIT_FUN_SET1;
lcd_data <= 'hzz;
en <= 'b1;
end INIT_FUN_SET1:
begin
lcd_data <= 'h30; //功能设定
state <= INIT_FUN_SET2;
end INIT_FUN_SET2:
begin
lcd_data <= 'h30; //功能设定
state <= INIT_DISPLAY;
end INIT_DISPLAY:
begin
lcd_data <= 'h0c; //显示设定
state <= INIT_CLEAR;
end INIT_CLEAR:
begin
lcd_data <= 'h01; //清屏
state <= INIT_DOT_SET;
end INIT_DOT_SET:
begin
lcd_data <= 'h06; //进入点设定
state <= SET_DDRAM;
end SET_DDRAM:
begin
if(!line_flag) begin
lcd_data <= 'h82;//1 line
end
else begin
lcd_data <= 'h90;//2 line
line_flag <= 'b0;
end state <= WRITE_DATA0;
end WRITE_DATA0: ////ff应答::
begin
num <= num + 'b1;
lcd_data <= dis_data;
if(num == 'd9) begin
line_flag <= 'b1;
state <= SET_DDRAM;
end
else if(num == 'd16)
state <= WRITE_DATA1;
else begin
state <= WRITE_DATA0;
end
end WRITE_DATA1: //回应的第一个数据高字节
begin
lcd_data <= dis_data_hig1;
state <= WRITE_DATA2;
end WRITE_DATA2://回应的第一个数据低字节
begin
lcd_data <= dis_data_low1;
state <= WRITE_BLANK1;
end WRITE_BLANK1: //写一个空格
begin
lcd_data <= " ";
state <= WRITE_DATA3;
end WRITE_DATA3: //回应的第二个数据高字节
begin
lcd_data <= dis_data_hig2;
state <= WRITE_DATA4;
end WRITE_DATA4://回应的第二个数据低字节
begin
lcd_data <= dis_data_low2;
state <= WRITE_BLANK2;
end WRITE_BLANK2: //写一个空格
begin
lcd_data <= " ";
state <= WRITE_DATA5;
end WRITE_DATA5: //回应的第三个数据高字节
begin
lcd_data <= dis_data_hig3;
state <= WRITE_DATA6;
end WRITE_DATA6://回应的第三个数据低字节
begin
lcd_data <= dis_data_low3;
state <= SET_DDRAM1;
end SET_DDRAM1:
begin
lcd_data <= 'h88;//3 line
state <= WRITE_DATA7;
end WRITE_DATA7: //f4应答
begin
if(num == 'd24) begin
state <= WRITE_DATA8;
end
else begin
num <= num + 'b1;
lcd_data <= dis_data;
state <= WRITE_DATA7;
end
end WRITE_DATA8: //第二次回应的高字节
begin
lcd_data <= dis_data_hig4;
state <= WRITE_DATA9;
end WRITE_DATA9://第二次回应的低字节
begin
lcd_data <= dis_data_low4;
state <= SET_DDRAM2;
end SET_DDRAM2:
begin
lcd_data <= 'h98;//4 line
state <= WRITE_DATA10;
end WRITE_DATA10:
begin
if(num == 'd29) begin
num <= 'd0;
state <= WRITE_DATA11;
end
else begin
num <= num + 'b1;
lcd_data <= dis_data;
state <= WRITE_DATA10;
end
end WRITE_DATA11:
begin
lcd_data <= dis_data_btn;
state <= WRITE_BLANK3;
end WRITE_BLANK3:
begin
lcd_data <= " ";
state <= WRITE_BLANK4;
end WRITE_BLANK4:
begin
lcd_data <= "(";
state <= VALUE_X_HIG;
end VALUE_X_HIG:
begin
lcd_data <= dis_x_hig;
state <= VALUE_X_LOW;
end VALUE_X_LOW:
begin
lcd_data <= dis_x_low;
state <= WRITE_BLANK5;
end WRITE_BLANK5:
begin
lcd_data <= " ";
state <= VALUE_Y_HIG;
end VALUE_Y_HIG:
begin
lcd_data <= dis_y_hig;
state <= VALUE_Y_LOW;
end VALUE_Y_LOW:
begin
lcd_data <= dis_y_low;
state <= WRITE_BLANK6;
end WRITE_BLANK6:
begin
lcd_data <= ")";
state <= SET_DDRAM;
end
/* STOP:
begin
en <= 1'b0;//显示完了,lcd_e就一直拉为低
state <= STOP;
end */ default: state <= IDLE;
endcase reg [:] dis_data;
always @(posedge sys_clk or negedge rst_n)
if(!rst_n)
dis_data <= 'hzz;
else
case(num)
//ps2 Mouse
'd0 : dis_data <= "P";
'd1 : dis_data <= "S";
'd2 : dis_data <= "2";
'd3 : dis_data <= " ";
'd4 : dis_data <= "M";
'd5 : dis_data <= "o";
'd6 : dis_data <= "u";
'd7 : dis_data <= "s";
'd8 : dis_data <= "e";
'd9 : dis_data <= " ";
//ff应答:
'd10 : dis_data <= "f";//8'h66;
'd11 : dis_data <= "f";//8'h66;
'd12 : dis_data <= 8'hd3;
'd13 : dis_data <= 8'ha6;
'd14 : dis_data <= 8'hb4;
'd15 : dis_data <= 8'hf0;
'd16 : dis_data <= " ";
//f4应答:
'd17 : dis_data <= "f";
'd18 : dis_data <= "4";
'd19 : dis_data <= 8'hd3;
'd20 : dis_data <= 8'ha6;
'd21 : dis_data <= 8'hb4;
'd22 : dis_data <= 8'hf0;
'd23 : dis_data <= " ";
//按键:
'd24 : dis_data <= 8'hb0;
'd25 : dis_data <= 8'hb4;
'd26 : dis_data <= 8'hbc;
'd27 : dis_data <= 8'hfc;
'd28 : dis_data <= " ";
default: dis_data <= 'h00;
endcase
/***************************************************/
assign lcd_rw = 'b0;//只有写模式
assign lcd_psb = 'b1;//并口模式
assign lcd_en = en ? lcd_clk : 'b0;
/***************************************************/
endmodule

ps2_mouse_top.v

 module  ps2_mouse_top(
//input
sys_clk,
rst_n, //inout
ps2_clk,
ps2_data, //output
lcd_rs,
lcd_rw,
lcd_en,
lcd_data,
// lcd_psb
); input sys_clk;
input rst_n; inout ps2_clk;
inout ps2_data; output lcd_rs;//H:data L:command
output lcd_rw;//H:read module L:write module
output lcd_en;//H active
output [:] lcd_data;
//output lcd_psb;//H:parallel module L:SPI module wire send_done_sig;
wire rx_done_sig;
wire [:] data_buf;
wire rx_en;
wire send_en;
wire [:] send_cmd;
wire [:] dis_data_low1;
wire [:] dis_data_hig1;
wire [:] dis_data_low2;
wire [:] dis_data_hig2;
wire [:] dis_data_low3;
wire [:] dis_data_hig3;
wire [:] dis_data_low4;
wire [:] dis_data_hig4;
wire [:] dis_data_btn;
wire [:] dis_x_low;
wire [:] dis_x_hig;
wire [:] dis_y_low;
wire [:] dis_y_hig;
//控制模块例化
ps2_data_control u1_control(
//input
.sys_clk(sys_clk),
.rst_n(rst_n),
.send_done_sig(send_done_sig), //发送完标志
.rx_done_sig(rx_done_sig), //接收完标志
.data_buf(data_buf), //接收到的数据 //output
.rx_en(rx_en), //接收使能
.send_en(send_en), //发送使能
.send_cmd(send_cmd), //要发送的命令
.dis_data_low1(dis_data_low1), //要显示的数据
.dis_data_hig1(dis_data_hig1), .dis_data_low2(dis_data_low2), //要显示的数据
.dis_data_hig2(dis_data_hig2), .dis_data_low3(dis_data_low3), //要显示的数据
.dis_data_hig3(dis_data_hig3), .dis_data_low4(dis_data_low4), //要显示的数据
.dis_data_hig4(dis_data_hig4), .dis_x_low(dis_x_low),
.dis_x_hig(dis_x_hig), .dis_y_low(dis_y_low),
.dis_y_hig(dis_y_hig), .dis_data_btn(dis_data_btn)
);
//显示模块例化
LCD12864 u2_lcd(
//input
.sys_clk(sys_clk),
.rst_n(rst_n),
.dis_data_low1(dis_data_low1), //要显示的数据
.dis_data_hig1(dis_data_hig1), .dis_data_low2(dis_data_low2), //要显示的数据
.dis_data_hig2(dis_data_hig2), .dis_data_low3(dis_data_low3), //要显示的数据
.dis_data_hig3(dis_data_hig3), .dis_data_low4(dis_data_low4), //要显示的数据
.dis_data_hig4(dis_data_hig4), .dis_x_low(dis_x_low),
.dis_x_hig(dis_x_hig), .dis_y_low(dis_y_low),
.dis_y_hig(dis_y_hig), .dis_data_btn(dis_data_btn), //output
.lcd_rs(lcd_rs),
.lcd_rw(lcd_rw),
.lcd_en(lcd_en),
.lcd_data(lcd_data),
// .lcd_psb(lcd_psb)
);
//发送模块例化
ps2_send_control u3_send(
//input
.sys_clk(sys_clk),
.rst_n(rst_n),
.send_en(send_en), //发送使能
.send_cmd(send_cmd), //要发送的命令 0xf4 //output
.send_done_sig(send_done_sig),//发送完标志 //inout
.ps2_clk(ps2_clk), //鼠标时钟
.ps2_data(ps2_data) //鼠标数据
);
//接收模块例化
ps2_rx_control u4_rx(
//input
.sys_clk(sys_clk),
.rst_n(rst_n),
.ps2_clk_in(ps2_clk), //鼠标时钟
.ps2_data_in(ps2_data), //鼠标数据
.rx_en(rx_en), //接收模块使能信号 //output
.rx_done_sig(rx_done_sig), //接收完标志信号
.data_buf(data_buf) //保存接收到的数据
);
endmodule

总结:

1、上个试验“PS2鼠标+LCD12864实验(调试未成功)”,为什么没有调试成功呢,主要还是细节没注意,要彻底弄清楚每个时钟节拍会做什么动作,尤其是到了一组数据发送完后,一些应答位停止位可以不关心,但一定得知道到它何时产生,在哪变化,只有清楚了每一个细节后,成功才会向你招手。

2、遇到困难时,不要气馁,但反复检查代码时就是没有发现问题,此时应该好好休息一下,因为大脑已经陷入了僵局状态,很难找到问题,且耽误时间,打击自个的信心。

>>>>在ps2_send_control.v中,奇偶校验理解的不对,详细介绍见“奇偶校验位产生器

PS2鼠标+LCD12864实验——终于OK 了的更多相关文章

  1. PS2鼠标+LCD12864实验(调试未成功)

    此试验我一人调试许久都未成功,但发送ff时,读出来的数据确是对的,一开始让我窃喜,但发送f4时,读出来的数据确是错的,哎让苦恼啊,能力有限,只能先暂时就这样吧,那位什么还要贴出来呢,有两个原因: 1. ...

  2. PS2键盘 + LCD12864 实验

    本实验是通过LCD12864来显示键盘上被按下的按键,实验比较简单,在LCD12864固定的DDRAM地址上显示,缺点就是不能保存上一次被按的内容,后者会覆盖掉前面,所以屏上仅有一个字符显示.保存上一 ...

  3. 基于I2C EPPRPM(AT24C02B) + LCD12864实验

    本次实验目的:在指定的EPPROM地址中,写入一数据,延时100MS后,在从该地址中读取,并在LCD上显示. 该实验在前两天就开始做了,一开始并没有成功,读出的一直0x00,当时也调了一会,但跳回到P ...

  4. 【Azure IoT DevKit】实验终于做完了

    大家好,我是MSP李桑榆 今天终于把几个Azure IoT DevKit的小实验的视频给做完了. 不敢说什么指导,只是给大家一个参考.因为Devkit不需要你写一行代码,只需要你按着步骤来,并没有什么 ...

  5. 基于Verilog HDL 各种实验

    菜鸟做的的小实验链接汇总:           1.基于Verilog HDL 的数字时钟设计 2.乘法器 3.触发器(基本的SR触发器.同步触发器.D触发器) 4.基于Verilog HDL的ADC ...

  6. 如何关闭win7的ps/2兼容鼠标(触屏版)

    买了一个新电脑联想ThinkPad E555 可是刚拿到是个win10 的系统,用习惯了win7,win0不太好用, 然后帮我刷成了win7,之后一切都好,性能也是让我很满意,但是却关不掉触控板,于是 ...

  7. 简单三段式状态机实验2-LCD12864

    此实验是在“基于I2C EPPRPM(AT24C02B) + LCD12864实验”基础上,把LCD模块里的一段式状态机改成三段式,I2C EPPROM模块暂时未改出来,一步一步来吧,改完后代码下载到 ...

  8. 20192204 2019-2020-2 《Python程序设计》实验四报告

    20192204 2019-2020-2 <Python程序设计>实验四报告 课程:<Python程序设计> 班级: 1922 姓名: 李龙威 学号:20192204 实验教师 ...

  9. FPGA/SOPC学习转载

    转自小時不識月http://www.cnblogs.com/yuphone/archive/2010/08/27/docs_plan.html 新网址为:http://andrewz.cn [连载计划 ...

随机推荐

  1. view视图--display中echo出ob_get_contents的缓冲内容--(实现,拼接好文件--导入文件)

    view.php01默认设置有3个公共的属性,其他属性.后面实例化的时候.通过传递参数.foreach遍历,不断的增加属性02view对象的实例化.位置在-->控制器父类的构造方法中视图的目录名 ...

  2. sort函数使用的基本知识

    STL中就自带了排序函数sortsort 对给定区间所有元素进行排序  要使用此函数只需用#include <algorithm> sort即可使用,语法描述为:sort(begin,en ...

  3. C# Memcached缓存

    net/c#分布式缓存系统Memcached简介与实践--非常好 缘起: 在数据驱动的web开发中,经常要重复从数据库中取出相同的数据,这种重复极大的增加了数据库负载.缓存是解决这个问题的好办法.但是 ...

  4. LeetCode OJ 111. Minimum Depth of Binary Tree

    Given a binary tree, find its minimum depth. The minimum depth is the number of nodes along the shor ...

  5. IOS传值之Block传值(二)

    @interface QWViewController : UIViewController @property(nonatomic,strong)UILabel *label; @property( ...

  6. 文件系统满的话(filesystem full),该如何处理。

    #!/bin/bash function ergodic(){ ` do "/"$file ] then ergodic $"/"$file else loca ...

  7. Ztree当节点没有下级时不显示下拉图标

    select o.*,(select count(*) from sys_org t where t.orgsupid=o.orgid) isLeaf from sys_org o where 1=1

  8. 689D Magic Odd Square 奇数幻方

    1 奇数阶幻方构造法 (1) 将1放在第一行中间一列; (2) 从2开始直到n×n止各数依次按下列规则存放:按 45°方向行走,向右上,即每一个数存放的行比前一个数的行数减1,列数加1 (3) 如果行 ...

  9. HDU 1010 Tempter of the Bone DFS(奇偶剪枝优化)

    需要剪枝否则会超时,然后就是基本的深搜了 #include<cstdio> #include<stdio.h> #include<cstdlib> #include ...

  10. UIApplicationDelegate 协议 浅析

    @protocol UIApplicationDelegate<NSObject> @optional - (void)applicationDidFinishLaunching:(UIA ...