zoukankan      html  css  js  c++  java
  • 【黑金原创教程】【FPGA那些事儿驱动篇I 】实验十一:PS/2模块⑤ — 扩展鼠标

    实验十一:PS/2模块⑤ — 扩展鼠标

    当普通鼠标即三键鼠标再也无法满足需求的时候,扩展鼠标即滚轮鼠标就诞生了,然而实验十一的实验目的就是实现滚轮鼠标的驱动。不过,进入整体之前,先让我们来了解一下鼠标的常用命令。

    clip_image002

    图11.1 命令F3,设置采样频率。

    命令F3也是Set Sample Rate,主要是用来设置采集频率。笔者曾经说过,采集频率就是鼠标采集按键状况还有位置状况的间隔时间,默认下是100次/秒。如图11.1所示,FPGA先发送命令数据8’hF3,事后鼠标会反馈8’hFA以示接收成功,余下FPGA再发送参数数据8’d200,鼠标接收成功后也会反馈 8’hFA。如此一来,鼠标的采集频率从原本的 100次/秒,变成 200次/秒。

    clip_image004

    图11.2 命令E8,设置分辨率。

    命令E8也是 Set Resolution,主要是用来设置分辨率。所谓分辨率就是位置对应寄存器计数的单位,默认下是4计数/mm,亦即 1mm 的距离,鼠标计数4下。如图11.2所示,FPGA先发送命令数据 8’hE8,鼠标接收以后便反馈 8’hFA,FPGA随之也会发送参数数据 8’h01,鼠标接收以后也会反馈数据 8’hFA。完后,鼠标的分辨从原本的 4计数/mm 变成 2计数/mm。

    参数数据所对应的分辨率如表11.1所示:

    表11.1 参数数据所对应的分辨率。

    参数数据

    分辨率

    8’h00

    1计数/mm

    8’h01

    2计数/mm

    8’h02

    4计数/mm

    8’h03

    8计数/mm

    clip_image006

    图11.3 命令F6,使用默认参数。

    假设笔者手痒,不小心打乱鼠标内部的参数数据,此刻笔者可以发送命令F6,即Set Defaults将参数数据回复成原来的缺省值。如图11.3所示,FPGA先发送命令数据8’hF6

    ,鼠标完成接收以后便会反馈8’hFA。

    clip_image008

    图11.4 命令F4使能报告,命令F5关闭报告。

    PS/2鼠标不像PS/2键盘,上电并且完成初始化以后它便会陷入发呆状态,如果不发送命令数据8’hF4(即Enable Data Report)手动开启鼠标的水龙头,鼠标是不会发送报告(即夹杂按键状况与位置状况的数据)。如图11.4所示,FPGA先发送命令数据8’hF4,鼠标接收以后便会反馈8’hFA,事后鼠标立即处于就绪状态,一旦按键状况或者位置状况发生改变,鼠标就会发送报告。

    假设读者觉得鼠标太唠叨,什么大事小事都报告,笔者可以发送命令数据 8’hF5(即 Disable Data Report)为了使其闭嘴。如图11.4所示,FPGA先发送命令数据 8’hF4,鼠标接收完毕以后便会反馈8’hFA,事后鼠标就成为闭嘴状态,大事小事再也不会烦人。如果读者觉得寂寞,读者可以再度发送命令数据 8’hF4,让鼠标再度唱歌。

    clip_image010

    图11.5 命令F2,读取鼠标ID。

    为了区分鼠标是普通鼠标还是扩展鼠标,期间我们必须使用命令8’hF2,即 Get Device ID。如图11.5所示,FPGA发送命令数据 8’hF2,鼠标接收以后先反馈 8’hFA,再来便发送鼠标ID。如果内容是8’h00,则表示该鼠标只是普通鼠标 ... 反之,如果内容是 8’h03,那么该鼠标就是扩展鼠标。因为如此,我们需要更改一下伪函数,结果如代码11.1所示:

    1.             32: // Press low PS2_CLK 100us
    2.            if( C1 == T100US -1 ) begin C1 <= 13'd0; i <= i + 1'b1; end
    3.            else begin isQ1 = 1'b1; rCLK <= 1'b0; C1 <= C1 + 1'b1; end
    4.                          
    5.            33: // release PS2_CLK and set in ,PS2_DAT set out
    6.            begin isQ1 <= 1'b0; rCLK <= 1'b1; isQ2 <= 1'b1; i <= i + 1'b1; end
    7.                          
    8.            34: // start bit 1
    9.            begin rDAT <= 1'b0; i <= i + 1'b1; end
    10.                          
    11.            35,36,37,38,39,40,41,42,43:  // data bit 9
    12.            if( isH2L ) begin rDAT <= T[ i-35 ]; i <= i + 1'b1; end
    13.                          
    14.            44: // stop bit 1
    15.            if( isH2L ) begin rDAT <= 1'b1; i <= i + 1'b1; end
    16.                          
    17.            45: // Ack bit
    18.            if( isH2L ) begin i <= i + 1'b1; end
    19.                          
    20.            46: // PS2_DAT set in
    21.            begin isQ2 <= 1'b0; i <= i + 1'b1; end
    22.                          
    23.            /***********/ // Receive 1st Frame
    24.                         
    25.            47,48,49,50,51,52,53,54,55,56,57: // Ingnore 
    26.            if( isH2L ) i <= i + 1'b1;
    27.                          
    28.             58: // Check comd F2
    29.             if( T[7:0] == 8'hF2 ) i <= i + 1'b1;
    30.             else i <= Go;
    31.                          
    32.             /***********/ // Receive 2nd Frame
    33.                          
    34.             59:  // Start bit 1
    35.             if( isH2L ) i <= i + 1'b1; 
    36.                          
    37.             60,61,62,63,64,65,66,67,68: // Data bit 9
    38.             if( isH2L ) begin T[i-60] <= PS2_DAT; i <= i + 1'b1; end
    39.                          
    40.             69: // Stop bit 1
    41.             if( isH2L ) i <= Go;

    代码11.1

    如代码11.1所示,步骤32~57则是发送一帧数据又忽略一帧反馈,基本上与实验十一模一样。至于第58行则是用来判断,FPGA所发送的命令是否是 8’hF2即 Get Device ID

    ?如果是,步骤则继续读取操作,因为命令8’hF2令鼠标反馈8’hFA之余,还会导致鼠标会发送一帧ID数据。否则的话,即表示其他命令,步骤返回。步骤59~69是用来读取下一帧ID数据,期间步骤60~68用来读取 8位数据位,还有1位校验位。完后,步骤便返回。

    小时候的笔者很爱假扮刺客,笔者与近邻的小孩就总是瞎着玩,其它小朋友则扮演秘密商人。刺客为了与秘密商人进行交易,两者之间必须经过暗语核对,例如:

    “阳光的男孩赤裸裸 ... ”,对方问道。

    “对面的女来看过来 ... ”,笔者答道。

    滚轮鼠标也是扩展鼠标,上电以后也不会立即变成扩展鼠标,如果扩展鼠标不经过核对暗语,扩展鼠标也是一只普通的3键鼠标而已 ... 反之,如果完成暗语核对,扩展鼠标才会发挥滚轮功能。

    clip_image012

    图11.6 设置扩展鼠标的暗语。

    如图11.6所示,那是设置扩展鼠标的暗语:

    发送命令数据 8’hF3,接收反馈8’hFA,再发送参数数据 8’hC8,在接收反馈8’hFA;

    发送命令数据 8’hF3,接收反馈8’hFA,再发送参数数据 8’h64,在接收反馈8’hFA;

    发送命令数据 8’hF3,接收反馈8’hFA,再发送参数数据 8’h50,在接收反馈8’hFA;

    发送命令数据 8’hF2,接收反馈8’hFA,再接收鼠标ID8’h03。

    完后,鼠标便成为扩展鼠标,内部也自动初始化,然后进入默认模式。

    clip_image014

    图11.7 扩展鼠标标示的位置。

    普通鼠标相较扩展鼠标,它多了滚轮功能,即鼠标除了标示左键,中键,右键,X还有Y以外,扩展还会标示Z。如图11.7所示,X与Y可以看成面积,至于Z则可以看成上下。当鼠标向西移动,X呈现正直,反之负值;当鼠标向北移动,Y呈现正直,反之负值;当滚动向下活动,Z呈现正直,反之负值。

    clip_image016

    图11.8 扩展鼠标的报告长度。

    为此,扩展鼠标相较普通鼠标,报告长度则多了一个字节。如图11.8所示,当鼠标察觉变化以后,鼠标便会发送4个字节长度的报告,然而字节之间的位分配如表11.1所示:

    表11.1 Device ID 为 8’h03 的报告内容。

    字节/位

    [7]

    [6]

    [5]

    [4]

    [3]

    [2]

    [1]

    [0]

    字节一

    Y溢出位

    X溢出位

    Y[8]符号位

    X[8]符号位

    保留

    中键

    右键

    左键

    字节二

    X[7:0]

    字节三

    Y[7:0]

    字节四

    保留

    保留

    保留

    保留

    Z[3]符号位

    Z[2]

    Z[1]

    Z[0]

    笔者需要补充一下 ... 由于早期Intel 称王,所以扩展鼠标标准都是Intel说话算话,Device ID 为 8’h03 就是其中一种扩展标准。如表11.1所示,字节一至字节三基本上变化不大,反之字节四则稍微不同。字节四的[2..0]位是 Z[2:0],字节四的[3]是Z[3],也是Z的符号位。换句话说,寄存器Z有4位,内容用补码表示。

    clip_image018

    图11.9 扩展鼠标的位置范围。

    图11.9表示扩展鼠标的位置范围,X与Y与普通鼠标一样,Z比较畸形一点,因为Z向上不是正直而是负值,反之亦然。Z的有效范围是 4’b1001~4’b0111或者 -7~7,也就是说滚轮向下活动,寄存器Z就递增,向上滚动,寄存器Z就递减。

    上述内容理解完毕以后,我们便可以开始建模了:

    clip_image020

    图11.10 实验十一的建模图。

    如图11.10所示,组合模块 ps2_demo 包含的内容与实验十相差不了多少,不过却少了正直化的即时操作。期间,PS/2初始化功能模块的 oEn 有两位,oEn[1] 拉高表示鼠标为扩展鼠标,oEn[0] 拉高表示鼠标为普通鼠标。PS/2读取功能模块的 oData[2:0] 直接驱动LED资源, oData[27:4]则驱动数码管基础模块的 iData。

    ps2_init_funcmod.v

    clip_image022

    图11.11 PS/2初始化功能模块的建模图。

    如图11.11所示,PS/2初始化功能模块有两位oEn,[1]拉高表示鼠标为扩展鼠标,[0]拉高则表示鼠标为普通鼠标。

    1.    module ps2_init_funcmod
    2.    (
    3.         input CLOCK, RESET,
    4.         inout PS2_CLK, 
    5.         inout PS2_DAT,
    6.         output [1:0]oEn
    7.    );  
    8.        parameter T100US = 13'd5000;
    9.        parameter FF_Write = 7'd32;

    以上内容为相关的出入端声明。第8行是100us的常量声明,第9行则是伪函数的入口地址。

    11.         /*******************************/ // sub1
    12.         
    13.        reg F2,F1; 
    14.         
    15.        always @ ( posedge CLOCK or negedge RESET )
    16.             if( !RESET )
    17.                  { F2,F1 } <= 2'b11;
    18.              else 
    19.                  { F2, F1 } <= { F1, PS2_CLK };
    20.    
    21.         /*******************************/ // core
    22.         
    23.         wire isH2L = ( F2 == 1'b1 && F1 == 1'b0 );

    以上内容是用来检测电平变化的周边操作,第23行则是下降沿的即时声明。

    24.         reg [8:0]T;
    25.         reg [6:0]i,Go;
    26.         reg [12:0]C1;
    27.         reg rCLK,rDAT;
    28.         reg isQ1,isQ2,isEx;
    29.         reg [1:0]isEn;
    30.         
    31.         always @ ( posedge CLOCK or negedge RESET )
    32.             if( !RESET )
    33.                  begin
    34.                         T <= 9'd0;
    35.                         C1 <= 13'd0;
    36.                         { i,Go } <= { 7'd0,7'd0 };
    37.                         { rCLK,rDAT } <= 2'b11;
    38.                         { isQ1,isQ2,isEx } <= 3'b000;
    39.                         isEn <= 2'b00;
    40.                    end
    41.               else  

    以上内容是相关的寄存器声明,第33~39行则是这群寄存器的复位操作。其中isEn有两位,isEx为扩展鼠标的立旗。

    42.                    case( i )
    43.                     
    44.                         /***********/ // INIT Mouse 
    45.                          
    46.                          0: // Send F3  1111_0011
    47.                          begin T <= { 1'b1, 8'hF3 }; i <= FF_Write; Go <= i + 1'b1; end
    48.                          
    49.                          1: // Send C8  1100_1000
    50.                          begin T <= { 1'b0, 8'hC8 }; i <= FF_Write; Go <= i + 1'b1; end
    51.                          
    52.                          2: // Send F3 1111_0011
    53.                          begin T <= { 1'b1, 8'hF3 }; i <= FF_Write; Go <= i + 1'b1; end
    54.                          
    55.                          3: // Send 64 0110_1000
    56.                          begin T <= { 1'b0, 8'h64 }; i <= FF_Write; Go <= i + 1'b1; end
    57.                          
    58.                          4: // Send F3 1111_0011
    59.                          begin T <= { 1'b1, 8'hF3 }; i <= FF_Write; Go <= i + 1'b1; end
    60.                          
    61.                          5: // Send 50 0101_0000
    62.                          begin T <= { 1'b1, 8'h50 }; i <= FF_Write; Go <= i + 1'b1; end
    63.                          
    64.                          6: // Send F2  1111_0010
    65.                          begin T <= { 1'b0, 8'hF2 }; i <= FF_Write; Go <= i + 1'b1; end
    66.                          
    67.                          7: // Check Mouse ID 00(normal), 03(extend)
    68.                          if( T[7:0] == 8'h03 ) begin isEx <= 1'b1; i <= i + 1'b1; end
    69.                          else if( T[7:0] == 8'h00 ) begin isEx <= 1'b0; i <= i + 1'b1; end
    70.                        
    71.                          8: // Send F4 1111_0100
    72.                          begin T <= { 1'b0, 8'hF4 }; i <= FF_Write; Go <= i + 1'b1; end
    73.                          
    74.                          9:
    75.                          if( isEx ) isEn[1] <= 1'b1;
    76.                          else if( !isEx ) isEn[0] <= 1'b1;
    77.                          

    以上内容是核心操作。步骤0~9是主操作,步骤0~6则是发送用来开启扩展鼠标的暗语,步骤7用来判断鼠标返回的 Device ID 是否为 8’h03,如果是 isEx 立旗,否则 isEx 消除立旗。步骤8用来使能鼠标。步骤9根据 isEx 的状态再来决定 isEn的结果, 如果isEx为1 isEn[1] 便拉高,否则 isEx 拉高,完后步骤停留。

    78.                          /****************/ // PS2 Write Function
    79.                          
    80.                          32: // Press low PS2_CLK 100us
    81.                          if( C1 == T100US -1 ) begin C1 <= 13'd0; i <= i + 1'b1; end
    82.                          else begin isQ1 = 1'b1; rCLK <= 1'b0; C1 <= C1 + 1'b1; end
    83.                          
    84.                          33: // release PS2_CLK and set in ,PS2_DAT set out
    85.                          begin isQ1 <= 1'b0; rCLK <= 1'b1; isQ2 <= 1'b1; i <= i + 1'b1; end
    86.                          
    87.                          34: // start bit 1
    88.                          begin rDAT <= 1'b0; i <= i + 1'b1; end
    89.                          
    90.                          35,36,37,38,39,40,41,42,43:  // data bit 9
    91.                          if( isH2L ) begin rDAT <= T[ i-35 ]; i <= i + 1'b1; end
    92.                          
    93.                          44: // stop bit 1
    94.                          if( isH2L ) begin rDAT <= 1'b1; i <= i + 1'b1; end
    95.                          
    96.                          45: // Ack bit
    97.                          if( isH2L ) begin i <= i + 1'b1; end
    98.                          
    99.                          46: // PS2_DAT set in
    100.                          begin isQ2 <= 1'b0; i <= i + 1'b1; end
    101.                          
    102.                          /***********/ // Receive 1st Frame
    103.                         
    104.                          47,48,49,50,51,52,53,54,55,56,57: // Ingnore 
    105.                          if( isH2L ) i <= i + 1'b1;
    106.                          
    107.                          58: // Check comd F2
    108.                          if( T[7:0] == 8'hF2 ) i <= i + 1'b1;
    109.                          else i <= Go;
    110.                          

    以上内容是部分核心操作。步骤32~58是部分伪函数,内容则是发送一帧数据,再读取一帧反馈,完后便进入步骤58判断,发送的命令是否为 8’hF2,如果是便继续步骤,否则便返回步骤。

    111.                          /***********/ // Receive 2nd Frame
    112.                          
    113.                          59:  // Start bit 1
    114.                          if( isH2L ) i <= i + 1'b1; 
    115.                          
    116.                          60,61,62,63,64,65,66,67,68: // Data bit 9
    117.                          if( isH2L ) begin T[i-60] <= PS2_DAT; i <= i + 1'b1; end
    118.                          
    119.                          69: // Stop bit 1
    120.                          if( isH2L ) i <= Go;
    121.                                              
    122.                     endcase
    123.         

    以上内容是部分核心操作。步骤59~69也是部分伪函数,主要用来读取下一帧数据的字节内容,在此是针对命令8’hF2,也就是Device ID。读完一帧数据以后便返回步骤。

    124.         assign PS2_CLK = isQ1 ? rCLK : 1'bz;
    125.         assign PS2_DAT = isQ2 ? rDAT : 1'bz;
    126.         assign oEn = isEn;
    127.      
    128.    endmodule

    以上内容为驱动输出声明。

    ps2_read_funcmod.v

    clip_image024

    图11.12 PS/2读功能模块的建模图。

    实验十一的PS/2读功能模块与实验十相比,左边的 iEn出入多出一位以外,右边的oData也多出一个字节。

    1.    module ps2_read_funcmod
    2.    (
    3.         input CLOCK, RESET,
    4.         input PS2_CLK,PS2_DAT,
    5.         input [1:0]iEn,
    6.         output oTrig,
    7.         output [31:0]oData
    8.    );  
    9.         parameter FF_Read = 7'd32;

    以上内容是相关的出入端声明。第9行是伪函数的入口。

    10.    
    11.         /*******************************/ // sub1
    12.         
    13.        reg F2,F1; 
    14.         
    15.        always @ ( posedge CLOCK or negedge RESET )
    16.             if( !RESET )
    17.                  { F2,F1 } <= 2'b11;
    18.              else 
    19.                  { F2, F1 } <= { F1, PS2_CLK };
    20.    
    21.         /*******************************/ // core
    22.         
    23.         wire isH2L = ( F2 == 1'b1 && F1 == 1'b0 );

    以上内容是检测电平变化的周边操作,第23行则是下降沿的即时声明。

    24.         reg [31:0]D1;
    25.         reg [7:0]T;
    26.         reg [6:0]i,Go;
    27.         reg isDone;
    28.         
    29.         always @ ( posedge CLOCK or negedge RESET )
    30.             if( !RESET )
    31.                  begin
    32.                         D1 <= 32'd0;
    33.                         T <= 8'd0;
    34.                         { i,Go } <= { 7'd0,7'd0 };
    35.                         isDone <= 1'b0;
    36.                    end

    以上内容为相关的寄存器声明以及复位操作。

    37.               else if( iEn[1] )  
    38.                    case( i )
    39.                     
    40.                         /***********/ // Extend Mouse Read Data 
    41.                          
    42.                          0: // Read Data 1st byte
    43.                          begin i <= FF_Read; Go <= i + 1'b1; end
    44.                          
    45.                          1: // Store Data 1st byte
    46.                          begin D1[7:0] <= T; i <= i + 1'b1; end
    47.                          
    48.                          2: // Read Data 2nd byte
    49.                          begin i <= FF_Read; Go <= i + 1'b1; end
    50.                          
    51.                          3: // Store Data 2nd byte
    52.                          begin D1[15:8] <= T; i <= i + 1'b1; end
    53.                          
    54.                          4: // Read Data 3rd byte
    55.                          begin i <= FF_Read; Go <= i + 1'b1; end
    56.                          
    57.                          5: // Store Data 3rd byte
    58.                          begin D1[23:16] <= T; i <= i + 1'b1; end
    59.                          
    60.                          6: // Read Data 4rd byte
    61.                          begin i <= FF_Read; Go <= i + 1'b1; end
    62.                          
    63.                          7: // Store Data 4rd byte
    64.                          begin D1[31:24] <= T; i <= i + 1'b1; end
    65.                          
    66.                          8:
    67.                          begin isDone <= 1'b1; i <= i + 1'b1; end
    68.                          
    69.                          9:
    70.                          begin isDone <= 1'b0; i <= 7'd0; end

    以上内容为部分核心操作。第37行的 if( iEn[1] ) 表示下面所有内容都是扩展鼠标的核心操作。步骤0~7则是读取4个字节的数据,步骤8~9用来产生完成信号以示一次性的报告已经接收完毕。

    71.                          
    72.                          /****************/ // PS2 Write Function
    73.                          
    74.                          32: // Start bit
    75.                          if( isH2L ) i <= i + 1'b1; 
    76.                          
    77.                          33,34,35,36,37,38,39,40:  // Data byte 
    78.                          if( isH2L ) begin T[i-33] <= PS2_DAT; i <= i + 1'b1;  end
    79.                          
    80.                          41: // Parity bit
    81.                          if( isH2L ) i <= i + 1'b1;
    82.                          
    83.                          42: // Stop bit
    84.                          if( isH2L ) i <= Go;
    85.                          
    86.                     endcase

    以上内容为部分核心操作。步骤32~42是读取一帧数据的伪函数。

    87.                else if( iEn[0] )  
    88.                    case( i )
    89.                     
    90.                         /***********/ // Normal Mouse Read Data  
    91.                          
    92.                          0: // Read Data 1st byte
    93.                          begin i <= FF_Read; Go <= i + 1'b1; end
    94.                          
    95.                          1: // Store Data 1st byte
    96.                          begin D1[7:0] <= T; i <= i + 1'b1; end
    97.                          
    98.                          2: // Read Data 2nd byte
    99.                          begin i <= FF_Read; Go <= i + 1'b1; end
    100.                          
    101.                          3: // Store Data 2nd byte
    102.                          begin D1[15:8] <= T; i <= i + 1'b1; end
    103.                          
    104.                          4: // Read Data 3rd byte
    105.                          begin i <= FF_Read; Go <= i + 1'b1; end
    106.                          
    107.                          5: // Store Data 3rd byte
    108.                          begin D1[23:16] <= T; i <= i + 1'b1; end
    109.                          
    110.                          6:
    111.                          begin isDone <= 1'b1; i <= i + 1'b1; end
    112.                          
    113.                          7:
    114.                          begin isDone <= 1'b0; i <= 7'd0; end
    115.                          

    以上内容为部分核心操作。第87行的 if( iEn[0] ) 表示下面的内容均为普通鼠标的核心操作。步骤0~5用来读取3个字节的内容,步骤6~7则用来产生完成信号以示一次性的报告已经读取完毕。

    116.                          /****************/ // PS2 Write Function
    117.                          
    118.                          32: // Start bit
    119.                          if( isH2L ) i <= i + 1'b1; 
    120.                          
    121.                          33,34,35,36,37,38,39,40:  // Data byte
    122.                          if( isH2L ) begin T[i-33] <= PS2_DAT; i <= i + 1'b1;  end
    123.                          
    124.                          41: // Parity bit
    125.                          if( isH2L ) i <= i + 1'b1;
    126.                          
    127.                          42: // Stop bit
    128.                          if( isH2L ) i <= Go;
    129.                            
    130.                     endcase
    131.         

    以上内容为部分核心操作。步骤32~42是读取一帧数据的伪函数。

    132.         assign oTrig = isDone;
    133.         assign oData = D1;
    134.      
    135.    endmodule

    以上内容是输出驱动声明。

    ps2_demo.v

    笔者就不重复粘贴实验十一的建模图了,具体内容我们还是来看代码吧。

    1.    module ps2_demo
    2.    (
    3.         input CLOCK, RESET,
    4.         inout PS2_CLK, PS2_DAT,
    5.         output [7:0]DIG,
    6.         output [5:0]SEL,
    7.         output [2:0]LED
    8.    );
    9.        wire [1:0]EnU1;
    10.    
    11.         ps2_init_funcmod U1
    12.         (
    13.             .CLOCK( CLOCK ),
    14.              .RESET( RESET ),
    15.              .PS2_CLK( PS2_CLK ), // < top
    16.              .PS2_DAT( PS2_DAT ), // < top
    17.              .oEn( EnU1 ) // > U2
    18.         );
    19.         
    20.         wire [31:0]DataU2;
    21.         
    22.          ps2_read_funcmod U2
    23.         (
    24.             .CLOCK( CLOCK ),
    25.              .RESET( RESET ),
    26.              .PS2_CLK( PS2_CLK ), // < top
    27.              .PS2_DAT( PS2_DAT ), // < top
    28.              .iEn( EnU1 ),      // < U1
    29.              .oTrig(),
    30.              .oData( DataU2 )  // > U3
    31.         );
    32.         
    33.        smg_basemod U3
    34.        (
    35.           .CLOCK( CLOCK ),
    36.           .RESET( RESET ),
    37.            .DIG( DIG ),  // > top
    38.            .SEL( SEL ),  // > top
    39.            .iData( { 2'd0,DataU2[5],DataU2[4],DataU2[27:24],DataU2[23:16],DataU2[15:8] }) // < U2
    40.        );
    41.        
    42.        assign LED = {DataU2[1], DataU2[2], DataU2[0]};
    43.                          
    44.    endmodule

    上诉内容的连线部署基本上与图11.10差不了多少,期间第39行的 2’d0,DataU2[5],DataU2[4] 表示数码管的第一位显示 X 与 Y的符号位;DataU2[27:24] 表示数码管的第二位显示 Z的内容;DataU2[23:16] 表示数码管的第三至第四位显示 Y 的内容;DataU2[15:8] 表示数码管的第五至第六位显示 X 的内容。第42行则表示 LED[2]显示右键,LED[1]显示中键,LED[0]显示左键。

    编译完毕并且下载程序。当鼠标向西南方移动的时候,第一位数码管便会显示 4’h3,即 4’b0011,也就是说 X 与 Y 的符号位都是拉高状态(负值)。当滚轮向上滚动的时候,第二位数码管便会显示 4’hF,即4’b1111,也就是Z为负值 -1(只要滚动速度够快,负值还能更小)。至于数码管第3~4显示Y的内容(补码形式),数码管5~6则显示X的内容(补码形式)。

    细节一: 两个人,两把汤匙

    1.       else if( iEn[1] ) 
    2.           case( i )
    3.              扩展鼠标的核心操作;
    4.              伪函数;
    5.           endcase
    6.       else if(isEn[0])
    7.          case(i)
    8.               普通鼠标的核心操作;
    9.               伪函数;
    10.          endcase

    代码11.2

    PS/2 读取功能模块有一个有趣的现象,即资源多义性的问题。如代码11.2所示,PS/2读取功能模块用 if( iEn[1] ) 与 if( iEn[0] ) 表示该模块针对两种鼠标的读取操作。这种感觉好比一对兄弟在吃饭 ... 正常情况下,当然是一个人一把汤匙才对,这种比喻完全对应代码11.2的内容。

    PS/2读取功能模块负责两种鼠标的读取操作之际,里边好比有一对兄弟,一个人负责扩展鼠标的读取操作,另一个人则针对普通鼠标的读取操作。期间,伪函数就是某种操作资源,也可以看成是汤匙。为了不让两位兄弟争用一把汤匙而吵架,身为设计者的我们,应该为每个人分配一把汤匙。

    对此,我们必须多花一些钱买另一把汤匙,这样做我们可能多消耗一些逻辑资源。不过,家和为贵,为使模块可以和谐共处以致提高表达能力,要笔者多消耗一些逻辑资源,笔者也觉得值得。

    细节二:完整的个体模块

    clip_image026

    图11.13 PS/2鼠标基础模块的建模图。

    图11.13是PS/2鼠标基础模块的建模图。

    ps2mouse_basemod.v
    1.    module ps2mouse_basemod
    2.    (
    3.         input CLOCK, RESET,
    4.         inout PS2_CLK, PS2_DAT,
    5.         output oTrig,
    6.         output [31:0]oData
    7.    );
    8.        wire [1:0]EnU1;
    9.    
    10.         ps2_init_funcmod U1
    11.         (
    12.              .CLOCK( CLOCK ),
    13.              .RESET( RESET ),
    14.              .PS2_CLK( PS2_CLK ), // < top
    15.              .PS2_DAT( PS2_DAT ), // < top
    16.              .oEn( EnU1 ) // > U2
    17.         );
    18.         
    19.          ps2_read_funcmod U2
    20.         (
    21.              .CLOCK( CLOCK ),
    22.              .RESET( RESET ),
    23.              .PS2_CLK( PS2_CLK ), // < top
    24.              .PS2_DAT( PS2_DAT ), // < top
    25.              .iEn( EnU1 ),      // < U1
    26.              .oTrig( oTrig ),  // > top
    27.              .oData( oData )  // > top
    28.         );
    29.                          
    30.    endmodule
  • 相关阅读:
    使用FolderBrowserDialog组件选择文件夹
    使用OpenFileDialog组件打开多个文
    使用OpenFileDialog组件打开对话框
    获取弹出对话框的相关返回值
    PAT 甲级 1139 First Contact (30 分)
    PAT 甲级 1139 First Contact (30 分)
    PAT 甲级 1138 Postorder Traversal (25 分)
    PAT 甲级 1138 Postorder Traversal (25 分)
    PAT 甲级 1137 Final Grading (25 分)
    PAT 甲级 1137 Final Grading (25 分)
  • 原文地址:https://www.cnblogs.com/alinx/p/3962540.html
Copyright © 2011-2022 走看看