|
| 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 | +} |
0 commit comments