FPGA——PS/2驱动

概述

PS/2驱动框架分为四个部分:

1.预处理部分,用来同步ps/2_clk和ps/2_sda,从而进行边沿检测。

2.接收部分,当flag来了,接收sda的数据。

3.译码部分,根据ps/2数据手册对接收的数据进行转译。

4.显示部分,将转译过来的内容显示在数码管上。(因为我只做了数字0-9的显示,所以4位的data就足够了)

框架如下图所示:

预处理部分

首先对ps/2_clk进行上升沿寄存,因为时钟都是上升沿采样。通过寄存两拍(check_reg_clk)和寄存一拍的取反(~syn_reg_clk[1])相与得到flag_neg。正常采样的点为A点,但因为A点采不到,所以C作为A的近似点采样,寄存一拍(sda : C)后要采样的数据正好和flag_neg对齐。

预处理代码

module pre_handle (

         input    wire         clk,
	 
	 input    wire         ps2_clk,
	 input    wire         ps2_sda,
	 
	 output   wire         flag_neg,
	 output   wire         sda

);
    reg            [1:0]  syn_reg_clk; //同步寄存ps2_clk
    reg            [1:0]  syn_reg_sda;//同步寄存ps2_sda
    reg                   check_reg_clk;	 

    always @ (posedge clk) syn_reg_clk <= {syn_reg_clk[0],ps2_clk};
	 
    always @ (posedge clk) syn_reg_sda <= {syn_reg_sda[0],ps2_sda};
	 
    assign sda = syn_reg_sda[1];

    always @ (posedge clk) check_reg_clk <= syn_reg_clk[1];

    assign flag_neg = check_reg_clk & (~syn_reg_clk[1]);	 

endmodule 

接收部分

在接收部分定义了一个11位的捕获寄存器cap_reg,把每一次新接收到的sda都放在cap_reg的第一位,以此类推,接收11位后就是一个完整的数据,【停止位,校验位,8位数据位,起始位】

10

9

8

87

6

5

4

3

2

1

0

start

 

 

 

 

 

 

 

 

 

 

10

9

8

87

6

5

4

3

2

1

0

A0

start

 

 

 

 

 

 

 

 

 

10

9

8

87

6

5

4

3

2

1

0

A1

A0

start

 

 

 

 

 

 

 

 

10

9

8

87

6

5

4

3

2

1

0

A2

A1

A0

start

 

 

 

 

 

 

 

10

9

8

87

6

5

4

3

2

1

0

A3

A2

A1

A0

start

 

 

 

 

 

 

10

9

8

87

6

5

4

3

2

1

0

A4

A3

A2

A1

A0

start

 

 

 

 

 

10

9

8

87

6

5

4

3

2

1

0

A5

A4

A3

A2

A1

A0

start

 

 

 

 

10

9

8

87

6

5

4

3

2

1

0

A6

A5

A4

A3

A2

A1

A0

start

 

 

 

10

9

8

87

6

5

4

3

2

1

0

A7

A6

A5

A4

A3

A2

A1

A0

start

 

 

10

9

8

87

6

5

4

3

2

1

0

校验

A7

A6

A5

A4

A3

A2

A1

A0

start

 

10

9

8

87

6

5

4

3

2

1

0

Stop

校验

A7

A6

A5

A4

A3

A2

A1

A0

start

 

最开始接收起始位 start 放在第10,然后接收A0放在第10位,start位向右平移,处于第9位。此类推 当全部接收完,start位在第0位。

最后 第9位为检验位

[8:1]为数据位

把9-1为进行异或(^[9:1]),若异或结果为1,说明数据正确 把[8:1]输出给数码管。若不正确则不输出。

接收部分代码

module ps2_rec ( 
         input    wire         clk,
	 input    wire         rst_n,
	 
	 input    wire         flag,
	 input    wire         sda,
	 
	 output   reg   [7:0]  rec_code,
	 output   reg          valid_flag,
	 output   reg          error_flag
);
         reg            [3:0]  cnt;
	 reg            [10:0] cap_reg;//捕获寄存器
	 wire                  flag_recdone; //完成标志 
	 
    always @ (posedge clk) begin
	     if (rst_n == 0)
	         cap_reg <= 0;
	     else
		 if(flag)
		     cap_reg <= {sda,cap_reg[10:1]};//脉冲来了 把每次新来的sda放在cap_reg的高位
		 else
		     cap_reg <= cap_reg;
	 end

	 always @ (posedge clk) begin
	     if (rst_n == 0)
		      cnt <= 0;
		  else
		      if(cnt < 11)
		          if (flag)
		              cnt <= cnt + 1'b1;
			  else
			      cnt <= cnt;
		      else
		          cnt <= 0; //cnt == 11时直接清零 保持一个周期
	 end
	 
	 assign flag_recdone = (cnt == 11); //cnt == 11这一个周期正好是脉冲完成的标志
	 
	 always @ (posedge clk) begin
	     if (rst_n == 0)
		 rec_code <= 0;
	     else
		 if (flag_recdone && ^cap_reg[9:1] == 1)//接收完成且为奇校验
	             rec_code <= cap_reg[8:1]; //认为cap_reg[8:1]是接收的信息
	         else
		     rec_code <= rec_code;
	 end
	 
	 always @ (posedge clk) begin
	     if (rst_n == 0)
	         valid_flag <= 0;
	     else
		 if (flag_recdone && ^cap_reg[9:1] == 1) //接收完成且为奇校验
		     valid_flag <= 1; //正确接收
	         else
		     valid_flag <= 0;
	 end
	 
	 always @ (posedge clk) begin
	     if (rst_n == 0)
	         error_flag <= 0;
		 else
		     if (flag_recdone && ^cap_reg[9:1] == 0) //接收完成但不是奇校验
		         error_flag <= 1; //有错误
		     else
		         error_flag <= 0;
	 end
endmodule 

译码部分

定义一个16位的缓存数据,把新来的8位数据放在后8位。

0000

0000

0000

0000

显示为

0

0

1

C

A

1

C

1

C

A

1

C

1

C

A

                                                                                     .

A

                                                                                     .

A

                                                                                     .

A

1

C

1

C

A

1

C

F

0

0

F

0

1

C

0

1

C

3

2

B

3

2

3

2

B

                                                                                     .

B

                                                                                     .

B

                                                                                     .

B

3

2

3

2

B

3

2

F

0

0

F

0

3

2

0

 开始16bit数都为0 当A按下时 显示001C 持续按住A 显示1C1C1CF0为瞬态(处于按下与松开之间的状态)  按键结束为F01C 这时再按下按键B 显示为1C32  持续按住B 显示323232F0为瞬态(处于按下与松开之间的状态)。按键结束为F032

总结:高8位为F0时,显示为零。高8为不为F0时,显示低8位对应的码值。

译码代码

module ps2_decoder (

    input    wire         clk,
    input    wire         rst_n,
	
    input    wire         flag,
    input    wire  [7:0]  code,
	
    output   reg   [3:0]  data	
 
);

    reg            [15:0] buf_code;
	 
	 always @ (posedge clk) begin
	     if (rst_n == 0)
		      buf_code <= 0;
		  else
		      if (flag)
				    buf_code <= {buf_code[7:0],code};//新来的8位放后面
				 else
				    buf_code <= buf_code;
	 end
	 
	 always @ (posedge clk) begin
	     if (rst_n == 0)
		      data <= 0;
		  else
		      if (buf_code[15:8] == 8'hf0)
				    data <= 0;
			   else
				    case (buf_code[7:0])
					     8'h45    :    data<=0;
						  8'h16    :    data<=1;
						  8'h1e    :    data<=2;
						  8'h26    :    data<=3;
						  8'h25    :    data<=4;
						  8'h2e    :    data<=5;
						  8'h36    :    data<=6;
						  8'h3d    :    data<=7;
						  8'h3e    :    data<=8;
					          8'h46    :    data<=9;
					          8'h1c    :    data<=10;
					          8'h32    :    data<=11;
						  8'h21    :    data<=12;
						  8'h23    :    data<=13;
						  8'h24    :    data<=14;
						  8'h2b    :    data<=15;
						  default  :    data<=0;
					 endcase
	 end
endmodule 

 显示部分

显示部分就是把译码中的4位data显示在数码管上,这个根据数码管驱动不同略有区别。

总结

最后就是把4个模块实例化到一个总代码中,编译成功后如下图所示:

 最后的RTL级视图也和我们的框架图一致,ps/2驱动就完成了。

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值