Permalink
Browse files

Keypad engine functions and arduino example

Added keypad(byte rows, byte columns, byte sleepTime, byte scanTime) and
nsigned int readKeyData() to sx1509Class
  • Loading branch information...
1 parent 9a60047 commit 3e6afa1687eae381cd5d27fa310fdeb3e42ce21d Jim Lindblom committed Nov 20, 2012
@@ -0,0 +1,217 @@
+/* SX1509 Library Example 03
+ Keypad interfacing
+ by: Jim Lindblom
+ SparkFun Electronics
+ license: Beerware. Please use, reuse, share, and modify this
+ code. I'd ask that you maintain attribution and open-source.
+ If you find it useful, you can buy me a beer when we meet
+ some day.
+
+ This example shows how you can use the following SX1509
+ library methods:
+ - constructor
+ - init()
+ - configClock()
+ - keypad(numRows, numCols, sleepTime, scanTime)
+ - readKeyData()
+ - debounceConfig(debounceTime)
+
+ Hardware: The SX1509 should be hooked up like so:
+ SX1509 Pin Arduino Pin
+ 3.3V ---------- 3.3V
+ GND ----------- GND
+ SDA ----------- A4 (or SDA on newer boards)
+ SCL ----------- A5 (or SCL on newer boards)
+ nRST ---------- 8 (could be any unused digital pin)
+ nINT ---------- 7 (could be any unused digital pin)
+ OSCIO is not used in this example.
+
+ See the SX1509_ADDRESS defines to decide which address you need
+ to send to the constructor. By default the SX1509 Breakout
+ sets both ADDR pins to 0 (so 0x3E I2C address).
+
+ In addition SX1509 is connected to a 4x4 matrix of 16 buttons.
+ I used the SparkFun 4x4 button pad breakout:
+ https://www.sparkfun.com/products/8033
+ You could tie 4 of those together and interface this chip
+ with the 8x8 array of 64(!) buttons.
+
+ The four rows of the button matrix should be connected to
+ the SX1509's 0:3 pins. The columns of the button matrix should
+ be connected to the SX1509's 8:11 pins.
+*/
+
+#include <Wire.h> // Wire.h library is required to use SX1509 lib
+#include <sx1509_library.h> // Include the SX1509 library
+
+// Uncomment one of the four lines to match your SX1509's address
+// pin selects. SX1509 breakout defaults to [0:0] (0x3E).
+const byte SX1509_ADDRESS = 0x3E; // SX1509 I2C address (00)
+//const byte SX1509_ADDRESS = 0x3F; // SX1509 I2C address (01)
+//const byte SX1509_ADDRESS = 0x70; // SX1509 I2C address (10)
+//const byte SX1509_ADDRESS = 0x71; // SX1509 I2C address (11)
+
+// Arduino pin definitions
+const byte resetPin = 8;
+const byte interruptPin = 7;
+
+//////////////////////////////////
+//// Global Variables ///////////
+//////////////////////////////////
+// Here we'll define the number of rows and columns our keypad
+// matrix has. Each of these values can be between 1 and 8.
+// 4x4 = 16 buttons
+const byte numRows = 4;
+const byte numCols = 4;
+// This key map will be used to define what keypress is sent to
+// the computer when a key on the keypad is pressed.
+char keyMap[numRows][numCols] = {
+ {'1','2','3','4'},
+ {'q','w','e','r'},
+ {'a','s','d','f'},
+ {'z','x','c','v'}};
+
+// Create a new sx1509Class object
+sx1509Class sx1509(SX1509_ADDRESS, resetPin, interruptPin);
+
+void setup()
+{
+ // This example will use a Leonardo to send USB keypresses to
+ // the computer its connected to. The 'Keyboard.' statements
+ // can easily be replaced by Serial.print's, etc.
+ #ifdef HID_ENABLED
+ Keyboard.begin();
+ #else
+ Serial.begin(9600);
+ #endif
+
+ // Must first initialize the sx1509:
+ sx1509.init();
+ // In order to use the keypad, the clock must first be
+ // configured. We can call configureClock() with the default
+ // parameters (2MHz internal oscillator, no clock in/out).
+ sx1509.configClock();
+ // Next call the keypad function with the number of rows
+ // and columns.
+ //sx1509.keypad(numRows, numCols); // Basic keypad init
+ // There are two optional parameters in the keypad method:
+ // sleepTime and scanTime. Each of these values can be between
+ // 0 and 7. If not set, these values default to 0 (sleep off
+ // scan time set to 1ms).
+ byte sleepTime = 7;
+ byte scanTime = 2; // Scan time per row
+ sx1509.keypad(numRows, numCols, sleepTime, scanTime); // Advanced keypad init
+ // We can also debounce the keypad inputs. The debounceConfig
+ // method takes one parameter. Similar to the scanTime it's a
+ // 3-bit value (0-7). This value must be <= scanTime!
+ byte debounceTime = 1; // The debounce config value
+ sx1509.debounceConfig(debounceTime);
+}
+
+//////////////////////////////////
+//// Loop Variables /////////////
+//////////////////////////////////
+unsigned int keyData; // The raw data from the key press register
+unsigned int previousKeyData = 0; // previously read raw data
+byte activeRow; // The row of the button being pressed
+byte activeColumn; // The column of the button being pressed
+// These variables are used to emulate a key-hold. While the key
+// is held down, there's a long delay before the second character
+// is printed. Then a shorter delay between the remaining key presses
+unsigned int holdCount = 0;
+// This behavior is highly dependent on scanTime and debounceConfig
+// which are set in the setup.
+const byte holdCountMax = 25;
+// These releaseCount variables
+// The keypad engin on the SX1509 doesn't generate an interrupt
+// when a key is relased. So we'll use this counter to generate
+// releases.
+unsigned int releaseCount = 0; // Our counter
+unsigned int releaseCountMax = 100; // Top, in about milliseconds
+
+// The loop will poll the interrupt pin. If the pin
+// is pulled low by the SX1509, we'll read the keypad data and
+// sort it into row and column, and send the corresponding key
+// press out to the computer.
+void loop()
+{
+ // The interrupt is active low, and pulled-up otherwise.
+ // The interrupt will be activated whenever a key press is read
+ if (!digitalRead(interruptPin))
+ {
+ // readKeyData() returns a 16-bit word of data. The lower 8-bits
+ // represent each of the up-to 8 rows. The upper 8-bits
+ // correspond to the columns. A 1 in a bit position means
+ // that a button in that row or column is being pressed.
+ keyData = sx1509.readKeyData();
+
+ // Next, we'll sort out which row and column are being pressed.
+ // And we'll send out a keypress over USB HID
+ activeRow = keyData & 0xFF; // The row is the lower 8-bits
+ activeColumn = keyData >> 8; // column is the upper 8-bits
+ // The getBitPosition functio will return which bit is our 1
+ activeRow = getBitPosition(activeRow);
+ activeColumn = getBitPosition(activeColumn);
+
+ // If it's a new button press spit it out, reset hold delay
+ if (keyData != previousKeyData)
+ {
+ holdCount = 0;
+ // Keyboard.write is a Leonardo-specific Arduino function.
+ // It'll perform a key press and release just like any
+ // keyboard connected to your computer. For testing, this
+ // could easily be replaced by
+ #ifdef HID_ENABLED
+ Keyboard.write(keyMap[activeRow][activeColumn]);
+ #else
+ Serial.print(keyMap[activeRow][activeColumn]);
+ #endif
+ }
+ else
+ {
+ holdCount++; // Increment holdCount
+ // This works as something of a key-press delay. Hold
+ // down a key on your computer to see what I'm talking
+ // about. After the initial delay, all characters following
+ // will stream out quickly.
+ if (holdCount > holdCountMax)
+ {
+ #ifdef HID_ENABLED
+ Keyboard.write(keyMap[activeRow][activeColumn]);
+ #else
+ Serial.print(keyMap[activeRow][activeColumn]);
+ #endif
+ }
+ }
+ // Reset release count since there's been a key-press
+ releaseCount = 0;
+ // Set keyData as previousKeyData
+ previousKeyData = keyData;
+ }
+
+ // If no keys have been pressed we'll continuously increment
+ // releaseCount. Eventually creating a release, once the count
+ // hits the max.
+ releaseCount++;
+ if (releaseCount == releaseCountMax)
+ {
+ releaseCount = 0;
+ previousKeyData = 0;
+ }
+ delay(1); // This gives releaseCountMax a more intuitive unit
+}
+
+// This function scours a byte and returns the position of the
+// first byte it sees a 1. Great if our data bytes only have
+// a single 1 in them! Should return 0-7 if it sees a 1, 255 otherwise
+byte getBitPosition(byte dataByte)
+{
+ for (int i=0; i<8; i++)
+ {
+ if (dataByte & (1<<i))
+ {
+ return i;
+ }
+ }
+ return 255; // Otherwise return an error
+}
@@ -26,6 +26,8 @@ interruptSource KEYWORD2
configClock KEYWORD2
debounceEnable KEYWORD2
debounceConfig KEYWORD2
+keypad KEYWORD2
+readKeyData KEYWORD2
#######################################
# Constants (LITERAL1)
@@ -264,6 +264,93 @@ void sx1509Class::ledDriverInit(byte pin, byte freq, bool log)
writeWord(REG_DATA_B, tempWord);
}
+// readKeyData()
+// This function returns a 16-bit value containing the values of
+// RegKeyData1 and RegKeyData2. However the bit-values are all
+// complemented, so there should only be a 1 where the active columns
+// and rows are represented.
+unsigned int sx1509Class::readKeyData()
+{
+ return (0xFFFF ^ readWord(REG_KEY_DATA_1));
+}
+
+// keypad(byte rows, byte columns, byte sleepTime, byte scanTime)
+// This function will initialize the keypad function on the SX1509.
+// a rows x columns matrix of buttons will be created.
+// - rows is the number of rows in the button matrix.
+// - This value must be between 1 and 7. 0 will turn it off.
+// - 1 = 2 rows, 7 = 8 rows, etc.
+// - columns is the number of columns in the button matrix
+// - This value should be between 0 and 7.
+// - 0 = 1 column, 7 = 8 columns, etc.
+// - sleepTime sets the Auto-sleep time of the keypad engine.
+// - This value should be between 0 and 7. See the comments in the
+// function for their values
+// - scanTime sets the scan time per row.
+// - This value should be betwee 0 and 7. See the comments for what
+// effect this value will have.
+void sx1509Class::keypad(byte rows, byte columns, byte sleepTime, byte scanTime)
+{
+ unsigned int tempWord;
+ byte tempByte;
+
+ // Set regDir 0:7 outputs, 8:15 inputs:
+ tempWord = readWord(REG_DIR_B);
+ for (int i=0; i<rows; i++)
+ tempWord &= ~(1<<i);
+ for (int i=8; i<(columns * 2); i++)
+ tempWord |= (1<<i);
+ writeWord(REG_DIR_B, tempWord);
+
+ // Set regOpenDrain on 0:7:
+ tempByte = readByte(REG_OPEN_DRAIN_A);
+ for (int i=0; i<rows; i++)
+ tempByte |= (1<<i);
+ writeByte(REG_OPEN_DRAIN_A, tempByte);
+
+ // Set regPullUp on 8:15:
+ tempByte = readByte(REG_PULL_UP_B);
+ for (int i=0; i<columns; i++)
+ tempByte |= (1<<i);
+ writeByte(REG_PULL_UP_B, tempByte);
+
+ // Enable and configure debouncing on 8:15:
+ tempByte = readByte(REG_DEBOUNCE_ENABLE_B);
+ for (int i=0; i<columns; i++)
+ tempByte |= (1<<i);
+ writeByte(REG_DEBOUNCE_ENABLE_B, tempByte);
+ writeByte(REG_DEBOUNCE_CONFIG, (scanTime & 0b111)); // Debounce must be less than scan time
+
+ // RegKeyConfig1 sets the auto sleep time and scan time per row
+ // Auto sleep time: 3-bit value ~
+ // 000 : OFF
+ // 001 : 128ms x 2MHz/fOSC
+ // 010 : 256ms x 2MHz/fOSC
+ // 011 : 512ms x 2MHz/fOSC
+ // 100 : 1sec x 2MHz/fOSC
+ // 101 : 2sec x 2MHz/fOSC
+ // 110 : 4sec x 2MHz/fOSC
+ // 111 : 8sec x 2MHz/fOSC
+ // Scan time per row: 3-bit value, must be set above debounce time ~
+ // 000 : 1ms x 2MHz/fOSC
+ // 001 : 2ms x 2MHz/fOSC
+ // 010 : 4ms x 2MHz/fOSC
+ // 011 : 8ms x 2MHz/fOSC
+ // 100 : 16ms x 2MHz/fOSC
+ // 101 : 32ms x 2MHz/fOSC
+ // 110 : 64ms x 2MHz/fOSC
+ // 111 : 128ms x 2MHz/fOSC
+ sleepTime = (sleepTime & 0b111)<<4;
+ scanTime &= 0b111; // Scan time is bits 2:0
+ tempByte = sleepTime | scanTime;
+ writeByte(REG_KEY_CONFIG_1, tempByte);
+
+ // RegKeyConfig2 tells the SX1509 how many rows and columns we've got going
+ rows = (rows - 1) & 0b111; // 0 = off, 0b001 = 2 rows, 0b111 = 8 rows, etc.
+ columns = (columns - 1) & 0b111; // 0b000 = 1 column, ob111 = 8 columns, etc.
+ writeByte(REG_KEY_CONFIG_2, (rows << 3) | columns);
+}
+
// sync(void)
// This function resets the PWM/Blink/Fade counters, syncing any blinking LEDs
// Two functions are performed:
@@ -49,7 +49,8 @@ class sx1509Class
void configClock(byte oscSource = 2, byte oscPinFunction = 0, byte oscFreqOut = 0, byte oscDivider = 1);
void debounceEnable(byte pin);
void debounceConfig(byte configVaule);
- // void keypad(byte rows, byte columns);
+ void keypad(byte rows, byte columns, byte sleepTime = 0, byte scanTime = 0);
+ unsigned int readKeyData();
// void levelShifter();
};

0 comments on commit 3e6afa1

Please sign in to comment.