Using Matrix Keyboard With Educake: 86duino
Using Matrix Keyboard With Educake: 86duino
www.86duino.com
-1-
86Duino
www.86duino.com
列0~3
-2-
86Duino
www.86duino.com
Using push button control, which requires one I/O pin for each button,
it takes 16 I/O pins to provide 16 button control. A 4x4 matrix
keyboard with 16 input controls only occupies 8 I/O pins. The
electronic circuitry to implement matrix keyboard is different from a
simple push button control. Each of the 16 buttons on a 4x4 matrix
keyboard has two pins, where one of the pin is connected to 3 other
buttons on the same row and the other pin is connected to 3 other
buttons on the same column, as shown in Figure-1.
The process and codes to read the status for each button on a matrix
keyboard is more complex than the simple push button control, where
each button is linked to one I/O pin. To read the status of the buttons
on a matrix keyboard, you need to write codes to scan and read button
status one row at a time or one column at a time. Figure-2 shows the
process to scan through and read button status, one row at a time.
-3-
86Duino
www.86duino.com
codes, you can scan through each row or each Column to read button
status. For the exercise in this application note, we will use a 4x4
matrix keyboard, as shown in figure-3.
0 行0~3 3
0 1 2 3 A
4 5 6 B
列0~3
7 8 9 C
3 * 0 # D
針腳定義,依序為:
[列0,列1,列2,列3,行0,行1,行2,行3]
-4-
86Duino
www.86duino.com
Connection from the matrix keyboard for row 0~3 and column 0~3 are
connected to Pin 9~2 on the EduCake. Launch 86Duino Coding IDE
and enter the following codes:
const int Rows = 4; // Number of rows in the matrix
const int Cols = 4; // Number of columns in the matrix
-5-
86Duino
www.86duino.com
void setup()
{
Serial.begin(115200);
}
void loop( )
{
for( int row = 0; row < Rows; row++ ) // Scan column
{
// Voltage for this column goes LOW
digitalWrite( row_pins[row], LOW );
for( int col = 0; col < Cols; col++) // Scan column
{
// Read voltage level from column
// It’s Low when the button is pressed.
boolean result = !digitalRead( col_pins[col] );
-6-
86Duino
www.86duino.com
delay( 20 );
}
After the above sketch (code) is deployed to the EduCake, launch the
serial monitor and press some buttons on the matrix keyboard to see
the associated output to the serial monitor, as shown in the following
figure:
-7-
86Duino
www.86duino.com
In the beginning, there are code entries to configure and set the size
of columns and rows for the matrix keyboard and associate the “char
keys ()” with the button layout on the keyboard, initialize the
“keys_status_last()” array which is used to cache the previous button
status and initialize the “row_pins()” and “col_pins()” arrays to link
the pins from the matrix keypad to the I/O pins on the 86Duino
EduCake.
In the “setup()” function, the I/O pins associated with the
“col_pins()” array are configured as input with internal pullup
resistor, and the I/O pins associated with the “row_pins()” array are
configured as output. The last line of code configure the Baud Rate
for the serial port.
Please note the I/O pins associated with the col_pins() array are
configured as INPUT_PULLUP mode, and the pins associated with the
row_pins() array are configured as OUTPUT and initialized to HIGH.
Each of the pins associated with the row_pins() array is pull low to scan
and detect button status. When a button is pressed, a voltage LOW
condition is created, as shown in the following figure:
Digital Pin
HIGH
HIGH
LOW
In the main program loop, there are two nested For loops. The first
For loop is used to scan the rows. The second For loop, nested within
the first For loop, is used to scan the columns, to read voltage status
associate with each of the button.
-8-
86Duino
www.86duino.com
In the first For loop, as the code scan through each of the rows, the
following line of code set voltage LOW condition to the row being
scanned:
digitalWrite(row_pins[row], LOW);
When the button is not pressed, the column which the button is
attached to is in voltage HIGH condition. When a button is pressed, it
bridges the column which the button is attached to the row currently
scan and cause voltage LOW condition to the column.
In the second For loop, it loops through the following line of code to
detect button press status for the button attached to each of the
column:
Boolean result = !digitalRead(col_pins[col]);
Then, the following line of code compare the current button status
with the previously scanned status to detect the following condition:
Press and hold: When the current status indicates the button
is pressed and the previously scanned status is also pressed, it
indicates the button has been pressed continuously.
New button pressed event: When the current status indicates
the button is pressed and the previously scanned status is not
pressed, it indicates a new button press event.
Button released event: When the current status indicates the
button is not pressed and the previously scanned status is
pressed, it indicates the button was pressed and just released.
While it’s simple, the above button status scanning technique are
useful in different type of application, to enter information needed by
the program, to control program execution, such as code that control
a motor. When press and hold the button, keep the motor running.
When button is released, stop the motor.
-9-
86Duino
www.86duino.com
-10-
86Duino
www.86duino.com
#include <Keypad.h>
if( keypad4X4.getKeys( ) )
{
// Check each of the button within the 4x4 keypad object
for( int i = 0; i < LIST_MAX; i++)
{
// If button status changed, output to serial monitor
if( keypad4X4.key[i].stateChanged )
{
Serial.print("Button ");
// output character symbol for the pressed button
// to serial monitor
Serial.print(keypad4X4.key[i].kchar);
switch( keypad4X4.key[i].kstate )
{
case PRESSED:
Serial.println(" pressed.");
break;
case HOLD:
Serial.println(" hold.");
break;
case RELEASED:
Serial.println(" released.");
break;
case IDLE:
-11-
86Duino
www.86duino.com
Serial.println(" idle.");
}
}
} // end for
} // end if ( keypad4X4.getKeys( ) )
delay( 20 );
}
Compile and upload the above sketch to the EduCake and launch the
serial monitor.
As you press a button on the keypad, you can see corresponding
output from the serial monitor match the pressed key, similar to the
example in the first exercise.
However, in addition to the matrix size declaration, associating button
symbol to the matrix array and I/O pins from the EduCake to the
matrix keypad that are similar to the code in the first exercise, the
[#include <Keypad.h>] statement is added along with the following
line of code that create the Keypad class object to take advantage of
the function provided by the Keypad library, enabling us to simplify
the code needed to work with matrix keypad:
[ Keypad keypad4x4 = Keypad(makeKeymap(keys), row_pins, col_pins,
Rows, Cols ); ]
The only code needed in the setup() section is to initialize the serial
port.
In the main program loop, after using the “ keypad4x4.getKeys() ”
function to read button status from the keypad object, the codes
within the following For loop iterate through the keypad object to read
the status for each of the button and output to the serial monitor:
For(int i = 0; i < LIST_MAX; i++)
Within the above For loop, the Keypad library provide the following
functions that help simplify the code:
- The “ keypad4x4.key[i].stateChanged “ function is used to detect
when the status of the button has changed.
-12-
86Duino
www.86duino.com
-13-
86Duino
www.86duino.com
-14-
86Duino
www.86duino.com
// MAX7219 register
#define max7219_REG_noop 0x00
#define max7219_REG_digit0 0x01
#define max7219_REG_digit1 0x02
#define max7219_REG_digit2 0x03
#define max7219_REG_digit3 0x04
#define max7219_REG_digit4 0x05
#define max7219_REG_digit5 0x06
#define max7219_REG_digit6 0x07
#define max7219_REG_digit7 0x08
#define max7219_REG_decodeMode 0x09
#define max7219_REG_intensity 0x0a
#define max7219_REG_scanLimit 0x0b
#define max7219_REG_shutdown 0x0c
#define max7219_REG_displayTest 0x0f
class LEDmat8{
public:
LEDmat8( int DIN, int LOAD, int CLOCK );
void Init( );
void DrawLED( byte *LED_matrix );
//~LEDmat8( );
void SPI_SendByte( byte data );
void MAX7219_1Unit( byte reg_addr, byte reg_data );
private:
int DIN_pin;
int LOAD_pin;
int CLOCK_pin;
};
-15-
86Duino
www.86duino.com
#include <LEDmat8.h>
void LEDmat8::Init( )
{
pinMode( DIN_pin, OUTPUT );
pinMode( CLOCK_pin, OUTPUT );
pinMode( LOAD_pin, OUTPUT );
while( i > 0 )
{
mask = (0x01 << (i - 1)); // Bitmask, starting from
left
digitalWrite( CLOCK_pin, LOW ); //
if ( data & mask ) { // Use Bitmask to determine
corresponding bit
digitalWrite( DIN_pin, HIGH ); // If it’s 1,DIN
output HIGH
}
else{
digitalWrite( DIN_pin, LOW ); // If it’s 0, DIN output
LOW
-16-
86Duino
www.86duino.com
}
digitalWrite( CLOCK_pin, HIGH ); //
i = i - 1; // move to next bit
}
}
-17-
86Duino
www.86duino.com
void setup ( ) {
LedMatrix.Init();
delay(1000);
}
void loop ( ) {
ClearLED_Data( );
-18-
86Duino
www.86duino.com
-19-
86Duino
www.86duino.com
default:
break;
}
}
} // end for
}
-20-
86Duino
www.86duino.com
After the above code is compiled and uploaded, you can press a
button on the keypad to turn on the corresponding LED.
The above example uses the Keypad library and 8x8 LED application
code from a previous application note, encapsulate the code from the
8x8 LED application into the LEDmat8 library.
The variable declaration in the beginning section is the same as in the
2nd exercise with the following variables for the LED matrix added:
LED_Data_8x8[8] byte array to hold LED display data.
ClearLED_Data() function to clear data in the LED_Data_8x8[]
byte array.
LedMatrix variable that represent the LED matrix object.
Wihin the setup() function, the LedMatrix.Init() function is called to
initialize the LED matrix, looping through the LED matrix and call the
ClearLED_Data() function to clear data. The keypad4x4.getKeys()
function is called to retrieve update keypad button status. Then, the
keypad4x4.key[i].kstate function inside a For loop to read button
status for each of the button, follow by a series of switch statements to
set the corresponding display status to the LED_Data_8x8[] array. The
LedMatrix.DrawLED() function is call last within the Setup() function to
draw the LED display (turning on LED corresponding to the button
pressed).
The LED display corresponding to the button in the above example is
based on the orientation of the matrix keypad and LED matrix, as
shown in the figure below:
-21-
86Duino
www.86duino.com
上
(0, 0)
D # 0 *
C 9 8 7
9127 X A M
B 6 5 4
A 3 2 1
(3, 3) LED_Data_8X8[7]
If the LED display on the LED matrix display does not correspond to
the button press on the keypad, you need to change the orientation of
the keypad, LED matrix or modify the code to get the expected result.
You can experiment and change the button press condition in the
following statement to see different result on the LED matrix:
- If (keypad4x4.key[i].kstate == PRESSED)
-22-
86Duino
www.86duino.com
-23-
86Duino
www.86duino.com
-24-
86Duino
www.86duino.com
Serial.println("-----------------------------------------------")
;
Serial.println("Game end!");
Serial.print(" Total Score : ");Serial.println(score);
Serial.println(" - Press 'S' or 'R' to play again.");
Serial.println("-----------------------------------------------")
;
runGame = false;
void setup ( ) {
LedMatrix.Init( );
randomSeed( analogRead(0) );// Initialize random number generator
Serial.begin( 115200 );
delay( 4000 );
Serial.println("--------------------------------------------");
Serial.print(" You Have ");
Serial.print(GAME_TIME);
Serial.println(" Seconds To Play Each Game.");
Serial.println(" - Press 'S' To Start Game.");
Serial.println(" - Press 'R' To Reset Game.");
Serial.println(" - Press 'E' To End Game.");
Serial.println("--------------------------------------------");
-25-
86Duino
www.86duino.com
void loop ( ) {
loopCount++;
if(loopCount>LOOPCOUNT_MAX){
loopCount = 0;
}
-26-
86Duino
www.86duino.com
-27-
86Duino
www.86duino.com
-28-
86Duino
www.86duino.com
default:
break;
}
}
}// end for
}
}
}
delay( DELAY_TIME );
-29-
86Duino
www.86duino.com
Compile and upload the sketch to EduCake. Then, launch the serial
monitor. As the code execute, the serial monitor display information
showing how to start, reset and end the game. Each game session
goes on for 30 seconds. When you start the game, the serial monitor
display score for the game as you play, as shown in the following
figure:
The code for this exercise includes variables such as「score」to keep
track of game score,「gameTime」to keep track of elapsed game time,
「runGame」to indicate whether a game is active and「loopCount」to
keep track of number of game loop and update data accordingly. In
addition to these variables, the following #define statements were
used to define game parameters:
#define DELAY_TIME (To define the delay time between loop)
#define LOOPCOUNT_MAX (To define the maximum loop before
refresh the game)
-30-
86Duino
www.86duino.com
The「ClearMoleData( )」「
, ClearKeyData( )」「
, GameStart ( )」and「GameEnd
( )」functions are used to control game flow.
Within the「setup ( )」function, to support the random variables needed for
the game, the 「randomSeed( analogRead(0) );」statement is used to
initialize random variable generator. The「analogRead(0)」function, used
as the parameter for the randomSeed() function, is link to an I/O pin that
is not connected, to insure randomness.
Following is the flow chart for the game:
loop( ) 如果收到S
開始遊戲
runGame 為 true
檢查遊戲執行狀態
還沒超過 GAME_TIME*1000
runGame 為 false
檢查遊戲進行時間
如果loopCount到達最大值,則產生亂
已超過 數地鼠地圖
結束遊戲 偵測按鍵狀態,並建立新的按鍵地圖
檢查按鍵地圖與地鼠地圖,當按鍵按下
的地方符合地鼠出現位置,則消除地鼠,
分數+1
依據消除後的地鼠地圖繪製畫面,
將4x4的地圖資料擴充到8x8 LED 矩陣
所需資料
繪製LED圖案
-31-
86Duino
www.86duino.com
Mole_Data[4][4]
地鼠地圖
LED_Data_8X8[8]
(0, 0)
用在LED顯示
新地鼠地圖
(3, 3)
Key_Data[4][4]
按鍵地圖 一對一檢查兩地圖
相同位置,如果都 擴充資料為8x8
(0, 0)
是1,則把地鼠地圖
此位置的數值=0,
同時得分數值+1
(3, 3)
實際按鍵符
號對應位置
D # 0 *
C 9 8 7
B 6 5 4
A 3 2 1
-32-
86Duino
www.86duino.com
that can appear at the same time) and gameTime (which control
available time to play the game).
-33-