0
\$\begingroup\$

I have a simple board with 6 buttons, consisting of 3 columns and 2 rows I would like to detect the pressed button. My code below works with this 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;

I have 4 states, start and a state for each column. I would like to improve my board and prevent glitches. I would like to add fatures like if 2 buttons pressed at the same time, I should get the first value which reached few miliseconds or nanoseconds before the other one. Also I would like to prevent a second value being read or a third when already a button is being pressed.

So, no matter how many buttons pressed, only one value should be read. I thought about adding a 5th state, idle, when this occurs but then how do I get out of idle?

Instead of idle I can also switch to state "start" but then this would make the send signal "0" which would be false if already a button is being pressed. Also how many processes are in this FSM? What is a process in VHDL? Is it 3; reset, clk and state_inc? statce_inc becomes "1" every 200Hz with the help of a counter. I added some debouncing code I found from the internet but doesn't help :( Anyone has a debouncing suggestion?

Another issue, I would like to get rid of the state start actually, the only reason I have it is because I should assign my_switch to "001" before state1 . In my first implementation I was assigning my_switch to "001" in state1 and "010" in state2 and "100" in state3. In this implementation since my keypad is

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

When I pressed 2, 1 was displayed when I press 3, 2 was displayed. I was thinking Cpp/Java way but I was wrong... then I added a dummy state start just to set my_switch before entering the real scanning... So if I can solve this I can also save a state! Suggestions are welcome! I solved this with entering the first scan without a set value. Do you have better ideas?

\$\endgroup\$
6
  • \$\begingroup\$ What is bcd_val? Which signals are your column and row inputs? \$\endgroup\$
    – stanri
    Commented May 29, 2014 at 20:12
  • \$\begingroup\$ @StaceyAnne my_switch (column) is output signal and bcd_val(row) is input , thus bcd_val should be 2 bits long sorry for that I'm updating now the code \$\endgroup\$
    – Anarkie
    Commented May 29, 2014 at 20:33
  • \$\begingroup\$ What is the state_inc for? \$\endgroup\$
    – stanri
    Commented May 30, 2014 at 8:22
  • \$\begingroup\$ @StaceyAnne Thanks a lot firstly for the answer! state_inc is an enable signal, it is '1' every 200 Hz \$\endgroup\$
    – Anarkie
    Commented May 30, 2014 at 11:22
  • \$\begingroup\$ I'm curious to know why you're enabling the state machine periodically, it should work enabled all the time. \$\endgroup\$
    – stanri
    Commented May 30, 2014 at 13:25

1 Answer 1

2
\$\begingroup\$

What you can do is create an idle state, and when you detect a button press you switch straight to the idle. Then you can have a counter in idle which waits for a while before you start scanning buttons again.

For example:

-- 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";

There is one process in this code you've posted.

It starts with

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

and ends with

end process scan_fsm;

The clock and reset are part of the sensitivity list. That means that this code is only going to be triggered when either clock changes or reset changes.

De-bouncing can be done in a separate process. Basically, you want to make sure that the button was pressed for a number of microseconds before it triggers. What we want to do is save the value of the input for a number of clock cycles. Then only register a 1 when the history has been constantly 1 for for the past 32 clock cycles.

Example:

-- 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
\$\endgroup\$
1
  • \$\begingroup\$ I have one more question when I press 3 and 1 at the same time scanned_val gets 0, when I press 3 and 6 it also gets 0 so the same column and row keys get 0 when they are both pressed at the same time because of when others => scannel_val <= "0000"; I think the result should be zero as well when I press 3 and 4 at the same time because column gets 001 and 100 then becomes 101 which should be considered as "others", isnt it? Or I would like to change the code instead of making the values zero when pressed at the same time from the same column or row, keep the first pressed value? \$\endgroup\$
    – Anarkie
    Commented May 31, 2014 at 8:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.