AFON // радиотехник affonya@mail.ru
← Проекты на VHDL

Проект 32. Приёмник клавиатуры PS/2

В отличие от UART, интерфейс PS/2 тактируется самой клавиатурой. Схема ловит спад этого такта и принимает одиннадцатибитный кадр со скан-кодом.

Задача

Принять скан-код нажатой клавиши по интерфейсу PS/2.

Тип схемы: Синхронная схема с внешним тактированием от устройства.
Модуль: ps2_rx. Среда: Quartus, Vivado, ISE или свободный GHDL для моделирования. Теорию по языку смотрите в статье VHDL — язык описания аппаратуры, по самим микросхемам — на странице ПЛИС.

Алгоритм

  1. Синхронизировать линии CLK и DATA клавиатуры с системным тактом.
  2. Отслеживать спад линии CLK — по нему клавиатура выставляет очередной бит.
  3. Сдвигать принятые биты в регистр кадра.
  4. После одиннадцати бит выделить восемь бит данных как скан-код.

Код на VHDL

Код проверен компилятором GHDL: проходит анализ и элаборацию без ошибок и предупреждений. Скачать: v32.vhd

--=====================================================
-- Проект 32. Приёмник клавиатуры PS/2
-- Ловит одиннадцатибитный кадр (старт, 8 бит данных,
-- паритет, стоп) по спаду сигнала CLK клавиатуры.
--=====================================================
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity ps2_rx is
    port (
        clk      : in  std_logic;                       -- системный такт
        rst      : in  std_logic;
        ps2_clk  : in  std_logic;                        -- линия CLK клавиатуры
        ps2_data : in  std_logic;                        -- линия DATA клавиатуры
        scancode : out std_logic_vector(7 downto 0);
        valid    : out std_logic
    );
end entity ps2_rx;

architecture rtl of ps2_rx is
    signal clk_sync  : std_logic_vector(2 downto 0) := "111";
    signal data_sync : std_logic_vector(1 downto 0) := "11";
    signal shreg      : std_logic_vector(10 downto 0) := (others => '1');
    signal bitcnt      : integer range 0 to 10 := 0;
begin
    process (clk, rst)
    begin
        if rst = '1' then
            clk_sync  <= "111";
            data_sync <= "11";
            bitcnt    <= 0;
            valid     <= '0';
        elsif rising_edge(clk) then
            clk_sync  <= clk_sync(1 downto 0) & ps2_clk;
            data_sync <= data_sync(0) & ps2_data;
            valid     <= '0';

            -- клавиатура сама формирует такт; ловим его спад
            if clk_sync(2) = '1' and clk_sync(1) = '0' then
                shreg <= data_sync(1) & shreg(10 downto 1);   -- сдвиг с приёмом бита
                if bitcnt = 10 then
                    bitcnt <= 0;
                    valid  <= '1';                       -- кадр из 11 бит принят
                else
                    bitcnt <= bitcnt + 1;
                end if;
            end if;
        end if;
    end process;

    -- в кадре: бит0 старт, биты1..8 данные, бит9 паритет, бит10 стоп
    scancode <= shreg(8 downto 1);
end architecture rtl;

Пояснения

  • PS/2 — редкий пример интерфейса, где такт задаёт периферия, а не хост; схеме остаётся только синхронизировать и ловить фронты.
  • Кадр включает старт-бит, восемь бит данных, бит паритета и стоп-бит — в учебном варианте паритет не проверяется.
  • Клавиатура передаёт данные младшим битом вперёд, поэтому сдвиг идёт справа налево, в отличие от передатчика UART в этом же разделе.

Что дальше

Следующие проекты — в разделе проекты на VHDL. Сравнить подход с языком Verilog можно в статье Verilog, а разобраться в устройстве самих кристаллов — в статье ПЛИС: FPGA и CPLD.