interruptores de estado en FSM

0

Tengo un tablero simple con 6 botones, que consta de 3 columnas y 2 filas. Me gustaría detectar el botón presionado. Mi código de abajo funciona con este fsm:

  scan_fsm : process (reset, clk)
  begin  -- process key_scanner

     if reset = '1' then 
            Send <= '0';
            fsm_state <= start;
            scannel_val <= "0000";

    elsif clk'event and clk = '1' then

      if state_inc = '1' then

        scan_complete <= '0';

        case fsm_state is

          when start =>
               Send <= '0';
            scannel_val <= "0000";
            my_switch   <= "001";
            fsm_state <= state1;

          when state1 => 
            case bcd_val is
              when "01" =>
                    Send <= '1';
                    scannel_val <= "0001";
              when "10" =>
                    Send <= '1';
                    scannel_val <= "0010"; -- Value 2 
              when others =>
                    scannel_val <= "0000";
                    Send <= '0';
            end case;
            my_switch   <= "010";
            fsm_state <= state2;

          when state2 => 
            case bcd_val is
              when "01" => 
                  Send <= '1';
                  scannel_val <= "0011"; 
              when "10" => 
                  Send <= '1';
                  scannel_val <= x"0100";  -- Value 4
              when others => 
                    scannel_val <= "0000";
                    Send <= '0'; 
            end case;
            my_switch   <= "100";
            fsm_state <= state3;

          when state3 => 
            case bcd_val is
              when "01" => 
                    Send <= '1';
                    scannel_val <= "0101"; 
              when "10" => 
                    Send <= '1';
                    scannel_val <= "0110";
              when others => 
                    scannel_val <= "0000";
                    Send <= '0'; 
            end case;
                my_switch   <= "001";
            fsm_state <= state1;
            scan_complete <= '1';

           when others => scannel_val <= "0000";
        end case;

      end if;
    end if;
  end process scan_fsm;

Tengo 4 estados, inicio y un estado para cada columna. Me gustaría mejorar mi tabla y evitar fallos. Me gustaría agregar valores como si al presionar 2 botones al mismo tiempo, debería obtener el primer valor que alcanzó pocos milisegundos o nanosegundos antes que el otro. También me gustaría evitar que se lea un segundo valor o un tercero cuando ya se está presionando un botón.

Entonces, no importa cuántos botones se presionen, solo se debe leer un valor. Pensé en agregar un quinto estado, inactivo, cuando esto ocurre, pero entonces, ¿cómo puedo salir del inactivo?

En lugar de inactivo, también puedo cambiar al estado "inicio", pero esto haría que la señal send "0" sea falsa si ya se presiona un botón. Además, ¿cuántos procesos hay en este FSM? ¿Qué es un proceso en VHDL? ¿Es 3? resetear, clk y state_inc? statce_inc se convierte en "1" cada 200Hz con la ayuda de un contador. Agregué un código de rebote que encontré en Internet, pero no ayuda :( ¿Alguien tiene una sugerencia de rebote?

Otro problema, me gustaría deshacerme del estado start en realidad, la única razón por la que lo tengo es porque debo asignar my_switch a "001" antes de state1 . En mi primera implementación, estaba asignando my_switch a "001" en state1 y "010" en state2 y "100" en state3 . En esta implementación ya que mi teclado es

______
|1|2|3|
|4|5|6|

Cuando presioné 2, se mostró 1 cuando presioné 3, se mostró 2. Estaba pensando en Cpp / Java pero estaba equivocado ... luego agregué un estado ficticio start solo para configurar my_switch antes de ingresar al escaneo real ... ¡Así que si puedo resolver esto también puedo guardar un estado! Las sugerencias son bienvenidas! Resolví esto al ingresar el primer escaneo sin un valor establecido. ¿Tienes mejores ideas?

    
pregunta Anarkie

1 respuesta

2

Lo que puedes hacer es crear un estado inactivo, y cuando detectas un botón, cambias directamente al modo inactivo. Luego, puede tener un contador inactivo que espera un rato antes de comenzar a escanear los botones nuevamente.

Por ejemplo:

-- include numeric std
signal idle_counter : unsigned(31 downto 0);

      ...

      when state3 => 
        case bcd_val is
          when "01" => 
                Send <= '1';
                scannel_val <= "0101"; 
                fsm_state <= idle;   -- switch to idle
          when "10" => 
                Send <= '1';
                scannel_val <= "0110";
                fsm_state <= idle;   -- switch to idle
          when others => 
                scannel_val <= "0000";
                Send <= '0'; 
                fsm_state <= state1;   -- carry on scanning
        end case;
        idle_counter <= (others => '0');
        ...
      when idle =>
        if (idle_counter < some_timeout) then  -- some_timeout is how long you want to wait (in clock cycles)
           idle_counter <= idle_counter + "1"; -- increment counter
           fsm_state <= idle;                  -- stay here
        else
           idle_counter <= (others => '0');    -- reset counter
           fsm_state <= state1;                -- go back into checking
        end
        --scannel_val is valid here, so you can do something with it if you'd like.
        ...
      when others => scannel_val <= "0000";

Hay un proceso en este código que has publicado.

Comienza con

scan_fsm : process (reset, clk) <= Sensitivity list
begin  -- process key_scanner

y termina con

end process scan_fsm;

El reloj y el reinicio forman parte de la lista de sensibilidad. Eso significa que este código solo se activará cuando el reloj cambie o el reinicio cambie.

La eliminación de rebotes se puede hacer en un proceso separado. Básicamente, desea asegurarse de que se presionó el botón durante varios microsegundos antes de activarse. Lo que queremos hacer es guardar el valor de la entrada para varios ciclos de reloj. Luego, solo registre un 1 cuando el historial haya sido constantemente 1 durante los últimos 32 ciclos de reloj.

Ejemplo:

-- these are our row inputs (to become bdc_val)
signal row1 : in std_logic; 
signal row2 : in std_logic;

...
-- this is our history vector
signal row1_z : std_logic_vector(31 downto 0);
signal row2_z : std_logic_vector(31 downto 0);

-- debounced signal
signal bcd_val : std_logic_vector(1 downto 0);

-- a whole vector of ones for convenience
signal ones    : std_logic_vector(31 downto 0);
...

ones <= (others => '1');

-- generate our histories
gen_z : process (reset, clk)
begin  
  if (reset = '1') then
    row1_z <= (others => '0');
    row2_z <= (others => '0');
    bcd_val <= (others => '0');
  elsif (rising_edge(clk)) then
    row1_z(31 downto 1) <= row1_z(30 downto 0); -- shift everything up 1
    row1_z(0) <= row1;                          -- save the most recent input
    row2_z(31 downto 1) <= row2_z(30 downto 0); -- shift everything up 1
    row2_z(0) <= row2;                          -- save the most recent input

    -- we only want bcd_val to be 1 when the entire row history for the past 32 cc is 1
    if (row1_z = ones) then
      bcd_val(0) <= '1';
    else 
      bcd_val(0) <= '0';
    end

    if (row2_z = ones) then
      bcd_val(1) <= '1';
    else 
      bcd_val(1) <= '0';
    end
  end
end
    
respondido por el stanri

Lea otras preguntas en las etiquetas