今天做CPLD/FPGA 的PS/2 键盘,用状态机做;
对状态机理解不透 ,导致程序可以综合,但是生成的状态机图错误,各状态间没有关系,后进过群里 做逻辑的朋友指点:
我的状态机是在状态里判断时钟边沿,
改成在时钟边沿条件下选择状态;
改好了后程序通过,但是读出的PS/2键盘通码错误(键盘有自己的通吗和段码,按下某个间比如A ,通码就是1C,放开这个A键断码就是F01C,就是说A键已经弹起了);
我想到是不是PS/2键盘的输出时钟问题,我测了下PS/2 CLK的时钟 大约在13K,我想应该是边沿毛刺 对数据造成了误读,所以我对PS/2 键盘的时钟寄存了1次,建议大家寄存2次(用系统时钟22.1184M做触发器的时钟,在时钟上升沿把PS/2键盘的时钟输入到寄存器然后输出), 做些滤波处理好
做了上面 再读数据,键盘通码显示正确;
相信很多朋友也看到了一些FPGA的板子 有源时钟是先过芯片一次再出来进CPLD/FPGA 这样的波形就很好了,
切记切记!!! 缓存2次,这在多时钟设计上可以避免亚稳态的发生...
PS/2 数据格式 11位数据 1个起始位总为1,8位数据位(低位在前),1位奇校验,1位停止位总为1;和串口通信格式一样,
还一个要说明的是,直接接PS/2键盘上去,键盘上的3个灯永远也不会亮,需要主机到键盘通信 复位 ,工作方式什么的把!!!网上的程序多没有这样的语句,买来的书也没有这个东西;PS/2 协议上介绍,这个东西我没有做,先把基本的做起来;
PS/2 设备(键盘,鼠标)是自己产生时钟的,不需要主机(HOST)产生时钟,主机可以是PC,CPLD/FPGA,MCU等。同时主机可以抑制PS/2设备通信通过下拉时钟线;
PS/2设备的时钟线和数据线多是集电极开路输出的,所以一定要上拉电阻,平时 时钟线 数据线多是1,当有动作时候,PS/2设备自动输出时钟和相应的数据,我们只学要在时钟的下降沿读入数据就可以了:
下面是PS/2技术参考,中英文对照,有点错误,比如在主机到设备的通行中
有个是应该 释放 时钟线的,他写成了释放数据线;
http://www.mcu-fpga.com/download/PS2_Technology_Reference.pdf
我博客里也有几篇其他的PS/2的资料,大家找找可以看下
下面是程序:
用状态机读出PS/2键盘发来的11位数据,取出8个数据位,在发光二极管上显示
没有判断 起始位 校验 停止位; 明天还要继续做新功能,这个先练练手 给大家
参考了,我也很菜,大家多提点意见啊!!!
按下A 通码是1C 一直按着不放 输出的永远是1C 断开A,输出的是F01C
先送F0,在送1C,所以在LED上显示的还是1C;
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_unsigned.ALL;
use IEEE.STD_LOGIC_arith.ALL;
entity PS2_Keyboard is
Port (clk: in std_logic; --22.1184M system clock
--reset: in std_logic; --Reset Keyboard
PS2_CLK: in std_logic; --PS/2 Keyboard clock
PS2_Data: in std_logic; --PS/2 Keyboard data
data_out : out std_logic_vector(7 downto 0); --to 8 bits led
data3 : out std_logic_vector(2 downto 0) --to IO
);
end PS2_Keyboard;
architecture behav of PS2_Keyboard is
type state_type IS (start_state,data_state,parity_state,stop_state);
signal present_state,next_state : state_type;
--signal timer0 : std_logic_vector(15 downto 0) := x"0000";
--signal en_timer0 : std_logic := '1';
signal start_bit : std_logic;
signal parity_bit : std_logic;
signal stop_bit : std_logic;
signal recive_data : std_logic_vector(7 downto 0);
signal i: integer range 0 to 15 := 0;
signal PS2_CLKP : std_logic;
begin
data3(0)<=start_bit;
data3(1)<=parity_bit;
data3(2)<=stop_bit;
--state register process
state_reg:process
begin
wait until clk'event AND clk = '1';
PS2_CLKP <= PS2_CLK;
present_state <= next_state;
end process;
state_switch:process(PS2_CLKP)
begin
if PS2_CLKP'event and PS2_CLKP='0' then
case present_state is
when start_state =>
start_bit <= PS2_Data;
next_state <= data_state;
when data_state =>
if (i = 7) then
i <= 0;
data_out(7 downto 0) <= NOT recive_data(7 downto 0);
next_state <= parity_state;
else
recive_data(i) <= PS2_Data;
i <= i+1;
next_state <= data_state;
end if;
when parity_state =>
parity_bit <= PS2_Data;
next_state <= stop_state;
when stop_state =>
stop_bit <= PS2_Data;
next_state <= start_state;
when others =>
next_state <= start_state;
end case;
end if;
end process;
end behav;
第二天:
我想我是不是一定要用状态机呢?
先认为有必要吧!
再看我的状态
我认为的这11个PS/2数据是完整的,即一有PS/2时钟信号就是连续的11个下降沿和对应的11位数据,如果中间突然断开了,程序锁死在某个状态下出不来;等下次数据来临读出的数据位也是错位或错误的;
比如在进入接受8个数据位的时候突然没有了PS/2时钟那要怎么办,状态机一直处在接受8位数据位状态,下个时钟到来起始位的数据就成了数据位,错乱啊!
我没有判断 1。起始位 2.奇偶校验位 3.停止位 的正确与否
你一直按住PS/2键盘不放 键盘会一直发出时钟和数据,他们是有段间隔时间的;
文章评论(0条评论)
登录后参与讨论