Basado en Sasszem Aquí tenemos una implementación en VHDL con sincronizadores, desbordadores y un estado interno dentro de Multiplexer
para recordar el estado de NUM LOCK que Converter
utiliza para seleccionar la conversión correcta. La simulación muestra la salida ASCII tanto en formato sin signo como en carácter.
Sincronizador
Sincroniza la entrada (señales de fila y columna) con el reloj del sistema.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity Sync is
generic
(
SYNC_BITS: positive := 3; -- Number of bits in the synchronisation buffer (2 minimum).
BIT_WIDTH: positive := 8
);
port
(
clock : in std_logic;
input : in std_logic_vector(BIT_WIDTH - 1 downto 0); -- Asynchronous input.
output: out std_logic_vector(BIT_WIDTH - 1 downto 0) -- Synchronous output.
);
end entity;
architecture V1 of Sync is
constant SYNC_BUFFER_MSB: positive := SYNC_BITS - 1;
subtype TSyncBuffer is std_logic_vector(SYNC_BUFFER_MSB downto 0);
type TSyncBuffers is array(0 to BIT_WIDTH - 1) of TSyncBuffer;
signal sync_buffers: TSyncBuffers;
begin
assert SYNC_BITS >= 2 report "Need a minimum of 2 bits in the synchronisation buffer.";
process(all)
begin
if rising_edge(clock) then
for i in 0 to BIT_WIDTH - 1 loop
sync_buffers(i) <= sync_buffers(i)(SYNC_BUFFER_MSB - 1 downto 0) & input(i);
end loop;
end if;
for i in 0 to BIT_WIDTH - 1 loop
output(i) <= sync_buffers(i)(SYNC_BUFFER_MSB);
end loop;
end process;
end architecture;
Destapador
Desembolsa la entrada sincronizada (señales de fila y columna), y a continuación emite un change
estroboscópico cuando se ha desbordado con éxito. Esta luz estroboscópica se utiliza para activar Multiplexer
para procesar la pulsación de la tecla.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity Debounce is
generic
(
CLOCK_PERIOD : time := 20 ns;
DEBOUNCE_PERIOD: time := 20 ms; -- Rule of thumb for a keyboard button.
BIT_WIDTH : positive := 8
);
port
(
clock : in std_logic;
input : in std_logic_vector(BIT_WIDTH - 1 downto 0); -- Asynchronous and noisy input.
output: out std_logic_vector(BIT_WIDTH - 1 downto 0) := (others => '0'); -- Synchronised, debounced and filtered output.
change: out std_logic := '0' -- Goes high for 1 clock cycle on change of input.
);
end entity;
architecture V1 of Debounce is
constant MAX_COUNT: natural := DEBOUNCE_PERIOD / CLOCK_PERIOD - 1;
signal counter: natural range 0 to MAX_COUNT := 0; -- Specify the range to reduce number of bits that are synthesised.
begin
process(all)
variable change_internal: std_logic := '0';
begin
if rising_edge(clock) then
counter <= 0; -- Freeze the counter by default to reduce switching losses when input equals output.
change <= '0'; -- Goes high for 1 clock cycle on change of input.
if counter = MAX_COUNT then -- If successfully debounced, notify what happened.
output <= input;
change <= change_internal; -- Goes high for 1 clock cycle on change of input.
elsif input /= output then -- Hysteresis.
counter <= counter + 1; -- Only increment when input and output differ.
end if;
end if;
-- Change detection.
if input /= output then
change_internal := '1';
else
change_internal := '0';
end if;
end process;
end architecture;
Multiplexor
Se ha añadido un estroboscopio de tecla pulsada para activar el procesamiento de la tecla. Cambia el estado de BLOQUEO DE NÚMERO si la tecla presionada es la tecla de BLOQUEO DE NÚMERO. Este estado es utilizado por Converter
para convertir la clave en ASCII.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity Multiplexer is
port
(
reset: in std_logic;
clock: in std_logic;
key_pressed_strobe: in std_logic;
columns : in std_logic_vector(3 downto 0);
rows : in std_logic_vector(3 downto 0);
binary_key : out std_logic_vector(7 downto 0);
is_key_pressed : out std_logic;
is_num_lock_on : out std_logic
);
end Multiplexer;
architecture data_flow of Multiplexer is
signal is_key_pressed_internal : std_logic;
begin
process(all)
begin
if reset then
binary_key <= "00000000";
is_key_pressed <= '0';
is_num_lock_on <= '0';
elsif rising_edge(clock) then
if key_pressed_strobe then
binary_key <= rows & columns;
is_key_pressed <= is_key_pressed_internal;
if columns = "0001" and rows = "0001" then
is_num_lock_on <= not is_num_lock_on;
end if;
end if;
end if;
if columns = "0000" or rows = "0000" then
is_key_pressed_internal <= '0';
else
is_key_pressed_internal <= '1';
end if;
end process;
end data_flow;
Convertidor
Se ha añadido una señal is_num_lock_on
para indicar el estado de bloqueo de números. Se han añadido un par de subtipos llamados TKey
y TAscii
con algunas constantes para cada clave para hacer el código más legible y mantenible.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity Converter is
port
(
hex_key : in std_logic_vector(7 downto 0);
is_num_lock_on : in std_logic;
output : out std_logic_vector(7 downto 0)
);
end Converter;
architecture data_flow of Converter is
subtype TKey is std_logic_vector(7 downto 0);
subtype TAscii is std_logic_vector(7 downto 0);
constant KEY_NUMLOCK : TKey := "00010001"; -- Num Lock
constant KEY_0 : TKey := "01010001"; -- 0
constant KEY_1 : TKey := "01000001"; -- 1
constant KEY_2 : TKey := "01000010"; -- 2
constant KEY_3 : TKey := "01000011"; -- 3
constant KEY_4 : TKey := "00110001"; -- 4
constant KEY_5 : TKey := "00110010"; -- 5
constant KEY_6 : TKey := "00110011"; -- 6
constant KEY_7 : TKey := "00100001"; -- 7
constant KEY_8 : TKey := "00100010"; -- 8
constant KEY_9 : TKey := "00100011"; -- 9
constant KEY_MULTIPLY : TKey := "00010011"; -- *
constant KEY_DIVIDE : TKey := "00010010"; -- /
constant KEY_PLUS : TKey := "00100100"; -- +
constant KEY_SUBTRACT : TKey := "00010100"; -- -
constant KEY_DOT : TKey := "01010011"; -- .
constant KEY_ENTER : TKey := "01000100"; -- Enter
constant ASCII_0 : TAscii := x"30"; -- 0
constant ASCII_1 : TAscii := x"31"; -- 1
constant ASCII_2 : TAscii := x"32"; -- 2
constant ASCII_3 : TAscii := x"33"; -- 3
constant ASCII_4 : TAscii := x"34"; -- 4
constant ASCII_5 : TAscii := x"35"; -- 5
constant ASCII_6 : TAscii := x"36"; -- 6
constant ASCII_7 : TAscii := x"37"; -- 7
constant ASCII_8 : TAscii := x"38"; -- 8
constant ASCII_9 : TAscii := x"39"; -- 9
constant ASCII_MULTIPLY : TAscii := x"2A"; -- *
constant ASCII_DIVIDE : TAscii := x"2F"; -- /
constant ASCII_PLUS : TAscii := x"2B"; -- +
constant ASCII_SUBTRACT : TAscii := x"2D"; -- -
constant ASCII_DOT : TAscii := x"2E"; -- .
constant ASCII_ENTER : TAscii := x"0D"; -- Enter
constant ASCII_NULL : TAscii := x"00"; -- Null
begin
process(all)
begin
if is_num_lock_on then
case hex_key is
when KEY_0 => output <= ASCII_0;
when KEY_1 => output <= ASCII_1;
when KEY_2 => output <= ASCII_2;
when KEY_3 => output <= ASCII_3;
when KEY_4 => output <= ASCII_4;
when KEY_5 => output <= ASCII_5;
when KEY_6 => output <= ASCII_6;
when KEY_7 => output <= ASCII_7;
when KEY_8 => output <= ASCII_8;
when KEY_9 => output <= ASCII_9;
when KEY_MULTIPLY => output <= ASCII_MULTIPLY;
when KEY_DIVIDE => output <= ASCII_DIVIDE;
when KEY_PLUS => output <= ASCII_PLUS;
when KEY_SUBTRACT => output <= ASCII_SUBTRACT;
when KEY_DOT => output <= ASCII_DOT;
when KEY_ENTER => output <= ASCII_ENTER;
when others => output <= ASCII_NULL;
end case;
else
case hex_key is
when KEY_MULTIPLY => output <= ASCII_MULTIPLY;
when KEY_DIVIDE => output <= ASCII_DIVIDE;
when KEY_PLUS => output <= ASCII_PLUS;
when KEY_SUBTRACT => output <= ASCII_SUBTRACT;
when KEY_DOT => output <= ASCII_DOT;
when KEY_ENTER => output <= ASCII_ENTER;
when others => output <= ASCII_NULL;
end case;
end if;
end process;
end data_flow;
Banco de pruebas
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
entity Keypad_TB is
end;
architecture V1 of Keypad_TB is
constant CLOCK_PERIOD : time := 50 ns;
constant DEBOUNCE_PERIOD: time := 200 ns; -- Use ~20 ms for actual keypad.
signal halt_sys_clock: boolean := false;
signal reset: std_logic := '0';
signal clock: std_logic := '0';
signal input: std_logic_vector(7 downto 0);
signal input_sync: std_logic_vector(7 downto 0);
signal input_sync_db: std_logic_vector(7 downto 0);
alias rows is input_sync_db(7 downto 4);
alias columns is input_sync_db(3 downto 0);
signal change: std_logic;
signal key: std_logic_vector(7 downto 0);
signal ascii: std_logic_vector(7 downto 0);
signal is_key_pressed: std_logic;
signal is_num_lock_on: std_logic;
component Sync is
generic
(
SYNC_BITS: positive := 3; -- Number of bits in the synchronisation buffer (2 minimum).
BIT_WIDTH: positive := 8
);
port
(
clock : in std_logic;
input : in std_logic_vector(BIT_WIDTH - 1 downto 0); -- Asynchronous input.
output: out std_logic_vector(BIT_WIDTH - 1 downto 0) -- Synchronous output.
);
end component;
component Debounce is
generic
(
CLOCK_PERIOD : time := 20 ns;
DEBOUNCE_PERIOD: time := 20 ms; -- Rule of thumb for a keyboard button.
BIT_WIDTH : positive := 8
);
port
(
clock : in std_logic;
input : in std_logic_vector(BIT_WIDTH - 1 downto 0); -- Asynchronous and noisy input.
output: out std_logic_vector(BIT_WIDTH - 1 downto 0) := (others => '0'); -- Synchronised, debounced and filtered output.
change: out std_logic := '0' -- Goes high for 1 clock cycle on change of input.
);
end component;
component Multiplexer is
port
(
reset: in std_logic;
clock: in std_logic;
key_pressed_strobe: in std_logic;
columns : in std_logic_vector(3 downto 0);
rows : in std_logic_vector(3 downto 0);
binary_key : out std_logic_vector(7 downto 0);
is_key_pressed : out std_logic;
is_num_lock_on : out std_logic
);
end component;
component Converter is
port
(
hex_key : in std_logic_vector(7 downto 0);
is_num_lock_on : in std_logic;
output : out std_logic_vector(7 downto 0)
);
end component;
begin
ClockGenerator:
process
begin
while not halt_sys_clock loop
clock <= not clock;
wait for CLOCK_PERIOD / 2.0;
end loop;
wait;
end process ClockGenerator;
Stimulus:
process
constant NUM_NOISE_SAMPLES: positive := 10;
constant SWITCH_TIME: time := 2 * DEBOUNCE_PERIOD;
variable seed1: positive := 1;
variable seed2: positive := 1;
variable rrand: real;
variable nrand: natural;
-- Performs noisy transition of sig from current value to final value.
procedure NoisyTransition(signal sig: out std_logic_vector(7 downto 0); final: std_logic_vector(7 downto 0)) is
constant initial: std_logic_vector(7 downto 0) := sig;
begin
for n in 1 to NUM_NOISE_SAMPLES loop
uniform(seed1, seed2, rrand);
nrand := natural(round(rrand));
if nrand = 0 then
sig <= initial;
else
sig <= final;
end if;
wait for CLOCK_PERIOD / 5.0;
end loop;
sig <= final;
wait for SWITCH_TIME;
end;
begin
reset <= '0';
input <= "00000000";
wait for 3 ns;
reset <= '1';
wait for CLOCK_PERIOD;
reset <= '0';
--
-- Input
--
-- Perform some noisy presses and releases.
NoisyTransition(input, "00010001"); -- Row 1, Column 1: Num Lock
NoisyTransition(input, "00000000");
NoisyTransition(input, "00100010"); -- Row 2, Column 2: 8
NoisyTransition(input, "00000000");
NoisyTransition(input, "00110011"); -- Row 3, Column 3: 6
NoisyTransition(input, "00000000");
NoisyTransition(input, "01000100"); -- Row 4, Column 4: ENTER
NoisyTransition(input, "00000000");
NoisyTransition(input, "00010001"); -- Row 1, Column 1: Num Lock
NoisyTransition(input, "00000000");
NoisyTransition(input, "00100010"); -- Row 2, Column 2: 8
NoisyTransition(input, "00000000");
NoisyTransition(input, "00110011"); -- Row 3, Column 3: 6
NoisyTransition(input, "00000000");
NoisyTransition(input, "01000100"); -- Row 4, Column 4: ENTER
NoisyTransition(input, "00000000");
halt_sys_clock <= true;
wait;
end process;
S1:
Sync
generic map
(
SYNC_BITS => 3,
BIT_WIDTH => 8 -- 4 bits for rows and 4 bits for columns.
)
port map
(
clock => clock,
input => input,
output => input_sync
);
D1:
Debounce
generic map
(
CLOCK_PERIOD => CLOCK_PERIOD,
DEBOUNCE_PERIOD => DEBOUNCE_PERIOD,
BIT_WIDTH => 8 -- 4 bits for rows and 4 bits for columns.
)
port map
(
clock => clock,
input => input_sync,
output => input_sync_db,
change => change
);
M1:
Multiplexer
port map
(
reset => reset,
clock => clock,
key_pressed_strobe => change,
columns => columns,
rows => rows,
binary_key => key,
is_key_pressed => is_key_pressed,
is_num_lock_on => is_num_lock_on
);
C1:
Converter
port map
(
hex_key => key,
is_num_lock_on => is_num_lock_on,
output => ascii
);
end architecture;
Simulación de teclado
Quartus BDF del teclado
Quartus RTL del teclado