//方法一://FPGA verilog PS2 鼠标控制LED //********************************************************************************************* /************************* PS2接收模块(电脑接收,鼠标发送)*********************************/ /* PS2_CLK信号由PS2设备产生,即鼠标在产生时钟的同时向主机送出数据,主机在PS2_CLK信号的下降沿读取(锁存)每个位。*/ `timescale 1ns / 1ps /////////////////////////////////// module ps2_rx ( input wire CLOCK, RESET, input wire PS2_DAT_in, //*ps2 DATA input wire PS2_CLK_in, //*ps2 CLOCK input wire rx_en, output reg rx_done_sig, output wire [7:0] d_rec ); /******************************PS2_CLK下降沿检测*****************************/ reg H2L_F1; reg H2L_F2; always @ ( posedge CLOCK or negedge RESET ) if( !RESET ) begin H2L_F1 <= 1'b1; H2L_F2 <= 1'b1; end else begin H2L_F1 <= PS2_CLK_in; H2L_F2 <= H2L_F1; end /****************************/ assign fall_edge = H2L_F2 & !H2L_F1; //************************************************************************** reg [3:0]i,j; reg [8:0]rData; always @ ( posedge CLOCK or negedge RESET ) if( !RESET ) begin i <= 4'd0; j <= 4'd0; rData <= 8'd0; rx_done_sig <= 1'b0; end else case( i ) 0: if (fall_edge & rx_en ) i<=i+1'b1; //0; bit-start 1: if(j==9) begin i<=i+1'b1;j<=0; end else begin if (fall_edge) begin rData[j]<=PS2_DAT_in ;j<=j+1'b1; end//1-8; bit-data and 9:bit-parity end 2: if (fall_edge) i<=i+1'b1; //10; bit-stop 3: begin rx_done_sig <= 1'b1; i <= i + 1'b1; end 4: begin rx_done_sig <= 1'b0; i <= 4'd0; end endcase assign d_rec = rData[7:0]; endmodule //************************************************************************************************************* /********************************** 发送模块(电脑发送,鼠标接受)***********************************************/ /**************************************************************************************************** **由于PS/2设备提供串行同步时钟,因此,如果主机发送数据,则主机要先把时钟线和数据线置为 请求发送的状态。主机通过下拉时钟线大于100us来抑制通讯,并且通过下拉数据线发出请求发送数据 的信号,然后释放时钟,PS/2设备检测到需要接收的数据时,它会产生时钟信号并记录8个数据位 和一个停止位。主机在时钟线变为低时准备数据到数据线。 ******************************************************************************************************/ `timescale 1ns / 1ps ///////////////////////////////////// module ps2_tx ( input wire CLOCK, RESET, input wire send_en, input wire [7:0] d_sen, inout wire PS2_DAT, PS2_CLK, output reg tx_done_sig ); //************************** reg H2L_F1; reg H2L_F2; always @ ( posedge CLOCK or negedge RESET ) if( !RESET ) begin H2L_F1 <= 1'b1; H2L_F2 <= 1'b1; end else begin H2L_F1 <= PS2_CLK; H2L_F2 <= H2L_F1; end /****************************/ assign fall_edge = H2L_F2 & !H2L_F1; //================================================= // state declaration parameter [2:0] idle = 3'b000, rts = 3'b001, start = 3'b010, send_data = 3'b011, stop = 3'b100; reg [2:0] state_reg; reg [3:0] i; reg [8:0] sdata; reg [12:0]count; reg PS2_CLK_out, PS2_DAT_out; reg tri_c, tri_d; // odd parity bit 奇校验位 wire odd_par; assign odd_par = ~(^d_sen); // (归约异或^)如果操作数(d_sen)中有偶数个1,那么^d_sen结果为0;否则结果为1 ,再取反即为奇校验位应设置的值 /////////////////////////////////////// always @(posedge CLOCK) if (!RESET) begin state_reg <= idle; count <= 0; sdata <= 0; i <= 0; PS2_CLK_out <= 1'b1; PS2_DAT_out <= 1'b1; tx_done_sig <= 1'b0; tri_c <= 1'b0; tri_d <= 1'b0; end else begin tx_done_sig<= 1'b0; tri_c <= 1'b0; tri_d <= 1'b0; PS2_CLK_out <= 1'b1; PS2_DAT_out <= 1'b1; /****************************************************************************************************************/ /********************* PS2控制器必须进入主机发送请的状态。这可以通过以下动作实现:********************************/ //1·PS2_CLK线首先被拉低至少在一个时钟周期(进入禁止传输Inhibit Transmission状态) //2·PS2_DATA线随后被拉低(提供的起始位帧传送) //3·PS2CLK线随后被释放(仍然保持PS2DATA低)。 //4·PS/2设备定期检查数据和时钟线是否为这种状态,当检测到,鼠标开始产生PS2_CLK信号,以便主机发送数据。*/ /****************************************************************************************************************/ case (state_reg) idle: begin if (send_en) begin sdata <= {odd_par, d_sen}; count <= 13'd4000; // to delay 200us state_reg <= rts; end end rts: // request to send 请求发送 if (count==0)state_reg <= start; else begin PS2_CLK_out <= 1'b0; count <= count-1'b1;tri_c <= 1'b1; end start: begin PS2_DAT_out <= 1'b0; tri_d <= 1'b1; if (fall_edge) begin i <= 4'h0; state_reg <= send_data; end end send_data: // 8bits data + 1bit parity if(i==9) state_reg <= stop; else begin PS2_DAT_out<=sdata[i]; tri_d <= 1'b1; if(fall_edge) begin i<=i+1'b1; end else state_reg <=state_reg; end stop: if (fall_edge) begin state_reg <= idle; tx_done_sig<= 1'b1; end endcase end // tri-state buffers /* inout在具体实现上一般用三态门来实现。三态门的第三个状态就是高阻'Z'。 当inout端口不输出时,将三态门置高阻。*/ assign PS2_CLK = (tri_c) ? PS2_CLK_out : 1'bz; assign PS2_DAT = (tri_d) ? PS2_DAT_out : 1'bz; endmodule /*******************************************************************************************************/ ////////////////////////////////////////////////////////////////////////////////////////////////////// /******************************************双向通信模块********************************************/ `timescale 1ns / 1ps //////////////////////////////////// module ps2_rxtx ( input wire CLOCK, RESET, input wire send_en, input wire rxen, inout wire PS2_DAT, PS2_CLK,//@@@@@ input wire [7:0] d_sen, output wire rx_done_sig, tx_done_sig, output wire [7:0] d_rec ); // instantiate ps2 receiver ps2_rx ps2_rx_unit(.CLOCK(CLOCK), .RESET(RESET), .rx_en(rxen), .PS2_DAT_in(PS2_DAT), .PS2_CLK_in(PS2_CLK),.rx_done_sig(rx_done_sig), .d_rec(d_rec)); // 实例化 ps2 transmitter ps2_tx ps2_tx_unit(.CLOCK(CLOCK), .RESET(RESET), .send_en(send_en), .d_sen(d_sen), .PS2_DAT(PS2_DAT), .PS2_CLK(PS2_CLK),.tx_done_sig(tx_done_sig)); endmodule //************************************************************************************************************* /*在Stream模式中,一旦鼠标检测到位移或发现一个或多个鼠标键的状态改变了,就发送位移数据包。数据报 告的最大速率被认为是采样速率。参数的范围从10采样点/秒到200采样点/秒。这个参数的缺省值是100采样点 /秒,主机可以用“设置采样速率”(OxF3)命令来改变它。Stream模式是操作的缺省模式。*/ /*******************************鼠标接口电路模块************************************************/ `timescale 1ns / 1ps //////////////////////////////// module mouse ( input wire CLOCK, RESET, inout wire PS2_DAT, PS2_CLK, //@@@ output wire [8:0] x_pos, y_pos, output wire [2:0] button, output reg done_sig, output wire rxen, output wire send_en_sig ); //////////////////////////////////////////////////////////////////////////////////////////////////// parameter STRM=8'hf4; // stream command F4 /******** OxF4(Enable Data Reporting) ,鼠标用“应答”(OxFA)回应命令,然后使能数据报告并复位它的位移计数器。************/ parameter [2:0] init1 = 3'b000, init2 = 3'b001, init3 = 3'b010, pack1 = 3'b011, pack2 = 3'b100, pack3 = 3'b101, done = 3'b110; reg [2:0] state_reg, state_next; wire[7:0] rx_data; reg send_en; reg rx_en; wire rx_done_sig, tx_done_sig; reg [8:0] x_reg, y_reg; reg [2:0] butn_reg; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //************************************************************************************************************* /*标准的鼠标有两个计数器保持位移的跟踪:X位移计数器和Y位移计数器。 **可存放9位的2进制补码,并且每个计数器都有相关的溢出标志。 **它们的内容连同三个鼠标按钮的状态一起以三字节移动数据包的形式发送给主机。 **位移计数器表示从最后一次位移数据包被送往主机后有位移量发生。 **位移计数器是一个9位2的补码整数。它的最高位作为符号位出现在位移数据包的第一个字节里。 ********************************************************************************************************** bit * 7 6 5 4 3 2 1 0 * ********************************************************************************************************** Byte1 * Y_overflow X_overflow Y_sign_bit X_sign_bit 1 Butn_Middle Butn_right Butn_left * ********************************************************************************************************** Byte2 * X_movement * ********************************************************************************************************** Byte3 * Y_movement * **********************************************************************************************************/ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ps2_rxtx ps2_unit(.CLOCK(CLOCK), .RESET(RESET), .send_en(send_en_sig),.rxen(rxen), .d_sen(STRM), .d_rec(rx_data), .PS2_DAT(PS2_DAT), .PS2_CLK(PS2_CLK), .rx_done_sig(rx_done_sig),.tx_done_sig(tx_done_sig)); //////////////////////////////// always @(posedge CLOCK) if (!RESET) begin state_reg <= init1; x_reg <= 0; y_reg <= 0; butn_reg <= 0; rx_en<=1'b0; send_en <= 1'b0; done_sig <= 1'b0; end else begin send_en <= 1'b0; done_sig <= 1'b0; case (state_reg) init1: begin send_en <= 1'b1; state_reg <= init2; end init2: // wait for send to complete if (tx_done_sig) begin state_reg <= init3; rx_en<=1'b1; end init3: // wait for acknowledge packet if (rx_done_sig) state_reg <= pack1; pack1: // wait for 1st data packet if (rx_done_sig) begin state_reg <= pack2; y_reg[8] <= rx_data[5]; ///坐标值的符号 x_reg[8] <= rx_data[4]; butn_reg <= rx_data[2:0]; //中 右 左 end pack2: // wait for 2nd data packet if (rx_done_sig) begin state_reg <= pack3; x_reg[7:0] <= rx_data; end pack3: // wait for 3rd data packet if (rx_done_sig) begin state_reg <= done; y_reg[7:0] <= rx_data; end done: begin done_sig <= 1'b1; state_reg <= pack1; end endcase end ///////////////////////////////////////// // output assign x_pos = x_reg; assign y_pos = y_reg; assign button = butn_reg; assign send_en_sig = send_en; assign rxen = rx_en; endmodule //***************************************************************************************************************** /**************************************鼠标控制LED模块******************************************/ `timescale 1ns / 1ps /////////////////////////////////// module mouse_LED ( input wire CLOCK, RESET, inout wire PS2_DAT, PS2_CLK, //@@@@ output reg [5:0] LED ); // signal declaration reg [9:0] p_reg; wire [2:0] button; wire [8:0] x; wire done_sig; //////////////////////////////////////////////////////////////////////////////////////////// mouse mouse_unit(.CLOCK(CLOCK), .RESET(RESET), .PS2_DAT(PS2_DAT), .PS2_CLK(PS2_CLK), .x_pos(x), .y_pos(), .button(button),.done_sig(done_sig)); //////////////////////////////////////////////////////////////////////////////////////////// always @(posedge CLOCK) if (!RESET) p_reg <= 0; else begin if(done_sig) begin if(button)p_reg<= {7'b0,button}; else p_reg <= {1'b0,x} ; end case (p_reg[2:0]) 3'b000: LED = 6'b001100; 3'b001: LED = 6'b000011; 3'b010: LED = 6'b110000; 3'b011: LED = 6'b111111; 3'b100: LED = 6'b001111; 3'b101: LED = 6'b111000; 3'b110: LED = 6'b000111; default: LED = 6'b010101; endcase end endmodule /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /***************************************************************************************************************************/ //方法二://FPGA verilog PS2 鼠标控制LED //********************************************************************************************* //PS2接收模块(鼠标发送,电脑接收) `timescale 1ns / 1ps /////////////////////////////////// module ps2_rx ( input wire clk, reset, input wire ps2d, //ps2 data input wire ps2c, //ps2 clk input wire rx_en, output reg rx_done_sig, output wire [7:0] d_rec ); // symbolic state declaration reg [7:0] filter_reg; wire [7:0] filter_next; reg ps2count_reg; wire ps2count_next; wire fall_edge; //================================================= // filter and falling-edge check for ps2c //================================================= always @(posedge clk) if (!reset) begin filter_reg <= 0; ps2count_reg <= 0; end else begin filter_reg <= filter_next; ps2count_reg <= ps2count_next; end assign filter_next = {ps2c, filter_reg[7:1]}; assign ps2count_next = (filter_reg==8'b11111111) ? 1'b1 :(filter_reg==8'b00000000) ? 1'b0 :ps2count_reg; assign fall_edge = ps2count_reg & ~ps2count_next; /**************************************************************************************/ /*相当于一个8位的移位寄存器,将PS_CLK的输入移入其中,即对PS_CLK连续采样超过8次,采样值都为1, **则此时PS_CLK处于稳定高电平,反之处于稳定的低电平,若由高到低变化,说明PS2时钟下降沿到来. /**************************************************************************************/ localparam [1:0] idle = 2'b00, receive = 2'b01, done = 2'b10; reg [1:0] state_reg, state_next; reg [3:0] num_reg, num_next; reg [10:0] data_reg, data_next; /////////////////////////////////////////// always @(posedge clk) if (!reset) begin state_reg <= idle; num_reg <= 0; data_reg <= 0; end else begin state_reg <= state_next; num_reg <= num_next; data_reg <= data_next; end // FSMD next-state logic always @* begin state_next = state_reg; num_next = num_reg; data_next = data_reg; rx_done_sig = 1'b0; case (state_reg) idle: if (fall_edge & rx_en) begin // shift in start bit data_next = {ps2d, data_reg[10:1]}; num_next = 4'd9; state_next = receive; end receive: // 8 data + 1 odd_parity + 1 stop if (fall_edge) begin data_next = {ps2d, data_reg[10:1]}; if (num_reg==0) state_next = done; else num_next = num_reg - 1; end done: // 1 extra clock to complete the last shift begin state_next = idle; rx_done_sig = 1'b1; end endcase end // output assign d_rec = data_reg[8:1]; // 接收到的数据 endmodule //******************************************************************************* //发送模块(电脑发送,鼠标接受) `timescale 1ns / 1ps ///////////////////////////////////// module ps2_tx ( input wire clk, reset, input wire send_en,//**** input wire [7:0] d_send, inout wire ps2d, ps2c, output reg rxen_out, tx_done_sig ); // symbolic state declaration localparam [2:0] idle = 3'b000, rts = 3'b001, start = 3'b010, data = 3'b011, stop = 3'b100; // signal declaration reg [2:0] state_reg, state_next; reg [3:0] num_reg, num_next; reg [8:0] data_reg, data_next; reg [12:0] count_reg, count_next; reg ps2c_out, ps2d_out; reg tri_c, tri_d; wire odd_par; //奇校验 // body //================================================= // filter and falling-edge tick generation for ps2c //================================================= /*****************************************************************/ reg H2L_F1; reg H2L_F2; always @ ( posedge clk or negedge reset ) if( !reset ) begin H2L_F1 <= 1'b1; H2L_F2 <= 1'b1; end else begin H2L_F1 <= ps2c; H2L_F2 <= H2L_F1; end /****************************/ assign fall_edge = H2L_F2 & !H2L_F1; //================================================= //************************************************* /**********1. XX_reg ---> XX_next ***************** ***********2. XX_next = ....... ***************** ***********3. XX_next ---> XX_reg ***************** **************************************************/ //================================================= // FSMD state & data registers always @(posedge clk) if (!reset) begin state_reg <= idle; count_reg <= 0; num_reg <= 0; data_reg <= 0; end else begin state_reg <= state_next; count_reg <= count_next; num_reg <= num_next; data_reg <= data_next; end // odd_parity bit 计算所发送数据的校验位 assign odd_par = ~(^d_send); // (归约异或^)如果操作数中有偶数个1,那么结果为0;否则结果为1 // FSMD next-state logic always @* begin state_next = state_reg; count_next = count_reg; num_next = num_reg; data_next = data_reg; ps2c_out = 1'b1; ps2d_out = 1'b1; tri_c = 1'b0; tri_d = 1'b0; rxen_out = 1'b0; tx_done_sig = 1'b0; case (state_reg) idle: begin rxen_out = 1'b1;//@@@@@@@@@@@@@@@@@@@@@@@ if (send_en)//**** begin data_next = {odd_par, d_send}; count_next = 13'h1fff; // 2^13-1 to delay 164us state_next = rts; //请求发送 end end rts: // request to send begin ps2c_out = 1'b0; tri_c = 1'b1; count_next = count_reg - 1; if (count_reg==0) //FPGA拉低PS2C 164us state_next = start; end start: begin ps2d_out = 1'b0; tri_d = 1'b1; if (fall_edge) begin num_next = 4'h8; state_next = data; end end data: // 8 data + 1 odd_parity //************************************* begin ps2d_out = data_reg[0]; tri_d = 1'b1; if (fall_edge) begin data_next = {1'b0, data_reg[8:1]}; if (num_reg == 0) state_next = stop; else num_next = num_reg - 1; end end //************************************** stop: // assume floating high for ps2d if (fall_edge) begin state_next = idle; tx_done_sig = 1'b1; end endcase end // tri-state buffers assign ps2c = (tri_c) ? ps2c_out : 1'bz; assign ps2d = (tri_d) ? ps2d_out : 1'bz; endmodule //****************************************************************************************************** //双向通信模块 `timescale 1ns / 1ps //////////////////////////////////// module ps2_rxtx ( input wire clk, reset, input wire send_en,//**** inout wire ps2d, ps2c, input wire [7:0] d_send, output wire rx_done_sig, tx_done_sig, output wire [7:0] d_rec ); // signal declaration wire rxen_out; // body // instantiate ps2 receiver ps2_rx ps2_rx_unit(.clk(clk), .reset(reset), .rx_en(rxen_out), .ps2d(ps2d), .ps2c(ps2c),.rx_done_sig(rx_done_sig), .d_rec(d_rec)); // instantiate ps2 transmitter ps2_tx ps2_tx_unit(.clk(clk), .reset(reset), .send_en(send_en), .d_send(d_send), .ps2d(ps2d), .ps2c(ps2c),.rxen_out(rxen_out), .tx_done_sig(tx_done_sig)); endmodule //************************************************************************************************************* //**************************************鼠标接口电路模块********************************** `timescale 1ns / 1ps //////////////////////////////// module mouse ( input wire clk, reset, inout wire ps2d, ps2c, output wire [8:0] xm, ym, output wire [2:0] button, output reg done_sig, output wire send_en_out ); // constant declaration localparam STRM=8'hf4; // stream command F4 // symbolic state declaration localparam [2:0] init1 = 3'b000, init2 = 3'b001, init3 = 3'b010, pack1 = 3'b011, pack2 = 3'b100, pack3 = 3'b101, done = 3'b110; // signal declaration reg [2:0] state_reg, state_next; wire [7:0] rx_data; reg send_en;//** wire rx_done_sig, tx_done_sig; reg [8:0] x_reg, y_reg, x_next, y_next; reg [2:0] button_reg, button_next; // body // instantiation ps2_rxtx ps2_unit(.clk(clk), .reset(reset), .send_en(send_en_out), .d_send(STRM), .d_rec(rx_data), .ps2d(ps2d), .ps2c(ps2c), .rx_done_sig(rx_done_sig),.tx_done_sig(tx_done_sig)); // body // FSMD state and data registers always @(posedge clk) if (!reset) begin state_reg <= init1; x_reg <= 0; y_reg <= 0; button_reg <= 0; end else begin state_reg <= state_next; x_reg <= x_next; y_reg <= y_next; button_reg <= button_next; end // FSMD next-state logic always @* begin state_next = state_reg; send_en = 1'b0; //@@@@ done_sig = 1'b0; x_next = x_reg; y_next = y_reg; button_next = button_reg; case (state_reg) init1: begin send_en = 1'b1; //@@@@@ state_next = init2; end init2: // wait for send to complete if (tx_done_sig) state_next = init3; init3: // wait for acknowledge packet if (rx_done_sig) state_next = pack1; pack1: // wait for 1st data packet if (rx_done_sig) begin state_next = pack2; y_next[8] = rx_data[5]; x_next[8] = rx_data[4]; button_next = rx_data[2:0]; end pack2: // wait for 2nd data packet if (rx_done_sig) begin state_next = pack3; x_next[7:0] = rx_data; end pack3: // wait for 3rd data packet if (rx_done_sig) begin state_next = done; y_next[7:0] = rx_data; end done: begin done_sig = 1'b1; state_next = pack1; end endcase end // output assign xm = x_reg; assign ym = y_reg; assign button = button_reg; assign send_en_out=send_en; endmodule //***************************************************************************************************************** //鼠标控制led测试模块 `timescale 1ns / 1ps /////////////////////////////////// module mouse_led ( input wire clk, reset, inout wire ps2d, ps2c, output reg [5:0] led ); // signal declaration reg [9:0] p_reg; wire [9:0] p_next; wire [8:0] xm; wire [2:0] button; wire done_sig; // body // instantiation mouse mouse_unit(.clk(clk), .reset(reset), .ps2d(ps2d), .ps2c(ps2c), .xm(xm), .ym(), .button(button),.done_sig(done_sig)); // counter always @(posedge clk) if (!reset) p_reg <= 0; else p_reg <= p_next; assign p_next = (~done_sig) ? p_reg : // no activity (button[0]) ? 10'b0 : // left button (button[1]) ? 10'h3ff : // right button p_reg + {xm[8], xm}; // x movement always @* case (p_reg[9:7]) 3'b000: led = 6'b001100; 3'b001: led = 6'b111111; 3'b010: led = 6'b100000; 3'b011: led = 6'b010000; 3'b100: led = 6'b001111; 3'b101: led = 6'b110000; 3'b110: led = 6'b000011; default: led = 6'b000001; endcase endmodule |
|