Intel® Quartus® Prime Software
Intel® Quartus® Prime Design Software, Design Entry, Synthesis, Simulation, Verification, Timing Analysis, System Design (Platform Designer, formerly Qsys)

Keypad Scanner help!

Altera_Forum
Honored Contributor II
3,082 Views

Hi, 

I am new to VHDL. I am having issues implementing a 3 x 3 keypad scanner. The similar technique I have used in C code but it doesn't seem to work with VHDL. I scan each column by putting a logic zero on it , then reading each row and looking for a zero which would tell me on which column and row the key has been pressed. In this case however, by using my code below, it only detects keys pressed in the final column (3, 6 , 9) as it is the last test in the process. Could someone please tell me why this is wrong and suggest a proper way to do this. I appreciate your help. Thanks alot in advance. 

 

------------------------------------------------------------------ ENTITY KeypadScanner IS PORT ( clock_50 : IN STD_LOGIC ; LED : OUT STD_LOGIC_VECTOR (7 DOWNTO 0); Keypad_write : OUT STD_LOGIC_VECTOR (3 DOWNTO 0); Keypad_read : IN STD_LOGIC_VECTOR (3 DOWNTO 0) ); END KeypadScanner; ------------------------------------------------------------------- ARCHITECTURE behaviour of KeypadScanner IS SIGNAL delay : INTEGER:= 1000; BEGIN PROCESS (clock_50) BEGIN Keypad_write <= "0111" after 10000 ns; --put logic zero on 1st column and scan the rows If (Keypad_read = "0111" ) THEN LED<= "00000001"; ELSIf (Keypad_read = "1011") THEN LED<= "00000100"; ELSIf (Keypad_read = "1101") THEN LED<= "00000111"; END IF; Keypad_write <= "1011" after 10000 ns;--put logic zero on 2nd column and scan the rows If (Keypad_read = "0111") THEN LED<= "00000010"; ELSIf (Keypad_read = "1011") THEN LED<= "00000101"; ELSIf (Keypad_read = "1101") THEN LED<= "00001000"; END IF; Keypad_write <= "1101" after 10000 ns;--put logic zero on 3rd column and scan the rows If (Keypad_read = "0111") THEN LED<= "00000011"; ELSIf (Keypad_read = "1011") THEN LED<= "00000110"; ELSIf (Keypad_read = "1101") THEN LED<= "00001001"; END IF; END PROCESS; END behaviour;
0 Kudos
12 Replies
Altera_Forum
Honored Contributor II
1,991 Views

Hi, welcome to the dark side of VHDL if you are a software programmer !!! ;-) 

 

--- Quote Start ---  

VHDL is NOT a programming language. It describes hardware. 

--- Quote End ---  

 

 

1) Do you want you code to be synthesized (and to be put on FPGA) ?  

If so, forget the clauses "after", they are simply ignored. 

That why only the 3rd part of your code is "active", the others are simply optimized away, because Keypad_write will always be "1101" (You could find that in Quartus warning messages) 

 

2) Make a really synchronous process, in fact a D Flip Flop with Enable like that :  

PROCESS (clock_50) BEGIN IF rising_edge(clock_50) THEN -- clock signal IF pulse_10us = '1' then -- enable signal -- your sequential synchronous statements here END IF; END IF; -- absolutely nothing here END PROCESS;  

 

3) You need a signal that supplies 1 pulse whose width = clock_50 period, whose period is 10 us. 

You can obtain that with a synchronous counter (quite easy to design, good exercise). 

 

4) You need a one-hot counter ("0001" then "0010" then "0100" then "1000") simply by rotate one hot bit each 10us (= the above Enable signal). 

that you simply invert for "keyboard_write" 

 

5) IMPORTANT 

You don't cover all possible cases !! 

 

6) Don't use multiple IF statements in a process, because the signals are affected by the last statement 

In your case, you should use  

CASE Keypad_write IS WHEN "0111" => If (Keypad_read = "0111" ) THEN LED<= "00000001"; ELSIf (Keypad_read = "1011") THEN LED<= "00000100"; ELSIf (Keypad_read = "1101") THEN LED<= "00000111"; END IF; WHEN .... => .... WHEN OTHERS => .... END CASE;  

Be strongly aware that there is a latence of 10us on Keyboard_write, so you need to insert one 10us Enable D Flip Flop on Keyboard_read
0 Kudos
Altera_Forum
Honored Contributor II
1,991 Views

Thanks alot for the great reply. I understand that VHDL is for describing hardware. My problem is that I have been writing so much software and now when I go to write VHDL , I am in the same mind frame ))  

Yes, I would like my code to be synthesized. I am just wondering, why is the 10 us delay necessary? Cheers
0 Kudos
Altera_Forum
Honored Contributor II
1,991 Views

 

--- Quote Start ---  

 

 

5) IMPORTANT 

You don't cover all possible cases !! 

 

 

--- Quote End ---  

 

 

If the code is synchronous, then this is not important.
0 Kudos
Altera_Forum
Honored Contributor II
1,991 Views

So here is my second attempt. It works better, but the correct values are displayed on the LEDs only when I keep the key pressed down. When I release, the value displayed is the one on the third column again on the same row. Does anyone know where I am still going wrong? Also, are there too many processes here? Could I have designed this a better way? Thanks very much for any help as I am just in the learning phase still with VHDL. 

 

ARCHITECTURE behaviour of KeypadScanner IS SIGNAL pulse_10us : STD_LOGIC; SIGNAL count : INTEGER := 0; SIGNAL Key_write : STD_LOGIC_VECTOR (3 DOWNTO 0) := "0111"; BEGIN Keypad_write<= Key_write; ---------------------------- PROCESS (clock_50) BEGIN If (clock_50'EVENT AND clock_50 = '1' ) THEN count<= count + 1; If (count = 500) THEN count<= 0; END IF; END IF; END PROCESS; -------------------------------- PROCESS (clock_50) BEGIN If (clock_50'EVENT AND clock_50 = '1' ) THEN IF (count = 0) THEN pulse_10us<= '1'; ELSIF (count = 1) THEN pulse_10us<= '0'; END IF; END IF; END PROCESS; ------------------------------------- PROCESS (clock_50) BEGIN If (clock_50'EVENT AND clock_50 = '1' ) THEN -- clock signal IF pulse_10us = '1' THEN -- enable signal If (Key_write = "0111") THEN Key_write<= "1011"; ELSIF (Key_write = "1011") THEN Key_write<= "1101"; ELSIF (Key_write = "1101") THEN Key_write<= "0111"; END IF; END IF; END IF; END PROCESS; -------------------------------------------- PROCESS (clock_50) BEGIN If (clock_50'EVENT AND clock_50 = '1' ) THEN -- clock signal IF pulse_10us = '1' THEN -- enable signal CASE Key_write IS WHEN "0111" => If (Keypad_read = "0111" ) THEN LED<= "00000001"; ELSIf (Keypad_read = "1011") THEN LED<= "00000100"; ELSIf (Keypad_read = "1101") THEN LED<= "00000111"; END IF; WHEN "1011" => If (Keypad_read = "0111" ) THEN LED<= "00000010"; ELSIf (Keypad_read = "1011") THEN LED<= "00000101"; ELSIf (Keypad_read = "1101") THEN LED<= "00001000"; END IF; WHEN "1101" => If (Keypad_read = "0111" ) THEN LED<= "00000011"; ELSIf (Keypad_read = "1011") THEN LED<= "00000110"; ELSIf (Keypad_read = "1101") THEN LED<= "00001001"; END IF; WHEN OTHERS => LED<= "11111111"; END CASE; END IF; END IF; END PROCESS; ------------------------------------------ END behaviour;
0 Kudos
Altera_Forum
Honored Contributor II
1,991 Views

powerful you have become ! 

 

why is the 10 us delay necessary? 

First, Keyboard_read is asynchronous, to keep the logic synchronous, you need to synchronize the inputs (by adding a DFF (D Flip Flop))  

Better is to put at least 2 DFF on inputs to prevent metastability. 

 

Because, In the process, " Keypad_write <= "0111"; " means that Keypad_write is updated at the end or process and NOT immediately. 

So when you read "Keypad_read", your read the state of your keypad when keypad_write is in the state BEFORE "0111". 

To read "Keypad_read" with correct Keypad_write, you have to wait the next clock. 

 

Examples are available :  

http://www.coe.montana.edu/ee/lameres/courses/eele367_spring13/misc_handouts/chapter_08_notes.pdf 

http://www.cems.uwe.ac.uk/~ngunton/worksheets/keypad.vhd 

 

EDIT: 

Some tricks: 

PROCESS (clock_50) BEGIN If (clock_50'EVENT AND clock_50 = '1' ) THEN -- it is a count down here -- produce pulse when reach 0 If (count = 0) THEN -- little better to compare to 0 count<= 500; pulse_10us <= '1'; ELSE count <= count -1; pulse_10us <= '0' END IF; END IF; END PROCESS;  

 

 

Usually, you miss the last ELSE statement in IF...THEN...ELSIF...ELSE....END IF; 

This missing can produce lacthes or combinatory loops which are very bad.
0 Kudos
Altera_Forum
Honored Contributor II
1,991 Views

I still don't understand why this is wrong. Initially, at the beginning of the Architecture , I initialise Keypad_write with "0111", so on the first clock cycle, the read process should read Keypad_write as "0111" and perform the correct action.? Then the next clock cycle after that, the write process would have previously changed Keypad_write to "1011" and so the read process will then process it. Could you please show me what I am doing wrong and how to resolve this? It's driving me mad. Help much appreciated. Thanks alot

0 Kudos
Altera_Forum
Honored Contributor II
1,990 Views

YES YES YES ! with your previous code, you are right.  

Sorry I hadn't read the whole code. 

 

A common mistake for beginner is  

... SIGNAL sig : std_ulogic_vector(3 downto 0) := "0000"; PROCESS (clk) BEGIN If rising_edge(clk) THEN sig <= "1010"; -- sig will be affected at the end on the process, so here at the first clk edge, -- sig still equals to "0000" for this time ! IF sig = "1010" then -- instructions here -- that are NOT executed because sig = "0000" -- will be executed at the next clk edge ELSE -- instructions that will be executed the first time END IF; END IF; END PROCESS;
0 Kudos
Altera_Forum
Honored Contributor II
1,991 Views

@Tricky : It is not important because it keeps the signals BUT can produce unwanted behaviour.

0 Kudos
Altera_Forum
Honored Contributor II
1,991 Views

 

--- Quote Start ---  

@Tricky : It is not important because it keeps the signals BUT can produce unwanted behaviour. 

--- Quote End ---  

 

 

Assinging a signal in ALL cases in a clocked process can also produce unwanted behaviour, or just extra unwanted code. 

I want to increment a counter only when another counter = 10 

 

process(clk) begin if rising_edge(clk) then if cnta = 10 then cntb <= cntb + 1; else cntb <= cntb; --there is no point in this code. It makes NO difference to the behaviour. end if; end if; end process;
0 Kudos
Altera_Forum
Honored Contributor II
1,991 Views

I still can't see where I am going wrong.  

 

The Keypad behaves normally only with the keys pressed in the final column.  

 

If the keys in the second column are pressed, the correct value is displayed on the LEDs only as long as the the key is held pressed, when released, it displays the key on the same row but third column.  

 

If the keys in the first column are pressed, it has no effect.  

 

Help ))
0 Kudos
Altera_Forum
Honored Contributor II
1,991 Views

Hi, do you pulse_10us is really 20ns high (=period of clock_50) ? I have a doubt. 

Do you scope the row and col of your keypad ? 

 

row = key_write, col = key_read ? 

 

I have simulated your design,  

LED are dangling with 1,2,3 or 4,5,6 or 7,8,9, (each same row) You have to stop scanning when you encounter a key press. 

The final value of LED depends at which moment you depress the key. BUT your description on the post above let me think that you may have wrong connection between FPGA and Keypad. 

 

 

Regards
0 Kudos
Altera_Forum
Honored Contributor II
1,991 Views

Thanks for taking time to investigate this. My pulse is 10us high. When scanning the keypad, I write a zero on each column and read the rows. This works fine when I implement the same technique in software and run it on the NIOS. How do I stop scanning when I encounter a key press here in this case? Sorry, I am new to VHDL. I have done lots of software programming at college but I am determined to learn VHDL on my own.

0 Kudos
Reply