Skip to content

Commit 3e6afa1

Browse files
author
Jim Lindblom
committed
Keypad engine functions and arduino example
Added keypad(byte rows, byte columns, byte sleepTime, byte scanTime) and nsigned int readKeyData() to sx1509Class
1 parent 9a60047 commit 3e6afa1

File tree

4 files changed

+308
-1
lines changed

4 files changed

+308
-1
lines changed
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
/* SX1509 Library Example 03
2+
Keypad interfacing
3+
by: Jim Lindblom
4+
SparkFun Electronics
5+
license: Beerware. Please use, reuse, share, and modify this
6+
code. I'd ask that you maintain attribution and open-source.
7+
If you find it useful, you can buy me a beer when we meet
8+
some day.
9+
10+
This example shows how you can use the following SX1509
11+
library methods:
12+
- constructor
13+
- init()
14+
- configClock()
15+
- keypad(numRows, numCols, sleepTime, scanTime)
16+
- readKeyData()
17+
- debounceConfig(debounceTime)
18+
19+
Hardware: The SX1509 should be hooked up like so:
20+
SX1509 Pin Arduino Pin
21+
3.3V ---------- 3.3V
22+
GND ----------- GND
23+
SDA ----------- A4 (or SDA on newer boards)
24+
SCL ----------- A5 (or SCL on newer boards)
25+
nRST ---------- 8 (could be any unused digital pin)
26+
nINT ---------- 7 (could be any unused digital pin)
27+
OSCIO is not used in this example.
28+
29+
See the SX1509_ADDRESS defines to decide which address you need
30+
to send to the constructor. By default the SX1509 Breakout
31+
sets both ADDR pins to 0 (so 0x3E I2C address).
32+
33+
In addition SX1509 is connected to a 4x4 matrix of 16 buttons.
34+
I used the SparkFun 4x4 button pad breakout:
35+
https://www.sparkfun.com/products/8033
36+
You could tie 4 of those together and interface this chip
37+
with the 8x8 array of 64(!) buttons.
38+
39+
The four rows of the button matrix should be connected to
40+
the SX1509's 0:3 pins. The columns of the button matrix should
41+
be connected to the SX1509's 8:11 pins.
42+
*/
43+
44+
#include <Wire.h> // Wire.h library is required to use SX1509 lib
45+
#include <sx1509_library.h> // Include the SX1509 library
46+
47+
// Uncomment one of the four lines to match your SX1509's address
48+
// pin selects. SX1509 breakout defaults to [0:0] (0x3E).
49+
const byte SX1509_ADDRESS = 0x3E; // SX1509 I2C address (00)
50+
//const byte SX1509_ADDRESS = 0x3F; // SX1509 I2C address (01)
51+
//const byte SX1509_ADDRESS = 0x70; // SX1509 I2C address (10)
52+
//const byte SX1509_ADDRESS = 0x71; // SX1509 I2C address (11)
53+
54+
// Arduino pin definitions
55+
const byte resetPin = 8;
56+
const byte interruptPin = 7;
57+
58+
//////////////////////////////////
59+
//// Global Variables ///////////
60+
//////////////////////////////////
61+
// Here we'll define the number of rows and columns our keypad
62+
// matrix has. Each of these values can be between 1 and 8.
63+
// 4x4 = 16 buttons
64+
const byte numRows = 4;
65+
const byte numCols = 4;
66+
// This key map will be used to define what keypress is sent to
67+
// the computer when a key on the keypad is pressed.
68+
char keyMap[numRows][numCols] = {
69+
{'1','2','3','4'},
70+
{'q','w','e','r'},
71+
{'a','s','d','f'},
72+
{'z','x','c','v'}};
73+
74+
// Create a new sx1509Class object
75+
sx1509Class sx1509(SX1509_ADDRESS, resetPin, interruptPin);
76+
77+
void setup()
78+
{
79+
// This example will use a Leonardo to send USB keypresses to
80+
// the computer its connected to. The 'Keyboard.' statements
81+
// can easily be replaced by Serial.print's, etc.
82+
#ifdef HID_ENABLED
83+
Keyboard.begin();
84+
#else
85+
Serial.begin(9600);
86+
#endif
87+
88+
// Must first initialize the sx1509:
89+
sx1509.init();
90+
// In order to use the keypad, the clock must first be
91+
// configured. We can call configureClock() with the default
92+
// parameters (2MHz internal oscillator, no clock in/out).
93+
sx1509.configClock();
94+
// Next call the keypad function with the number of rows
95+
// and columns.
96+
//sx1509.keypad(numRows, numCols); // Basic keypad init
97+
// There are two optional parameters in the keypad method:
98+
// sleepTime and scanTime. Each of these values can be between
99+
// 0 and 7. If not set, these values default to 0 (sleep off
100+
// scan time set to 1ms).
101+
byte sleepTime = 7;
102+
byte scanTime = 2; // Scan time per row
103+
sx1509.keypad(numRows, numCols, sleepTime, scanTime); // Advanced keypad init
104+
// We can also debounce the keypad inputs. The debounceConfig
105+
// method takes one parameter. Similar to the scanTime it's a
106+
// 3-bit value (0-7). This value must be <= scanTime!
107+
byte debounceTime = 1; // The debounce config value
108+
sx1509.debounceConfig(debounceTime);
109+
}
110+
111+
//////////////////////////////////
112+
//// Loop Variables /////////////
113+
//////////////////////////////////
114+
unsigned int keyData; // The raw data from the key press register
115+
unsigned int previousKeyData = 0; // previously read raw data
116+
byte activeRow; // The row of the button being pressed
117+
byte activeColumn; // The column of the button being pressed
118+
// These variables are used to emulate a key-hold. While the key
119+
// is held down, there's a long delay before the second character
120+
// is printed. Then a shorter delay between the remaining key presses
121+
unsigned int holdCount = 0;
122+
// This behavior is highly dependent on scanTime and debounceConfig
123+
// which are set in the setup.
124+
const byte holdCountMax = 25;
125+
// These releaseCount variables
126+
// The keypad engin on the SX1509 doesn't generate an interrupt
127+
// when a key is relased. So we'll use this counter to generate
128+
// releases.
129+
unsigned int releaseCount = 0; // Our counter
130+
unsigned int releaseCountMax = 100; // Top, in about milliseconds
131+
132+
// The loop will poll the interrupt pin. If the pin
133+
// is pulled low by the SX1509, we'll read the keypad data and
134+
// sort it into row and column, and send the corresponding key
135+
// press out to the computer.
136+
void loop()
137+
{
138+
// The interrupt is active low, and pulled-up otherwise.
139+
// The interrupt will be activated whenever a key press is read
140+
if (!digitalRead(interruptPin))
141+
{
142+
// readKeyData() returns a 16-bit word of data. The lower 8-bits
143+
// represent each of the up-to 8 rows. The upper 8-bits
144+
// correspond to the columns. A 1 in a bit position means
145+
// that a button in that row or column is being pressed.
146+
keyData = sx1509.readKeyData();
147+
148+
// Next, we'll sort out which row and column are being pressed.
149+
// And we'll send out a keypress over USB HID
150+
activeRow = keyData & 0xFF; // The row is the lower 8-bits
151+
activeColumn = keyData >> 8; // column is the upper 8-bits
152+
// The getBitPosition functio will return which bit is our 1
153+
activeRow = getBitPosition(activeRow);
154+
activeColumn = getBitPosition(activeColumn);
155+
156+
// If it's a new button press spit it out, reset hold delay
157+
if (keyData != previousKeyData)
158+
{
159+
holdCount = 0;
160+
// Keyboard.write is a Leonardo-specific Arduino function.
161+
// It'll perform a key press and release just like any
162+
// keyboard connected to your computer. For testing, this
163+
// could easily be replaced by
164+
#ifdef HID_ENABLED
165+
Keyboard.write(keyMap[activeRow][activeColumn]);
166+
#else
167+
Serial.print(keyMap[activeRow][activeColumn]);
168+
#endif
169+
}
170+
else
171+
{
172+
holdCount++; // Increment holdCount
173+
// This works as something of a key-press delay. Hold
174+
// down a key on your computer to see what I'm talking
175+
// about. After the initial delay, all characters following
176+
// will stream out quickly.
177+
if (holdCount > holdCountMax)
178+
{
179+
#ifdef HID_ENABLED
180+
Keyboard.write(keyMap[activeRow][activeColumn]);
181+
#else
182+
Serial.print(keyMap[activeRow][activeColumn]);
183+
#endif
184+
}
185+
}
186+
// Reset release count since there's been a key-press
187+
releaseCount = 0;
188+
// Set keyData as previousKeyData
189+
previousKeyData = keyData;
190+
}
191+
192+
// If no keys have been pressed we'll continuously increment
193+
// releaseCount. Eventually creating a release, once the count
194+
// hits the max.
195+
releaseCount++;
196+
if (releaseCount == releaseCountMax)
197+
{
198+
releaseCount = 0;
199+
previousKeyData = 0;
200+
}
201+
delay(1); // This gives releaseCountMax a more intuitive unit
202+
}
203+
204+
// This function scours a byte and returns the position of the
205+
// first byte it sees a 1. Great if our data bytes only have
206+
// a single 1 in them! Should return 0-7 if it sees a 1, 255 otherwise
207+
byte getBitPosition(byte dataByte)
208+
{
209+
for (int i=0; i<8; i++)
210+
{
211+
if (dataByte & (1<<i))
212+
{
213+
return i;
214+
}
215+
}
216+
return 255; // Otherwise return an error
217+
}

Arduino/libraries/SX1509/keywords.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ interruptSource KEYWORD2
2626
configClock KEYWORD2
2727
debounceEnable KEYWORD2
2828
debounceConfig KEYWORD2
29+
keypad KEYWORD2
30+
readKeyData KEYWORD2
2931

3032
#######################################
3133
# Constants (LITERAL1)

Arduino/libraries/SX1509/sx1509_library.cpp

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,93 @@ void sx1509Class::ledDriverInit(byte pin, byte freq, bool log)
264264
writeWord(REG_DATA_B, tempWord);
265265
}
266266

267+
// readKeyData()
268+
// This function returns a 16-bit value containing the values of
269+
// RegKeyData1 and RegKeyData2. However the bit-values are all
270+
// complemented, so there should only be a 1 where the active columns
271+
// and rows are represented.
272+
unsigned int sx1509Class::readKeyData()
273+
{
274+
return (0xFFFF ^ readWord(REG_KEY_DATA_1));
275+
}
276+
277+
// keypad(byte rows, byte columns, byte sleepTime, byte scanTime)
278+
// This function will initialize the keypad function on the SX1509.
279+
// a rows x columns matrix of buttons will be created.
280+
// - rows is the number of rows in the button matrix.
281+
// - This value must be between 1 and 7. 0 will turn it off.
282+
// - 1 = 2 rows, 7 = 8 rows, etc.
283+
// - columns is the number of columns in the button matrix
284+
// - This value should be between 0 and 7.
285+
// - 0 = 1 column, 7 = 8 columns, etc.
286+
// - sleepTime sets the Auto-sleep time of the keypad engine.
287+
// - This value should be between 0 and 7. See the comments in the
288+
// function for their values
289+
// - scanTime sets the scan time per row.
290+
// - This value should be betwee 0 and 7. See the comments for what
291+
// effect this value will have.
292+
void sx1509Class::keypad(byte rows, byte columns, byte sleepTime, byte scanTime)
293+
{
294+
unsigned int tempWord;
295+
byte tempByte;
296+
297+
// Set regDir 0:7 outputs, 8:15 inputs:
298+
tempWord = readWord(REG_DIR_B);
299+
for (int i=0; i<rows; i++)
300+
tempWord &= ~(1<<i);
301+
for (int i=8; i<(columns * 2); i++)
302+
tempWord |= (1<<i);
303+
writeWord(REG_DIR_B, tempWord);
304+
305+
// Set regOpenDrain on 0:7:
306+
tempByte = readByte(REG_OPEN_DRAIN_A);
307+
for (int i=0; i<rows; i++)
308+
tempByte |= (1<<i);
309+
writeByte(REG_OPEN_DRAIN_A, tempByte);
310+
311+
// Set regPullUp on 8:15:
312+
tempByte = readByte(REG_PULL_UP_B);
313+
for (int i=0; i<columns; i++)
314+
tempByte |= (1<<i);
315+
writeByte(REG_PULL_UP_B, tempByte);
316+
317+
// Enable and configure debouncing on 8:15:
318+
tempByte = readByte(REG_DEBOUNCE_ENABLE_B);
319+
for (int i=0; i<columns; i++)
320+
tempByte |= (1<<i);
321+
writeByte(REG_DEBOUNCE_ENABLE_B, tempByte);
322+
writeByte(REG_DEBOUNCE_CONFIG, (scanTime & 0b111)); // Debounce must be less than scan time
323+
324+
// RegKeyConfig1 sets the auto sleep time and scan time per row
325+
// Auto sleep time: 3-bit value ~
326+
// 000 : OFF
327+
// 001 : 128ms x 2MHz/fOSC
328+
// 010 : 256ms x 2MHz/fOSC
329+
// 011 : 512ms x 2MHz/fOSC
330+
// 100 : 1sec x 2MHz/fOSC
331+
// 101 : 2sec x 2MHz/fOSC
332+
// 110 : 4sec x 2MHz/fOSC
333+
// 111 : 8sec x 2MHz/fOSC
334+
// Scan time per row: 3-bit value, must be set above debounce time ~
335+
// 000 : 1ms x 2MHz/fOSC
336+
// 001 : 2ms x 2MHz/fOSC
337+
// 010 : 4ms x 2MHz/fOSC
338+
// 011 : 8ms x 2MHz/fOSC
339+
// 100 : 16ms x 2MHz/fOSC
340+
// 101 : 32ms x 2MHz/fOSC
341+
// 110 : 64ms x 2MHz/fOSC
342+
// 111 : 128ms x 2MHz/fOSC
343+
sleepTime = (sleepTime & 0b111)<<4;
344+
scanTime &= 0b111; // Scan time is bits 2:0
345+
tempByte = sleepTime | scanTime;
346+
writeByte(REG_KEY_CONFIG_1, tempByte);
347+
348+
// RegKeyConfig2 tells the SX1509 how many rows and columns we've got going
349+
rows = (rows - 1) & 0b111; // 0 = off, 0b001 = 2 rows, 0b111 = 8 rows, etc.
350+
columns = (columns - 1) & 0b111; // 0b000 = 1 column, ob111 = 8 columns, etc.
351+
writeByte(REG_KEY_CONFIG_2, (rows << 3) | columns);
352+
}
353+
267354
// sync(void)
268355
// This function resets the PWM/Blink/Fade counters, syncing any blinking LEDs
269356
// Two functions are performed:

Arduino/libraries/SX1509/sx1509_library.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ class sx1509Class
4949
void configClock(byte oscSource = 2, byte oscPinFunction = 0, byte oscFreqOut = 0, byte oscDivider = 1);
5050
void debounceEnable(byte pin);
5151
void debounceConfig(byte configVaule);
52-
// void keypad(byte rows, byte columns);
52+
void keypad(byte rows, byte columns, byte sleepTime = 0, byte scanTime = 0);
53+
unsigned int readKeyData();
5354
// void levelShifter();
5455
};
5556

0 commit comments

Comments
 (0)