weiqi7777

FPGA之PS2

0
阅读(2044)

PS2协议也很简单,和串口接收差不多,只是数据要多一个奇偶校验位。

其协议时序如下,只考虑PS2接收,即只接收外部PS2数据。

clip_image002

有两根线,一根时钟线,一根数据线。在没有数据传输的时候,两根线都是高电平。传输的时候,PS2器件会开始启动时钟和数据变换,进行数据的传输。

从时序可以看出,数据再时钟的上升沿变化,所以对于接收,要在时钟的下降沿进行数据的采集。

开始进行FPGA设计:

代码也很简单,首先检测时钟的下降沿,一旦有下降沿,开始接收数据。在时钟的下降沿,对数据进行采集。直到所有数据采集完成。结束接收数据。等待下一次。

这里,可以采用状态机来设计。就比较简单。我采用的不是状态机设计。

以下是设计代码:


module ps_2_module( input clk, //clk 50M input rst_n, //reset signal ,active low input ps2_data, //ps2 data input input ps2_clk, //ps2 clk input output reg finish, //receive ps2 data finish output reg [7:0] ps2_receive_data //receive ps2 8-bits data ); /********************judge ps2_clk falling edge*********************/ reg ps2_clk_r ; reg ps2_clk_r_r ; wire ps2_clk_falling ; always@( posedge clk ) begin if( !rst_n ) begin ps2_clk_r <= 'b1 ; ps2_clk_r_r <= 'b1 ; end else begin ps2_clk_r <= ps2_clk ; ps2_clk_r_r <= ps2_clk_r ; end end assign ps2_clk_falling = ps2_clk_r_r & ( !ps2_clk_r ) ; /********************judge ps2_data falling edge*********************/ //receive 10-bits data. but the high 8-bits is real data. the latter bit is parity bit and stop bit reg [9:0] ps2_receive_data_reg; reg [3:0] i; reg start_flag; //if ps2_data generate a falling edge,that indicate receive mode start assign start = ps2_clk_falling; always@(posedge clk or negedge rst_n) begin if(!rst_n) begin start_flag <= 'b0; ps2_receive_data <= 'd0; finish <= 'b0; end else if(start) start_flag <= 1'b1; else if(i >= 10 && start_flag == 1) begin start_flag <= 1'b0; ps2_receive_data <= ps2_receive_data_reg[7:0]; finish <= 1'b1; end else finish <= 1'b0; end always@(posedge clk or negedge rst_n) begin if(!rst_n) begin ps2_receive_data_reg <= 'd0; i <= 'd0; end else begin if(start_flag) //receive data begin if( ps2_clk_falling == 1 ) begin ps2_receive_data_reg <= {ps2_data,ps2_receive_data_reg[9:1]}; i <= i +4'd1; end end else begin ps2_receive_data_reg <= 'd0; i <='d0; end end end endmodule


其中,关键的代码是以下下降沿检测代码:


/********************judge ps2_clk falling edge*********************/ reg ps2_clk_r ; reg ps2_clk_r_r ; wire ps2_clk_falling ; always@( posedge clk ) begin if( !rst_n ) begin ps2_clk_r <= 'b1 ; ps2_clk_r_r <= 'b1 ; end else begin ps2_clk_r <= ps2_clk ; ps2_clk_r_r <= ps2_clk_r ; end end assign ps2_clk_falling = ps2_clk_r_r & ( !ps2_clk_r ) ; /********************judge ps2_data falling edge*********************/


对输入信号进行两级寄存,然后对寄存的数据进行判断,即可知道数据是否为下降沿。

一旦有下降沿,就启动接收数据。


always@(posedge clk or negedge rst_n) begin if(!rst_n) begin start_flag <= 'b0; ps2_receive_data <= 'd0; finish <= 'b0; end else if(start) start_flag <= 1'b1; else if(i >= 10 && start_flag == 1) begin start_flag <= 1'b0; ps2_receive_data <= ps2_receive_data_reg[7:0]; finish <= 1'b1; end else finish <= 1'b0;


有下降沿,start_flag为1,使能接收数据,当数据接收到10位,即8位数据,一位奇偶校验位,一位停止位,结束接收数据,将finish信号置高,同时,将接受的数据寄存到ps2_receive_data中。

那么问题来了,这里为什么是接收11位数据,不是还有一位开始位了。这里就要注意数据移位的使能移位信号是什么。

看如下移位代码:


if(start_flag) //receive data begin if( ps2_clk_falling == 1 ) begin ps2_receive_data_reg<= {ps2_data,ps2_receive_data_reg[9:1]}; i <= i +4'd1; end end


在接收数据情况下,每当有时钟下降沿,就将ps2_receive_data_reg(这里用来接收数据)就向右移一位,同时高位补ps2_data,因为ps2传输数据是低位在前,所以这里要这么处理。

然后就发现,当start_flag为1的时候,这时候ps2_clk_falling这个信号已经为0了,所以后面的移位操作就没有执行,就相当于这个接收,是将开始位给忽略的。所以就只有10位数据了。而开始位对于我们没有什么用,所以忽略也没有什么关系。

image

从上面的仿真图,就比较清楚了。在start_flag有效的时候,ps2_clk_falling已经无效了。所以我们是不会接收到开始位数据的。ps2_receive_data_reg接收到的第一个数据就是数据最低位。

将串口和PS2级联起来,串口将PS2接收到的数据发出来,通过串口助手查看接收到的值。

clip_image006

按下A键,PS2接收到的数据室1C,因为A的编码就是1C。断开A键,接收到两个数据,一个F0 ,一个1C。F0是断码,表示后面编码对应的键释放。如果长按A键,就会接收一对1C。

至此,就搞定了PS2了。大家可以用状态机来设计,状态机设计的话,就比较简单了。

Baidu
map