diff --git a/doc/keybrd_library_developer_guide.md b/doc/keybrd_library_developer_guide.md index 42b69a8..11453ff 100644 --- a/doc/keybrd_library_developer_guide.md +++ b/doc/keybrd_library_developer_guide.md @@ -12,55 +12,6 @@ This guide is for the maintainers and developers of the keybrd library and it's It is assumed the reader is familiar with the C++ language including pointers, objects, classes, static class variables, composition, aggregation, inheritance, polymorphism, and enum. Row, Scanner, and Debouncer classes use bit manipulation. -Custom Row classes ------------------- -Row classes are central to the keybrd library. -Row is an abstract base class that allows flexibility in designing derived Row classes: -* Row functions can be overridden in a derived class -* choice of Debouncers -* choice of Scanners - -This example illustrates the custom Row classes for a fictional keybrd_Ext extension library. -The keybrd_Ext library is for a split keyboard with a matrix on each hand and sticky keys. - -Row_Ext::keyWasPressed() overrides Row::keyWasPressed() which is used to unstick sticky keys. - -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 peripheral matrix. - -Class inheritance diagram -``` - - Row - | - Row_Ext (override Row::keyWasPressed() ) - / \ - Row_Ext_uC Row_Ext_ShiftRegisters (inherit Row_Ext::keyWasPressed() ) - - - Scanner_uC Scanner_ShiftRegs74HC165 - -``` - -Dependency diagram -``` - - ________ Row_Ext_uC[1] ______________ - / | \ - Scanner_uC[1] Debouncer_Samples[1] Key[1..*] - / | - strobePin[1] Code[1..*] - - - _________ Row_Ext_ShiftRegisters[1] ________ - / \ \ - Scanner_ShiftRegs74HC165[1] Debouncer_Samples[1] Key[1..*] - | | - strobePin[1] Code[1..*] - -``` - Class inheritance diagrams -------------------------- @@ -73,15 +24,15 @@ Keybrd library class inheritance diagram Scanner_uC Scanner_IOE Scanner_ShiftRegsPISO - PortIOE + PortIOE - PortWrite - | - PortWrite_PCA9655E (one PortWrite class for each IOE type) + PortWrite + / \ + PortWrite_PCA9655E PortWrite_MCP23S17 (one PortWrite class for each IOE type) - PortRead - | - PortRead_PCA9655E (one PortRead class for each IOE type) + PortRead + / \ + PortRead_PCA9655E PortRead_MCP23S17 (one PortRead class for each IOE type) _ LED _ / \ @@ -127,48 +78,48 @@ Dependency diagrams Dependency diagram of example single-layer keyboard with LEDs ``` - _ Row_uC[1..*] _ - / | \ - Scanner_uC Debouncer Keys[1..*] __ - | \ - Code[1..*] Code_LEDLock[1..*] - | - LED_PinNumber[1] + ____ Row ______ + / | \ + Scanner_uC Debouncer Keys __ + | | \ + readPins Code Code_LEDLock + | + LED_PinNumber ``` Dependency diagram of example multi-layer keyboard with layer LEDs ``` - LayerStates[1..*] - ________ Row_uC[1..*] ___________/__ | \ - / | \ / \ | \ - Scanner_uC[1] Debouncer[1] Keys[1..*] / Code_Layer[1..*] LED_PinNumber[0..*] - | / - Code[1..*] + LayerStates + ___________ Row _______/__ | \ + / / \ / \ | \ + Scanner_uC Debouncer Keys / Code_Layer LED_PinNumber + | \ / + readPins Code ``` -Dependency diagram of example peripheral matrix with shift registers +Dependency diagram of example shift registers Row ``` - Row_ShiftRegisters[1..*] - / \ \ - RowScanner_ShiftRegisters Debouncer Keys[1..*] - | - Code[1..*] + _______ Row _______ + / | \ + RowScanner_ShiftRegsPISO Debouncer Keys + | + Code ``` -Dependency diagram of example peripheral matrix with I/O Expander and LEDs +Dependency diagram of example I/O expander matrix with LEDs ``` - _____ 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..*] + _________ Row _________ + / \ \ + __ Scanner_IOE __ Debouncer Keys + / | \ / \ + strobePin PortWrite PortRead Code Code_LEDLock + | \ / \ | + | PortIOE readPins LED + \___________________________/ \ + pin ``` @@ -246,7 +197,6 @@ Trace of keybrd scan Arduino does not have a debugger. 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. ``` loop() for each row diff --git a/doc/keybrd_library_user_guide.md b/doc/keybrd_library_user_guide.md index 5556168..3dc3275 100644 --- a/doc/keybrd_library_user_guide.md +++ b/doc/keybrd_library_user_guide.md @@ -175,6 +175,9 @@ In this example, row_0 has 2 read pins and 2 keys: Key* ptrsKeys_0[] = { &s_a, &s_b }; Row_uC row_0(0, readPins, READ_PIN_COUNT, ptrsKeys_0); ``` +* The scanner should have enough readPins to cover all the keys of the longest row. + (rows with fewer keys will have unused read pins) +* read_pins_t size in keybrd/src/config_keybrd.h file should cover all the read pins. * Some of the constructors take array-element-count arguments, make sure that the correct counts are passed to the constructors. Or use sizeof() like the preceding example. * For multi-layered keyboards, the number of codes in each Key_Layered should equal the number of layers. diff --git a/examples/keybrd_PCA9655E/keybrd_PCA9655E.ino b/examples/keybrd_PCA9655E/keybrd_PCA9655E.ino index 670a3e4..d76f188 100644 --- a/examples/keybrd_PCA9655E/keybrd_PCA9655E.ino +++ b/examples/keybrd_PCA9655E/keybrd_PCA9655E.ino @@ -26,9 +26,9 @@ ScanDelay scanDelay(9000); // ================ LEFT SCANNER =============== uint8_t readPins_L[] = {0, 1}; -uint8_t readPinCount_L = sizeof(readPins_L)/sizeof(*readPins_L); +uint8_t READPIN_COUNT_L = sizeof(readPins_L)/sizeof(*readPins_L); -Scanner_uC scanner_L(HIGH, readPins_L, readPinCount_L); +Scanner_uC scanner_L(HIGH, readPins_L, READPIN_COUNT_L); // =============== RIGHT SCANNER =============== const uint8_t PortIOE::DEVICE_ADDR = 0x18; diff --git a/src/Debouncer_Not.cpp b/src/Debouncer_Not.cpp index 4138226..7237c58 100644 --- a/src/Debouncer_Not.cpp +++ b/src/Debouncer_Not.cpp @@ -1,6 +1,7 @@ #include "Debouncer_Not.h" -/* debounce() sets debounced and returns debouncedChanged. All variables are bitwise. +/* debounce() sets debounced and returns debouncedChanged. +All parameters and variables are bitwise. For parameters, 1 means pressed, 0 means released. For return, 1 means debounced changed. */ diff --git a/src/Debouncer_Samples.cpp b/src/Debouncer_Samples.cpp index 70f2c67..0bbf7d9 100644 --- a/src/Debouncer_Samples.cpp +++ b/src/Debouncer_Samples.cpp @@ -31,7 +31,8 @@ strong electromagnetic interference (EMI) may need a larger SAMPLE_COUNT_MACRO f */ #include "Debouncer_Samples.h" -/* debounce() sets debounced and returns debouncedChanged. All variables are bitwise. +/* debounce() sets debounced and returns debouncedChanged. +All parameters and variables are bitwise. For parameters, 1 means pressed, 0 means released. For return, 1 means debounced changed. */ diff --git a/src/LED_PCA9655E.h b/src/LED_PCA9655E.h index 5da2111..f82588a 100644 --- a/src/LED_PCA9655E.h +++ b/src/LED_PCA9655E.h @@ -15,7 +15,7 @@ class LED_PCA9655E: public LED //PortIOE& port; //const uint8_t outputByteCommand; //General Purpose Input/Ouput register address PortWrite_PCA9655E& refPort; - const uint8_t pin; //bitwise pin to LED + const uint8_t pin; //bitwise IOE pin to LED public: LED_PCA9655E(PortWrite_PCA9655E& refPort, const uint8_t pin) diff --git a/src/PortIOE.h b/src/PortIOE.h index 041e7c6..1eec752 100644 --- a/src/PortIOE.h +++ b/src/PortIOE.h @@ -29,7 +29,7 @@ portNumber: If the I/O expander uses port letters, use 0 inplace of A, use 1 inp struct PortIOE { static const uint8_t DEVICE_ADDR; - const uint8_t num; //port number + const uint8_t num; //port identification number uint8_t outputVal; //bitwise value of output register for LEDs PortIOE(const uint8_t portNumber) diff --git a/src/PortMCP23S17.cpp b/src/PortMCP23S17.cpp index 8b09604..c4bda04 100644 --- a/src/PortMCP23S17.cpp +++ b/src/PortMCP23S17.cpp @@ -4,7 +4,7 @@ */ uint8_t PortMCP23S17::transfer(const uint8_t command, const uint8_t registerAddr, const uint8_t data) { - uint8_t portState; //bit wise + uint8_t portState; //bitwise digitalWrite(SS, LOW); //enable Slave Select SPI.transfer(command); //write or read command diff --git a/src/PortRead_MCP23S17.cpp b/src/PortRead_MCP23S17.cpp index c8c3399..3807061 100644 --- a/src/PortRead_MCP23S17.cpp +++ b/src/PortRead_MCP23S17.cpp @@ -9,7 +9,7 @@ void PortRead_MCP23S17::begin(const uint8_t strobeOn) { pullUp = readPins; } - else + else //active high requires external pull-down resistors { pullUp = 0; } diff --git a/src/Row.cpp b/src/Row.cpp index 696431d..965fb34 100644 --- a/src/Row.cpp +++ b/src/Row.cpp @@ -26,13 +26,13 @@ void Row::process() /* send() calls key's press() or release() function if key was pressed or released. -Both parameters are bitwise. +Parameter debouncedChanged is bitwise. */ void Row::send(const uint8_t keyCount, const read_pins_t debouncedChanged) { read_pins_t isFallingEdge; //bitwise, 1 means falling edge read_pins_t isRisingEdge; //bitwise, 1 means rising edge - read_pins_t readMask; //bitwise, active bit is 1 + read_pins_t readPosition; //bitwise, active bit is 1 uint8_t i; //index for ptrsKeys[i] array //bit=1 if last debounced changed from 1 to 0, else bit=0 @@ -41,15 +41,15 @@ void Row::send(const uint8_t keyCount, const read_pins_t debouncedChanged) //bit=1 if last debounced changed from 0 to 1, else bit=0 isRisingEdge = debouncedChanged & debounced; - for (readMask=1, i=0; i < keyCount; readMask<<=1, i++) //for each key in row + for (readPosition=1, i=0; i < keyCount; readPosition<<=1, i++) //for each key in row { //release before press avoids impossible key sequence - if (readMask & isFallingEdge) //if key was released + if (readPosition & isFallingEdge) //if key was released { ptrsKeys[i]->release(); } - if (readMask & isRisingEdge) //if key was pressed + if (readPosition & isRisingEdge) //if key was pressed { ptrsKeys[i]->press(); keyWasPressed(); diff --git a/src/Row.h b/src/Row.h index e975e2c..4cee19f 100644 --- a/src/Row.h +++ b/src/Row.h @@ -7,11 +7,12 @@ #include #include #include +#include /* strobePin has one of two formats: * if refScanner a Scanner_uC, then strobePin is an Arduino pin number connected to this row - * if refScanner a Scanner_IOE, then strobePin is bitwise, 1 indicating IC pin connected to this row + * otherwise strobePin is bitwise, 1 indicating an IC pin connected to this row */ class Row { @@ -25,7 +26,8 @@ class Row Key *const *const ptrsKeys; //array of Key pointers protected: const uint8_t keyCount; //number of read pins - Debouncer_Samples debouncer; + //Debouncer_Samples debouncer; + Debouncer_Not debouncer; //todo read_pins_t debounced; //bitwise state of keys after debouncing, 1=pressed, 0=released public: Row(ScannerInterface& refScanner, const uint8_t strobePin, diff --git a/src/Scanner_IOE.cpp b/src/Scanner_IOE.cpp index e16042f..4c3eb77 100644 --- a/src/Scanner_IOE.cpp +++ b/src/Scanner_IOE.cpp @@ -17,8 +17,8 @@ void Scanner_IOE::begin() } /* scan() is called on every iteration of sketch loop(). +strobePin is bitwise, 1 means that row pin is active. scan() strobes the row's strobePin and retuns state of port's input pins. -Bitwise variables are 1 bit per key. */ read_pins_t Scanner_IOE::scan(const uint8_t strobePin) { @@ -27,6 +27,7 @@ read_pins_t Scanner_IOE::scan(const uint8_t strobePin) //strobe on refPortWrite.write(strobePin, strobeOn); delayMicroseconds(3); //time to stabilize voltage + //delayMicroseconds(300); //todo //read the port pins readState = refPortRead.read(); diff --git a/src/config_keybrd.h b/src/config_keybrd.h index 0a840bd..9a6f50f 100644 --- a/src/config_keybrd.h +++ b/src/config_keybrd.h @@ -8,11 +8,14 @@ If your 8-bit AVR (Teensy 2) is running low on memory, using a smaller type save Using smaller types on a 32-bit uC (Teensy LC) would accomplish nothing. */ -/* Use a read_pins_t size that covers the last 1 bit in bitwise Scanner_IOE::strobePin. +/* Use a read_pins_t size that covers all read pins of all Scanner objects i.e. + For Scanner_uC: read_pins_t bits >= Scanner_uC::readPinCount + For Scanner_ShiftRegsPISO: read_pins_t bits >= Scanner_ShiftRegsPISO::byte_count * 8 + (For Scanner_IOE: I/O expanders are assumed to have 8 bits per port or less) */ -//typedef uint8_t read_pins_t; +typedef uint8_t read_pins_t; //typedef uint16_t read_pins_t; -typedef uint32_t read_pins_t; +//typedef uint32_t read_pins_t; /* SAMPLE_COUNT_MACRO is used in Debouncer_Samples.h SAMPLE_COUNT_MACRO = 4 is very reliable for a keyboard. diff --git a/tutorials/keybrd_1_breadboard/keybrd_1_breadboard.ino b/tutorials/keybrd_1_breadboard/keybrd_1_breadboard.ino index ada3efe..dd45bf4 100644 --- a/tutorials/keybrd_1_breadboard/keybrd_1_breadboard.ino +++ b/tutorials/keybrd_1_breadboard/keybrd_1_breadboard.ino @@ -2,15 +2,15 @@ | Layout | **0** | **1** | |:------:|-------|-------| -| **0** | 1 | a | -| **1** | 2 | b | +| **0** | 1 | 2 | +| **1** | a | b | */ // ################## GLOBAL ################### // ================= INCLUDES ================== -#include #include #include #include +#include // ============ SPEED CONFIGURATION ============ ScanDelay scanDelay(9000); @@ -29,11 +29,11 @@ Code_Sc s_1(KEY_1); Code_Sc s_2(KEY_2); // =================== ROWS ==================== -Key* ptrsKeys_0[] = { &s_1, &s_a }; +Key* ptrsKeys_0[] = { &s_1, &s_2 }; uint8_t keyCount_0 = sizeof(ptrsKeys_0)/sizeof(*ptrsKeys_0); Row row_0(scanner, 0, ptrsKeys_0, keyCount_0); -Key* ptrsKeys_1[] = { &s_2, &s_b }; +Key* ptrsKeys_1[] = { &s_a, &s_b }; uint8_t keyCount_1 = sizeof(ptrsKeys_1)/sizeof(*ptrsKeys_1); Row row_1(scanner, 1, ptrsKeys_1, keyCount_1); diff --git a/tutorials/keybrd_2_single-layer/keybrd_2_single-layer.ino b/tutorials/keybrd_2_single-layer/keybrd_2_single-layer.ino index 0c87124..1bbaac1 100644 --- a/tutorials/keybrd_2_single-layer/keybrd_2_single-layer.ino +++ b/tutorials/keybrd_2_single-layer/keybrd_2_single-layer.ino @@ -2,20 +2,20 @@ This sketch: is firmware for a simple 1-layer keyboard - runs on the first two rows and columns of a breadboard keyboard + runs on two rows and two columns of a breadboard keyboard This layout table shows how keys are arranged on the keyboard: | Layout | **0** | **1** | |:------:|-------|-------| -| **0** | shift | a | -| **1** | b | c | +| **0** | 1 | 2 | +| **1** | a | b | The layout's row and column numbers are in the headers. Each cell in the table's body represents a key. The following sketch is annotated with a walk-through narrative enclosed in comment blocks. -Each comment block explains the next one or two lines of code. +Each comment block explains one or two lines of code after the comnent. keybrd objects are instantiated under the "GLOBAL" heading. The keyboard runs at the end of the sketch, under the "MAIN" heading. @@ -24,37 +24,40 @@ The keyboard runs at the end of the sketch, under the "MAIN" heading. /* ================= INCLUDES ================== All the includes in this sketch are to keybrd library classes. */ -#include #include -#include +#include +#include +#include /* ============ SPEED CONFIGURATION ============ -ScanDelay specifies microsecond between matrix scans. Keyboard switches are made of moving contacts. When the contacts close, they bounce apart one or more times before making steady contact. ScanDelay gives the switches time to debounce. +ScanDelay specifies microsecond between matrix scans. */ ScanDelay scanDelay(9000); -/* ================ ACTIVE STATE =============== -The read pins detect which keys are pressed while a row is strobed. -STROBE_ON and STROBE_OFF define the logic levels for the strobe. -"Active low" means that if a switch is pressed (active), the read pin is low. -To make this sketch active low, STROBE_ON should be LOW (tutorial 6 coveres this in more detail). -*/ -const bool Scanner_uC::STROBE_ON = LOW; //set scanner for active low -const bool Scanner_uC::STROBE_OFF = HIGH; - -/* ================= PINS ================= -Microcontroller 14 and 15 are connected to the matrix columns. -These readPins detect which keys are pressed while a row is strobed. - +/* ================== SCANNER ================== +Microcontroller pins 14 and 15 are connected to the matrix columns. sizeof() is used to compute the number of array elements. -This eliminates the risk of forgetting to update the count +This eliminates the risk of a programmer forgetting to update a count after adding or removing an element from the array. */ uint8_t readPins[] = {14, 15}; -uint8_t READ_PIN_COUNT = sizeof(readPins)/sizeof(*readPins); +uint8_t readPinCount = sizeof(readPins)/sizeof(*readPins); + +/* +The first parameter of the scanner constructor defines the logic level for the strobes. +"Active low" means that if a switch is pressed (active), the read pin is low. +The scanner uses readPins, readPinCount to read the colums. +*/ +Scanner_uC scanner(LOW, readPins, readPinCount); + +/* HOW SCANNER OBJECTS WORK +The scanner object strobes a row. +If a key is pressed, the LOW strobe pulls that readPin LOW. +Then the scanner reads its readPins. +*/ /* =================== CODES =================== Four Codes are instantiated, one for each key in the layout. @@ -65,25 +68,27 @@ When Code_Sc is pressed, it sends the scancode. */ Code_Sc s_a(KEY_A); Code_Sc s_b(KEY_B); -Code_Sc s_c(KEY_C); -Code_Sc s_shift(MODIFIERKEY_LEFT_SHIFT); + +Code_Sc s_1(KEY_1); +Code_Sc s_2(KEY_2); /* =================== ROWS ==================== Here we pack Code objects into Row objects. The Row objects names in this sketch start with a "row_" followed by a row number. -Row_uC constructor has four parameters: - 1) strobePin connected to the row. - 2) readPins[] connected to the colums. - 3) the number of readPins. - 4) ptrsKeys[] containing all the Code objects of the row, one Code object per key. - +Row constructor has four parameters: + 1) scanner + 2) strobePin connected to the row. + 3) ptrsKeys[] containing all the Code objects of the row, one Code object per key. + 4) the number of keys in the row. */ -Key* ptrsKeys_0[] = { &s_shift, &s_a }; -Row_uC row_0(0, readPins, READ_PIN_COUNT, ptrsKeys_0); +Key* ptrsKeys_0[] = { &s_1, &s_2 }; +uint8_t keyCount_0 = sizeof(ptrsKeys_0)/sizeof(*ptrsKeys_0); +Row row_0(scanner, 0, ptrsKeys_0, keyCount_0); -Key* ptrsKeys_1[] = { &s_b, &s_c }; -Row_uC row_1(1, readPins, READ_PIN_COUNT, ptrsKeys_1); +Key* ptrsKeys_1[] = { &s_a, &s_b }; +uint8_t keyCount_1 = sizeof(ptrsKeys_1)/sizeof(*ptrsKeys_1); +Row row_1(scanner, 1, ptrsKeys_1, keyCount_1); /* ################### MAIN #################### setup() is used to initialize the keyboard firmware. Keyboard.begin() should be called once. @@ -99,7 +104,7 @@ Each row object strobes its strobePin and reads the readPins. And when a key press is detected, the row sends the key's scancode. scanDelay creates time intervals between matrix scans. -A debouncer uses this time interval to debounce key presses and releases. +The delay is needed so that the debouncer is not overwelmed. */ void loop() { diff --git a/tutorials/keybrd_3a_multi-layer/keybrd_3a_multi-layer.ino b/tutorials/keybrd_3a_multi-layer/keybrd_3a_multi-layer.ino index 556b454..fe782da 100644 --- a/tutorials/keybrd_3a_multi-layer/keybrd_3a_multi-layer.ino +++ b/tutorials/keybrd_3a_multi-layer/keybrd_3a_multi-layer.ino @@ -10,7 +10,7 @@ This sketch: | **1** | fn | b 2 | Each cell in the table's body represents a key. -The layered keys in row 0 have two layers; one character for each layer. +The layered keys in column 1 have two layers; one character for each layer. Letters 'a' and 'b' are on the normal layer. Numbers '1' and '2' are on the fn layer. Holding the fn key down makes it the active layer. Releasing the fn key restores the normal layer. */ @@ -23,19 +23,18 @@ Holding the fn key down makes it the active layer. Releasing the fn key restore #include //Matrix -#include +#include +#include #include // ============ SPEED CONFIGURATION ============ ScanDelay scanDelay(9000); -// ================ ACTIVE STATE =============== -const bool Scanner_uC::STROBE_ON = LOW; -const bool Scanner_uC::STROBE_OFF = HIGH; - -// =================== PINS ==================== +// ================== SCANNER ================== uint8_t readPins[] = {14, 15}; -uint8_t READ_PIN_COUNT = sizeof(readPins)/sizeof(*readPins); +uint8_t readPinCount = sizeof(readPins)/sizeof(*readPins); + +Scanner_uC scanner(LOW, readPins, readPinCount); /* =================== CODES =================== The CODES section instantiates six codes, one for each item in the layout. @@ -52,8 +51,8 @@ LayerState layerState; /* NORMAL=0 and FN=1. LayerState's default layer id is 0. The Code_LayerHold constructor has two parameters: - 1) the layer that will be active while the key is held down. - 2) a LayerState + 1) the layer that will be active while the key is held down + 2) a LayerState that will keep track of the active layer When l_fn is pressed, it tells layerState to change the active layer to 1. When l_fn is released, it tells layerState that layer 1 is released, and layerState restores the default layer. */ @@ -69,8 +68,6 @@ Code_Sc s_shift(MODIFIERKEY_LEFT_SHIFT); /* =================== KEYS ==================== Here we pack Codes into keys. The Key_LayeredKeysArray constructor takes one array of Code pointers - one Code object per layer. -Key_LayeredKeysArray uses layer id numbers as array indexes. -Thus Key_LayeredKeysArray calls the Code corresponding to the active layer id. The Key object names in this sketch start with a "k_" followed by row-column coordinates. */ @@ -86,8 +83,9 @@ Thus Key_LayeredKeysArray can call layerState to get the active layer id. LayerStateInterface& Key_LayeredKeysArray::refLayerState = layerState; /* HOW LAYERED OBJECTS WORK -When a Key_LayeredKeysArray object is pressed, it gets the active layer id from layerState -It then uses the layer id as an array index to send the scancode for the active layer. +When a Key_LayeredKeysArray object is pressed, it gets the active layer id from layerState. +It then uses the layer id as an array index to call the Code of the active layer. +The Code object then sends its scancode over USB. */ /* =================== ROWS ==================== @@ -98,10 +96,12 @@ So rows can contain a mix of codes and multi-layered keys. Arrays ptrsKeys_0[] and ptrsKeys_1[] contain both Code pointers and Key pointers. */ Key* const ptrsKeys_0[] = { &s_shift, &k_01 }; -Row_uC row_0(0, readPins, READ_PIN_COUNT, ptrsKeys_0); +uint8_t keyCount_0 = sizeof(ptrsKeys_0)/sizeof(*ptrsKeys_0); +Row row_0(scanner, 0, ptrsKeys_0, keyCount_0); Key* const ptrsKeys_1[] = { &l_fn, &k_11 }; -Row_uC row_1(1, readPins, READ_PIN_COUNT, ptrsKeys_1); +uint8_t keyCount_1 = sizeof(ptrsKeys_1)/sizeof(*ptrsKeys_1); +Row row_1(scanner, 1, ptrsKeys_1, keyCount_1); // ################### MAIN #################### void setup() 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 index df4dd1d..8131083 100644 --- 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 @@ -1,7 +1,14 @@ /* keybrd_4c_split_with_IOE.ino +This sketch: + is a simple 1-layer keyboard + runs on two matrices of a breadboard keyboard + is annotated with a walk-through narrative + +This layout table shows left and right matrices: + | Left | **0** | **1** | | Right | **0** | **1** | -|:-----:|-------|-------| |:-----:|-------|-------| +|:-----:|-------|-------|-|:-----:|-------|-------| | **1** | 1 | 2 | | **1** | 3 | 4 | | **0** | a | b | | **0** | c | d | */ @@ -24,22 +31,44 @@ 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}; -uint8_t readPinCount = sizeof(readPins)/sizeof(*readPins); +const uint8_t READPIN_COUNT = sizeof(readPins)/sizeof(*readPins); -Scanner_uC scanner_L(LOW, readPins, readPinCount); +Scanner_uC scanner_L(LOW, readPins, READPIN_COUNT); // =============== RIGHT SCANNER =============== -const uint8_t PortIOE::DEVICE_ADDR = 0x20; //MCP23S17 address, all 3 ADDR pins are grounded +/* +The right matrix is scanned by an I/O expander. +The I/O expander device address is configured by hardware pins. +DEVICE_ADDR is a static variable of class PortIOE. +*/ +const uint8_t PortIOE::DEVICE_ADDR = 0x20; //MCP23S17 address with all 3 ADDR pins are grounded + +todo explain port num and shift notation << +/* +port_B stobes the row while port_A reads the colums. + +port_A is assigned port identification number 0. +port_A is assigned to portRead, which reads port_A pins 0 and 1. +"<<" (bit shift left) and "|" (or) are bitwise operators. +Pin numbers to be read are to the right of "1<<" and delimited by "|". +*/ PortIOE port_A(0); -PortRead_MCP23S17 portRead_A(port_A, 1<<0 | 1<<1 ); +PortRead_MCP23S17 portRead(port_A, 1<<0 | 1<<1 ); +/* +port_B is assigned port identification number 1. +port_B is assigned to portWrite. +*/ PortIOE port_B(1); -//PortWrite_MCP23S17 portWrite_B(port_B); //for LEDs -PortWrite_MCP23S17 portWrite_B(port_B); +//PortWrite_MCP23S17 portWrite(port_B); //for LEDs todo +PortWrite_MCP23S17 portWrite(port_B); -Scanner_IOE scanner_R(LOW, portWrite_B, portRead_A); +Scanner_IOE scanner_R(LOW, portWrite, portRead); // =================== CODES =================== Code_Sc s_a(KEY_A); @@ -53,7 +82,18 @@ Code_Sc s_3(KEY_3); Code_Sc s_4(KEY_4); // =================== ROWS ==================== +/* +Left row names contain the letter 'L', while right row names conatain the letter 'R'. + +The first parameteer of a Row constructor specifies the scanner. +The second parameter of the Row constructor specifies the Row's strobePin. +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 bitwise, 1 indicating an IC pin connected to this 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); @@ -63,6 +103,9 @@ 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, &s_4 }; const uint8_t KEY_COUNT_R0 = sizeof(ptrsKeys_R0)/sizeof(*ptrsKeys_R0); Row row_R0(scanner_R, 1<<0, ptrsKeys_R0, KEY_COUNT_R0); diff --git a/tutorials/tutorial_0_introduction.md b/tutorials/tutorial_0_introduction.md index 2d0da0f..b499189 100644 --- a/tutorials/tutorial_0_introduction.md +++ b/tutorials/tutorial_0_introduction.md @@ -2,7 +2,7 @@ Tutorial 0 - Introduction ========================= The first two tutorials are intended to be read in sequence: * Tutorial 1 builds a breadboard keyboard and covers basic keyboard-hardware knowledge. -* Tutorial 2 covers basic keybrd sketch knowledge needed to understand the remaining tutorials. +* Tutorial 2 covers basic keybrd-sketch knowledge needed to understand the remaining tutorials. Tutorials from 3 up can be read in any order. Tutorials 2 through 7 use the keyboard breadboard that was built in tutorial 1. diff --git a/tutorials/tutorial_10_writing_IOE_Port_classes.md b/tutorials/tutorial_10_writing_IOE_port_classes.md similarity index 67% rename from tutorials/tutorial_10_writing_IOE_Port_classes.md rename to tutorials/tutorial_10_writing_IOE_port_classes.md index d2e98c6..1f5a713 100644 --- a/tutorials/tutorial_10_writing_IOE_Port_classes.md +++ b/tutorials/tutorial_10_writing_IOE_port_classes.md @@ -2,36 +2,24 @@ Tutorial 10 - writing new IOE Port classes ========================================== Port classes are the keybrd library's interface to I/O expander ports. -To write a new Port class: +Steps to writing a new port class: 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). + * 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 + * 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/ todo internal pull-up resistors - /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. +4. Study a simple keybrd sketch that uses an I/O expander. + * [SPI I/O expander example sketch](keybrd_4c_split_with_IOE/keybrd_4c_split_with_IOE.ino) + * [I2C I/O expander example sketch](../examples/keybrd_PCA9655E/keybrd_PCA9655E.ino) +5. Study other keybrd port classes. + * SPI I/O expander port classes: PortMCP23S17 PortWrite_MCP23S17 PortRead_MCP23S17 + * I2C I/O expander port classes: PortWrite_PCA9655E PortRead_PCA9655E +6. Write the port classes for your I/O expander. Debugging I/O expander code is hard because SPI or I2C protocol adds a level of indirection.
diff --git a/tutorials/tutorial_1_breadboard_keyboard.md b/tutorials/tutorial_1_breadboard_keyboard.md index 07ade64..0c0e074 100644 --- a/tutorials/tutorial_1_breadboard_keyboard.md +++ b/tutorials/tutorial_1_breadboard_keyboard.md @@ -3,7 +3,7 @@ Tutorial 1 - breadboard keyboard In this tutorial, you will build a breadboard keyboard with 4 keys. The keyboad will be used in tutorials 2 through 7. -When you finish this tutorial you will have a working keyboard and understand how a key matrix works. +When you finish this tutorial you will have a working keyboard and an understanding of how a key matrix works. Why a solderless breadboard keyboard is useful ---------------------------------------------- @@ -21,8 +21,6 @@ Breadboard keyboards are useful for: * learning the firmware development workflow * prototyping circuits before making a PCB -Arduino simulation software is an alternative to breadboards; I haven't tried that. - Breadboard keyboard starter kit ------------------------------- The parts needed to build the tutorial breadboard keyboards are listed in [breadboard_keyboard_supplies.ods](breadboard_keyboard_supplies.ods). @@ -79,15 +77,15 @@ A short wire connects the bottom row to the microcontroller. Switch-diode pairs, in series, connect rows to columns. -Tutorials 2 and 3 use the basic breadboard keyboard pictured above. -Tutorials 4, 5, and 6 will add more components to the basic breadboard keyboard. +Tutorials 2 and 3 use the same basic breadboard keyboard pictured above. +Tutorials 4, 5, and 6 add more components to the basic breadboard keyboard. Positioning components as shown in the picture will provide space for those components. Breadboard keyboard assembly instructions: -1. Bend and cut leads to fit breadboard. - * tactile-switch-lead - * diodes (save the cut offs for steps 2, 3, and tutorial 4) +1. Shape leads to fit breadboard. + * cut tactile-switch leads to length + * bend and cut diode leads (save the cut offs for steps 2, 3, and tutorial 4) ![bend diodes](keybrd_1_breadboard/diodes_bend_en_masse.JPG "bend diodes") @@ -99,7 +97,7 @@ Breadboard keyboard assembly instructions: * Teensy LC is on the left * switch leads are oriented to connect diodes to columns (pictured below) * diode cut offs connect terminal strips into columns - * diodes connect switches to rows; orient diodes with cathode (banded end) towards the row (blue bus strip) + * diodes connect switches to rows; orient diodes with cathode (banded end) towards the row (blue bus) ![switch orientation](keybrd_1_breadboard/switch_orientation.JPG "switch orientation") @@ -122,11 +120,11 @@ Follow the [keybrd Library User's Guide](../doc/keybrd_library_user_guide.md) to Compile and load the [keybrd_1_breadboard.ino](/tutorials/keybrd_1_breadboard/keybrd_1_breadboard.ino) sketch into the keyboard's controller. The operating system will take 1 to 6 seconds to recognize the USB keyboard. -Then pressing the keys should type the characters 1, a, b, c. +Then pressing the keys should type the characters 1, 2, a, b. +Congratulations, you have a working keyboard. How a key matrix works ---------------------- -Congratulations, you have a working breadboard keyboard. Now we fill in some details of how it all works. This excellent article explains how key matrix, diodes, and ghosting work: @@ -140,7 +138,7 @@ The breadboard keyboards in this series of tutorials do it the other way: > Output pins power rows and input pins detect the power on columns. -The keybrd library uses the word "strobe". +The keybrd library uses the word "strobe", which means powering one row for a very short time. Strobe pins are output pins connected to rows. One row at a time is strobed for the purpose of reading input pins. diff --git a/tutorials/tutorial_2_single-layer_keyboard.md b/tutorials/tutorial_2_single-layer_keyboard.md index 5284acb..8b0b4a0 100644 --- a/tutorials/tutorial_2_single-layer_keyboard.md +++ b/tutorials/tutorial_2_single-layer_keyboard.md @@ -10,15 +10,17 @@ After reading the sketch you will be able to modify it to suite your own single- Exercises --------- -1) Read the three class definitions #included in the sketch. +1) Read the four class definitions #included in the sketch. Classes are defined in the [keybrd library](../src/). -2) Add a third column to the breadboard keyboard and sketch. +2) Add a fifth key to the breadboard keyboard and sketch. +Not all rows have to have the same number of keys. +The scanner should have enough readPins to cover all the keys of the longest row. | Layout |**0**|**1**|**2**| |:------:|:---:|:---:|:---:| -| **0** | k | e | y | -| **1** | b | r | d | +| **0** | 1 | 2 | 3 | +| **1** | a | b |
Creative Commons License
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_3a_multi-layer_keyboard.md b/tutorials/tutorial_3a_multi-layer_keyboard.md index b1789aa..23fe2dc 100644 --- a/tutorials/tutorial_3a_multi-layer_keyboard.md +++ b/tutorials/tutorial_3a_multi-layer_keyboard.md @@ -22,7 +22,7 @@ It will run on the basic breadboard keyboard described in [tutorial_1_breadboard ![basic breadboard keyboard](keybrd_1_breadboard/breadboard_keyboard_2x2.JPG "basic breadboard keyboard") -Read the sketch annotations to understand how multi-layer keyboards work. +The sketch annotations explain how multi-layer keyboards work. The sketch uses three layer-scheme classes: * LayerState * Code_LayerHold @@ -35,11 +35,11 @@ Pseudo code for simple layer scheme The following pseudo code is of three keybrd library classes. It has just enough detail to show the internal workings of layer schemes. -**Key_Layer** objects change the active layer when pressed. +**Code_Layer** objects change the active layer when pressed. The "layer" variable is a layer id number. -When a Key_Layer object is pressed, it tells LayerState to update the active layer. +When a Code_Layer object is pressed, it tells LayerState to update the active layer. ``` -class Key_Layer +class Code_Layer { int layer; LayerState& refLayerState; @@ -59,7 +59,7 @@ class LayerState ``` **Key_LayeredKeysArray** objects contain an array of keys, one key for each layer. -Key_LayeredKeysArray use layer ids as array indexes. +Key_LayeredKeysArray objects use layer ids as Key_LayeredKeysArray indexes. When a Key_LayeredKeysArray object is pressed, it gets the active layer from LayerState, and sends the corresponding key. ``` class Key_LayeredKeysArray @@ -73,9 +73,9 @@ class Key_LayeredKeysArray Dependency diagram ``` - +-----------+ - | Key_Layer | - +-----------+ + +------------+ + | Code_Layer | + +------------+ | |setActiveLayer() | @@ -96,7 +96,7 @@ Layer-scheme classes There are several layer scheme-classes to choose from. You can view all the class definitions in the [keybrd library](../src/). -Key_Layer classes include: +Code_Layer classes include: * Code_LayerHold * Code_LayerLock @@ -107,10 +107,9 @@ Key_Layered classes include: * Key_LayeredKeysArray * Code_LayeredScSc * Code_LayeredCodeSc -* Code_LayeredCodeCode The basic LayerState provided by the keybrd library is sufficient for implementing ordinary layer schemes. -For experimental layer schemes, you would need to create a custom LayerState class, and possibly Key_Layer and Key_Layered custom layer classes as well. +For experimental layer schemes, you would need to create a custom LayerState class, and possibly custom Code_Layer and Key_Layered classes as well. Single-layer Codes ------------------ diff --git a/tutorials/tutorial_4b_split_keyboard_with_shift_registers.md b/tutorials/tutorial_4b_split_keyboard_with_shift_registers.md new file mode 100644 index 0000000..d06996c --- /dev/null +++ b/tutorials/tutorial_4b_split_keyboard_with_shift_registers.md @@ -0,0 +1,83 @@ +keybrd Tutorial 4b - split keyboard with shift registers +======================================================== +When you finish this tutorial you will be able to be able to modify a split keybrd sketch with 10 to 24 keys on the peripheral hand. + +Overview of split keyboard with shift registers +------------------------------------------------ +Only the right matrix is shown. The left matrix was omitted to reduce clutter. + +The layout has 2 rows and 7 columns. +Electronically, the matrix only has one row. +Diodes are not needed because there is only one row. + +The two black rectangles are shift registers daisy chained together. +Shift register details are in the SN74HC165N datasheet. + +![breadboard keyboard with shift_registers](keybrd_4b_split_keyboard_with_shift_registers/shift_reg_front.JPG ) + +Building a split breadboard keyboard with shift registers +--------------------------------------------------------- +Add components to the breadboard as shown in the picture. + +Each shift register has a small notch on one end to identify pin 1. +In the picture, pin 1s are on the left end. +Shift registers are chained together by colored wires that lay flat on the breadboard. + +Each shift register has 8 parallel input pins, 4 on each side. +There are 14 keys, so 2 of the input pins are unused. +Used input pins are connected to 10k pull-down resistor which are grounded (blue bus). +Unused input pins are grounded (blue bus). + +A decoupling capacitor between the power and ground wires prevents power disturbance. + +Switches are connected to power (red bus) and shift register input pins (jumpers). + +I apologize for not having a schematic. This table should help you figure out the pictures: + +``` +74HC165 left (lower half of breadboard) +NAME PIN# DESCRIPTION TO TEENSY LC PIN# CHAIN +SH/LD 1 shift or load input CS0 10 green wire +CLK 2 clock input SCK0 13 yellow wire + D4 3 parallel input blue bus + D5 4 parallel input blue bus + D6 5 parallel input blue bus + D7 6 parallel input blue bus +/QH 7 ~serial output +GND 8 ground gnd blue bus + +74HC165 right (upper half of breadboard) +NAME PIN# DESCRIPTION TO TEENSY LC PIN# CHAIN +VCC 16 power pin 3.3V red wire +CLK INH 15 clock inhibit blue bus + D3 14 parallel input blue bus + D2 13 parallel input blue bus + D1 12 parallel input blue bus + D0 11 parallel input blue bus +SER 10 serial input blue wire to next QH + QH 9 serial output MISO0 12 blue wire to previous SER + +``` + +Sketch for split keyboard with shift registers +---------------------------------------------- +[keybrd_4b_split_keyboard_with_shift_registers.ino](keybrd_4b_split_keyboard_with_shift_registers/keybrd_4b_split_keyboard_with_shift_registers.ino) is a simple sketch with two shift registers. +The sketch will run on the above breadboard keyboard. + +The sketch has code for both left and right matrix. +Notice that the left matrix is active low, while the right matrix is active high. + +Exercises +--------- +1. Guess what happens if an unused input pin is not grounded? Try it. + +2. Add a left matrix to Teensy. + There is room between Teensy and the shift registers for a 1-column matrix. + The bus strips are occupied by the right keys, so use terminal strips instead. + Other wise it is similar to the 2-column matrix in [tutorial_1_breadboard_keyboard.md](tutorial_1_breadboard_keyboard.md). + +
+Creative Commons License
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. + +## parts for breadboard_keyboard_supplies.ods + diff --git a/tutorials/tutorial_4c_split_keyboard_with_IOE.md b/tutorials/tutorial_4c_split_keyboard_with_IOE.md new file mode 100644 index 0000000..0c1298e --- /dev/null +++ b/tutorials/tutorial_4c_split_keyboard_with_IOE.md @@ -0,0 +1,53 @@ +keybrd Tutorial 4 - split keyboard with I/O Expander +==================================================== +When you finish this tutorial you will be able to be able to modify a 2-matrix keybrd sketch to suite your own split keyboard design. + +Overview of split keyboard with I/O Expander +-------------------------------------------- +The breadboard in this picture models a split keyboard. +![breadboard keyboard with 2 rows and 4 columns of keys](images/breadboard_keyboard_2x5_labeled.jpg "2x5 breadboard keyboard") + +The breadboard's four bus strips are used as rows. +Two rows (blue buses) are connected to the microcontroller. +Two rows (red buses) are connected to the I/O expander. + +The I/O expander is a MCP23S17. +It has a small notch on one end, which identifies pin 1. +In the picture, pin 1 is on the right end. + +The MCP23S17 communicates via SPI protocol, where Teensy LC is the master, and MCP23S17 is slave. +The Teensy LC and MCP23S17 are connected by 6 jumper wires: + +|CONNECTION |Teensy LC|MCP23S17| +|:------------------:|---------|--------| +|ground | GND | VSS | +|power | 3.3v | VDD | +|Serial Clock | SCK0 | SCK | +|Master Out, Slave In| MOSI0 | SI | +|Master In, Slave Out| MISO0 | SO | +|Chip Select | CS0 | /CS | + +A decoupling capacitor suppresses high-frequency noise from the power supply. + +MCP23S17's I/O expander address is configured by hardware pins. +The MCP23S17 with all address pins grounded has an device address of 0x20. + +The MCP23S17's /RESET pin is connected to VDD. + +The MCP23S17 I/O expander has two ports. Each port has eight pins. +Port B is connected to the matrix's rows. Port A is connected to the matrix's columns. + +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. +Refer to the MCP23S17 datasheet to locate its pins. + + + +Sketch for split keyboard with I/O Expander +------------------------------------------- +The [keybrd_4c_split_with_IOE.ino](keybrd_4c_split_with_IOE/keybrd_4c_split_with_IOE.ino) + sketch explains how the I/O Expander works on a keyboard. + +Creative Commons License
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_6_active_high.md b/tutorials/tutorial_6_active_high.md index 4d5c4e9..6c174b2 100644 --- a/tutorials/tutorial_6_active_high.md +++ b/tutorials/tutorial_6_active_high.md @@ -70,8 +70,7 @@ By comparing the above tables, one can see what changes need to be made: * flip the diodes so that the cathode (banded end) are towards the read pins * swap the STROBE_ON and STROBE_OFF values -The red bus is grounded. -The pull-down resistors plug into the red bus and column read pins. +The pull-down resistors plug into ground (red bus) and column read pins. The [keybrd_6_active_highsketch.ino](keybrd_6_active_high/keybrd_6_active_high.ino) is the tutorial 1 sketch with STROBE_ON and STROBE_OFF values swapped.