Improvement suggestions | Improvement suggestions | ||||
----------------------- | ----------------------- | ||||
We need to know what improvements to the keybrd library would help you create your keyboard design. | |||||
Before requesting an improvement, please check [planned_features list](doc/planned_features.md) | |||||
We need to know what keybrd library improvements would help you create keyboard designs. | |||||
Before requesting an improvement, please check the [planned_features list](doc/planned_features.md). | |||||
Submit improvement suggestions to [GitHub issues](https://github.com/wolfv6/Keybrd/issues) | Submit improvement suggestions to [GitHub issues](https://github.com/wolfv6/Keybrd/issues) | ||||
or [geekhack thread](https://geekhack.org/index.php?topic=83599.0). | or [geekhack thread](https://geekhack.org/index.php?topic=83599.0). | ||||
Bug reports | Bug reports | ||||
----------- | ----------- | ||||
A bug report is the first step in finding a bug. | |||||
Once it is found, correcting it is usually relatively easy. | |||||
Submitting a bug report is the first step in fixing a bug. | |||||
Once it is found, correcting a bug is usually relatively easy. | |||||
Please submit bug reports to [GitHub issues](https://github.com/wolfv6/Keybrd/issues) | Please submit bug reports to [GitHub issues](https://github.com/wolfv6/Keybrd/issues) | ||||
or [geekhack thread](https://geekhack.org/index.php?topic=83599.0). | or [geekhack thread](https://geekhack.org/index.php?topic=83599.0). | ||||
Code contributions | Code contributions | ||||
------------------ | ------------------ | ||||
Unsure where to begin contributing to keybrd code? | |||||
Unsure of where to begin contributing to keybrd code? | |||||
You can start by looking through the improvement suggestions, bug reports, and [planned_features](doc/planned_features.md). | You can start by looking through the improvement suggestions, bug reports, and [planned_features](doc/planned_features.md). | ||||
Git commit message style guide: | Git commit message style guide: | ||||
A thriving project is more than a pile of code. | A thriving project is more than a pile of code. | ||||
It's the packaging, explanation, outreach, and empathy of maintainers that make a good project great. | It's the packaging, explanation, outreach, and empathy of maintainers that make a good project great. | ||||
### Beta testing | |||||
keybrd library has been Alpha tested on the DodoHand keyboard, tutorial sketches, Teensy 2.0, and Teensy LC. What we need now are Beta testers: | |||||
* use the tutorials (feedback from noobs is especially valuable) | |||||
* use the keybrd library to implement your own keyboard design | |||||
### keybrd tutorials usability survey | |||||
We want feedback on keybrd tutorial usability from real users (feedback from noobs is especially valuable). | |||||
Please take the survey after completing a tutorial. | |||||
Answer the questions you feel like answering, or make up your own questions. | |||||
Feedback from Beta testers will be used to make improvements to the keybrd library. | |||||
* How easy was it to find the relevant tutorial? | |||||
* Which tutorial did you read? | |||||
* Did the tutorial provide complete information? | |||||
* What other tutorial topics would be useful? | |||||
* Are you satisfied with the tutorial? | |||||
* Other comments or suggestions. | |||||
Post the completed Q & A on [geekhack thread](https://geekhack.org/index.php?topic=83599.0) with a heading "Tutorial usability survey". | |||||
If you prefer your answers remain confidential, pm the completed Q & A to [wolfv](https://geekhack.org/index.php?action=pm;sa=send;u=25471). | |||||
Usability survey results will be used to make improvements to the tutorials. | |||||
### keybrd library usability survey | |||||
We want feedback on keybrd library usability from real users. | |||||
Please fill the survey after using the keybrd library to implement a keyboard design. | |||||
Answer the questions you feel like answering, or make up your own questions. | |||||
* How easy was it to find relevant information? | |||||
* Did the User guide provide complete information? | |||||
* What other keyboard firmware have you used? | |||||
* What pros and cons did you find compared to other keyboard firmware? | |||||
* Did you publish your keyboard firmware? If so, please provide a link. | |||||
* Are you satisfied with the keybrd library? | |||||
* Other comments or suggestions. | |||||
Post the completed Q & A on [geekhack thread](https://geekhack.org/index.php?topic=83599.0) with a heading "keybrd library usability survey". | |||||
If you prefer your answers remain confidential, pm the completed Q & A to [wolfv](https://geekhack.org/index.php?action=pm;sa=send;u=25471). | |||||
Usability survey results will be used to make improvements to the keybrd library. | |||||
### Schematics | ### Schematics | ||||
The most glaring deficiency is the tutorials' lack of schematics. | The most glaring deficiency is the tutorials' lack of schematics. | ||||
Schematics would be an improvement over the current photos. | |||||
Schematics would be easier to read than the current photos. | |||||
Schematics are not my area of expertise. Use what ever you think would be the best solution for the tutorials: | |||||
Schematics are not my area of expertise. Use what ever would be the best solution for the tutorials: | |||||
* schematics | * schematics | ||||
* breadboard drawings | * breadboard drawings | ||||
* Arduino simulation software | * Arduino simulation software | ||||
Suggest a clarification, simplification, correction, or other improvement. | Suggest a clarification, simplification, correction, or other improvement. | ||||
We need the perspective of people new to the project to see these things. | We need the perspective of people new to the project to see these things. | ||||
Sometimes just changing a word or two makes a big difference. | Sometimes just changing a word or two makes a big difference. | ||||
Please submit improvement and errata to [GitHub issues](https://github.com/wolfv6/Keybrd/issues) | |||||
or [geekhack thread](https://geekhack.org/index.php?topic=83599.0). | |||||
Text file documentation style guide: | Text file documentation style guide: | ||||
* Use Markdown with a .md suffix. | * Use Markdown with a .md suffix. | ||||
* "Underline" first-level (=) and second-level (-) headings (because easier to read in plain text). | * "Underline" first-level (=) and second-level (-) headings (because easier to read in plain text). | ||||
* Capitalize first letter of headings (no extra capitalization in headings). | * Capitalize first letter of headings (no extra capitalization in headings). | ||||
### Write a sketch for a keyboard that is popular with Arduino-compatible controllers | |||||
Write a keybrd sketch for a keyboard that is popular with Arduino-compatible controllers. | |||||
* You should own the keyboard so you can test the firmware | |||||
* The keyboard should have an Arduino compatible controller (e.g. ErgoDox and Phantom use Teensy 2.0) | |||||
* The layout should be a plain baseline layout (QWERTY) | |||||
Other owners of that model keyboard can then easily modify and compile the sketch on Arduino IDE. | |||||
The README should have: | |||||
* brief description of the electronics | |||||
* link to a hardware page | |||||
* list any firmware deficiencies | |||||
Follow the "Publish" instructions in tutorial_8c_sharing_your_keybrd_sketch.md | |||||
### Blog | ### Blog | ||||
You have a fresh perspective of how the keybrd library works. | You have a fresh perspective of how the keybrd library works. | ||||
This makes you the perfect person to write an introductory blog explaining the project. | This makes you the perfect person to write an introductory blog explaining the project. | ||||
Submitting a pull request | Submitting a pull request | ||||
------------------------- | ------------------------- | ||||
Pull request is the preferred way to contribute code and documentation. | Pull request is the preferred way to contribute code and documentation. | ||||
If you want to contribute some other way, please make a request in the [GitHub issues](https://github.com/wolfv6/Keybrd/issues). | |||||
If you want to contribute some other way, please make a request in | |||||
[GitHub issues](https://github.com/wolfv6/Keybrd/issues) | |||||
or [geekhack thread](https://geekhack.org/index.php?topic=83599.0). | |||||
/* simulate one scan of a key martix, on open-drain active-low I/O expander | |||||
BREADBOARD SETUP ******************************* | |||||
Teensy LC connected to MCP23018 I/O expander via I2C | |||||
4.7k Ohm pullup resistors on SDA and SCL | |||||
use volt meter to measure pin voltages | |||||
MCP23018 has open-drain outputs (open-drain can only sink current) | |||||
MCP23018 PIN DIAGRAM *************************** | |||||
write port B read port A | |||||
GND VSS 1 28 NC | |||||
NC 2 27 GPA7 | |||||
jumper0 GPB0 3 26 GPA6 | |||||
GPB1 4 25 GPA5 | |||||
GPB2 5 24 GPA4 jumper4 | |||||
GPB3 6 23 GPA3 | |||||
jumper4 GPB4 7 22 GPA2 | |||||
GPB5 8 21 GPA1 | |||||
GPB6 9 20 GPA0 jumper0 | |||||
GPB7 10 19 INTA | |||||
power VDD 11 18 INTB | |||||
SCL SCL 12 17 NC | |||||
SDA SDA 13 16 RESET power | |||||
NC 14 15 ADDR GND | |||||
*/ | |||||
#include "Wire.h" | |||||
const uint8_t ADDR = 0x20; //MCP23018 I2C address with ADDR pin grounded | |||||
void setup() | |||||
{ | |||||
delay(1000); //time for Serial print to work | |||||
// ================= configure ================ | |||||
Serial.print("config "); | |||||
Wire.begin(); | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(0x01); //IODIRB Configure | |||||
Wire.write(0); //as output | |||||
Wire.endTransmission(); | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(0x00); //IODIRA Configuration | |||||
Wire.write(~0); //as input | |||||
Wire.endTransmission(); | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(0x0C); //GPPUA pull-up | |||||
Wire.write(~0); //pull-up enabled | |||||
Wire.endTransmission(); | |||||
// =================== scan =================== | |||||
Serial.println("scan"); | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(0x13); //GPIOB output | |||||
Wire.write(B00001111); //pins 0-3 off, pins 4-7 sink on (strobe, LED on) | |||||
Wire.endTransmission(); | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(0x12); //GPIOA (immediately before requestFrom) | |||||
Wire.endTransmission(); | |||||
Wire.requestFrom(ADDR, static_cast<uint8_t>(1)); //request one byte from GPIOA read | |||||
Serial.print("portA="); | |||||
Serial.println(Wire.read(), BIN); //prints portA=11101111 | |||||
} | |||||
void loop() { } |
/* simulate one scan of a key martix, on source active-high I/O expander | |||||
BREADBOARD SETUP ******************************* | |||||
Teensy LC connected to MCP23017 I/O expander via I2C | |||||
10k Ohm external pulldown resistors on port A | |||||
4.7k Ohm pullup resistors on SDA and SCL | |||||
use volt meter to measure pin voltages | |||||
MCP23017 PIN DIAGRAM *************************** | |||||
write port B read port A | |||||
jumper0 GPB0 1 26 GPA7 pulldown | |||||
GPB1 2 25 GPA6 pulldown | |||||
GPB2 3 24 GPA5 pulldown | |||||
GPB3 4 23 GPA4 pulldown jumper4 | |||||
jumper4 GPB4 5 22 GPA3 pulldown | |||||
GPB5 6 21 GPA2 pulldown | |||||
GPB6 7 20 GPA1 pulldown | |||||
GPB7 8 19 GPA0 pulldown jumper0 | |||||
power VDD 9 18 INTA | |||||
GND VSS 10 28 INTB | |||||
NC 11 27 RESET power | |||||
SCL SCL 12 17 A2 gnd | |||||
SDA SDA 13 16 A1 gnd | |||||
NC 14 15 A0 gnd | |||||
*/ | |||||
#include "Wire.h" | |||||
const uint8_t ADDR = 0x20; //MCP23017 I2C address with all ADDR pins grounded | |||||
void setup() | |||||
{ | |||||
delay(1000); //time for Serial print to work | |||||
// ================= configure ================ | |||||
Serial.print("config "); | |||||
Wire.begin(); | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(0x01); //IODIRB Configure | |||||
Wire.write(0); //as output | |||||
Wire.endTransmission(); | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(0x00); //IODIRA Configuration | |||||
Wire.write(~0); //as input | |||||
Wire.endTransmission(); | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(0x0C); //GPPUA pull-up | |||||
Wire.write(0); //pull-up disabled | |||||
Wire.endTransmission(); | |||||
// =================== scan =================== | |||||
Serial.println("scan"); | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(0x13); //GPIOB output | |||||
Wire.write(B11110000); //pins 0-3 ground, pins 4-7 power (strobe, LED on) | |||||
Wire.endTransmission(); | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(0x12); //GPIOA (immediately before requestFrom) | |||||
Wire.endTransmission(); | |||||
Wire.requestFrom(ADDR, static_cast<uint8_t>(1)); //request one byte from GPIOA read | |||||
Serial.print("portA="); | |||||
Serial.println(Wire.read(), BIN); //prints portA=00010000 | |||||
} | |||||
void loop() { } |
/* simulate one scan of a key martix, on source active-low I/O expander | |||||
BREADBOARD SETUP ******************************* | |||||
Teensy LC connected to MCP23017 I/O expander via I2C | |||||
4.7k Ohm pullup resistors on SDA and SCL | |||||
use volt meter to measure pin voltages | |||||
MCP23017 PIN DIAGRAM *************************** | |||||
write port B read port A | |||||
jumper0 GPB0 1 26 GPA7 | |||||
GPB1 2 25 GPA6 | |||||
GPB2 3 24 GPA5 | |||||
GPB3 4 23 GPA4 jumper4 | |||||
jumper4 GPB4 5 22 GPA3 | |||||
GPB5 6 21 GPA2 | |||||
GPB6 7 20 GPA1 | |||||
GPB7 8 19 GPA0 jumper0 | |||||
power VDD 9 18 INTA | |||||
GND VSS 10 28 INTB | |||||
NC 11 27 RESET power | |||||
SCL SCL 12 17 A2 gnd | |||||
SDA SDA 13 16 A1 gnd | |||||
NC 14 15 A0 gnd | |||||
*/ | |||||
#include "Wire.h" | |||||
const uint8_t ADDR = 0x20; //MCP23017 I2C address with all ADDR pins grounded | |||||
void setup() | |||||
{ | |||||
delay(1000); //time for Serial print to work | |||||
// ================= configure ================ | |||||
Serial.print("config "); | |||||
Wire.begin(); | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(0x01); //IODIRB Configure | |||||
Wire.write(0); //as output | |||||
Wire.endTransmission(); | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(0x00); //IODIRA Configuration | |||||
Wire.write(~0); //as input | |||||
Wire.endTransmission(); | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(0x0C); //GPPUA pull-up | |||||
Wire.write(~0); //pull-up enabled | |||||
Wire.endTransmission(); | |||||
// =================== scan =================== | |||||
Serial.println("scan"); | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(0x13); //GPIOB output | |||||
Wire.write(B00001111); //pins 0-3 power (strobe, LED on), pins 4-7 ground | |||||
Wire.endTransmission(); | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(0x12); //GPIOA (immediately before requestFrom) | |||||
Wire.endTransmission(); | |||||
Wire.requestFrom(ADDR, static_cast<uint8_t>(1)); //request one byte from GPIOA read | |||||
Serial.print("portA="); | |||||
Serial.println(Wire.read(), BIN); //prints portA=11101111 | |||||
} | |||||
void loop() { } |
keybrd version 0.x.x is for initial development. | keybrd version 0.x.x is for initial development. | ||||
keybrd version 1.0.0 will be released when the public API is stable. | keybrd version 1.0.0 will be released when the public API is stable. | ||||
< !-- | |||||
Unreleased | |||||
---------- | |||||
--> | |||||
0.6.0 (2016-09-28) | 0.6.0 (2016-09-28) | ||||
------------------ | ------------------ | ||||
* Enhancements | * Enhancements |
Top priority | Top priority | ||||
------------ | ------------ | ||||
* Beta testing | |||||
* Add breadboard keyboard schematics to tutorials | |||||
* User testing | |||||
Medium priority | Medium priority | ||||
--------------- | --------------- | ||||
* Add matrix-to-layout mapping array (to decouple key matrix from layout) | |||||
* Add tutorial_4b_split_keyboard_with_shift_registers | |||||
* Add matrix-to-layout mapping (to decouple key matrix from layout) | |||||
* Add breadboard keyboard schematics to tutorials | |||||
Low priority | Low priority | ||||
------------ | ------------ | ||||
* Add MCP23S18 I/O expander with Serial Peripheral Interface (SPI) |
/* keybrd_MCP23018.ino | |||||
This sketch: | |||||
is a simple 1-layer keyboard | |||||
runs on two matrices of a breadboard keyboard | |||||
Controller I/O expander | |||||
| Left | **0** | **1** | | Right | **0** | **1** | | |||||
|-------|-------|-------| |-------|-------|-------| | |||||
| **1** | 1 | 2 | | **1** | 3 | 4 | | |||||
| **0** | a | b | | **0** | c | d | | |||||
*/ | |||||
// ################## GLOBAL ################### | |||||
// ================= INCLUDES ================== | |||||
#include <ScanDelay.h> | |||||
#include <Code_Sc.h> | |||||
#include <Code_LEDLock.h> | |||||
#include <Row.h> | |||||
//left matrix | |||||
#include <Scanner_uC.h> | |||||
//right matrix | |||||
#include <Port_MCP23018.h> | |||||
#include <Scanner_IOE.h> | |||||
#include <LED_Port.h> | |||||
// ============ SPEED CONFIGURATION ============ | |||||
ScanDelay scanDelay(9000); | |||||
/* ================ LEFT SCANNER =============== | |||||
Left matrix rows work the same as the ones in keybrd_2_single-layer.ino | |||||
*/ | |||||
uint8_t readPins[] = {14, 15}; | |||||
const uint8_t readPinCount = sizeof(readPins)/sizeof(*readPins); | |||||
Scanner_uC scanner_L(LOW, readPins, readPinCount); | |||||
/* =============== RIGHT SCANNER =============== | |||||
The right matrix is scanned by an I/O expander. | |||||
The MCP23018 address is set by grounding or powering pins. | |||||
*/ | |||||
const uint8_t IOE_ADDR = 0x20; //MCP23018 address pin grounded | |||||
/* | |||||
Normally all strobe pins are on one port, and all the read pins are on the other port. | |||||
In this example, portB stobes the row while portA reads the colums. | |||||
Port_MCP23018 constructor parameters are: deviceAddr, portNum, readPins. | |||||
readPins is a bit pattern, where 0=output, 1=input. | |||||
In portA, the first two pins are set to input for reading. | |||||
"<<" (bit shift left) and "|" (OR) are bitwise operators. | |||||
Pin numbers to be read are delimited by "|" and to the right of "1<<". | |||||
*/ | |||||
Port_MCP23018 portA(IOE_ADDR, 0, 1<<0 | 1<<1 ); //read pins 0, 1 | |||||
Port_MCP23018 portB(IOE_ADDR, 1, 0); | |||||
Scanner_IOE scanner_R(LOW, portB, portA); | |||||
/* ================= RIGHT LED ================= | |||||
The LED_Port constructor parameters are a port and pin number that is connected to an LED. | |||||
*/ | |||||
LED_Port LED_capsLck(portA, 1<<7); | |||||
// =================== CODES =================== | |||||
Code_Sc s_a(KEY_A); | |||||
Code_Sc s_b(KEY_B); | |||||
Code_Sc s_c(KEY_C); | |||||
Code_Sc s_d(KEY_D); | |||||
Code_Sc s_1(KEY_1); | |||||
Code_Sc s_2(KEY_2); | |||||
Code_Sc s_3(KEY_3); | |||||
Code_Sc s_4(KEY_4); | |||||
Code_LEDLock o_capsLock(KEY_CAPS_LOCK, LED_capsLck);//todo was testing LED, restore s_4 when done | |||||
/* =================== ROWS ==================== | |||||
Left row names contain the letter 'L', while right row names conatain the letter 'R'. | |||||
Row constructor parameters are: scanner, strobePin, ptrsKeys[], keyCount. | |||||
strobePin has one of two formats: | |||||
* if refScanner a Scanner_uC, then strobePin is an Arduino pin number connected to this row | |||||
* otherwise strobePin is a bit pattern, 1 indicating an IC pin connected to the row | |||||
*/ | |||||
/* ---------------- LEFT ROWS ------------------ | |||||
The left rows have a Scanner_uC and Arduino pin numbers to strobe. | |||||
*/ | |||||
Key* ptrsKeys_L0[] = { &s_1, &s_2 }; | |||||
const uint8_t KEY_COUNT_L0 = sizeof(ptrsKeys_L0)/sizeof(*ptrsKeys_L0); | |||||
Row row_L0(scanner_L, 0, ptrsKeys_L0, KEY_COUNT_L0); | |||||
Key* ptrsKeys_L1[] = { &s_a, &s_b }; | |||||
const uint8_t KEY_COUNT_L1 = sizeof(ptrsKeys_L1)/sizeof(*ptrsKeys_L1); | |||||
Row row_L1(scanner_L, 1, ptrsKeys_L1, KEY_COUNT_L1); | |||||
/* ---------------- RIGHT ROWS ----------------- | |||||
The right rows have a Scanner_IOE and pin bits to strobe. | |||||
*/ | |||||
Key* ptrsKeys_R0[] = { &s_3, &o_capsLock }; | |||||
const uint8_t KEY_COUNT_R0 = sizeof(ptrsKeys_R0)/sizeof(*ptrsKeys_R0); | |||||
Row row_R0(scanner_R, 1<<0, ptrsKeys_R0, KEY_COUNT_R0); | |||||
Key* ptrsKeys_R1[] = { &s_c, &s_d }; | |||||
const uint8_t KEY_COUNT_R1 = sizeof(ptrsKeys_R1)/sizeof(*ptrsKeys_R1); | |||||
Row row_R1(scanner_R, 1<<1, ptrsKeys_R1, KEY_COUNT_R1); | |||||
// ################### MAIN #################### | |||||
void setup() | |||||
{ | |||||
Keyboard.begin(); | |||||
scanner_R.begin(); | |||||
} | |||||
void loop() | |||||
{ | |||||
//left matrix | |||||
row_L0.process(); | |||||
row_L1.process(); | |||||
//right matrix | |||||
row_R0.process(); | |||||
//Keyboard.println(" 0"); | |||||
//delay(2000); | |||||
row_R1.process(); | |||||
//Keyboard.println(" 1"); | |||||
//delay(2000); | |||||
scanDelay.delay(); | |||||
//Keyboard.println(" end loop");//todo | |||||
//debug.print_scans_per_second(); | |||||
//debug.print_microseconds_per_scan(); | |||||
} |
#include "LED_shiftRegs.h" | |||||
/* constructor | |||||
*/ | |||||
LED_shiftRegs::LED_shiftRegs(const uint8_t slaveSelect, const uint8_t pin) | |||||
:slaveSelect(slaveSelect), pin(pin) | |||||
{ | |||||
pinMode(slaveSelect, OUTPUT); | |||||
} | |||||
/* begin() should be called once from sketch setup(). | |||||
Initializes shift register's shift/load pin. | |||||
*/ | |||||
void LED_shiftRegs::begin() | |||||
{ | |||||
SPI.begin(); | |||||
digitalWrite(slaveSelect, HIGH); | |||||
} | |||||
//todo preserve other LED values, similar to Port_PCA9655E outputVal | |||||
void LED_shiftRegs::on() | |||||
{ | |||||
digitalWrite(slaveSelect, LOW); | |||||
SPI.transfer(pin); | |||||
digitalWrite (slaveSelect, HIGH); | |||||
} | |||||
void LED_shiftRegs::off() | |||||
{ | |||||
digitalWrite(slaveSelect, LOW); | |||||
SPI.transfer(0); | |||||
digitalWrite (slaveSelect, HIGH); | |||||
} |
#ifndef LED_SHIFTREGS_H | |||||
#define LED_SHIFTREGS_H | |||||
#include <Arduino.h> | |||||
#include <inttypes.h> | |||||
#include <SPI.h> | |||||
#include <LEDInterface.h> | |||||
/* A LED_shiftRegs turns LED on and off. | |||||
shift register RCLK pin a.k.a. SS or ST | |||||
*/ | |||||
class LED_shiftRegs: public LEDInterface | |||||
{ | |||||
private: | |||||
const uint8_t slaveSelect;//controller pin number connected to shift register RCLK | |||||
const uint8_t pin; //bit pattern, shift register pin that is connected to an LED | |||||
public: | |||||
LED_shiftRegs(const uint8_t slaveSelect, const uint8_t pin); | |||||
void begin(); | |||||
virtual void on(); | |||||
virtual void off(); | |||||
}; | |||||
#endif |
public: | public: | ||||
LED_uC(const uint8_t pin): pin(pin) | LED_uC(const uint8_t pin): pin(pin) | ||||
{ | { | ||||
pinMode(pin, OUTPUT); | |||||
pinMode(pin, OUTPUT);//todo move to .cpp file | |||||
} | } | ||||
virtual void on(); | virtual void on(); | ||||
virtual void off(); | virtual void off(); |
#include "Port_MCP23018.h" | |||||
//todo add Port_MCP23018::write() like Port_MCP23S17::transer() ?? | |||||
/* beginProtocol() is called from Scanner_IOE::begin(). Initiates I2C bus. | |||||
MCP23018 supports I2C SCL Clock Frequencies: 100 kHz, 400 kHz, 1000 kHz (Datasheet page 1 & 6) | |||||
The electrical limitation to bus speed is bus capacitance and the length of the wires involved. | |||||
Longer wires require lower clock speeds. | |||||
http://playground.arduino.cc/Main/WireLibraryDetailedReference > Wire.setclock() | |||||
*/ | |||||
void Port_MCP23018::beginProtocol() | |||||
{ | |||||
Wire.begin(); //initiate I2C bus to 100 kHz | |||||
//Wire.setClock(400000L); //set I2C bus to 400 kHz (have not tested 400 kHz) | |||||
} | |||||
/* begin() is called from Scanner_IOE::begin(). | |||||
Configures port's IODIR and GPPU. | |||||
*/ | |||||
void Port_MCP23018::begin(const uint8_t strobeOn) | |||||
{ | |||||
uint8_t pullUp; //bits, GPPU 0=pull-up disabled, 1=pull-up enabled | |||||
if (strobeOn == LOW) //if active low | |||||
{ | |||||
pullUp = readPins; //0=pull-up disabled (for LED), 1=pull-up enabled (for read) | |||||
} | |||||
else //if active high | |||||
{ | |||||
pullUp = 0; //0=pull-up disabled (for external pull-down resistors) | |||||
} | |||||
Wire.beginTransmission(deviceAddr); | |||||
Wire.write(portNum); //IODIR | |||||
Wire.write(readPins); //0=output (for strobe and LED), 1=input (for read) | |||||
Wire.endTransmission(); | |||||
Wire.beginTransmission(deviceAddr); | |||||
Wire.write(portNum + 0x0C); //GPPU | |||||
Wire.write(pullUp); | |||||
Wire.endTransmission(); | |||||
} | |||||
/* write() sets pin output to logicLevel. | |||||
pin is bit pattern, where pin being strobed is 1. | |||||
logicLevel is HIGH or LOW. | |||||
write() does not overwrite the other pins. | |||||
*/ | |||||
void Port_MCP23018::write(const uint8_t pin, const bool logicLevel) | |||||
{ | |||||
if (logicLevel == LOW) | |||||
{ | |||||
outputVal &= ~pin; //set pin output to low | |||||
//Keyboard.print(" low"); | |||||
} | |||||
else | |||||
{ | |||||
outputVal |= pin; //set pin output to high | |||||
//Keyboard.print(" high"); | |||||
} | |||||
//Keyboard.print(" outputVal=");//todo | |||||
//Keyboard.println(outputVal); | |||||
Wire.beginTransmission(deviceAddr); | |||||
Wire.write(portNum + 0x12); //GPIO | |||||
Wire.write(outputVal); | |||||
Wire.endTransmission(); | |||||
//delay(4000); | |||||
} | |||||
/* read() returns portState. | |||||
Only portState bits of readPins are valid. | |||||
*/ | |||||
uint8_t Port_MCP23018::read() | |||||
{ | |||||
Wire.beginTransmission(deviceAddr); | |||||
Wire.write(portNum + 0x12); //GPIO | |||||
Wire.endTransmission(false); //MCP23018 needs false to send a restart ??really? | |||||
Wire.requestFrom(deviceAddr, 1u); //request one byte from input port | |||||
return Wire.read(); | |||||
} |
#ifndef PORT_MCP23018_H | |||||
#define PORT_MCP23018_H | |||||
#include <Arduino.h> | |||||
#include <inttypes.h> | |||||
#include <Wire.h> | |||||
#include <PortInterface.h> | |||||
/* | |||||
write pins are connected to matrix Row (strobe pin) or LED. | |||||
readPins are connected to matrix column to read which keys are pressed. | |||||
MCP23018 has open-drain outputs (open-drain can only sink current). If LEDs are used, connect: | |||||
LED anodes (the longer lead) to power | |||||
LED cathodes (the shorter lead) to GPIO pin | |||||
Instantiation | |||||
------------ | |||||
Example instantiation: | |||||
const uint8_t IOE_ADDR = 0x20; //MCP23018 address pin grounded | |||||
Port_MCP23018 portB(IOE_ADDR, 1, 0); //all pins are set to output for strobes and LEDs | |||||
Port_MCP23018 portA(IOE_ADDR, 0, 1<<0 | 1<<1 ); //pin 0 and pin 1 are set to input for reading, | |||||
//remaining pins can be used for LEDs | |||||
Diode orientation | |||||
---------------- | |||||
Diode orientation is explained in keybrd_library_user_guide.md > Diode orientation | |||||
MCP23018 data sheet | |||||
---------------- | |||||
http://ww1.microchip.com/downloads/en/DeviceDoc/22103a.pdf | |||||
*/ | |||||
class Port_MCP23018 : public PortInterface | |||||
{ | |||||
private: | |||||
const uint8_t deviceAddr; | |||||
const uint8_t portNum; //port identification number, 0=A, 1=B | |||||
uint8_t outputVal; //bit pattern for strobe and LEDs | |||||
const uint8_t readPins; //bit pattern, IODIR 0=output, 1=input | |||||
public: | |||||
Port_MCP23018(const uint8_t deviceAddr, const uint8_t portNum, const uint8_t readPins) | |||||
: deviceAddr(deviceAddr), portNum(portNum), outputVal(0), readPins(readPins) {} | |||||
void beginProtocol(); | |||||
void begin(const uint8_t strobeOn); | |||||
virtual void write(const uint8_t pin, const bool logicLevel); | |||||
virtual uint8_t read(); | |||||
}; | |||||
#endif |
return portState; | return portState; | ||||
} | } | ||||
/* begin() is called from Scanner_IOE::begin(). Initiates SPI bus. | |||||
/* beginProtocol() is called from Scanner_IOE::begin(). Initiates SPI bus. | |||||
*/ | */ | ||||
void Port_MCP23S17::beginProtocol() | void Port_MCP23S17::beginProtocol() | ||||
{ | { | ||||
{ | { | ||||
uint8_t pullUp; //bits, GPPU 0=pull-up disabled, 1=pull-up enabled | uint8_t pullUp; //bits, GPPU 0=pull-up disabled, 1=pull-up enabled | ||||
if (strobeOn == LOW) //if active low, use internal pull-up resistors | |||||
if (strobeOn == LOW) //if active low | |||||
{ | { | ||||
pullUp = readPins; | |||||
pullUp = readPins; //0=pull-up disabled (for LED), 1=pull-up enabled (for read) | |||||
} | } | ||||
else //active high requires external pull-down resistors | |||||
else //if active high | |||||
{ | { | ||||
pullUp = 0; | |||||
pullUp = 0; //0=pull-up disabled (for external pull-down resistors) | |||||
} | } | ||||
transfer(deviceAddr << 1, portNum, readPins); //configure IODIR | transfer(deviceAddr << 1, portNum, readPins); //configure IODIR |
#include "Port_PCA9655E.h" | #include "Port_PCA9655E.h" | ||||
/* begin() is called from Scanner_IOE::begin(). Initiates I2C bus. | |||||
/* beginProtocol() is called from Scanner_IOE::begin(). Initiates I2C bus. | |||||
PCA9655E supports I2C SCL Clock Frequencies: 100 kHz, 400 kHz, 1000 kHz (Datasheet page 1 & 6) | PCA9655E supports I2C SCL Clock Frequencies: 100 kHz, 400 kHz, 1000 kHz (Datasheet page 1 & 6) | ||||
The electrical limitation to bus speed is bus capacitance and the length of the wires involved. | The electrical limitation to bus speed is bus capacitance and the length of the wires involved. |
void Scanner_ShiftRegsPISOMultiRow::init(const uint8_t strobePin) | void Scanner_ShiftRegsPISOMultiRow::init(const uint8_t strobePin) | ||||
{ | { | ||||
pinMode(strobePin, OUTPUT); | pinMode(strobePin, OUTPUT); | ||||
//digitalWrite(strobePin, strobeOff);//todo is this needed? | |||||
} | } | ||||
/* begin() should be called once from sketch setup(). | /* begin() should be called once from sketch setup(). | ||||
*/ | */ | ||||
void Scanner_ShiftRegsPISOMultiRow::begin() | void Scanner_ShiftRegsPISOMultiRow::begin() | ||||
{ | { | ||||
digitalWrite(slaveSelect, HIGH); | |||||
SPI.begin(); | |||||
} | } | ||||
/* scan() strobes the row's strobePin and returns state of the shift register's input pins. | /* scan() strobes the row's strobePin and returns state of the shift register's input pins. | ||||
{ | { | ||||
read_pins_t readState = 0; //bits, 1 means key is pressed, 0 means released | read_pins_t readState = 0; //bits, 1 means key is pressed, 0 means released | ||||
//strobe row on | |||||
//strobe on | |||||
digitalWrite(strobePin, strobeOn); | digitalWrite(strobePin, strobeOn); | ||||
delayMicroseconds(3); //time to stablize voltage | |||||
//SPI.beginTransaction( SPISettings(5000000, MSBFIRST, SPI_MODE0) ); //control SPI bus, 5 MHz | |||||
//digitalWrite(slaveSelect, LOW); //assert slave | |||||
delayMicroseconds(3); //time to stabilize photo-transistor todo needed? | |||||
delayMicroseconds(50); //todo for sr2_LEDs_strobe.ino | |||||
//read all the column pins | //read all the column pins | ||||
digitalWrite(slaveSelect, LOW); //load parallel inputs to the register | |||||
digitalWrite(slaveSelect, HIGH); //shift the data toward a serial output | digitalWrite(slaveSelect, HIGH); //shift the data toward a serial output | ||||
SPI.transfer(&readState, byte_count); | SPI.transfer(&readState, byte_count); | ||||
digitalWrite(slaveSelect, LOW); //load parallel inputs to registers | |||||
//digitalWrite(slaveSelect, HIGH); //de-assert slave | |||||
//SPI.endTransaction(); | |||||
//strobe row off | |||||
//strobe off | |||||
digitalWrite(strobePin, strobeOff); | digitalWrite(strobePin, strobeOff); | ||||
return readState; | return readState; |
/* Scanner_ShiftRegsPISOMultiRow reads shift registers. | /* Scanner_ShiftRegsPISOMultiRow reads shift registers. | ||||
This was tested on 74HC165 shift registers, which are Parallel-In-Serial-Out (PISO). | This was tested on 74HC165 shift registers, which are Parallel-In-Serial-Out (PISO). | ||||
Upto 4 shift registers can be in a daisy chained for a total of 32 read pins. | |||||
Shift registers can be daisy chained for a total of 32 read pins. | |||||
Example instantiation: | Example instantiation: | ||||
Scanner_ShiftRegsPISOMultiRow scanner_R(HIGH, SS, 4); | Scanner_ShiftRegsPISOMultiRow scanner_R(HIGH, SS, 4); | ||||
"strobeOn" paramter is active state HIGH or LOW. | "strobeOn" paramter is active state HIGH or LOW. | ||||
"slaveSelect" paramter can be any controller pin connected to shift register's SHIFT-LOAD pin. | "slaveSelect" paramter can be any controller pin connected to shift register's SHIFT-LOAD pin. | ||||
slaveSelect pin SS (Arduino pin 10) has the fastest scan. | |||||
"byte_count" is the number of bytes to read from shift registers (1 to 4). | "byte_count" is the number of bytes to read from shift registers (1 to 4). | ||||
byte_count should cover all the row's keys: | byte_count should cover all the row's keys: | ||||
Hardware setup: | Hardware setup: | ||||
Each row needs to be connected to a strobe pin from the controller. | Each row needs to be connected to a strobe pin from the controller. | ||||
Switche and diode in series are connected to shift-register parallel-input pins and strobed row. | |||||
Switch and diode in series, connect shift-register parallel-input pins to strobed row. | |||||
For active low: | For active low: | ||||
Shift-register parallel-input pins need 10k Ohm pull-up resistors powered. | Shift-register parallel-input pins need 10k Ohm pull-up resistors powered. |
#include "Scanner_ShiftRegsPISOSingleRow.h" | #include "Scanner_ShiftRegsPISOSingleRow.h" | ||||
/* constructor | /* constructor | ||||
Parameter strobeOn is not used. | |||||
*/ | */ | ||||
Scanner_ShiftRegsPISOSingleRow::Scanner_ShiftRegsPISOSingleRow(const bool strobeOn, | Scanner_ShiftRegsPISOSingleRow::Scanner_ShiftRegsPISOSingleRow(const bool strobeOn, | ||||
const uint8_t slaveSelect, const uint8_t byte_count) | const uint8_t slaveSelect, const uint8_t byte_count) | ||||
} | } | ||||
/* scan() returns state of the shift register's input pins. | /* scan() returns state of the shift register's input pins. | ||||
Parameter strobePin is not used. | |||||
No strobe pin is needed, the shift register is wired so the strobe is effectivley always "on". | No strobe pin is needed, the shift register is wired so the strobe is effectivley always "on". | ||||
Bit patterns are 1 bit per key. | Bit patterns are 1 bit per key. | ||||
*/ | */ |
"strobeOn" paramter is ignored, but should be active state HIGH or LOW required by ScannerInterface. | "strobeOn" paramter is ignored, but should be active state HIGH or LOW required by ScannerInterface. | ||||
"slaveSelect" paramter can be any controller pin connected to shift register's SHIFT-LOAD pin. | "slaveSelect" paramter can be any controller pin connected to shift register's SHIFT-LOAD pin. | ||||
slaveSelect pin SS (Arduino pin 10) has the fastest scan. | |||||
"byte_count" is the number of bytes to read from shift registers (1 to 4). | "byte_count" is the number of bytes to read from shift registers (1 to 4). | ||||
byte_count should cover all the row's keys: | byte_count should cover all the row's keys: | ||||
Hardware setup: | Hardware setup: | ||||
There is only one row, and it is permanently active. | There is only one row, and it is permanently active. | ||||
Switches are connected to shift-register parallel-input pins (diodes are not needed) and row. | |||||
Switches are connected to shift-register parallel-input pins and row. | |||||
Diodes are not needed because there is only one row. | |||||
For active low: | For active low: | ||||
Shift-register parallel-input pins need 10k Ohm pull-up resistors powered. | Shift-register parallel-input pins need 10k Ohm pull-up resistors powered. |
Controller Two shift registers daisy chained | Controller Two shift registers daisy chained | ||||
| Left |**0**| | Right |**0**|**1**|**2**|**3**|**4**|**5**|**6**| | | Left |**0**| | Right |**0**|**1**|**2**|**3**|**4**|**5**|**6**| | ||||
|:-----:|-----| |:-----:|-----|-----|-----|-----|-----|-----|-----| | |||||
|-------|-----| |-------|-----|-----|-----|-----|-----|-----|-----| | |||||
| **0** | x | | **0** | 0 | 1 | 2 | 3 | 4 | 5 | 6 | | | **0** | x | | **0** | 0 | 1 | 2 | 3 | 4 | 5 | 6 | | ||||
| **1** | y | | **1** | a | b | c | d | e | f | g | | | **1** | y | | **1** | a | b | c | d | e | f | g | | ||||
*/ | */ | ||||
Scanner_uC scanner_L(LOW, readPins_L, readPinCount_L); //active LOW | Scanner_uC scanner_L(LOW, readPins_L, readPinCount_L); //active LOW | ||||
/* --------------- RIGHT SCANNER --------------- | |||||
use slaveSelect pin SS (Arduino pin 10) for fastest scan | |||||
*/ | |||||
Scanner_ShiftRegsPISOSingleRow scanner_R(HIGH, SS, 2); //active HIGH | |||||
// --------------- RIGHT SCANNER --------------- | |||||
Scanner_ShiftRegsPISOSingleRow scanner_R(HIGH, 10, 2); //active HIGH | |||||
// =================== ROWS ==================== | // =================== ROWS ==================== | ||||
// ----------------- LEFT ROWS ----------------- | // ----------------- LEFT ROWS ----------------- |
Controller I/O expander | Controller I/O expander | ||||
| Left | **0** | **1** | | Right | **0** | **1** | | | Left | **0** | **1** | | Right | **0** | **1** | | ||||
|:-----:|-------|-------|-|:-----:|-------|-------| | |||||
|-------|-------|-------| |-------|-------|-------| | |||||
| **1** | 1 | 2 | | **1** | 3 | 4 | | | **1** | 1 | 2 | | **1** | 3 | 4 | | ||||
| **0** | a | b | | **0** | c | d | | | **0** | a | b | | **0** | c | d | | ||||
*/ | */ |
/* keybrd_5b_LED_on_IOE.ino | /* keybrd_5b_LED_on_IOE.ino | ||||
This sketch: | This sketch: | ||||
is a simple 1-layer keyboard with CapsLck indicator LED on I/O expander | |||||
is a simple 1-layer keyboard with indicator LED on controller and 2 LEDs on I/O expander | |||||
runs on a two-matrix breadboard keyboard | runs on a two-matrix breadboard keyboard | ||||
modified keybrd_4c_split_keyboard_with_IOE.ino by adding LED_capsLck | modified keybrd_4c_split_keyboard_with_IOE.ino by adding LED_capsLck | ||||
Controller I/O expander | Controller I/O expander | ||||
| Left | **0** | **1** | | Right | **0** | **1** | | | Left | **0** | **1** | | Right | **0** | **1** | | ||||
|:-----:|-------|-------|-|:-----:|-------|-------| | |:-----:|-------|-------|-|:-----:|-------|-------| | ||||
| **1** |CapsLck| a 1 | | **1** | b 2 | c 3 | | |||||
| **0** | fn | x = | | **0** | y - | z / | | |||||
| **0** |CapsLck| a 1 | | **1** | b 2 | c 3 | | |||||
| **1** | fn | x = | | **0** | y - | z / | | |||||
*/ | */ | ||||
// ################## GLOBAL ################### | // ################## GLOBAL ################### | ||||
// ================= INCLUDES ================== | // ================= INCLUDES ================== | ||||
// ============ SPEED CONFIGURATION ============ | // ============ SPEED CONFIGURATION ============ | ||||
ScanDelay scanDelay(9000); | ScanDelay scanDelay(9000); | ||||
// ==================== IC ===================== | |||||
// ==================== ICs ==================== | |||||
// ---------------- LEFT SCANNER --------------- | // ---------------- LEFT SCANNER --------------- | ||||
uint8_t readPins[] = {14, 15}; | uint8_t readPins[] = {14, 15}; | ||||
const uint8_t readPinCount = sizeof(readPins)/sizeof(*readPins); | const uint8_t readPinCount = sizeof(readPins)/sizeof(*readPins); | ||||
// --------------- RIGHT SCANNER --------------- | // --------------- RIGHT SCANNER --------------- | ||||
const uint8_t IOE_ADDR = 0x20; //MCP23S17 address, all 3 ADDR pins are grounded | const uint8_t IOE_ADDR = 0x20; //MCP23S17 address, all 3 ADDR pins are grounded | ||||
Port_MCP23S17 portA(IOE_ADDR, 0, 1<<0 | 1<<1 ); //for read and LED | |||||
Port_MCP23S17 portB(IOE_ADDR, 1, 0); //for strobe and LED | |||||
Port_MCP23S17 portA(IOE_ADDR, 0, 1<<0 | 1<<1 ); //for read | |||||
Port_MCP23S17 portB(IOE_ADDR, 1, 0); //for strobe | |||||
Scanner_IOE scanner_R(LOW, portB, portA); | Scanner_IOE scanner_R(LOW, portB, portA); | ||||
Electrostatic discharge (ESD) safety | Electrostatic discharge (ESD) safety | ||||
------------------------------------ | ------------------------------------ | ||||
Static electricity can damage a microcontroller in ways that are hard to trouble shoot. | |||||
Static electricity can damage a ICs in ways that are hard to trouble shoot. | |||||
I live in a desert on a carpeted floor and get zapped by door knobs regularly. | |||||
Here is the ESD precaution I take whenever I handle a microcontroller: | |||||
Here is a ESD precaution for handling ICs: | |||||
1. Touch the bare metal on the back of my desktop computer (its grounded). | 1. Touch the bare metal on the back of my desktop computer (its grounded). | ||||
2. Then touch the metal USB connector case on the microcontroller. | |||||
Anti-static mat or anti-static wristband are also effective. | |||||
Being tethered by an anti-static wristband can be inconvenient (wireless antistatic wrist straps are a scam). | |||||
2. Then touch the IC or circuit (if its a micro-controller, touch the metal USB connector case). | |||||
I take these ESD precaution because I live in a dry environment on a carpeted floor. | |||||
Not everyone needs to take ESD precautions: | Not everyone needs to take ESD precautions: | ||||
* http://forum.arduino.cc/index.php?topic=4643.0 | * http://forum.arduino.cc/index.php?topic=4643.0 | ||||
* https://forums.adafruit.com/viewtopic.php?f=8&t=12128 | * https://forums.adafruit.com/viewtopic.php?f=8&t=12128 | ||||
Anti-static mat or anti-static wristband are also effective. | |||||
But being tethered by an anti-static wristband can be inconvenient (wireless antistatic wrist straps are a scam). | |||||
Building a basic breadboard keyboard | Building a basic breadboard keyboard | ||||
------------------------------------ | ------------------------------------ | ||||
The basic breadboard keyboard has 4 switches. | The basic breadboard keyboard has 4 switches. |
| Layout |**0**|**1**|**2**| | | Layout |**0**|**1**|**2**| | ||||
|:------:|:---:|:---:|:---:| | |:------:|:---:|:---:|:---:| | ||||
| **0** | 1 | 2 | 3 | | | **0** | 1 | 2 | 3 | | ||||
| **1** | a | b | | |||||
| **1** | a | b | | | |||||
[keybrd tutorials usability survey](../CONTRIBUTING.md##keybrd-tutorials-usability-survey) | |||||
<br> | <br> | ||||
<a rel="license" href="https://creativecommons.org/licenses/by/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://licensebuttons.net/l/by/4.0/88x31.png" /></a><br /><span xmlns:dct="http://purl.org/dc/terms/" property="dct:title">keybrd tutorial</span> by <a xmlns:cc="https://creativecommons.org/ns" href="https://github.com/wolfv6/keybrd" property="cc:attributionName" rel="cc:attributionURL">Wolfram Volpi</a> is licensed under a <a rel="license" href="https://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International License</a>.<br />Permissions beyond the scope of this license may be available at <a xmlns:cc="https://creativecommons.org/ns" href="https://github.com/wolfv6/keybrd/issues/new" rel="cc:morePermissions">https://github.com/wolfv6/keybrd/issues/new</a>. | <a rel="license" href="https://creativecommons.org/licenses/by/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://licensebuttons.net/l/by/4.0/88x31.png" /></a><br /><span xmlns:dct="http://purl.org/dc/terms/" property="dct:title">keybrd tutorial</span> by <a xmlns:cc="https://creativecommons.org/ns" href="https://github.com/wolfv6/keybrd" property="cc:attributionName" rel="cc:attributionURL">Wolfram Volpi</a> is licensed under a <a rel="license" href="https://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International License</a>.<br />Permissions beyond the scope of this license may be available at <a xmlns:cc="https://creativecommons.org/ns" href="https://github.com/wolfv6/keybrd/issues/new" rel="cc:morePermissions">https://github.com/wolfv6/keybrd/issues/new</a>. |
I/O Expanders can power LEDs, while PISO shift registers can not. | I/O Expanders can power LEDs, while PISO shift registers can not. | ||||
I2C is a little slow if the I/O expander is scanning more than 4 rows. | |||||
The keybrd_DH (DodoHand) sketch polls 5 rows over I2C at 7.5ms per keyboard scan. | |||||
Which is acceptable, but faster would be nicer. | |||||
I2C is fast enough for scanning up to 5 rows. | |||||
SPI is much faster, but requires 2 additional wires. | |||||
More scanning options are reviewed at http://www.openmusiclabs.com/learning/digital/input-matrix-scanning/ | |||||
## Cables table | ## Cables table | ||||
The 8-wire "GearIT Cat 6 Ethernet Flat Patch Cable 7 Feet" is very flexible. | The 8-wire "GearIT Cat 6 Ethernet Flat Patch Cable 7 Feet" is very flexible. | ||||
It's available at Walmart (9/19/16) if you want to feel the merchandise before you buy. | It's available at Walmart (9/19/16) if you want to feel the merchandise before you buy. | ||||
All the modular connectors are flat. | |||||
All the modular connector cables are available in the flat form factor. | |||||
For prototyping on perfboards, consider a 0.1" header. | For prototyping on perfboards, consider a 0.1" header. | ||||
**74HC165 left (lower half of breadboard)** | **74HC165 left (lower half of breadboard)** | ||||
|NAME |PIN#|DESCRIPTION |TO TEENSY LC PIN#|CHAIN | | |NAME |PIN#|DESCRIPTION |TO TEENSY LC PIN#|CHAIN | | ||||
|--------|:--:|--------------------|-----------------|-----------| | |||||
|:-------|:--:|:-------------------|:----------------|:----------| | |||||
|SH/LD |1 |shift or load input |CS0 10 |green wire | | |SH/LD |1 |shift or load input |CS0 10 |green wire | | ||||
|CLK |2 |clock input |SCK0 13 |yellow wire| | |CLK |2 |clock input |SCK0 13 |yellow wire| | ||||
| D4 |3 |parallel input | | | | | D4 |3 |parallel input | | | | ||||
**74HC165 right (upper half of breadboard)** | **74HC165 right (upper half of breadboard)** | ||||
|NAME |PIN#|DESCRIPTION |TO TEENSY LC PIN#|CHAIN | | |NAME |PIN#|DESCRIPTION |TO TEENSY LC PIN#|CHAIN | | ||||
|--------|:--:|--------------------|-----------------|-------------------------| | |||||
|:-------|:--:|:-------------------|:----------------|:------------------------| | |||||
|VCC |16 |power pin |3.3V |red wire | | |VCC |16 |power pin |3.3V |red wire | | ||||
|CLK INH |15 |clock inhibit | |blue bus | | |CLK INH |15 |clock inhibit | |blue bus | | ||||
| D3 |14 |parallel input | | | | | D3 |14 |parallel input | | | |
![breadboard keyboard with IOE](keybrd_4c_split_keyboard_with_IOE/front.JPG "breadboard keyboard with IOE") | ![breadboard keyboard with IOE](keybrd_4c_split_keyboard_with_IOE/front.JPG "breadboard keyboard with IOE") | ||||
I/O expanders | |||||
------------- | |||||
The MCP23S17 I/O expander has two ports with 8 I/O pins each. | |||||
I/O expander input/output pins are connected to a switch matrix. | |||||
Port B strobes one row at a time. | |||||
Port A reads the columns. | |||||
SPI and I2C communication protocols | |||||
----------------------------------- | |||||
SPI and I2C are popular communication protocols. | |||||
Either one can be used to connect split keyboards. | |||||
I2C is fast enough for scanning up to 5 rows. | |||||
The keybrd_DH (DodoHand) sketch polls 5 rows over I2C at 7.5ms per keyboard scan. | |||||
SPI is much faster, but requires 2 additional wires. | |||||
Use SPI if your key matrix has more then 5 rows. | |||||
| connection type | controller pins | wire count | | |||||
|------------------------|:---------------:|:----------:| | |||||
| I/O expander SPI | 4 | 6 | | |||||
| I/O expander I2C | 2 | 4 | | |||||
The example in this tutorial uses the SPI protocol. | |||||
Building a split keyboard with I/O Expander | Building a split keyboard with I/O Expander | ||||
------------------------------------------- | ------------------------------------------- | ||||
Starting with the basic breadboard keyboard described in [tutorial_1_breadboard_keyboard.md](tutorial_1_breadboard_keyboard.md), add parts as described above. | Starting with the basic breadboard keyboard described in [tutorial_1_breadboard_keyboard.md](tutorial_1_breadboard_keyboard.md), add parts as described above. |
Too little resistance will burn out the LED. | Too little resistance will burn out the LED. | ||||
The current supplied to an LED should always be limited by a resistor or some other device. | The current supplied to an LED should always be limited by a resistor or some other device. | ||||
LED current limiting resistor values | |||||
LED current-limiting resistor values | |||||
------------------------------------ | ------------------------------------ | ||||
In the section we will compute the minimum resistor value for maximum LED brightness. | In the section we will compute the minimum resistor value for maximum LED brightness. | ||||
------------------------------ | ------------------------------ | ||||
The [keybrd_5b_LED_on_IOE.ino](keybrd_5b_LED_on_IOE/keybrd_5b_LED_on_IOE.ino) sketch will run on the above breadboard keyboard. | The [keybrd_5b_LED_on_IOE.ino](keybrd_5b_LED_on_IOE/keybrd_5b_LED_on_IOE.ino) sketch will run on the above breadboard keyboard. | ||||
The sketch features: | |||||
The sketch demonstrates: | |||||
* multiple layers | * multiple layers | ||||
* split keyboard | * split keyboard | ||||
* LED on controller | * LED on controller |
/* unit test for Port_MCP23018 | |||||
Picture of hardware is in unit_tests/PortMCP23018_read/PortMCP23018_bb.JPG todo | |||||
The setup is an MCP23018 I/O expander on a Teensy LC controller. | |||||
MCP23018 port-A GPIO pins are not connected to anything. | |||||
Port-A GPIO-pin ouputs alternate between 0 and 3.3 volts. | |||||
volt meter between pin A1 and power because | |||||
MCP23018 has open-drain outputs (open-drain can only sink current) | |||||
Use a volt meter to measure port-A GPIO-pin outputs or red LED. | |||||
*/ | |||||
#include "Port_MCP23018.h" | |||||
const uint8_t IOE_ADDR = 0x20; //MCP23018 address ADDR pin grounded | |||||
Port_MCP23018 portA(IOE_ADDR, 0, 0); | |||||
void setup() | |||||
{ | |||||
delay(6000); | |||||
Keyboard.println("PortMCP23018_write.ino"); | |||||
portA.beginProtocol(); | |||||
portA.begin(LOW); //HIGH or LOW, not matter if readPins=0 | |||||
} | |||||
void loop() | |||||
{ | |||||
portA.write(~0, HIGH); //set all GPIOA pins HIGH | |||||
Keyboard.print("+"); | |||||
delay(2000); | |||||
portA.write(~0, LOW); //set all GPIOA pins LOW | |||||
Keyboard.print("0"); | |||||
delay(2000); | |||||
} |
Picture of hardware is in unit_tests/PortMCP23S17_read/PortMCP23S17_bb.JPG | Picture of hardware is in unit_tests/PortMCP23S17_read/PortMCP23S17_bb.JPG | ||||
The setup is an MCP23S17 I/O expander on a Teensy LC controller. | The setup is an MCP23S17 I/O expander on a Teensy LC controller. | ||||
MCP23S17 port-B pins are alternately grounded and energized. | MCP23S17 port-B pins are alternately grounded and energized. | ||||
portBState is a bitwise reading of port B. | |||||
output is: 10101010 | output is: 10101010 | ||||
*/ | */ | ||||
#include "PortIOE.h" | |||||
#include "PortMCP23S17.h" | |||||
#include "Port_MCP23S17.h" | |||||
const uint8_t PortIOE::DEVICE_ADDR = 0x20; //MCP23S17 address, all 3 ADDR pins are grounded | |||||
PortIOE portB(1); | |||||
PortMCP23S17 portBRead(portB, ~0); | |||||
const uint8_t IOE_ADDR = 0x20; //MCP23S17 address, all 3 ADDR pins grounded | |||||
Port_MCP23S17 portB(IOE_ADDR, 1, ~0); //read all pins | |||||
void setup() | void setup() | ||||
{ | { | ||||
uint8_t portBState; //bit pattern | |||||
uint8_t BitPattern; //reading of port B | |||||
delay(6000); | delay(6000); | ||||
portBRead.begin(HIGH); //HIGH or LOW, not matter | |||||
portB.begin(HIGH); //HIGH or LOW, does not matter | |||||
portBState = portBRead.read(); | |||||
Keyboard.print("portBState = "); | |||||
Keyboard.println(portBState, BIN); //prints 10101010 | |||||
BitPattern = portB.read(); | |||||
Keyboard.print("BitPattern = "); | |||||
Keyboard.println(BitPattern, BIN); //prints 10101010 | |||||
} | } | ||||
void loop() { } | void loop() { } |
/* unit test for PortMCP23S17 | |||||
/* unit test for Port_MCP23S17 | |||||
Picture of hardware is in unit_tests/PortMCP23S17_read/PortMCP23S17_bb.JPG | Picture of hardware is in unit_tests/PortMCP23S17_read/PortMCP23S17_bb.JPG | ||||
The setup is an MCP23S17 I/O expander on a Teensy LC controller. | The setup is an MCP23S17 I/O expander on a Teensy LC controller. | ||||
MCP23S17 port-A GPIO pins are not connected to anything. | MCP23S17 port-A GPIO pins are not connected to anything. | ||||
Port-A GPIO-pin ouputs alternate between 0 and 3.3 volts. | Port-A GPIO-pin ouputs alternate between 0 and 3.3 volts. | ||||
Use a volt meter to measure port-A GPIO-pin outputs. | |||||
OR low-voltage LED, with forward voltage less than 2 volts. | |||||
Use a volt meter to measure port-A GPIO-pin outputs or red LED. | |||||
*/ | */ | ||||
#include "PortIOE.h" | |||||
#include "PortMCP23S17.h" | |||||
#include "Port_MCP23S17.h" | |||||
const uint8_t PortIOE::DEVICE_ADDR = 0x20; //MCP23S17 address, all 3 ADDR pins are grounded | |||||
PortIOE portA(0); | |||||
PortMCP23S17 portAWrite(portA, 0); //PortAWrite needed for begin() | |||||
//const uint8_t GPIOA = 0x12; //LEDs are on port A | |||||
const uint8_t IOE_ADDR = 0x20; //MCP23S17 address, all 3 ADDR pins grounded | |||||
Port_MCP23S17 portA(IOE_ADDR , 0, 0); //PortAWrite needed for begin() | |||||
void setup() | void setup() | ||||
{ | { | ||||
delay(6000); | delay(6000); | ||||
portAWrite.begin(LOW); //HIGH or LOW, not matter if readPins=0 | |||||
Keyboard.print("start writing"); | |||||
Keyboard.println("start setup"); | |||||
portA.begin(LOW); //HIGH or LOW, not matter if readPins=0 | |||||
Keyboard.println("start loop"); | |||||
} | } | ||||
void loop() | void loop() | ||||
{ | { | ||||
portAWrite.write(~0, HIGH); //set all GPIOA pins HIGH | |||||
portA.write(~0, HIGH); //set all GPIOA pins HIGH | |||||
delay(2000); | delay(2000); | ||||
portAWrite.write(~0, LOW); //set all GPIOA pins LOW | |||||
portA.write(~0, LOW); //set all GPIOA pins LOW | |||||
delay(2000); | delay(2000); | ||||
} | } |