diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 3d18462..4bbc30e 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -7,8 +7,9 @@ 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)
-Submit improvement suggestions to [GitHub issues](https://github.com/wolfv6/Keybrd/issues).
-* The issue title should start with "suggestion:" followed by a descriptive title
+Submit improvement suggestions to [GitHub issues](https://github.com/wolfv6/Keybrd/issues)
+or [geekhack thread](https://geekhack.org/index.php?topic=83599.0).
+
* Provide a use case
* Explain why the improvement is useful
* Site other product examples where this improvement exists
@@ -16,6 +17,8 @@ Submit improvement suggestions to [GitHub issues](https://github.com/wolfv6/Keyb
Bug reports
-----------
A bug report is the first step in making the keybrd library work the way it's supposed to work.
+Submit bug reports to [GitHub issues](https://github.com/wolfv6/Keybrd/issues)
+or [geekhack thread](https://geekhack.org/index.php?topic=83599.0).
Please provide enough information so we can reproduce the bug behaviour!
* Complete sketch (copy & paste, attachment, or a link to the code)
@@ -52,7 +55,7 @@ A healthy project needs the perspective of many people.
* Documentation - Suggest a clarification, simplification, correction, or other improvement.
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.
-* [Current user contributions](https://geekhack.org/index.php?topic=83599.0) highlights contributions that are needed for the keybrd project's current stage of development.
+* [Current user contributions](https://geekhack.org/index.php?topic=83599.msg2223776#msg2223776) highlights contributions that are needed for the keybrd project's current stage of development.
Text file documentation style guide:
* Use Markdown with a .md suffix.
diff --git a/doc/keybrd_library_developer_guide.md b/doc/keybrd_library_developer_guide.md
index b912204..4506c49 100644
--- a/doc/keybrd_library_developer_guide.md
+++ b/doc/keybrd_library_developer_guide.md
@@ -27,7 +27,7 @@ Row_Ext::keyWasPressed() overrides Row::keyWasPressed() which is used to unstick
Row_Ext_uC and Row_Ext_ShiftRegisters are a custom classes composed of stock keybrd library classes.
Row_Ext_uC uses Scanner_uC to scan the primary matrix.
-Row_Ext_ShiftRegisters uses Scanner_ShiftRegs74HC165 to scan the secondary matrix.
+Row_Ext_ShiftRegisters uses Scanner_ShiftRegs74HC165 to scan the peripheral matrix.
Class inheritance diagram
```
@@ -68,7 +68,7 @@ Keybrd library class inheritance diagram
```
________ Row ___________
/ | \
- Row_uC Row_ShiftRegisters Row_IOE (to be added)
+ Row_uC Row_ShiftRegisters Row_IOE (todo to be added)
Scanner_uC Scanner_Port Scanner_ShiftRegs74HC165
@@ -78,7 +78,7 @@ Keybrd library class inheritance diagram
PortWrite
|
- PortWrite_PCA9655E (one PortWrite class for each IOE type)
+ PortWrite_PCA9655E (one PortWrite class for each IOE type)
PortRead
|
@@ -115,9 +115,9 @@ Keybrd library class inheritance diagram
| | |
| Code_LayeredScSc Code_LayeredCodeSc
|
- |__________________________________________
- \ \ \ \
- Code_Sc Code_Shift Code_AutoShift Code_LEDLock
+ |_________________________________________________________
+ \ \ \ \ \
+ Code_Sc Code_Shift Code_AutoShift Code_LEDLock Code_Null
/ \
Code_ScS Code_ScNS
@@ -149,7 +149,7 @@ Dependency diagram of example multi-layer keyboard with layer LEDs
```
-Dependency diagram of example secondary matrix with shift registers
+Dependency diagram of example peripheral matrix with shift registers
```
Row_ShiftRegisters[1..*]
/ \ \
@@ -159,17 +159,17 @@ Dependency diagram of example secondary matrix with shift registers
```
-Dependency diagram of example secondary matrix with I/O Expander and LEDs
+Dependency diagram of example peripheral matrix with I/O Expander and LEDs
```
- ___ Row_IOE[1..*] _________
- / \ \
- _ Scanner_Port[1] _ Debouncer[1] Keys[1..*] __
- / | \ | \
- PortWrite[1] RowPin[1] PortRead[1] Code[1..*] Code_LEDLock[1..*]
- \ / \ |
- \ / ColPins[1..*] LED[1]
- \ /
- PortIOE[0..*]
+ _____ Row_IOE[1..*] _________
+ / \ \
+ __ Scanner_Port[1] __ Debouncer[1] Keys[1..*] __
+ / | \ | \
+ PortWrite[1] strobePin[1] PortRead[1] Code[1..*] Code_LEDLock[1..*]
+ \ / \ |
+ \ / ColPins[1..*] LED[1]
+ \ /
+ PortIOE[0..*]
```
@@ -243,7 +243,7 @@ Following the style guide makes it easier for the next programmer to understand
Trace of keybrd scan
--------------------
Arduino does not have a debugger.
-So here is the next best thing; a list of functions in the order that they are called.
+So here is a list of functions in the order that they are called.
The trace is of a one-row single-layer keybrd scan.
Refer to it like a table of contents while reading the keybrd library.
diff --git a/src/PortRead.h b/src/PortRead.h
index 8293045..9f3da80 100644
--- a/src/PortRead.h
+++ b/src/PortRead.h
@@ -13,7 +13,7 @@ Details are in config_key.h
class PortRead
{
protected:
- const uint8_t readPins; //bitwise pin configuration, 1 means read column
+ const uint8_t readPins; //bitwise pin configuration, 1 means read pin
public:
PortRead(const uint8_t readPins): readPins(readPins) {}
virtual uint8_t read()=0;
diff --git a/src/PortRead_MCP23S17.cpp b/src/PortRead_MCP23S17.cpp
new file mode 100644
index 0000000..199bd64
--- /dev/null
+++ b/src/PortRead_MCP23S17.cpp
@@ -0,0 +1,23 @@
+#include "PortRead_MCP23S17.h"
+
+/*
+PortRead_MCP23S17::begin() is not needed because port direction is already configured to input by default.
+SPI bus is configured in PortWrite_MCP23S17::begin().
+*/
+
+/*
+returns port value
+*/
+uint8_t PortRead_MCP23S17::read()
+{
+ uint8_t portState; //bit wise
+
+ //slower clock
+ digitalWrite(SS, LOW); //enable Slave Select
+ SPI.transfer(port.ADDR << 1 | 1); //read command
+ SPI.transfer(port.num + 0x12); //register address to read data from
+ portState = SPI.transfer(0); //save the data (0 is dummy data to send)
+ digitalWrite(SS, HIGH); //disable Slave Select
+
+ return portState;
+}
diff --git a/src/PortRead_MCP23S17.h b/src/PortRead_MCP23S17.h
new file mode 100644
index 0000000..97ab958
--- /dev/null
+++ b/src/PortRead_MCP23S17.h
@@ -0,0 +1,42 @@
+#ifndef PORTREAD_MCP23S17_H
+#define PORTREAD_MCP23S17_H
+#include
+#include
+#include
+#include
+#include "PortIOE.h"
+
+/* One MCP23S17 I/O expander port connected to matrix columns.
+MCP23S17 does not have internal pull-up resistors (PCA9535E does).
+
+Instantiation
+ ------------
+readPins parameter is port's bitwise pin configuration
+ 1=configure as input (for pins connected to column)
+ 0=configure as output (for LED or not connected to a column)
+
+Example instantiation for column port 0, with pins 2 and 3 connected to columns:
+ PortIOE port0(0, 0);
+ PortRead_MCP23S17 colPort0(port0, 2<<0 | 1<<3 );
+Example instantiation for column port 1, with pins 2 and 3 connected to columns:
+ PortIOE port1(1, 0);
+ PortRead_MCP23S17 colPort1(port1, 2<<0 | 1<<3 );
+
+readPins are read from pin 0 on up.
+
+*/
+class PortRead_MCP23S17 : public PortRead
+{
+ private:
+ PortIOE& port;
+ public:
+/*
+todo not all PortRead_ classes need a readPins
+ move PortRead::readPins from PortRead to PortRead_PCA9655E
+ remove PortRead(0) initialization from this constructor
+*/
+ //The constructor initialization list is in .cpp
+ PortRead_MCP23S17(PortIOE& port) : PortRead(0), port(port) {}
+ virtual uint8_t read();
+};
+#endif
diff --git a/src/PortWrite_MCP23S17.cpp b/src/PortWrite_MCP23S17.cpp
new file mode 100644
index 0000000..73fd1c5
--- /dev/null
+++ b/src/PortWrite_MCP23S17.cpp
@@ -0,0 +1,50 @@
+#include "PortWrite_MCP23S17.h"
+
+void PortWrite_MCP23S17::writePort(const uint8_t registerAddr, const uint8_t data)
+{
+ //slower clock
+ //SPI.beginTransaction(SPISettings (SPI_CLOCK_DIV8, MSBFIRST, SPI_MODE0)); //control SPI bus todo move to begin()
+ digitalWrite(SS, LOW); //enable Slave Select
+ SPI.transfer(port.ADDR << 1); //write command
+ SPI.transfer(registerAddr); //register address to write data to
+ SPI.transfer(data); //data
+ digitalWrite(SS, HIGH); //disable Slave Select
+ //SPI.endTransaction(); //release the SPI bus
+}
+
+/*
+If PortRead_MCP23S17 is instantiated on the same port, do NOT use PortWrite_MCP23S17::begin().
+Otherwise readPins could be overwritten.
+Output pins can be used for strobe pins and LEDs.
+
+SPI.endTransaction() is not called because keyboard only has one SPI device, so no need to release the SPI bus
+*/
+void PortWrite_MCP23S17::begin()
+{
+ pinMode(SS, OUTPUT); //configure controller's Slave Select pin to output
+ digitalWrite(SS, HIGH); //disable Slave Select
+ SPI.begin();
+ SPI.beginTransaction(SPISettings (SPI_CLOCK_DIV8, MSBFIRST, SPI_MODE0)); //control SPI bus
+
+ writePort(port.num, 0); //configure port direction (port.num) to output (0)
+}
+
+/*
+pin is bitwise, where pin being strobed is 1.
+strobe is HIGH or LOW (for active high or active low).
+port.outputVal can be shared by LEDs.
+The functions does not reset the other pins so that they can be used for LEDs.
+*/
+void PortWrite_MCP23S17::write(const uint8_t pin, const bool strobe)
+{
+ if (strobe == LOW) //if active low
+ {
+ port.outputVal &= ~pin; //set pin output to low
+ }
+ else //if active high
+ {
+ port.outputVal |= pin; //set pin output to high
+ }
+
+ writePort(port.num + 0x12, port.outputVal); //set GPIO port pins for stobe and LEDs
+}
diff --git a/src/PortWrite_MCP23S17.h b/src/PortWrite_MCP23S17.h
new file mode 100644
index 0000000..7d2ea90
--- /dev/null
+++ b/src/PortWrite_MCP23S17.h
@@ -0,0 +1,46 @@
+#ifndef PORTWRITE_MCP23S17_H
+#define PORTWRITE_MCP23S17_H
+#include
+#include
+#include
+#include
+#include "PortIOE.h"
+
+/* One MCP23S17 I/O expander port connected to matrix rows.
+MCP23S17 does not have internal pull-up resistors (PCA9535E does).
+
+begin() configures column port's configuration and output.
+This should normally be called once in sketch's setup().
+If PortRead_MCP23S17 is instantiated on the same port, do NOT use PortWrite_MCP23S17::begin().
+Otherwise readPins could be overwritten.
+
+Instantiation
+ ------------
+Example instantiation for row port 0:
+ PortIOE port0(0, 0);
+ PortWrite_MCP23S17 rowPort0(port0);
+
+Example instantiation for row port 1:
+ PortIOE port1(1, 0);
+ PortWrite_MCP23S17 rowPort1(port1);
+
+Diode orientation
+ ----------------
+Diode orientation is explained in keybrd_library_user_guide.md > Diode orientation
+
+MCP23S17 data sheet
+ ----------------
+ http://www.onsemi.com/pub_link/Collateral/MCP23S17-D.PDF
+*/
+
+class PortWrite_MCP23S17 : public PortWrite
+{
+ private:
+ PortIOE& port;
+ void writePort(const uint8_t registerAddr, const uint8_t data);
+ public:
+ PortWrite_MCP23S17(PortIOE& port) : port(port) {}
+ void begin();
+ virtual void write(const uint8_t pin, const bool level);
+};
+#endif
diff --git a/src/Row.h b/src/Row.h
index 7b70013..a0b797a 100644
--- a/src/Row.h
+++ b/src/Row.h
@@ -14,8 +14,7 @@ class Row
Key *const *const ptrsKeys; //array of Key pointers
virtual void keyWasPressed();
protected:
- read_pins_t debounced; //bitwise state of keys after debouncing
- // 1 means pressed, 0 means released
+ read_pins_t debounced; //bitwise state of keys after debouncing, 1=pressed, 0=released
void send(const uint8_t readPinCount, const read_pins_t debouncedChanged);
public:
Row(Key* const ptrsKeys[]) : ptrsKeys(ptrsKeys), debounced(0) { }
diff --git a/src/Row_IOE.cpp b/src/Row_IOE.cpp
new file mode 100644
index 0000000..066623a
--- /dev/null
+++ b/src/Row_IOE.cpp
@@ -0,0 +1,12 @@
+#include "Row_IOE.h"
+
+void Row_IOE::process()
+{
+ //these variables are all bitwise, one bit per key
+ uint8_t readState; //1 means pressed, 0 means released
+ uint8_t debouncedChanged; //1 means debounced changed
+
+ readState = scanner.scan();
+ debouncedChanged = debouncer.debounce(readState, debounced);
+ send(readPinCount, debouncedChanged);
+}
diff --git a/src/Row_IOE.h b/src/Row_IOE.h
new file mode 100644
index 0000000..dd3b400
--- /dev/null
+++ b/src/Row_IOE.h
@@ -0,0 +1,26 @@
+#ifndef ROW_IOE_H
+#define ROW_IOE_H
+
+#include
+#include
+#include
+class PortWrite;
+class PortRead;
+
+/* Row_IOE is a row connected to an Input/Output Expander.
+Configuration and Instantiation instructions are in keybrd/src/Row_IOE.h
+*/
+class Row_IOE : public Row
+{
+ private:
+ Scanner_Port scanner;
+ Debouncer_Samples debouncer;
+ const uint8_t readPinCount; //number of read pins
+ public:
+ Row_IOE(PortWrite& refPortWrite, const uint8_t strobePin,
+ PortRead& refPortRead, const uint8_t readPinCount, Key *const ptrsKeys[])
+ : Row(ptrsKeys), scanner(refPortWrite, strobePin, refPortRead),
+ readPinCount(readPinCount) { }
+ void process();
+};
+#endif
diff --git a/src/Scanner_ShiftRegs74HC165.h b/src/Scanner_ShiftRegs74HC165.h
index 3f83f84..359f4b6 100644
--- a/src/Scanner_ShiftRegs74HC165.h
+++ b/src/Scanner_ShiftRegs74HC165.h
@@ -13,7 +13,7 @@ shift registers 74HC165 are Parallel-In-Serial-Out (PISO)
Upto 4 shift registers can be in a daisy chained for a total of 32 read pins.
For active low:
-Shift register parallel input pins have 10k pull-down resistors powered
+Shift register parallel input pins have 10k pull-up resistors powered
Orient diodes with cathode (banded end) towards the write pins (row)
Controller's MISO pin is connected to shift register's complementary serial output (/QH) pin
Use these two lines in the sketch:
diff --git a/tutorials/breadboard_keyboard_supplies.ods b/tutorials/breadboard_keyboard_supplies.ods
index 6acdd35..b185ce4 100644
Binary files a/tutorials/breadboard_keyboard_supplies.ods and b/tutorials/breadboard_keyboard_supplies.ods differ
diff --git a/tutorials/keybrd_1_breadboard/keybrd_1_breadboard.ino b/tutorials/keybrd_1_breadboard/keybrd_1_breadboard.ino
index ee185ec..398f3e9 100644
--- a/tutorials/keybrd_1_breadboard/keybrd_1_breadboard.ino
+++ b/tutorials/keybrd_1_breadboard/keybrd_1_breadboard.ino
@@ -32,7 +32,7 @@ Code_Sc s_c(KEY_C);
Key* ptrsKeys_0[] = { &s_1, &s_a };
Row_uC row_0(0, readPins, READ_PIN_COUNT, ptrsKeys_0);
-Key* ptrsKeys_1[] = { &s_b, &s_c };
+Key* ptrsKeys_1[] = { &s_b, &s_c };
Row_uC row_1(1, readPins, READ_PIN_COUNT, ptrsKeys_1);
// ################### MAIN ####################
diff --git a/tutorials/keybrd_4b_split_keyboard_with_shift_registers/keybrd_4b_split_keyboard_with_shift_registers.ino b/tutorials/keybrd_4b_split_keyboard_with_shift_registers/keybrd_4b_split_keyboard_with_shift_registers.ino
new file mode 100644
index 0000000..5831af4
--- /dev/null
+++ b/tutorials/keybrd_4b_split_keyboard_with_shift_registers/keybrd_4b_split_keyboard_with_shift_registers.ino
@@ -0,0 +1,96 @@
+/* tutorial_4a_split_keyboard_with_shift_registers.ino
+Tested on Teensy LC and two 74HC165 shift registers.
+
+The right matrix has 2 shift registers daisy chained.
+
+ Layout Layout
+| Left |**0** | Right |**0**|**1**|**2**|**3**|**4**|**5**|**6**|**7**|
+|:-----:|-----| |:-----:|-----|-----|-----|-----|-----|-----|-----|-----|
+| **0** | x | | **0** | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
+| **1** | y | | **1** | a | b | c | d | e | f | g | h |
+*/
+// ################## GLOBAL ###################
+// ================= INCLUDES ==================
+#include
+#include
+
+//Left matrix
+#include
+
+//Right matrix
+#include
+#include
+
+// =============== CONFIGURATION ===============
+ScanDelay scanDelay(9000);
+
+// =================== 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_e(KEY_E);
+Code_Sc s_f(KEY_F);
+Code_Sc s_g(KEY_G);
+
+Code_Sc s_x(KEY_X);
+Code_Sc s_y(KEY_Y);
+
+Code_Sc s_0(KEY_0);
+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_Sc s_5(KEY_5);
+Code_Sc s_6(KEY_6);
+
+// =============== LEFT MATRIX =================
+//set left matrix for active low
+const bool Scanner_uC::STROBE_ON = LOW;
+const bool Scanner_uC::STROBE_OFF = HIGH;
+
+//column pin
+uint8_t readPins[] = {14};
+uint8_t READ_PIN_COUNT = sizeof(readPins)/sizeof(*readPins);
+
+//rows
+Key* ptrsKeys_L0[] = { &s_x };
+Row_uC row_L0(0, readPins, READ_PIN_COUNT, ptrsKeys_L0);
+
+Key* ptrsKeys_L1[] = { &s_y };
+Row_uC row_L1(1, readPins, READ_PIN_COUNT, ptrsKeys_L1);
+
+// =============== RIGHT MATRIX ================
+//set matrix to active high
+const bool Scanner_ShiftRegs74HC165::STROBE_ON = HIGH;
+const bool Scanner_ShiftRegs74HC165::STROBE_OFF = LOW;
+
+//chip select pin
+const uint8_t Scanner_ShiftRegs74HC165::SHIFT_LOAD = 10;
+
+//rows
+Key* ptrsKeys_R0[] = { &s_6, &s_5, &s_4, &s_3, //shift regiser on right
+ &s_c, &s_d, &s_e, &s_f,
+ &s_2, &s_1, &s_0, &s_g, //shift regiser on left
+ &s_a, &s_b }; //unused input pins are grounded
+Row_ShiftRegisters row_R0(0, sizeof(ptrsKeys_R0)/sizeof(*ptrsKeys_R0), ptrsKeys_R0);
+
+// ################### MAIN ####################
+void setup()
+{
+ Keyboard.begin();
+ SPI.begin();
+ row_R0.begin();
+}
+
+void loop()
+{
+ //left matrix
+ row_L0.process();
+ row_L1.process();
+
+ //right matrix
+ row_R0.process();
+
+ scanDelay.delay();
+}
diff --git a/tutorials/keybrd_4c_split_with_IOE/keybrd_4c_split_with_IOE.ino b/tutorials/keybrd_4c_split_with_IOE/keybrd_4c_split_with_IOE.ino
new file mode 100644
index 0000000..656f571
--- /dev/null
+++ b/tutorials/keybrd_4c_split_with_IOE/keybrd_4c_split_with_IOE.ino
@@ -0,0 +1,105 @@
+/* keybrd_4c_split_with_IOE_annotated.ino
+
+| Left | **0** | **1** | | Right | **0** | **1** |
+|:-----:|-------|-------| |:-----:|-------|-------|
+| **0** | a | b | | **0** | 1 | 2 |
+| **1** | shift | c | | **1** | 3 | shift |
+
+| Left | **0** | **1** | | Right | **0** | **1** | todo
+|:-----:|-------|-------| |:-----:|-------|-------|
+| **1** | 1 | 2 | | **1** | 3 | 4 |
+| **0** | a | b | | **0** | c | d |
+*/
+// ################## GLOBAL ###################
+// ================= INCLUDES ==================
+#include
+#include
+
+//left matrix
+#include
+
+//right matrix
+#include
+#include
+#include
+#include
+
+// ============ SPEED CONFIGURATION ============
+ScanDelay scanDelay(9000);
+
+// ================ LEFT MATRIX ================
+// ---------------- ACTIVE STATE ---------------
+const bool Scanner_uC::STROBE_ON = LOW; //active low
+const bool Scanner_uC::STROBE_OFF = HIGH;
+
+// ------------------- PINS --------------------
+uint8_t readPins[] = {14, 15};
+
+// ================ RIGHT MATRIX ===============
+const bool Scanner_Port::STROBE_ON = HIGH; //active high
+const bool Scanner_Port::STROBE_OFF = LOW;
+
+const uint8_t PortIOE::ADDR = 0x18;
+
+// ------------------ PORT 1 -------------------
+PortIOE port1_R(1, 0);
+PortWrite_MCP23S17 portWrite1_R(port1_R);
+
+// ------------------ PORT 0 -------------------
+PortIOE port0_R(0, 0);
+PortWrite_MCP23S17 portWrite0_R(port0_R);
+PortRead_MCP23S17 portRead0_R(port0_R, 1<<0 | 1<<1 );
+
+// =================== CODES ===================
+Code_Sc s_shiftL(MODIFIERKEY_LEFT_SHIFT);
+Code_Sc s_shiftR(MODIFIERKEY_RIGHT_SHIFT);
+
+Code_Sc s_a(KEY_A);
+Code_Sc s_b(KEY_B);
+Code_Sc s_c(KEY_C);
+Code_Sc s_1(KEY_1);
+Code_Sc s_2(KEY_2);
+Code_Sc s_3(KEY_3);
+
+// =================== ROWS ====================
+// ---------------- LEFT ROWS ------------------
+Key* ptrsKeys_L0[] = { &s_a, &s_b };
+const uint8_t KEY_COUNT_L0 = sizeof(ptrsKeys_L0)/sizeof(*ptrsKeys_L0);
+Row_uC row_L0(0, readPins, KEY_COUNT_L0, ptrsKeys_L0);
+
+Key* ptrsKeys_L1[] = { &s_c, &s_shiftL };
+const uint8_t KEY_COUNT_L1 = sizeof(ptrsKeys_L1)/sizeof(*ptrsKeys_L1);
+Row_uC row_L1(1, readPins, KEY_COUNT_L1, ptrsKeys_L1);
+
+// ---------------- RIGHT ROWS -----------------
+Key* ptrsKeys_R0[] = { &s_1, &s_2 };
+const uint8_t KEY_COUNT_R0 = sizeof(ptrsKeys_R0)/sizeof(*ptrsKeys_R0);
+Row_IOE row_R0(portWrite1_R, 1<<0, portRead0_R, KEY_COUNT_R0, ptrsKeys_R0);
+
+Key* ptrsKeys_R1[] = { &s_3, &s_shiftR };
+const uint8_t KEY_COUNT_R1 = sizeof(ptrsKeys_R1)/sizeof(*ptrsKeys_R1);
+Row_IOE row_R1(portWrite1_R, 1<<1, portRead0_R, KEY_COUNT_R1, ptrsKeys_R1);
+
+// ################### MAIN ####################
+void setup()
+{
+ Keyboard.begin();
+ Wire.begin(); //Wire.begin() must be called before port begin()
+ portWrite1_R.begin();
+ portRead0_R.begin();
+}
+
+void loop()
+{
+ //left matrix
+ row_L0.process();
+ row_L1.process();
+
+ //right matrix
+ row_R0.process();
+ row_R1.process();
+
+ scanDelay.delay();
+ //debug.print_scans_per_second();
+ //debug.print_microseconds_per_scan();
+}
diff --git a/tutorials/tutorial_10_writing_IOE_Port_classes.md b/tutorials/tutorial_10_writing_IOE_Port_classes.md
index 1c34dfc..c3304dc 100644
--- a/tutorials/tutorial_10_writing_IOE_Port_classes.md
+++ b/tutorials/tutorial_10_writing_IOE_Port_classes.md
@@ -1,19 +1,38 @@
-Tutorial 10 - writing IOE Port classes
-===========================================
+Tutorial 10 - writing new IOE Port classes
+==========================================
Port classes are the keybrd library's interface to I/O expander ports.
-To write your own Port class:
+To write a new Port class:
-1. Get a copy of the I/O expander datasheet.
-2. Study other keybrd Port classes.
-
-For example, the keybrd_DH library uses these keybrd classes for its PCA9655E I/O expander:
-* PortWrite_PCA9655E
-* PortRead_PCA9655E
-* LED_PCA9655E
-
-Debugging I/O expander code is hard because SPI or I2C protocol adds a level of indirection.
-If you haven't written Arduino code for an I/O expander before, learn from an Arduiono I/O expander tutorial before attempting it here.
+1. Get a copy of the I/O expander's datasheet.
+2. An I/O expander will use one of two communication protocols: [http://www.byteparadigm.com/applications/introduction-to-i2c-and-spi-protocols/](SPI or I2C).
+ Refer to the [Arduino SPI](https://www.arduino.cc/en/Reference/SPI)
+ or [Arduino Wire (I2C)](https://www.arduino.cc/en/Reference/Wire) library
+3. Get familiar with your I/O expander.
+ Different I/O expanders use different commands (a.k.a. operation codes).
+ Refer to your I/O expander's datasheet for read and write commands.
+ Search for Arduino sketch examples containing your I/O expander
+ ([sumotoy](https://github.com/sumotoy/gpio_expander) has a large gpio expander library).
+ Write very simple read and write examples for your I/O expander.
+ Simple SPI I/O expander examples:
+ todo link, pictures
+ /home/wolfv/Documents/Arduino/demo/IOE_MCP23S17_read/
+ /home/wolfv/Documents/Arduino/demo/IOE_MCP23S17_write/
+ Simple I2C I/O expander examples:
+ todo link, pictures
+ read
+ write
+4. Study other keybrd Port classes.
+ Port classes for SPI MCP23S17 I/O expander:
+ *todo
+ *
+ *
+ Port classes for I2C PCA9655E I/O expander:
+ * PortWrite_PCA9655E todo link
+ * PortRead_PCA9655E
+ * LED_PCA9655E
+5. Write similar Port classes for your I/O expander.
+ Debugging I/O expander code is hard because SPI or I2C protocol adds a level of indirection.
keybrd tutorial by Wolfram Volpi is licensed under a Creative Commons Attribution 4.0 International License.
Permissions beyond the scope of this license may be available at https://github.com/wolfv6/keybrd/issues/new.
diff --git a/tutorials/tutorial_8b_creating_and_publishing_your_own_keybrd_extension_library.md b/tutorials/tutorial_8b_sharing_your_keybrd_extension_library.md
similarity index 77%
rename from tutorials/tutorial_8b_creating_and_publishing_your_own_keybrd_extension_library.md
rename to tutorials/tutorial_8b_sharing_your_keybrd_extension_library.md
index 86b6793..01d5161 100644
--- a/tutorials/tutorial_8b_creating_and_publishing_your_own_keybrd_extension_library.md
+++ b/tutorials/tutorial_8b_sharing_your_keybrd_extension_library.md
@@ -1,12 +1,12 @@
-Tutorial 8b - creating and publishing your own keybrd extension library
-=======================================================================
+Tutorial 8b - sharing your keybrd extension library
+===================================================
Publishing and listing your keybrd extension library allows others to find and install your library.
The keybrd extension library name should start with "keybrd_" so that it is easy for other people to find.
There are two ways to publish and list an Arduino library.
-Publishing anywhere with listing on Arduino Playground LibraryList
-------------------------------------------------------------------
+Publish anywhere and list on Arduino Playground
+-----------------------------------------------
Publishing your keybrd extension library with the following directory structure makes it easy for others to understand.
keybrd_MyKeyboard/
@@ -25,16 +25,13 @@ Publishing your keybrd extension library with the following directory structure
instantiations_codes.h
instantiations_rows.h
-When your ready to list your keybrd extension library, go to the [Arduino Playground keybrd page](http://playground.arduino.cc/Main/keybrd).
+When your ready to list your keybrd extension library,
+ add a link and short description of your keybrd extension library to the [Arduino Playground keybrd page](http://playground.arduino.cc/Main/keybrd) under "keybrd extension libraries".
Arduino playground is a wiki.
Links on how to edit the wiki are on the bottom left under "Participate".
-You can also add a picture of a keyboard that uses your keybrd extension library.
-Uploading files to the Playground is not allowed for standard users.
-So if you want to add a picture, it will need to be hosted somewhere else.
-
-Publishing on GitHub with listing on Arduino Library-Manager and Arduino Playground LibraryList
------------------------------------------------------------------------------------------------
+Publish on GitHub and list on Arduino Library-Manager and Arduino Playground
+----------------------------------------------------------------------------
The advantage of using GitHub is that users can submit pull requests.
The advantage of using Arduino Library-Manager is that users can easily find and install your library through the Arduino IDE.
@@ -78,14 +75,11 @@ Example library.properties file:
Instructions for listing a library on Arduino Library Manager are at:
https://github.com/arduino/Arduino/wiki/Library-Manager-FAQ
-After it has been accepted into the Arduino IDE Library Manager, add your keybrd extension library to the [Arduino Playground keybrd page](http://playground.arduino.cc/Main/keybrd).
+After it has been accepted into the Arduino IDE Library Manager,
+ add a link and short description of your keybrd extension library to the [Arduino Playground keybrd page](http://playground.arduino.cc/Main/keybrd) under "keybrd extension libraries".
Arduino playground is a wiki.
Links on how to edit the wiki are on the bottom left under "Participate".
-You can also add a picture of a keyboard that uses your keybrd extension library.
-Uploading files to the Playground is not allowed for standard users.
-So if you want to add a picture, it will need to be hosted somewhere else.
-
To publish a new release of a library that is already listed on Arduino Library Manager
1. Update the version in your library.properties file:
diff --git a/tutorials/tutorial_8c_sharing_your_keybrd_sketch.md b/tutorials/tutorial_8c_sharing_your_keybrd_sketch.md
new file mode 100644
index 0000000..97fd1c5
--- /dev/null
+++ b/tutorials/tutorial_8c_sharing_your_keybrd_sketch.md
@@ -0,0 +1,17 @@
+Tutorial 8c - sharing your keybrd sketch
+========================================
+keybrd sketches that use a keybrd extension library should be published in the extension library's examples directory.
+keybrd sketches that do not use a keybrd extension library can be published anywhere.
+
+Publishing and listing your keybrd sketch allows others to find your sketch.
+
+Publish anywhere and list on Arduino Playground
+-----------------------------------------------
+Publish your sketch anywhere. Some free places are:
+* GitHub repository
+* [GitHub Gist](https://help.github.com/categories/gists/)
+* [geekhack Making Stuff Together!](https://geekhack.org/index.php?board=117.0)
+
+Then add a link and short description of your keybrd sketch to the [Arduino Playground keybrd page](http://playground.arduino.cc/Main/keybrd) under "keybrd sketches".
+Arduino playground is a wiki.
+Links on how to edit the wiki are on the bottom left under "Participate".
diff --git a/unit_tests/MCP23S17_read/MCP23S17_read.ino b/unit_tests/MCP23S17_read/MCP23S17_read.ino
new file mode 100644
index 0000000..27911fd
--- /dev/null
+++ b/unit_tests/MCP23S17_read/MCP23S17_read.ino
@@ -0,0 +1,42 @@
+/* this works
+The setup is an MCP23S17 I/O expander on a Teensy LC controller.
+MCP23S17 port B pins are alternately grounded and energized.
+portBState is a bitwise reading of port B.
+output is: 10101010
+
+posted on http://arduino.stackexchange.com/questions/tagged/spi
+http://arduino.stackexchange.com/questions/28792/reading-an-mcp23s17-i-o-expander-port-with-the-arduino-spi-library
+*/
+#include
+
+const uint8_t ADDR = 0x20; //MCP23S17 address, all 3 ADDR pins are grounded
+const uint8_t OPCODE_READ = (ADDR << 1 | 0x01); //MCP23S17 opcode read has LSB set
+
+const uint8_t IODIRB = 0x01;
+const uint8_t GPIOB = 0x13;
+
+uint8_t portBState = 0; //bit wise
+
+void setup()
+{
+ Serial.begin(9600);
+ delay(1000);
+
+ pinMode(SS, OUTPUT); //configure controller's Slave Select pin to output
+ digitalWrite(SS, HIGH); //disable Slave Select
+ SPI.begin();
+
+ //IODIRB register is already configured to input by default
+
+ SPI.beginTransaction(SPISettings (SPI_CLOCK_DIV8, MSBFIRST, SPI_MODE0)); //gain control of SPI bus
+ digitalWrite(SS, LOW); //enable Slave Select
+ SPI.transfer(OPCODE_READ); //read command
+ SPI.transfer(GPIOB); //register address to read data from
+ portBState = SPI.transfer(0); //save the data (0 is dummy data to send)
+ digitalWrite(SS, HIGH); //disable Slave Select
+ SPI.endTransaction(); //release the SPI bus
+
+ Serial.println(portBState, BIN); //should print 10101010
+}
+
+void loop() { }
diff --git a/unit_tests/MCP23S17_write/MCP23S17_write.ino b/unit_tests/MCP23S17_write/MCP23S17_write.ino
new file mode 100644
index 0000000..76dfc69
--- /dev/null
+++ b/unit_tests/MCP23S17_write/MCP23S17_write.ino
@@ -0,0 +1,54 @@
+/* this works with volt meter (MCP23S17 on 3.3v does not output enough power for LEDs)
+LED lights w/o resistor, not light with 56 ohm resistor
+blink LED on MCP23S17 port A pin
+from Example 41.1 - Microchip MCP23017 with Arduino
+ http://tronixstuff.com/tutorials > chapter 41
+ http://tronixstuff.com/2011/08/26/tutorial-maximising-your-arduinos-io-ports/
+ John Boxall | CC by-sa-nc
+from http://69.5.26.215/forum/?id=10945&page=3 #35
+modified to test MCP23S17 (SPI) using syntax from
+ http://arduino.stackexchange.com/questions/16348/how-do-you-use-spi-on-an-arduino
+ >
+
+SPISettings from http://arduino.stackexchange.com/questions/14191/mcp23s17-programming-iodirx-register-works-in-loop-but-not-in-setup
+*/
+#include
+
+const uint8_t ADDR = 0x20; //MCP23S17 address, all ADDR pins are grounded
+const uint8_t OPCODE_WRITE = (ADDR << 1); //MCP23S17 opcode write has LSB clear
+
+const uint8_t IODIRA = 0x00; //LEDs are on port A
+const uint8_t GPIOA = 0x12;
+
+uint8_t LED_state = 0; //bit wise
+
+void IOEWrite(const uint8_t registerAddr, const uint8_t data)
+{
+ SPI.beginTransaction(SPISettings (SPI_CLOCK_DIV8, MSBFIRST, SPI_MODE0)); //slower clock
+ digitalWrite(SS, LOW); //enable Slave Select
+ SPI.transfer(OPCODE_WRITE); //write command
+ SPI.transfer(registerAddr); //register address to write data to
+ SPI.transfer(data); //data
+ digitalWrite(SS, HIGH); //disable Slave Select
+ SPI.endTransaction(); //release the SPI bus
+}
+
+void setup()
+{
+ Serial.begin(9600);
+
+ pinMode(SS, OUTPUT); //configure controller's Slave Select pin to output
+ digitalWrite(SS, HIGH); //disable Slave Select
+ SPI.begin();
+
+ IOEWrite(IODIRA, 0x00); //configure IODIRA register to output
+}
+
+void loop()
+{
+ IOEWrite(GPIOA, LED_state); //set all GPIOA pins
+
+ delay(2000);
+ //Serial.println(LED_state, BIN); //prints alternating 0 and 11111111
+ LED_state = ~LED_state; //toggle LED on/off
+}
diff --git a/unit_tests/PortRead_MCP23S17/PortRead_MCP23S17.ino b/unit_tests/PortRead_MCP23S17/PortRead_MCP23S17.ino
new file mode 100644
index 0000000..dd84376
--- /dev/null
+++ b/unit_tests/PortRead_MCP23S17/PortRead_MCP23S17.ino
@@ -0,0 +1,32 @@
+/* unit test for PortRead_MCP23S17
+The setup is an MCP23S17 I/O expander on a Teensy LC controller.
+MCP23S17 port-B pins are alternately grounded and energized.
+portBState is a bitwise reading of port B.
+output is: 10101010
+
+posted on http://arduino.stackexchange.com/questions/tagged/spi
+http://arduino.stackexchange.com/questions/28792/reading-an-mcp23s17-i-o-expander-port-with-the-arduino-spi-library
+*/
+#include "PortIOE.h"
+#include "PortRead_MCP23S17.h"
+#include "PortWrite_MCP23S17.h"
+
+const uint8_t PortIOE::ADDR = 0x20; //MCP23S17 address, all 3 ADDR pins are grounded
+PortIOE portB(1, 0);
+
+PortRead_MCP23S17 portBRead(portB);
+PortWrite_MCP23S17 portBWrite(portB); //PortBWrite needed for begin()
+
+void setup()
+{
+ uint8_t portBState; //bit wise
+
+ delay(6000);
+ portBWrite.begin();
+
+ portBState = portBRead.read();
+ Keyboard.print("portBState = ");
+ Keyboard.println(portBState, BIN); //should print 10101010
+}
+
+void loop() { }
diff --git a/unit_tests/PortWrite_MCP23S17/PortWrite_MCP23S17.ino b/unit_tests/PortWrite_MCP23S17/PortWrite_MCP23S17.ino
new file mode 100644
index 0000000..2901f5e
--- /dev/null
+++ b/unit_tests/PortWrite_MCP23S17/PortWrite_MCP23S17.ino
@@ -0,0 +1,35 @@
+/* unit test for PortRead_MCP23S17
+The setup is an MCP23S17 I/O expander on a Teensy LC controller.
+MCP23S17 port-A GPIO pins are not connected to anything.
+Port-A GPIO-pin ouputs alternate between 0 and 3.3 volts.
+
+Use a volt meter to measure port-A GPIO-pin ouputs.
+MCP23S17 on 3.3v does not output enough power to reliable light LEDs
+ LED lights w/o resistor
+ LED not light with 56 ohm resistor
+*/
+#include "PortIOE.h"
+#include "PortWrite_MCP23S17.h"
+
+const uint8_t PortIOE::ADDR = 0x20; //MCP23S17 address, all 3 ADDR pins are grounded
+PortIOE portA(0, 0);
+
+PortWrite_MCP23S17 portAWrite(portA); //PortAWrite needed for begin()
+
+const uint8_t GPIOA = 0x12; //LEDs are on port A
+
+void setup()
+{
+ delay(6000);
+ portAWrite.begin();
+ //Keyboard.print("start blinking");
+}
+
+void loop()
+{
+ portAWrite.write(~0, HIGH); //set all GPIOA pins HIGH
+ delay(2000);
+
+ portAWrite.write(~0, LOW); //set all GPIOA pins LOW
+ delay(2000);
+}