Lab 4
Lab 4
2. Keypad Interface
We will be using a 16-button keypad module PmodKYPD which plugs into one of the PMOD
connectors on the Nexys2 board. The module and its connections are shown in Figure 1. The 16-
buttons are labeled “0 1 2 3 4 5 6 7 8 9 A B C D E F”, so we can use this keyboard to enter
numbers in hexadecimal format. Documentation on the module can be found on the course
website.
The keyboard is set up as a matrix in which each row of buttons from left to right are tied to a
row pin, and each column from top to bottom is tied to a column pin. There are four row pins and
four column pins. When a button is pushed, it connects its row pin to its column pin.
Each row is normally held high (‘1’) by a pull-up resistor connected to VCC. To test the buttons
in a particular column, that column should be driven low (‘0’), while all other columns are held
high. If a button in that row is depressed while its column pin is low, it will pull the
corresponding row pin low. The FPGA can therefore read the state of the buttons by driving the
column lines and reading the row lines. The FPGA “walks” a logic ‘0’ through each column line
(in turn, while keeping the other column lines high) and reads the row pins to determine if any
buttons have been pushed.
3. Hardware Setup
Plug the PmodKYPD module into PMOD jack JA1 on the Nexys2 boards as shown in Figure 2.
Note that the PmodKYPD uses all 12 pins of jack JA1 and must be connected with a 12 pin
cable. Note the white alignment marks on the connectors as shown in Figure 2. The alignment
mark should be on the top-right for the connector attaching to the Nexys2 board and the top-left
for the connector attaching to the PmodKYPD module. Please be careful not to bend the pins on
the PmodKYPD module.
2
4.2 Add Source for “keypad”
Create a new VHDL source module called keypad and load the following source code into the
edit window. Expand the Synthesize command in the Process window and run Check Syntax
to verify that you have entered the code correctly.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity keypad is
Port ( samp_ck : in STD_LOGIC; -- clock to strobe columns
col : out STD_LOGIC_VECTOR (4 downto 1); -- output column lines
row : in STD_LOGIC_VECTOR (4 downto 1); -- input row lines
value : out STD_LOGIC_VECTOR (3 downto 0); -- hex value of key depressed
hit : out STD_LOGIC); -- indicates when a key has been pressed
end keypad;
-- This process synchronously tests the state of the keypad buttons. On each edge of samp_ck,
-- this module outputs a column code to the keypad in which one column line is held low while the
-- other three column lines are held high. The row outputs of that column are then read
-- into the corresponding column vector. The current column is then updated ready for the next
-- clock edge. Remember that curr_col is not updated until the process suspends.
strobe_proc: process
begin
wait until rising_edge(samp_ck);
case curr_col is
when "1110" =>
CV1 <= row; curr_col <= "1101";
when "1101" =>
CV2 <= row; curr_col <= "1011";
when "1011" =>
CV3 <= row; curr_col <= "0111";
when "0111" =>
CV4 <= row; curr_col <= "1110";
when others =>
curr_col <= "1110";
end case;
end process;
-- This process runs whenever any of the column vectors change. Each vector is tested to see
-- if there are any '0's in the vector. This would indicate that a button had been pushed in
-- that column. If so, the value of the button is output and the hit signal is assereted. If
-- not button is pushed, the hit signal is cleared
3
out_proc: process (CV1, CV2, CV3, CV4)
begin
hit <= '1';
if CV1(1) = '0' then value <= X"1";
elsif CV1(2) = '0' then value <= X"4";
elsif CV1(3) = '0' then value <= X"7";
elsif CV1(4) = '0' then value <= X"0";
elsif CV2(1) = '0' then value <= X"2";
elsif CV2(2) = '0' then value <= X"5";
elsif CV2(3) = '0' then value <= X"8";
elsif CV2(4) = '0' then value <= X"F";
elsif CV3(1) = '0' then value <= X"3";
elsif CV3(2) = '0' then value <= X"6";
elsif CV3(3) = '0' then value <= X"9";
elsif CV3(4) = '0' then value <= X"E";
elsif CV4(1) = '0' then value <= X"A";
elsif CV4(2) = '0' then value <= X"B";
elsif CV4(3) = '0' then value <= X"C";
elsif CV4(4) = '0' then value <= X"D";
else hit <= '0'; value <= X"0";
end if;
end process;
end Behavioral;
_______________________________________________________________________________
This module synchronously tests the state of the keypad buttons. On each rising edge of the
clock samp_ck (approx. every 1.5 ms) the module outputs a code to the 4 column lines of the
switch matrix, a code which will enable exactly one column. The first time it outputs 1110,
setting column 1 low and all others high. On the second edge, it outputs 1101, setting column 2
low and all others high, continuing to columns 3 and 4 and then returning to column 1 to repeat
the process. Each time it enables one of the columns, it reads the row lines into a column vector
to determine if any button in that column has been pushed. A separate process runs whenever
any of the column vectors change. When that happens, the process searches for a ‘0’ in any of
the column vectors. If it finds a ‘0’, it outputs the appropriate hex value and asserts the hit signal.
If there are no ‘0’s in the column vectors (meaning no buttons are currently being pushed), it
clears the hit signal. The code assumes that only one button is pushed at a time.
4
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity leddec16 is
Port (dig : in STD_LOGIC_VECTOR (1 downto 0); -- which digit to currently display
data : in STD_LOGIC_VECTOR (15 downto 0); -- 16-bit (4-digit) data
anode: out STD_LOGIC_VECTOR (3 downto 0); -- which anode to turn on
seg : out STD_LOGIC_VECTOR (6 downto 0)); -- segment code for current digit
end leddec16;
5
This module is similar to the code developed in Lab 2 to display a 16-bit hex counter. The four
7-segment displays are time multiplexed using an input signal dig which counts from 0 to 3 at a
rate of approx. 380 Hz. The four digits are thus refreshed approx. 95 times per second. The
segment code determines which anode to take low (‘0’). It also determines which 4-bits of the
16-bit data to display. These four bits are translated into a 7-segment code and output to the
common display segment lines.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity hexcalc is
Port ( clk_50MHz : in STD_LOGIC; -- system clock (50 MHz)
SEG7_anode : out STD_LOGIC_VECTOR (3 downto 0); -- anodes of four 7-seg displays
SEG7_seg : out STD_LOGIC_VECTOR (6 downto 0); -- common segments of 7-seg displays
bt_clr: in STD_LOGIC; -- calculator "clear" button
bt_plus: in STD_LOGIC; -- calculator "+" button
bt_eq: in STD_LOGIC; -- calculator "-" button
KB_col: out STD_LOGIC_VECTOR (4 downto 1); -- keypad column pins
KB_row: in STD_LOGIC_VECTOR (4 downto 1 )); -- keypad row pins
end hexcalc;
6
signal nx_operand, operand: std_logic_vector (15 downto 0); -- operand
signal display: std_logic_vector (15 downto 0); -- value to be displayed
signal led_mpx: STD_LOGIC_VECTOR (1 downto 0); -- 7-seg multiplexing clock
begin
ck_proc: process(clk_50MHz)
begin
if rising_edge( clk_50MHz) then -- on rising edge of clock
cnt <= cnt+1; -- increment counter
end if;
end process;
kp_clk <= cnt(15); -- keypad interrogation clock
sm_clk <= cnt(20); -- state machine clock
led_mpx <= cnt(18 downto 17); -- 7-seg multiplexing clock
kp1: keypad port map (samp_ck => kp_clk, col => KB_col,
row => KB_row, value => kp_value, hit => kp_hit);
led1: leddec16 port map (dig => led_mpx, data => display,
anode => SEG7_anode, seg => SEG7_seg);
7
nx_state <= ACC_RELEASE;
elsif bt_plus = '1' then
nx_state <= START_OP;
else nx_state <= ENTER_ACC;
end if;
when ACC_RELEASE => -- waiting for button to be released
if kp_hit = '0' then
nx_state <= ENTER_ACC;
else nx_state <= ACC_RELEASE;
end if;
when START_OP => -- ready to start entering 2nd operand
if kp_hit = '1' then
nx_operand <= X"000" & kp_value;
nx_state <= OP_RELEASE;
display <= operand;
else nx_state <= START_OP;
end if;
when OP_RELEASE => -- waiting for button ot be released
display <= operand;
if kp_hit = '0' then
nx_state <= ENTER_OP;
else nx_state <= OP_RELEASE;
end if;
when ENTER_OP => -- waiting for next digit in 2nd operand
display <= operand;
if bt_eq = '1' then
nx_acc <= acc + operand;
nx_state <=SHOW_RESULT;
elsif kp_hit = '1' then
nx_operand <= operand(11 downto 0) & kp_value;
nx_state <= OP_RELEASE;
else nx_state <= ENTER_OP;
end if;
when SHOW_RESULT => -- display result of addition
if kp_hit = '1' then
nx_acc <= X"000" & kp_value;
nx_state <= ACC_RELEASE;
else nx_state <= SHOW_RESULT;
end if;
end case;
end process;
end Behavioral;
_____________________________________________________________________
This is the top level that hooks it all together. This module:
a) Creates an instance of the keypad interface and 7-segment decoder interface modules
b) Make connection to the external keypad, display and buttons
c) Has a timing process to generate clocks for the keypad, display multiplexer and state
machine
8
d) Implements a finite state machine to implement the operations of the calculator in
response to button pushes.
The finite state machine uses a number of variables to keep track of the addition operation. The
variable acc is an accumulator that holds the current summation result. The variable operand
holds the value of the next operand that will be added to the accumulator. The variable display
holds the value currently being displayed on the 7-segment displays. The variable pr_state is the
current state of the state machine. Depending on the current state, the machine will react to
pushed keypad buttons or operation buttons to update variables, change the output and select the
next state. Operation of the state machine is summarized in Figure 3.
When the clear button is pushed, the machine enters the ENTER_ACC state. In this state the
machine waits for a keypad button to be pushed. When a keypad button is pushed, the code adds
the new digit to the 16-bit word in the accumulator and then waits in the ACC_RELEASE state
for the button to be released. It then returns to the ENTER_ACC state to wait for the next digit.
This process continues until the “+” button is pushed. The machine then enters the START_OP
state where it waits for the first digit of the second operand. Once a keypad button has been
pushed, it records the hex digit and then enters the OP_RELEASE state waiting for the keypad
9
button to be released. Once released, the machine enters the ENTER_OP state where it continues
to received operand digits each time a keypad button is pushed. This continues until the user
presses the “=’ button at which point it performs the addition and goes to the SHOW_RESULT
state. Once in the SHOW_RESULT state, it shows the result of the addition and waits for a
keypad button push to start a new calculation.
10
Now highlight the hexcalc module in the Hierarchy window and run Implement Design
followed by Generate Programming File (don’t forget to change the FPGA Start-up Clock to
be the JTAG Clock).
11