/ | \ / \ | / | \ / \ | ||||
strobePin PortWrite PortRead Code Code_LEDLock | strobePin PortWrite PortRead Code Code_LEDLock | ||||
| \ / \ | | | \ / \ | | ||||
| PortIOE readPins LED | |||||
| PortIOE readPins LED_IOE | |||||
\___________________________/ \ | \___________________________/ \ | ||||
pin | pin | ||||
Underscore delineates base class name and sub-class name. Capital letters delineate words. | Underscore delineates base class name and sub-class name. Capital letters delineate words. | ||||
Interface class names end with "Interface". | Interface class names end with "Interface". | ||||
Except for Key, because sketches look nicer with short names defining Key[] arrays. | |||||
Except for Key, to reduce clutter because sketches define so many Key[] arrays. | |||||
Layer-class naming conventions | Layer-class naming conventions | ||||
------------------------------ | ------------------------------ |
#include <Scanner_uC.h> | #include <Scanner_uC.h> | ||||
//right matrix | //right matrix | ||||
#include <PortPCA9655E.h> | |||||
#include <Port_PCA9655E.h> | |||||
#include <Scanner_IOE.h> | #include <Scanner_IOE.h> | ||||
// ============ SPEED CONFIGURATION ============ | // ============ SPEED CONFIGURATION ============ | ||||
// =============== RIGHT SCANNER =============== | // =============== RIGHT SCANNER =============== | ||||
const uint8_t IOE_ADDR = 0x18; | const uint8_t IOE_ADDR = 0x18; | ||||
PortPCA9655E port1(IOE_ADDR, 1, 0); //for strobe | |||||
PortPCA9655E port0(IOE_ADDR, 0, 1<<0 | 1<<1 ); //for read | |||||
Port_PCA9655E port1(IOE_ADDR, 1, 0); //for strobe | |||||
Port_PCA9655E port0(IOE_ADDR, 0, 1<<0 | 1<<1 ); //for read | |||||
Scanner_IOE scanner_R(HIGH, port1, port0); | Scanner_IOE scanner_R(HIGH, port1, port0); | ||||
#include <inttypes.h> | #include <inttypes.h> | ||||
/* Key is an interface class | /* Key is an interface class | ||||
Key class name does not end in "Interface" because sketches define so many Key[] arrays. | |||||
*/ | */ | ||||
class Key | class Key | ||||
{ | { |
#include "Scanner_ShiftRegsPISOMultiRow.h" | |||||
/* constructor | |||||
*/ | |||||
Scanner_ShiftRegsPISOMultiRow::Scanner_ShiftRegsPISOMultiRow(const bool strobeOn, | |||||
const uint8_t slaveSelect, const uint8_t byte_count) | |||||
: strobeOn(strobeOn), strobeOff(!strobeOn), | |||||
slaveSelect(slaveSelect), byte_count(byte_count) | |||||
{ | |||||
pinMode(slaveSelect, OUTPUT); | |||||
} | |||||
/* init() is called once for each row from Row constructor. | |||||
Configures controller to communicate with shift register matrix. | |||||
*/ | |||||
void Scanner_ShiftRegsPISOMultiRow::init(const uint8_t strobePin) | |||||
{ | |||||
pinMode(strobePin, OUTPUT); | |||||
} | |||||
/* begin() should be called once from sketch setup(). | |||||
Initializes shift register's shift/load pin. | |||||
*/ | |||||
void Scanner_ShiftRegsPISOMultiRow::begin() | |||||
{ | |||||
digitalWrite(slaveSelect, HIGH); | |||||
} | |||||
/* scan() strobes the row's strobePin and returns state of the shift register's input pins. | |||||
strobePin is Arduino pin number connected to this row. | |||||
Bit patterns are 1 bit per key. | |||||
*/ | |||||
read_pins_t Scanner_ShiftRegsPISOMultiRow::scan(const uint8_t strobePin) | |||||
{ | |||||
read_pins_t readState = 0; //bits, 1 means key is pressed, 0 means released | |||||
//strobe row on | |||||
digitalWrite(strobePin, strobeOn); | |||||
delayMicroseconds(3); //time to stablize voltage | |||||
//read all the column pins | |||||
digitalWrite(slaveSelect, LOW); //load parallel inputs to the register | |||||
digitalWrite(slaveSelect, HIGH); //shift the data toward a serial output | |||||
SPI.transfer(&readState, byte_count); | |||||
//strobe row off | |||||
digitalWrite(strobePin, strobeOff); | |||||
return readState; | |||||
} | |||||
#ifndef ROWSCANNER_SHIFTREGSPISOMULTIROW_H | |||||
#define ROWSCANNER_SHIFTREGSPISOMULTIROW_H | |||||
#include <Arduino.h> | |||||
#include <inttypes.h> | |||||
#include <config_keybrd.h> | |||||
#include <SPI.h> | |||||
#include <ScannerInterface.h> | |||||
/* Scanner_ShiftRegsPISOMultiRow reads shift registers. | |||||
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. | |||||
Example instantiation: | |||||
Scanner_ShiftRegsPISOMultiRow scanner_R(HIGH, SS, 4); | |||||
There are three Scanner_ShiftRegsPISOMultiRow parameters. | |||||
"strobeOn" paramter is active state HIGH or LOW. | |||||
"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 should cover all the row's keys: | |||||
byte_count*8 >= row's keyCount | |||||
Hardware setup: | |||||
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. | |||||
For active low: | |||||
Shift-register parallel-input pins need 10k Ohm 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 | |||||
For active high: | |||||
Shift-register parallel-input pins need 10k pull-down resistors grounded. | |||||
Orient diodes with cathode (banded end) towards the read pins. | |||||
Controller's MISO pin is connected to shift register's serial output (QH) pin | |||||
*/ | |||||
class Scanner_ShiftRegsPISOMultiRow : public ScannerInterface | |||||
{ | |||||
private: | |||||
const bool strobeOn; //logic level of strobe on, active state HIGH or LOW | |||||
const bool strobeOff; //logic level of strobe off, complement of strobeOn | |||||
const uint8_t slaveSelect; //controller's pin number that is | |||||
// connected to shift register's SHIFT-LOAD pin | |||||
const uint8_t byte_count; //number of bytes to read from shift registers | |||||
public: | |||||
Scanner_ShiftRegsPISOMultiRow(const bool strobeOn, | |||||
const uint8_t slaveSelect, const uint8_t byte_count); | |||||
virtual void init(const uint8_t strobePin); | |||||
virtual void begin(); | |||||
virtual read_pins_t scan(const uint8_t strobePin); | |||||
}; | |||||
#endif |
#include "Scanner_ShiftRegsPISOSingleRow.h" | |||||
/* constructor | |||||
*/ | |||||
Scanner_ShiftRegsPISOSingleRow::Scanner_ShiftRegsPISOSingleRow(const bool strobeOn, | |||||
const uint8_t slaveSelect, const uint8_t byte_count) | |||||
: slaveSelect(slaveSelect), byte_count(byte_count) | |||||
{ | |||||
pinMode(slaveSelect, OUTPUT); | |||||
} | |||||
/* init() is called once for each row from Row constructor. | |||||
*/ | |||||
void Scanner_ShiftRegsPISOSingleRow::init(const uint8_t strobePin) | |||||
{ | |||||
//empty function | |||||
} | |||||
/* begin() should be called once from sketch setup(). | |||||
Initializes shift register's shift/load pin. | |||||
*/ | |||||
void Scanner_ShiftRegsPISOSingleRow::begin() | |||||
{ | |||||
SPI.begin(); | |||||
digitalWrite(slaveSelect, HIGH); | |||||
} | |||||
/* scan() returns state of the shift register's input pins. | |||||
No strobe pin is needed, the shift register is wired so the strobe is effectivley always "on". | |||||
Bit patterns are 1 bit per key. | |||||
*/ | |||||
read_pins_t Scanner_ShiftRegsPISOSingleRow::scan(const uint8_t strobePin) | |||||
{ | |||||
read_pins_t readState = 0; //bits, 1 means key is pressed, 0 means released | |||||
//read all the column pins | |||||
digitalWrite(slaveSelect, LOW); //load parallel inputs to the register | |||||
digitalWrite(slaveSelect, HIGH); //shift the data toward a serial output | |||||
SPI.transfer(&readState, byte_count); | |||||
return readState; | |||||
} | |||||
#ifndef ROWSCANNER_SHIFTREGSPISOSINGLEROW_H | |||||
#define ROWSCANNER_SHIFTREGSPISOSINGLEROW_H | |||||
#include <Arduino.h> | |||||
#include <inttypes.h> | |||||
#include <config_keybrd.h> | |||||
#include <SPI.h> | |||||
#include <ScannerInterface.h> | |||||
/* Scanner_ShiftRegsPISOSingleRow reads shift registers. | |||||
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. | |||||
Example instantiation: | |||||
Row row_R0(scanner_R, 0, ptrsKeys_R0, sizeof(ptrsKeys_R0)/sizeof(*ptrsKeys_R0)); | |||||
Scanner_ShiftRegsPISOSingleRow scanner_R(HIGH, SS, 4); | |||||
The Row "strobePin" parameter is ignored. | |||||
In the above example, the "strobePin" argument is 0, but it doesn't matter what value is given. | |||||
There are three Scanner_ShiftRegsPISOSingleRow parameters. | |||||
"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 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 should cover all the row's keys: | |||||
byte_count*8 >= row's keyCount | |||||
Hardware setup: | |||||
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. | |||||
For active low: | |||||
Shift-register parallel-input pins need 10k Ohm pull-up resistors powered. | |||||
Switches connect powered row to parallel-input pins. | |||||
Controller's MISO pin is connected to shift register's complementary serial output (/QH) pin | |||||
For active high: | |||||
Shift-register parallel-input pins need 10k pull-down resistors grounded. | |||||
Switches connect grouned row to parallel-input pins. | |||||
Controller's MISO pin is connected to shift register's serial output (QH) pin | |||||
*/ | |||||
class Scanner_ShiftRegsPISOSingleRow : public ScannerInterface | |||||
{ | |||||
private: | |||||
const uint8_t slaveSelect; //controller's pin number that is | |||||
// connected to shift register's SHIFT-LOAD pin | |||||
const uint8_t byte_count; //number of bytes to read from shift registers | |||||
public: | |||||
Scanner_ShiftRegsPISOSingleRow(const bool strobeOn, | |||||
const uint8_t slaveSelect, const uint8_t byte_count); | |||||
void init(const uint8_t strobePin); | |||||
void begin(); | |||||
virtual read_pins_t scan(const uint8_t strobePin); | |||||
}; | |||||
#endif |
/* tutorial_4b_split_keyboard_with_shift_registers.ino | /* tutorial_4b_split_keyboard_with_shift_registers.ino | ||||
Tested on Teensy LC and two 74HC165 shift registers. | Tested on Teensy LC and two 74HC165 shift registers. | ||||
The right matrix has 2 shift registers daisy chained. | |||||
Layout Layout | |||||
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 | |
is a simple 1-layer keyboard | is a simple 1-layer keyboard | ||||
runs on two matrices of a breadboard keyboard | runs on two matrices of a breadboard keyboard | ||||
This layout table shows left and right matrices: | |||||
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 | | ||||
#include <Scanner_uC.h> | #include <Scanner_uC.h> | ||||
//right matrix | //right matrix | ||||
#include <PortMCP23S17.h> | |||||
#include <Port_MCP23S17.h> | |||||
#include <Scanner_IOE.h> | #include <Scanner_IOE.h> | ||||
// ============ SPEED CONFIGURATION ============ | // ============ SPEED CONFIGURATION ============ | ||||
/* | /* | ||||
Normally all strobe pins are on one port, and all the read pins are on the other port. | 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. | In this example, portB stobes the row while portA reads the colums. | ||||
PortMCP23S17 constructor parameters are: deviceAddr, portNum, readPins | |||||
Port_MCP23S17 constructor parameters are: deviceAddr, portNum, readPins | |||||
readPins is a bit pattern, where 0=output, 1=input. | readPins is a bit pattern, where 0=output, 1=input. | ||||
In portA, the first two pins are set to input for reading. | In portA, the first two pins are set to input for reading. | ||||
"<<" (bit shift left) and "|" (OR) are bitwise operators. | "<<" (bit shift left) and "|" (OR) are bitwise operators. | ||||
Pin numbers to be read are to the right of "1<<" and delimited by "|". | Pin numbers to be read are to the right of "1<<" and delimited by "|". | ||||
*/ | */ | ||||
PortMCP23S17 portA(IOE_ADDR, 0, 1<<0 | 1<<1 ); | |||||
PortMCP23S17 portB(IOE_ADDR, 1, 0); | |||||
Port_MCP23S17 portA(IOE_ADDR, 0, 1<<0 | 1<<1 ); | |||||
Port_MCP23S17 portB(IOE_ADDR, 1, 0); | |||||
Scanner_IOE scanner_R(LOW, portB, portA); | Scanner_IOE scanner_R(LOW, portB, portA); | ||||
// =================== CODES =================== | // =================== CODES =================== |
| Layout | **0** | **1** | | | Layout | **0** | **1** | | ||||
|:------:|-------|-------| | |:------:|-------|-------| | ||||
| **0** |CapsLck| a 1 | | | **0** |CapsLck| a 1 | | ||||
| **1** | fn | b 2 | | |||||
| **1** | fn | x = | | |||||
*/ | */ | ||||
// ################## GLOBAL ################### | // ################## GLOBAL ################### | ||||
Code_LEDLock o_capsLock(KEY_CAPS_LOCK, LED_CapsLck); | Code_LEDLock o_capsLock(KEY_CAPS_LOCK, LED_CapsLck); | ||||
Code_Sc s_a(KEY_A); | Code_Sc s_a(KEY_A); | ||||
Code_Sc s_b(KEY_B); | |||||
Code_Sc s_x(KEY_X); | |||||
Code_Sc s_1(KEY_1); | Code_Sc s_1(KEY_1); | ||||
Code_Sc s_2(KEY_2); | |||||
Code_Sc s_equal(KEY_EQUAL); | |||||
// =================== KEYS ==================== | // =================== KEYS ==================== | ||||
Key* const ptrsKeys_01[] = { &s_a, &s_1 }; | Key* const ptrsKeys_01[] = { &s_a, &s_1 }; | ||||
Key_LayeredKeys k_01(ptrsKeys_01); | Key_LayeredKeys k_01(ptrsKeys_01); | ||||
Key* const ptrsKeys_11[] = { &s_b, &s_2 }; | |||||
Key* const ptrsKeys_11[] = { &s_x, &s_equal }; | |||||
Key_LayeredKeys k_11(ptrsKeys_11); | Key_LayeredKeys k_11(ptrsKeys_11); | ||||
LayerStateInterface& Key_LayeredKeys::refLayerState = layerState; | LayerStateInterface& Key_LayeredKeys::refLayerState = layerState; |