抱着“不气馁、不放弃、誓不罢休、搞不定你我还能搞其他玩意吗”的心态,调试许久的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. LeetCode OJ 292.Nim Game

    You are playing the following Nim Game with your friend: There is a heap of stones on the table, eac ...

  2. hdu 1020

    //自信满满地交上去~~but...超时了 #include <iostream> #include <string.h> #include <stdio.h> u ...

  3. servlet第2讲(上集)----创建servlet实例(实现servlet接口)

  4. 页面跳转后样式丢失js失效

    2 页面跳转后样式丢失js失效 问题描述: 用ajax跳转的时候,从a.html跳转到b.html后,b.html的css以及js都失效了. 解决办法: 将当前页面需要用到的css以及js放在< ...

  5. Entity Framework Code First ---EF Power Tool 和MySql一起使用遇到的问题

    关于如何使用EF Power Tool的介绍请看 http://www.cnblogs.com/LingzhiSun/archive/2011/05/24/EFPowerTool_1.html, 这里 ...

  6. HTML/CSS font-family对应的中英文名称 宋体 微软雅黑

    宋体 SimSun 黑体 SimHei 微软雅黑 Microsoft YaHei 微软正黑体 Microsoft JhengHei 新宋体 NSimSun 新细明体 PMingLiU 细明体 Ming ...

  7. Application的多种值判断测试程序

    Application.Contents.Remove("FriendLink") Response.Write("Application.Contents.Remove ...

  8. 。net MVC 序列化 反序列化

           序列化 (Serialization)将对象的状态信息转换为可以存储或传输的形式的过程.在序列化期间,对象将其当前状态写入到临时或持久性存储区.以后,可以通过从存储区中读取或反序列化对象 ...

  9. CodeForces 567B Berland National Library hdu-5477 A Sweet Journey

    这类题一个操作增加多少,一个操作减少多少,求最少刚开始为多少,在中途不会出现负值,模拟一遍,用一个数记下最大的即可 #include<cstdio> #include<cstring ...

  10. js 弹层 提示

    <!DOCTYPE html><html style="height: 100%;"><head lang="en"> &l ...