FPGA——PS/2驱动"/>
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_clkreg [1:0] syn_reg_sda;//同步寄存ps2_sdareg 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) beginif (rst_n == 0)cap_reg <= 0;elseif(flag)cap_reg <= {sda,cap_reg[10:1]};//脉冲来了 把每次新来的sda放在cap_reg的高位elsecap_reg <= cap_reg;endalways @ (posedge clk) beginif (rst_n == 0)cnt <= 0;elseif(cnt < 11)if (flag)cnt <= cnt + 1'b1;elsecnt <= cnt;elsecnt <= 0; //cnt == 11时直接清零 保持一个周期endassign flag_recdone = (cnt == 11); //cnt == 11这一个周期正好是脉冲完成的标志always @ (posedge clk) beginif (rst_n == 0)rec_code <= 0;elseif (flag_recdone && ^cap_reg[9:1] == 1)//接收完成且为奇校验rec_code <= cap_reg[8:1]; //认为cap_reg[8:1]是接收的信息elserec_code <= rec_code;endalways @ (posedge clk) beginif (rst_n == 0)valid_flag <= 0;elseif (flag_recdone && ^cap_reg[9:1] == 1) //接收完成且为奇校验valid_flag <= 1; //正确接收elsevalid_flag <= 0;endalways @ (posedge clk) beginif (rst_n == 0)error_flag <= 0;elseif (flag_recdone && ^cap_reg[9:1] == 0) //接收完成但不是奇校验error_flag <= 1; //有错误elseerror_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 显示1C1C,1CF0为瞬态(处于按下与松开之间的状态) 按键结束为F01C 这时再按下按键B 显示为1C32 持续按住B 显示3232,32F0为瞬态(处于按下与松开之间的状态)。按键结束为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) beginif (rst_n == 0)buf_code <= 0;elseif (flag)buf_code <= {buf_code[7:0],code};//新来的8位放后面elsebuf_code <= buf_code;endalways @ (posedge clk) beginif (rst_n == 0)data <= 0;elseif (buf_code[15:8] == 8'hf0)data <= 0;elsecase (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;endcaseend
endmodule
显示部分
显示部分就是把译码中的4位data显示在数码管上,这个根据数码管驱动不同略有区别。
总结
最后就是把4个模块实例化到一个总代码中,编译成功后如下图所示:
最后的RTL级视图也和我们的框架图一致,ps/2驱动就完成了。
更多推荐
FPGA——PS/2驱动
发布评论