黑金社区 FPGA 那些事儿--Verilog 建模设计 学习 试验八 PS2 按键
PS2 按键
主要用2根数据线 1脚 DATA 2脚 CLK
每次传递11位数据,第0位位开始位 第1-8位 数据位,第9位 奇偶校验位 第10位 结束位
在CLK的下降沿更新
输出具体的值有一个 传说中的 第二套编码表格 见书P61
每次按下时为送出 通码(Make) 释放时 送出断码(Break) ,先输出0XF0 ,再输出代表当前按键的8位数据
整个检测按键模块 需要 一个检测低电平模块,一个解码模块
对于下降沿 检测,参照试验一,通过一个BOOL 运算 ,在每次测得PS2_clk 下降沿时 输出一个高电平,供给后面模块做判断用
/*************** Internal Registers *****************/ reg H2L_F1 ; reg H2L_F2 ; /****************** Internal Wire *******************/ /**************** Code Starts Here ******************/ always @ ( posedge clk or negedge rst_n ) if ( !rst_n ) begin H2L_F1 <= 1'b1 ; H2L_F2 <= 1'b1 ; end else begin H2L_F1 <= PS2_clk_pin ; H2L_F2 <= H2L_F1 ; end /*****************************************************/ assign H2L_sig = H2L_F2 & ( !H2L_F1 ) ; /*****************************************************/
解码模块:对于这种几个CLK的 这个是不是就是状态机了?用CASE语句,PS2_CLK 每一个CLK 都给一次状态,即每次H2L_sig输出置高电平时 给一次状态
在每一个状态中判断 是否有H2L_sig 即 PS2_CLK 是否有下降沿,有就更新下数据。若数据为0XF0 ,即按键释放了,不在更新数据。
在所有流程走完了,给出一个Done 信号,标志按键识别结束,可以用作给后面的模块做动作标志。
要养成给标记的习惯!
按键的数据是先地位到高位,DATA 赋值时也先低位到高位。千万注意 CASE语句里面 判断的BIT 也是数据 ,他用在了数组里面进行+ - 运算
/**************** Define Parameter *****************/ parameter M_START = 5'd0 ; // M_START 指PS2键盘 按下时产生的通码(Make) 第一位开始信号 parameter M_BIT1 = 5'd1 ; parameter M_BIT2 = 5'd2 ; parameter M_BIT3 = 5'd3 ; parameter M_BIT4 = 5'd4 ; parameter M_BIT5 = 5'd5 ; parameter M_BIT6 = 5'd6 ; parameter M_BIT7 = 5'd7 ; parameter M_BIT8 = 5'd8 ; parameter M_OECHECK = 5'd9 ; parameter M_END = 5'd10 ; parameter TYPE = 5'd11 ; // 判断数据是否为0XF0,若为0XF0 则为断码 释放按键 反之为通码 按下按键 parameter B_START = 5'd12 ; // B_START 指PS2键盘 释放时产生的断码(Break) 第一位开始信号 parameter B_BIT1 = 5'd13 ; parameter B_BIT2 = 5'd14 ; parameter B_BIT3 = 5'd15 ; parameter B_BIT4 = 5'd16 ; parameter B_BIT5 = 5'd17 ; parameter B_BIT6 = 5'd18 ; parameter B_BIT7 = 5'd19 ; parameter B_BIT8 = 5'd20 ; parameter B_OECHECK = 5'd21 ; parameter B_END = 5'd22 ; parameter DONE_H = 5'd23 ; parameter DONE_L = 5'd24 ; /*************** Internal Registers *****************/ reg [4:0] pin_bit ; reg [7:0] r_pin_data ; reg r_pin_done ; /****************** Internal Wire ******************/ /**************** Code Starts Here *****************/ always @ ( posedge clk or negedge rst_n ) if(!rst_n) begin r_pin_data <= 8'd0 ; r_pin_done <= 1'b0 ; pin_bit <= M_START ; end else case (pin_bit) M_START : pin_bit <= pin_bit + 1'b1 ; M_BIT1 , M_BIT2 , M_BIT3 , M_BIT4 , M_BIT5 , M_BIT6 , M_BIT7 , M_BIT8 : begin if(H2L_sig) begin r_pin_data[ pin_bit - 1 ] <= PS2_data_pin ; pin_bit <= pin_bit + 1'b1 ; end end M_OECHECK , M_END : pin_bit <= pin_bit + 1'b1 ; TYPE : if ( r_pin_data == 8'hF0 ) pin_bit <= DONE_H ; else pin_bit <= pin_bit + 1'b1 ; B_START ,B_BIT1 , M_BIT2 , M_BIT3 , M_BIT4 , M_BIT5 , M_BIT6 , M_BIT7 , M_BIT8 , B_OECHECK , B_END : if(H2L_sig) pin_bit <= pin_bit + 1'b1 ; DONE_H : begin r_pin_done <= 1'b1 ; pin_bit <= pin_bit + 1'b1 ; end DONE_L : begin r_pin_done <= 1'b0 ; pin_bit <= pin_bit + 1'b1 ; end endcase /*****************************************************/ assign PS2_data = r_pin_data ; assign PS2_done = r_pin_done ;
控制PS2:上面解码模块反馈出来的按键数据 去查编码表,就能得知对应的按键是多少了,然后在CASE 语句中该干嘛干嘛
1 2 /*************** Internal Registers *****************/ 3 wire [7:0] key_data = PS2_data ; 4 reg [3:0] r_data_out ; 5 6 /****************** Internal Wire ******************/ 7 wire key_en = PS2_done ; 8 9 /**************** Code Starts Here *****************/ 10 always @ ( posedge clk or negedge rst_n ) 11 if( !rst_n ) 12 begin 13 14 r_data_out <= 4'b1001 ; 15 //key_data <= 8'h00 ; wire 变量 always里面不能赋值 16 end 17 else if ( key_en ) 18 begin 19 case ( key_data ) 20 _W : 21 r_data_out <= { r_data_out[2:0] ,r_data_out[3] } ; 22 23 _X : 24 r_data_out <= { r_data_out[0] , r_data_out[3:1] } ; 25 26 _L_CTRL : 27 r_data_out <= { r_data_out[0] , r_data_out[1] , r_data_out[2] , r_data_out[3] } ; 28 29 endcase 30 31 end 32 33 /*****************************************************/ 34 assign data_out = r_data_out ; 35 36 /*****************************************************/
注意:wire 声明的变量 不能在always 语句里面赋值 。
reg 声明的变量才可以用在always 语句里面。
而且always语句完了之后,可以用 assign 语句 把里面的reg变量赋给外面的Wire类型变量。
顶层模块引用子模块时,中间流通的变量要声明为wire 类型,不能声明为reg类型。
子模块输出的数据千万要注意标明位数。
张小朋友要加油! 要抓紧啊~