MyFPGA Forum

 找回密码
 注册
搜索
查看: 8890|回复: 8
打印 上一主题 下一主题

一个简单的ps/2鼠标控制器

[复制链接]
跳转到指定楼层
1#
发表于 2010-3-23 10:23:55 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
旧式鼠标普遍采用ps/2协议和电脑进行通信,ps/2数据线使用开漏上拉的两根信号线和主机进行双向通信,一根数据线,一根时钟线。无论通信方向,时钟信号始终由ps/2设备产生;而无论何时,主机都拥有对通信的控制权,这可以通过拉低时钟信号超过一个从设备时钟周期时间(约100us)来抑制设备通信或者发起对设备的数据传输过程。
不过主机要告诉设备通信即将开始时,要经过如下过程:
1. Inhibit state 这时候主机将时钟线拉低(100us),数据线为高。
2. request state,这时候主机释放时钟线(高电平),同时拉低数据线,表示数据传输即将开始。
设备检测到这两个状态后,在不超过10ms的时间内,会产生时钟信号。且设备在时钟上升沿采样主机送来的数据,从而,主机可以利用时钟上升沿来移位数据到数据线,发送数据到设备。
主机发送数据到设备的格式为11bit(网络上的资料认为是12bit,包括一个起始位(始终为零)8bit数据,一个奇偶校验位(奇校验,即奇数个1时为1),一个停止位(高电平),一个ack位(低电平);不过据鄙人观察应该为11bit,没有起始位,因为设备给的时钟信号上升沿只有11个,当然你可以理解为主机释放时钟信号时候的上升沿为起始位),这11bit数据,真正由主机发起的只有9位,8个数据位和一个奇偶校验位,在传送完奇偶校验位后,主机应该释放数据线,从设备在下一个周期检测到数据线为高电平后,会在下下个时钟周期拉低数据线,作为acknowlegement表示数据已经收到。
设备在收到任何一个主机来的控制指令后,会首先发送一个response字节给主机,如果收到的为错误指令,会发送FC或者FE错误码给主机,如果是正确指令,发送FA码给主机。
鼠标在上电复位后会执行如下操作:
1. 发送FA,00,AA给系统。
2. 设置缺省模式,进入流模式(stream mode),禁止数据发送。
这时候设备等待主机发送指令给它,如果发送使能数据传输指令(0xF4)给设备,则设备在缺省模式下开始工作,并传送数据给主机,数据格式为每帧数据33bit,分为三个11bit,每11bit包括一个起始位(零),8bit数据,一个奇偶校验,一个停止位。第一个11bit的数据包含按键信息,滚轮信息。
第二个11bit数据包含X方向位移数据,以二进制补码格式发送,第三个11bit数据为Y方向位移信息,同样为二进制补码。
附上代码:
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_arith.all;
entity ps2 is
   port(clkin,rst        :in std_logic;
        ps2_clk,ps2_data :inout std_logic;
        lefbut,rigbut,midbutut std_logic;
        x_mov1,x_mov2,y_mov1,y_mov2 ut std_logic_vector(6 downto 0));
end entity;
architecture behav of ps2 is
--
signal byte_cnt,delay td_logic_vector(3 downto 0);
signal lef_latch,rig_latch,mid_latch td_logic;
signal x_latch,y_latch td_logic_vector(7 downto 0);
signal ps2_clk_in,ps2_clk_out,ps2_data_in,ps2_data_out td_logic;
signal shift_reg td_logic_vector(32 downto 0);
signal clk_div td_logic_vector(8 downto 0);
signal clk,ce,de td_logic;
signal dout_reg td_logic_vector(9 downto 0);
signal cnt td_logic_vector(7 downto 0);
--enable instruction 0xF4;
constant enable_data td_logic_vector(9 downto 0):="0111101000";
signal ct :std_logic_vector(5 downto 0);
--
signal ps2_clk_syn0,ps2_clk_syn1,ps2_dat_syn0,ps2_dat_syn1,dout_syn1,dout_syn0 :std_logic;
--type define
type st is (listen,pullclk,pulldata,trans);
signal state :st ;
component SEG7_LUT is
   port(oSEG ut std_logic_vector(6 downto 0);
        iDIG :in  std_logic_vector(3 downto 0));
end component;
begin
U1: SEG7_LUT port map(oSEG=>x_mov1,iDIG=>x_latch(3 downto 0));
U2: SEG7_LUT port map(oSEG=>x_mov2,iDIG=>x_latch(7 downto 4));
U3: SEG7_LUT port map(oSEG=>y_mov1,iDIG=>y_latch(3 downto 0));
U4: SEG7_LUT port map(oSEG=>y_mov2,iDIG=>y_latch(7 downto 4));
--clk_div,50MHz to 97.65625KHz;
process
begin
  wait until clkin='1';
  clk_div<=clk_div+1;
end process;
clk<=clk_div(8);
--sampling frame data;
process(ps2_clk_in,state)
begin
   if state=listen then
      if ps2_clk_in'event and ps2_clk_in='0' then
         shift_reg(31 downto 0)<=shift_reg(32 downto 1);
         shift_reg(32)<=ps2_data_in;
      end if;
   end if;
end process;
--after the controller send a command to device ,it'll sendback a response,with 11 bit.
--so when ct's value  less than 16,no load activity.
process(ps2_clk_in,cnt,state)
begin
   if cnt="11111111" then
      ct<=(others=>'0');
   elsif state=listen then
      if rising_edge(ps2_clk_in) then
         ct<=ct+1;
      end if;
   end if;
end process;
---idle state counter, watchdog like activity;
process(clk)
begin
   if clk'event and clk='1' then
      if (ps2_data_in='1' and ps2_clk_in='1') then
         cnt<=cnt+1;
      else
         cnt<=(others=>'0');
      end if;
   end if;
end process;
----

----latch data from shift_reg;output is of 2's complement;
process(clk)
begin
   if clk'event and clk='1' then
      if cnt="00011110" and (ct(5)='1'or ct(4)='1') then
      lef_latch<=shift_reg(1);
      rig_latch<=shift_reg(2);
      mid_latch<=shift_reg(3);
      x_latch<=conv_integer(x_latch)+shift_reg(19 downto 12);
      y_latch<=conv_integer(y_latch)+shift_reg(30 downto 23);
      end if;
   end if;
end process;
lefbut<=lef_latch;
rigbut<=rig_latch;
midbut<=mid_latch;
--
ps2_clk_syn0<=ps2_clk;
ps2_dat_syn0<=ps2_data;
--tristate output for ps2_clk and ps2_data;
process(ps2_clk,ce,ps2_clk_out)
begin
   if ce='1' then
      ps2_clk<=ps2_clk_out;
   else
      ps2_clk<='Z';
   end if;
end process;
--tristate output of ps2_data;
process(ps2_data,de,ps2_data_out)
begin
   if de='1' then
      ps2_data<=ps2_data_out;
   else
      ps2_data<='Z';
   end if;
end process;
--synchronize
process
begin
   wait until clk='1';
   ps2_clk_syn1<=ps2_clk_syn0;
   ps2_dat_syn1<=ps2_dat_syn0;
   ps2_clk_in<=ps2_clk_syn1;
   ps2_data_in<=ps2_dat_syn1;
end process;
--tranceive 0xF4 to device when you press the rst button,additional debouncing work

considered.enables data transfer.
process(clk)
begin
  if clk'event and clk='1' then
   case state is
   when listen =>  if rst='0' and cnt="11111111" then state<=pullclk;
                   end if;
                   ce<='0';de<='0';
   when pullclk => if delay="1100" then state<=pulldata;
                   end if;
                   ce<='1';de<='0';
   when pulldata=> state<=trans;
                   ce<='1';de<='1';
   when trans =>   if byte_cnt="1010" then state<=listen;
                   end if;
                   ce<='0';de<='1';
   when others  => state<=listen;
   end case;
  end if;
end process;
--pull clk low for about 100us
process(clk,state)
begin
   if state=pullclk then
      if clk'event and clk='1' then
         delay<=delay+1;
      end if;
   else
      delay<=(others=>'0');
   end if;
end process;
--tranceive data;adds up byte
process(ps2_clk_in,state)
begin
  if state=trans then
     if ps2_clk_in'event and ps2_clk_in='0' then
        dout_reg<='0'&dout_reg(9 downto 1);
     end if;
  else
     dout_reg<=enable_data;
  end if;
end process;
process(ps2_clk_in,state)
begin
if state=trans then
     if ps2_clk_in'event and ps2_clk_in='0' then
        byte_cnt<=byte_cnt+1;       
     end if;
  else
     byte_cnt<=(others=>'0');
  end if;
end process;
--output assignment;
ps2_data_out<=dout_reg(0);
ps2_clk_out<='0';                  
--

end behav;

评分

1

查看全部评分

2#
发表于 2010-3-24 09:35:29 | 只看该作者
旧式鼠标普遍采用ps/2协议和电脑进行通信,ps/2数据线使用开漏上拉的两根信号线和主机进行双向通信,一根数 ...
hdhuang 发表于 23-3-2010 10:23



很好!
原创作品!!
值得学习!!
希望见到更多原创作品!!!
3#
发表于 2010-3-24 10:02:22 | 只看该作者
数据发送的request state 叙述有误,应该为:控制器先拉低数据线,然后释放时钟线,在这个上升沿的数据作为起始位,设备检测到这个起始位后在不超过10ms的时间内给出时钟信号

评分

1

查看全部评分

4#
 楼主| 发表于 2010-3-24 10:05:21 | 只看该作者
确实,我先前的理解有问题,因为我始终找不到起始位,其实起始位蕴含在request状态中。
5#
发表于 2010-3-24 10:54:27 | 只看该作者
回复 3# allen_wind


厉害!

这,你都能发现,佩服!

6#
发表于 2010-4-18 14:17:11 | 只看该作者
居然代码上没有注释....这个读起来会比较麻烦..
7#
发表于 2010-6-15 11:46:46 | 只看该作者
thanks!!!
8#
发表于 2011-1-23 19:06:06 | 只看该作者
我是新手,虽然看不懂,但是也很受鼓舞~
9#
发表于 2011-3-17 20:00:05 | 只看该作者
寒假的时候玩了一个用Verilog写的电子琴~~~挺不错的
您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|Archiver|MyFPGA

GMT+8, 2024-5-3 19:14 , Processed in 0.052336 second(s), 15 queries .

Powered by Discuz! X3

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表