原创 verilog的PS2键盘解码

2010-7-6 18:31 1537 8 8 分类: FPGA/CPLD


标签: verilog  PS2键盘  FPGA  


verilog的PS2键盘解码




verilogPS2键盘解码<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />


 


    之前日志里深入探讨过PS/2键盘编解码以及数据传输协议,这次自己动手实现了利用FPGA接收键盘编码,然后通过串口传输到PC。做的比较简单,只是通过FPGA把大写字母A-Z转换成相应的ASCII码,只要字母按键被按下,就能在串口调试助手里显示相应大写字母。下面就共享代码吧!


    除了顶层模块,三个底层模块分别为PS/2传输处理模块、串口传输模块以及串口波特率选择模块(下面只给出顶层模块和PS/2传输处理模块的verilog代码,串口部分的可以参考上一篇日志http://blog.ednchina.com/ilove314/153551/Message.aspx)。


 


module ps2_key(clk,rst_n,ps2k_clk,ps2k_data,rs232_tx);


 


input clk; //50M时钟信号


input rst_n;  //复位信号


input ps2k_clk;   //PS2接口时钟信号


input ps2k_data;  //PS2接口数据信号


output rs232_tx;  // RS232发送数据信号


 


 


wire[7:0] ps2_byte;  // 1byte键值


wire ps2_state;      //按键状态标志位


 


wire bps_start;   //接收到数据后,波特率时钟启动信号置位


wire clk_bps; // clk_bps的高电平为接收或者发送数据位的中间采样点


 


ps2scan           ps2scan(   .clk(clk),            //按键扫描模块


                            .rst_n(rst_n),             


                            .ps2k_clk(ps2k_clk),


                            .ps2k_data(ps2k_data),


                             .ps2_byte(ps2_byte),


                            .ps2_state(ps2_state)


                            );


 


speed_select  speed_select( .clk(clk),


                                   .rst_n(rst_n),


                                   .bps_start(bps_start),


                                   .clk_bps(clk_bps)


                                   );


 


my_uart_tx    my_uart_tx(       .clk(clk),


                                   .rst_n(rst_n),


                                   .clk_bps(clk_bps),


                                   .rx_data(ps2_byte),


                                   .rx_int(ps2_state),


                                   .rs232_tx(rs232_tx),


                                   .bps_start(bps_start)


                                   );


 


Endmodule


 


 


module ps2scan(clk,rst_n,ps2k_clk,ps2k_data,ps2_byte,ps2_state);


 


input clk; //50M时钟信号


input rst_n;  //复位信号


input ps2k_clk;   //PS2接口时钟信号


input ps2k_data;  //PS2接口数据信号


output[7:0] ps2_byte;    // 1byte键值,只做简单的按键扫描


output ps2_state;    //键盘当前状态,ps2_state=1表示有键被按下


 


//------------------------------------------


reg ps2k_clk_r0,ps2k_clk_r1,ps2k_clk_r2;  //ps2k_clk状态寄存器


 


//wire pos_ps2k_clk;     // ps2k_clk上升沿标志位


wire neg_ps2k_clk;   // ps2k_clk下降沿标志位


 


always @ (posedge clk or negedge rst_n) begin


    if(!rst_n) begin


           ps2k_clk_r0 <= 1'b0;


           ps2k_clk_r1 <= 1'b0;


           ps2k_clk_r2 <= 1'b0;


       end


    else begin                         //锁存状态,进行滤波


           ps2k_clk_r0 <= ps2k_clk;


           ps2k_clk_r1 <= ps2k_clk_r0;


           ps2k_clk_r2 <= ps2k_clk_r1;


       end


end


 


assign neg_ps2k_clk = ~ps2k_clk_r1 & ps2k_clk_r2;    //下降沿


 


//------------------------------------------


reg[7:0] ps2_byte_r;     //PC接收来自PS2的一个字节数据存储器


reg[7:0] temp_data;  //当前接收数据寄存器


reg[3:0] num; //计数寄存器


 


always @ (posedge clk or negedge rst_n) begin


    if(!rst_n) begin


           num <= 4'd0;


           temp_data <= 8'd0;


       end


    else if(neg_ps2k_clk) begin //检测到ps2k_clk的下降沿


           case (num)


              4'd0:  num <= num+1'b1;


              4'd1:  begin


                         num <= num+1'b1;


                         temp_data[0] <= ps2k_data;  //bit0


                     end


              4'd2:  begin


                         num <= num+1'b1;


                         temp_data[1] <= ps2k_data;  //bit1


                     end


              4'd3:  begin


                         num <= num+1'b1;


                         temp_data[2] <= ps2k_data;  //bit2


                     end


              4'd4:  begin


                         num <= num+1'b1;


                         temp_data[3] <= ps2k_data;  //bit3


                     end


              4'd5:  begin


                         num <= num+1'b1;


                         temp_data[4] <= ps2k_data;  //bit4


                     end


              4'd6:  begin


                         num <= num+1'b1;


                         temp_data[5] <= ps2k_data;  //bit5


                     end


              4'd7:  begin


                         num <= num+1'b1;


                         temp_data[6] <= ps2k_data;  //bit6


                     end


              4'd8:  begin


                         num <= num+1'b1;


                         temp_data[7] <= ps2k_data;  //bit7


                     end


              4'd9:  begin


                         num <= num+1'b1;  //奇偶校验位,不做处理


                     end


              4'd10: begin


                         num <= 4'd0;  // num清零


                     end


              default: ;


              endcase


       end


end


 


reg key_f0;       //松键标志位,置1表示接收到数据8'hf0,再接收到下一个数据后清零


reg ps2_state_r;  //键盘当前状态,ps2_state_r=1表示有键被按下


 


always @ (posedge clk or negedge rst_n) begin //接收数据的相应处理,这里只对1byte的键值进行处理


    if(!rst_n) begin


           key_f0 <= 1'b0;


           ps2_state_r <= 1'b0;


       end


    else if(num==4'd10) begin   //刚传送完一个字节数据


           if(temp_data == 8'hf0) key_f0 <= 1'b1;


           else begin


                  if(!key_f0) begin //说明有键按下


                         ps2_state_r <= 1'b1;


                         ps2_byte_r <= temp_data; //锁存当前键值


                     end


                  else begin


                         ps2_state_r <= 1'b0;


                         key_f0 <= 1'b0;


                     end


              end


       end


end


 


reg[7:0] ps2_asci;   //接收数据的相应ASCII


 


always @ (ps2_byte_r) begin


    case (ps2_byte_r)    //键值转换为ASCII码,这里做的比较简单,只处理字母


       8'h15: ps2_asci <= 8'h51;   //Q


       8'h1d: ps2_asci <= 8'h57;   //W


       8'h24: ps2_asci <= 8'h45;   //E


       8'h2d: ps2_asci <= 8'h52;   //R


       8'h2c: ps2_asci <= 8'h54;   //T


       8'h35: ps2_asci <= 8'h59;   //Y


       8'h3c: ps2_asci <= 8'h55;   //U


       8'h43: ps2_asci <= 8'h49;   //I


       8'h44: ps2_asci <= 8'h4f;   //O


       8'h4d: ps2_asci <= 8'h50;   //P              


       8'h1c: ps2_asci <= 8'h41;   //A


       8'h1b: ps2_asci <= 8'h53;   //S


       8'h23: ps2_asci <= 8'h44;   //D


       8'h2b: ps2_asci <= 8'h46;   //F


       8'h34: ps2_asci <= 8'h47;   //G


       8'h33: ps2_asci <= 8'h48;   //H


       8'h3b: ps2_asci <= 8'h4a;   //J


       8'h42: ps2_asci <= 8'h4b;   //K


       8'h4b: ps2_asci <= 8'h4c;   //L


       8'h1z: ps2_asci <= 8'h5a;   //Z


       8'h22: ps2_asci <= 8'h58;   //X


       8'h21: ps2_asci <= 8'h43;   //C


       8'h2a: ps2_asci <= 8'h56;   //V


       8'h32: ps2_asci <= 8'h42;   //B


       8'h31: ps2_asci <= 8'h4e;   //N


       8'h3a: ps2_asci <= 8'h4d;   //M


       default: ;


       endcase


end


 


assign ps2_byte = ps2_asci;


assign ps2_state = ps2_state_r;


 


endmodule


 




系统分类: CPLD/FPGA   |   用户分类: FPGA设计感悟   |   来源: 原创   |   【推荐给朋友】   |   【添加到收藏夹】





4





阅读(2663)  |  评论(9)  |  收藏(2)  |  举报  



vote.gif投一票您将和博主都有获奖机会!




最新评论





  • ico.gif ilove314


    2008/11/17 22:40:00


    clk是主时钟,50M,所以做分频时钟ps2k_clk,确切的说不能叫分频,只是做一个计数器使能信号来触发得到需要的ps2k_clk,具体你可以参考我的代码



  • ico.gif rwp3053


    2008/11/16 21:57:57


    请问你的两个时钟clk和ps2k_clk时序是怎么配合的



  • ico.gif ilove314


    2008/11/15 22:24:44


    对,仅仅为了在ps2k_clk的下降沿得到一个时钟周期的高脉冲,因为我的代码中主要是要这一个时钟周期的高脉冲只是时序逻辑做处理



  • ico.gif feitianfeifei


    2008/11/15 21:10:42


    呵呵



  • ico.gif feitianfeifei


    2008/11/15 21:08:36


    我是verilog 的初学者。对于你的这个程序,大部分我是理解的,只不过对于其中的一段有点不太明白,assign neg_ps2k_clk = ~ps2k_clk_r1 & ps2k_clk_r2; //下降这个代码段的具体用意是什么,同时最后的assign 的语句的深层含义是什么?难道就是为了获取一个下降沿新号吗?



  • ico.gif feitianfeifei


    2008/11/15 21:08:11


    我是verilog 的初学者。对于你的这个程序,大部分我是理解的,只不过对于其中的一段有点不太明白,assign neg_ps2k_clk = ~ps2k_clk_r1 & ps2k_clk_r2; //下降这个代码段的具体用意是什么,同时最后的assign 的语句的深层含义是什么?难道就是为了获取一个下降沿新号吗?



  • ico.gif feitianfeifei


    2008/11/15 21:06:02


    我是verilog 的初学者。对于你的这个程序,大部分我是理解的,只不过对于其中的一段有点不太明白,always @ (posedge clk or negedge rst_n) begin if(!rst_n) begin ps2k_clk_r0 <= 1'b0; ps2k_clk_r1 <= 1'b0; ps2k_clk_r2 <= 1'b0; end else begin //锁存状态,进行滤波 ps2k_clk_r0 <= ps2k_clk; ps2k_clk_r1 <= ps2k_clk_r0; ps2k_clk_r2 <= ps2k_clk_r1; end end assign neg_ps2k_clk = ~ps2k_clk_r1 & ps2k_clk_r2; //下降这个代码段的具体用意是什么,同时最后的assign 的语句的深层含义是什么?难道就是为了获取一个下降沿新号吗?



  • ico.gif ilove314


    2008/10/15 21:28:50


    呵呵



  • ico.gif love01001


    2008/10/15 13:14:32


    thanks very much.i understand it in one hours.i have spend more than ten hours to read a huge example but still got nothing before.thanks for the short example.

文章评论0条评论)

登录后参与讨论
我要评论
0
8
关闭 站长推荐上一条 /2 下一条