引言
當前嵌入式系統技術已得到了廣泛應用,但傳統嵌入式系統的人機接口多采用小鍵盤操作的文本菜單方式,用戶操作較為不便。本設計利用FPGA實現對PS/2接口鼠標的控制,是在以VGA作為輸出設備的單片機系統上初步實現圖形化用戶界面的方案,它成本低、效果好,并且有很強的實用性。
FPGA(Field Programmable Gate Array)是 20世紀80年代中期出現的高密度、可編程邏輯器件,FPGA及其軟件系統是開發數字電路的最新技術,它利用EDA技術,以電路原理圖、硬件描述語言及狀態機等形式輸入設計邏輯,提供功能模擬、時序仿真等模擬手段,在功能模擬和時序仿真度滿足要求后,經過一系列變換,將輸入邏輯轉換成FPGA器件的編程文件,以實現專用集成電路。本設計選用Altera公司推出的CyclONe II系列的EP2C5T144C8 FPGA來設計PS/2接口,體積減小,可靠性提高。
PS/2接口和協議
接口的物理特性
PS/2接口用于許多現代的鼠標和鍵盤,由IBM最初開發和使用。物理上的PS/2接口有兩種類型的連接 器 :5腳的DIN和6腳的MINI-DIN。圖1就是兩種連接器的引腳定義。使用中,主機提供+5V電源給鼠標,鼠標的地連接到主機電源地上。
接口協議原理
PS/2鼠標接口采用一種雙向同步串行協議,即每在時鐘線上發一個脈沖,就在數據線上發送一位數據。在相互傳輸中,主機擁有總線控制權,即它可以在任何時候抑制鼠標的發送,方法是把時鐘線一直拉低,鼠標就不能產生時鐘信號并發送數據。在兩個方向的傳輸中,時鐘信號都由鼠標產生,主機不產生通信時鐘信號。
如果主機要發送數據,就必須控制鼠標產生時鐘信號,方法如下:主機首先下拉時鐘線至少100μS抑制通信,然后再下拉數據線,最后釋放時鐘線。鼠標檢測到這個時序狀態后,會在10mS內產生時鐘信號。如圖3中(A)時序段。主機和鼠標之間,傳輸數據幀的時序如圖2、圖3所示。
PS/2鼠標的工作模式和協議數據包格式
PS/2鼠標的四種工作模式
PS/2鼠標的四種工作模式分別是:Reset模式,當鼠標上電或主機發復位命令(0xFF)給它時,進入這種模式;STream模式,鼠標的默認模式,當鼠標上電或復位完成后,自動進入此模式,鼠標基本上以此模式工作;Remote模式,只有在主機發送了模式設置命令(0xF0)后,鼠標才進入這種模式;Wrap模式,這種模式只用于測試鼠標與主機連接是否正確。
數據包結構
PS/2鼠標在工作過程中,會及時把它的狀態數據發送給主機。發送的數據包格式如表1所示。
Byte1中的Bit0、Bit1、Bit2分別表示左、右、中鍵的狀態,狀態值0表示釋放,1表示按下;Byte2和Byte3分別表示X軸和Y軸方向的移動計量值,是二進制補碼值;Byte4的低四位表示滾輪的移動計量值,也是二進制補碼值,高四位作為擴展符號位。這種數據包由帶滾輪的三鍵三維鼠標產生,若是不帶滾輪的三鍵鼠標,產生的數據包沒有Byte4,其余的相同。
VGA信號時序
圖4所示是計算機VGA(640×480,60Hz)圖像格式的信號時序圖,其點時鐘DCLK為25.175MHz,場頻為59.94Hz。圖中,Vsync為場同步信號,場周期Tvsync為16.683mS,每場有525行,其中480行為有效顯示行,45行為場消隱期。場同步信號Vs每場有一個脈沖,該脈沖的低電平寬度twv為63μS(2行)。場 消隱期包括場同步時間twv、場消隱前肩tHV(13行)和場消隱后肩tVH(30行),共45行。行周期THSYNC為31.78μS,每顯示行包 括800點,其中640點為有效顯示,160點為行消隱期(非顯示 區)。行同步信號Hs每行有一個脈沖,該脈沖的低電平寬度tWH為3.81μS(即96個DCLK);行消隱期包括行同步時間tWH,行消隱前肩tHC(19個DCLK)和行消隱后肩tCH(45個DCLK),共160個點時鐘。復合消隱信號是行消隱信號和場消隱信號的邏輯與,在有效顯示期復合消隱信號為高電平,在非顯示區域它是低電平。
設計實現
實現功能
1、 用FPGA實現PS/2鼠標接口。
2、鼠標左鍵按下時十字形鼠標圖象的中間方塊改變顏色,右鍵按下時箭頭改變顏色。
3、 Reset按鍵:總復位。
設計原理
主機復位后,首先向鼠標發送初始化命令(0xf4)。當鼠標收到命令字后會給出一個應答字節(0xfa),主機根據應答字節來判斷鼠標是否正確應答。如果應答正確則接收鼠標數據包,然后從接收到的數據包中獲得鼠標位置及狀態數據,并輸出給顯示模塊。顯示模塊在CRT上顯示出當前鼠標的狀態和位置,否則,停止處理。如圖5。
如圖6,當狀態機m2_state復位時,即進入m2_reset狀態,并在 一個clk周期后進入m2_hold_clk_l狀態,當ps2_clk_hi_z(時鐘線)被拉低并保持400μS后進入m2_data_low_1狀態,此時向鼠標 發送起始 位和d[0]、d[1](d[0]=d[1]=0)。完成后進入m2_data_high_1狀態, 發送d[2](d[2]=1)并進入m2_data_low_2狀態,此時向鼠標發送d[3]位(d[3]=0), 完成發送進入m2_data_high_2狀態,向鼠標發送d[4]、d[5]、d[6]、d[7](d[4]=d[5]=d[6]=d[7]=1),完成發送進入m2_data_low_3狀 態,向鼠標發送奇偶校驗位,然后進入m2_data_high_3狀態,將數據線拉高,等待鼠標返回應答信號。若PS/2時鐘信號下降沿來臨時,數據線仍未變為高電平,則進入m2_error_no_ack狀態,此時握手失敗,系統將保持m2_error_no_ack狀態直到下一次復位,否則進入m2_await_response狀態接收應答字,接收完成進入m2_verify數據校驗,然后進入m2_use狀態,鎖存輸出數據,并進入m2_wait狀態,等待接收數據。當檢測到時鐘下降沿后進入m2_gather狀態,接收鼠標數據包,接收完成進入m2_verify狀態,此時便形成了數據接收循環。
PS/2程序源碼
entity mouse is
Port (clk : in std_logic; reset : in std_logic; ps2_clk : inout std_logic; ps2_data : inout std_logic; left_button : out std_logic; right_button : out std_logic; mousex: buffer std_logic_vector(9 downto 0); mousey: buffer std_logic_vector(9 downto 0); data_ready : out std_logic; error_no_ack : out std_logic);
end mouse;
architecture Behavioral of mouse is
--變量、信號定義(略)
begin
ps2_clk 《= ‘0’ when ps2_clk_hi_z=‘0’ else ‘Z’;
ps2_data 《= ‘0’ when ps2_data_hi_z=‘0’ else ‘Z’;
--檢測ps2clk上升沿和下降沿(略)
m2statech: process (reset, clk) ------------------m2 狀態
begin
if (reset=‘0’) then
m2_state 《= m2_reset;
elsif (clk‘event and clk=’1‘) then
m2_state 《= m2_next_state;
end if;
end procESS;
--m2 狀態傳輸邏輯
m2statetr: process (m2_state, q, fall,rise,watchdog_timer_done,bitcount,ps2_data,packet_good)
begin
ps2_clk_hi_z 《= ’1‘;
ps2_data_hi_z 《= ’1‘;
error_no_ack 《= ’0‘;
output_strobe 《= ’0‘;
case m2_state is
when m2_reset =》 -- 復位后向鼠標發送命令字
m2_next_state 《= m2_hold_clk_l;
when m2_wait =》
if (fall=’1‘) then
m2_next_state 《= m2_gather;
else
m2_next_state 《= m2_wait;
end if;
when m2_gather =》
if ((watchdog_timer_done=’1‘) and (bitcount=TOTAL_BITS))then
m2_next_state 《= m2_verify;
else
m2_next_state 《= m2_gather;
end if;
when m2_verify =》
--if (bitcount 《 TOTAL_BITS) then
--m2_next_state 《= m2_wait;
--else
m2_next_state 《= m2_use;
--end if;
when m2_use =》
output_strobe 《= ’1‘;
m2_next_state 《= m2_wait;
-- 用狀態機的9個狀態實現命令字傳輸,使鼠標進入“streaming”模式,并等待鼠標正確應答 [page]
when m2_hold_clk_l =》
ps2_clk_hi_z 《= ’0‘; -- 啟動看門狗!
if (watchdog_timer_done=’1‘) then
m2_next_state 《= m2_data_low_1;
else
m2_next_state 《= m2_hold_clk_l;
end if;
when m2_data_low_1 =》
ps2_data_hi_z 《= ’0‘; -- 數據位 開始位, d[0] and d[1]
if (fall=’1‘ and (bitcount = 2)) then
m2_next_state 《= m2_data_high_1;
else
m2_next_state 《= m2_data_low_1;
end if;
when m2_data_high_1 =》
ps2_data_hi_z 《= ’1‘; -- 數據位 d[2]
if (fall=’1‘ and (bitcount = 3)) then
m2_next_state 《= m2_data_low_2;
else
m2_next_state 《= m2_data_high_1;
end if;
when m2_data_low_2 =》
ps2_data_hi_z 《= ’0‘; -- 數據位 d[3]
if (fall=’1‘ and (bitcount = 4)) then
m2_next_state 《= m2_data_high_2;
else
m2_next_state 《= m2_data_low_2;
end if;
when m2_data_high_2 =》
ps2_data_hi_z 《= ’1‘; -- 數據位 d[4],d[5],d[6],d[7]
if (fall=’1‘ and (bitcount = 8)) then
m2_next_state 《= m2_data_low_3;
else
m2_next_state 《= m2_data_high_2;
end if;
when m2_data_low_3 =》
ps2_data_hi_z 《= ’0‘; -- 奇偶校驗位
if (fall=’1‘) then
m2_next_state 《= m2_data_high_3;
else
m2_next_state 《= m2_data_low_3;
end if;
when m2_data_high_3 =》
ps2_data_hi_z 《= ’1‘; -- 允許鼠標拉成低電平(ACK脈沖)
if (fall=’1‘ and (ps2_data=’1‘)) then
m2_next_state 《= m2_error_no_ack;
elsif (fall=’1‘ and (ps2_data=’0‘)) then
m2_next_state 《= m2_await_response;
else
m2_next_state 《= m2_data_high_3;
end if;
when m2_error_no_ack =》
error_no_ack 《= ’1‘;
m2_next_state 《= m2_error_no_ack;
-- 為了鼠標正確進入“streaming”模式,狀態極必須等待足夠長的時間,確保鼠標正確應答0xFA。
when m2_await_response =》
--if (bitcount = 22) then
m2_next_state 《= m2_verify;
--else
-- m2_next_state 《= m2_await_response;
--end if;
when others =》 m2_next_state 《= m2_wait;
end case;
end process;-----------------------------m2 狀態結束
-- 位計數器 (略)
-- 數據移位寄存器(略)
-- 看門狗時間計數器(略)
watchdog_timer_done 《= ’1‘ when (watchdog_timer_count=WATCHDOG-1) else ’0‘;
packet_good 《= ’1‘; -- 接收數據包有效標志
outdata: process (reset, clk) -- 輸出數據
begin
if (reset=’0‘) then
left_button 《= ’0‘;
right_button 《= ’0‘;
elsif (clk’event and clk=‘1’) then
if (output_strobe=‘1’) then
left_button 《= q(1);
right_button 《= q(2);
mouseyy 《= not (q(6) & q(6) & q(30 downto 23)) + “1”;
end if;
end if;
end process;
cordinatex: process (reset, clk)
begin
if (reset=‘0’) then
mousex 《= “0110010000”; -- 400
elsif (clk‘event and clk=’1‘) then
if (output_strobe=’1‘) then
if ((mousex 》= 797 and q(5)=’0‘) or (mousex 《= 2 and
q(5)=’1‘)) then
mousex 《= mousex;
else
mousex 《= mousex + (q(5) & q(5) & q(19 downto
12));--q(5):xsign q(6):ysign
end if;
end if;
end if;
end process;
cordinatey: process (reset, clk)
begin
if (reset=’0‘) then
mousey 《= “0100101100”; -- 300
elsif (clk’event and clk=‘1’) then
if (output_strobe=‘1’) then
if ((mousey 》= 597 and q(6)=‘1’) or (mousey 《= 2
and q(6)=‘0’)) then
mousey 《= mousey;
else
m ousey 《= mousey + mouseyy; --(q(6) & q(6) & q(30
downto 23));
end if;
end if;
end if;
end process;
data_ready 《= output_strobe;
end Behavioral;
結束語
該設計采用了清華大學THCII-1創新SoPC實驗套件進行綜合、仿真和下載,測試得到了滿意的效果,完整地實現了對PS/2和VGA的時序驅動。
該設計可以被應用到各種需要鼠標操作、以VGA作為顯示的嵌入式系統中,從而大大提高人機交互能力,降低了開發成本,提高了開發效率,使系統的穩定性也得到了可靠的保障。
責任編輯:gt
評論