//方法一://FPGA verilog PS2 鼠標控制LED //********************************************************************************************* /************************* PS2接收模塊(電腦接收,鼠標發(fā)送)*********************************/ /* PS2_CLK信號由PS2設備產(chǎn)生,即鼠標在產(chǎn)生時鐘的同時向主機送出數(shù)據(jù),主機在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 //************************************************************************************************************* /********************************** 發(fā)送模塊(電腦發(fā)送,鼠標接受)***********************************************/ /**************************************************************************************************** **由于PS/2設備提供串行同步時鐘,因此,如果主機發(fā)送數(shù)據(jù),則主機要先把時鐘線和數(shù)據(jù)線置為 請求發(fā)送的狀態(tài)。主機通過下拉時鐘線大于100us來抑制通訊,并且通過下拉數(shù)據(jù)線發(fā)出請求發(fā)送數(shù)據(jù) 的信號,然后釋放時鐘,PS/2設備檢測到需要接收的數(shù)據(jù)時,它會產(chǎn)生時鐘信號并記錄8個數(shù)據(jù)位 和一個停止位。主機在時鐘線變?yōu)榈蜁r準備數(shù)據(jù)到數(shù)據(jù)線。 ******************************************************************************************************/ `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); // (歸約異或^)如果操作數(shù)(d_sen)中有偶數(shù)個1,那么^d_sen結(jié)果為0;否則結(jié)果為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控制器必須進入主機發(fā)送請的狀態(tài)。這可以通過以下動作實現(xiàn):********************************/ //1·PS2_CLK線首先被拉低至少在一個時鐘周期(進入禁止傳輸Inhibit Transmission狀態(tài)) //2·PS2_DATA線隨后被拉低(提供的起始位幀傳送) //3·PS2CLK線隨后被釋放(仍然保持PS2DATA低)。 //4·PS/2設備定期檢查數(shù)據(jù)和時鐘線是否為這種狀態(tài),當檢測到,鼠標開始產(chǎn)生PS2_CLK信號,以便主機發(fā)送數(shù)據(jù)。*/ /****************************************************************************************************************/ 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 請求發(fā)送 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在具體實現(xiàn)上一般用三態(tài)門來實現(xiàn)。三態(tài)門的第三個狀態(tài)就是高阻'Z'。 當inout端口不輸出時,將三態(tài)門置高阻。*/ 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模式中,一旦鼠標檢測到位移或發(fā)現(xiàn)一個或多個鼠標鍵的狀態(tài)改變了,就發(fā)送位移數(shù)據(jù)包。數(shù)據(jù)報 告的最大速率被認為是采樣速率。參數(shù)的范圍從10采樣點/秒到200采樣點/秒。這個參數(shù)的缺省值是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)回應命令,然后使能數(shù)據(jù)報告并復位它的位移計數(shù)器。************/ 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; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //************************************************************************************************************* /*標準的鼠標有兩個計數(shù)器保持位移的跟蹤:X位移計數(shù)器和Y位移計數(shù)器。 **可存放9位的2進制補碼,并且每個計數(shù)器都有相關的溢出標志。 **它們的內(nèi)容連同三個鼠標按鈕的狀態(tài)一起以三字節(jié)移動數(shù)據(jù)包的形式發(fā)送給主機。 **位移計數(shù)器表示從最后一次位移數(shù)據(jù)包被送往主機后有位移量發(fā)生。 **位移計數(shù)器是一個9位2的補碼整數(shù)。它的最高位作為符號位出現(xiàn)在位移數(shù)據(jù)包的第一個字節(jié)里。 ********************************************************************************************************** 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接收模塊(鼠標發(fā)送,電腦接收) `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連續(xù)采樣超過8次,采樣值都為1, **則此時PS_CLK處于穩(wěn)定高電平,反之處于穩(wěn)定的低電平,若由高到低變化,說明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]; // 接收到的數(shù)據(jù) endmodule //******************************************************************************* //發(fā)送模塊(電腦發(fā)送,鼠標接受) `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 計算所發(fā)送數(shù)據(jù)的校驗位 assign odd_par = ~(^d_send); // (歸約異或^)如果操作數(shù)中有偶數(shù)個1,那么結(jié)果為0;否則結(jié)果為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; //請求發(fā)送 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 |
|