diff --git a/examples/tests_regression/classes/Code_Sc_LED.cpp b/examples/tests_regression/classes/Code_Sc_LED.cpp new file mode 100644 index 0000000..389f8de --- /dev/null +++ b/examples/tests_regression/classes/Code_Sc_LED.cpp @@ -0,0 +1,13 @@ +#include "Code_Sc_LED.h" + +void Code_Sc_LED::press() +{ + Keyboard.press(scancode); + refLED.on(); +} + +void Code_Sc_LED::release() +{ + Keyboard.release(scancode); + refLED.off(); +} diff --git a/examples/tests_regression/classes/Code_Sc_LED.h b/examples/tests_regression/classes/Code_Sc_LED.h new file mode 100644 index 0000000..6832702 --- /dev/null +++ b/examples/tests_regression/classes/Code_Sc_LED.h @@ -0,0 +1,21 @@ +#ifndef CODE_SC_LED_H +#define CODE_SC_LED_H +#include +#include +#include +#include + +/* Class Code_Sc_LED sends a scancode when press() or release() is called. +"S" stands for Scancode. +*/ +class Code_Sc_LED : public Code +{ + private: + const uint16_t scancode; + LED& refLED; + public: + Code_Sc_LED(const uint16_t scancode, LED& refLED): scancode(scancode), refLED(refLED) { } + virtual void press(); + virtual void release(); +}; +#endif diff --git a/examples/tests_regression/keybrd/classes b/examples/tests_regression/keybrd/classes new file mode 120000 index 0000000..9046554 --- /dev/null +++ b/examples/tests_regression/keybrd/classes @@ -0,0 +1 @@ +../classes \ No newline at end of file diff --git a/examples/tests_regression/keybrd/keybrd.ino b/examples/tests_regression/keybrd/keybrd.ino new file mode 100644 index 0000000..8ef3d19 --- /dev/null +++ b/examples/tests_regression/keybrd/keybrd.ino @@ -0,0 +1,150 @@ +/* this works on Teensy LC 1*bb, active low and active high +MCP23018 is not working, MCP23018::begin() hangs, details in setup() + +| Layout | **0** | **1** | +|:------:|-------|-------| +| **0** | a | b | +| **1** | c | d | +*/ +// ################## GLOBAL ################### +// ================= INCLUDES ================== +#include + +//LEDs +#include +#include "classes/Code_Sc_LED.h" //symlink: ln -s ../classes classes + +//IOE Ports +#include "IOExpanderPort.h" +#include +#include + +//Codes +#include + +//Matrix +#include +#include +#include + +// =============== CONFIGURATION =============== +const unsigned int RowBase::DELAY_MICROSECONDS = 500; + +//activeLow has diode cathode (band) on row +//activeHigh has diode cathode (band) on col, and pull down resistors on cols +//0=active low, 1= active high +const bool RowScanner_PinsArray::activeHigh = 0; +//const bool RowScanner_PinsBitwise::activeHigh = 0; + +Debug debug; + +// ================ LEFT PORTS ================= +uint8_t readPins[] = {14, 15}; +uint8_t READ_PIN_COUNT = sizeof(readPins)/sizeof(*readPins); + +LED_PinNumber LED1(16); //Teensy LC pins 16, 17 are 20 ma + +// ================ RIGHT PORTS ================ +/* +const uint8_t IOExpanderPort::ADDR = 0x20; + +IOExpanderPort portA(0, 0); +RowPort_MCP23018 rowPort(portA); + +IOExpanderPort portB(1, 0); +ColPort_MCP23018 colPort(portB, 1<<0 | 1<<1 ); +*/ +// =================== CODES =================== +Code_Sc s_a(KEY_A); +Code_Sc s_b(KEY_B); +Code_Sc s_c(KEY_C); +Code_Sc_LED s_d(KEY_D, LED1); + +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); +Code_Sc s_7(KEY_7); +Code_Sc s_z(KEY_Z); + +// ================= LEFT ROWS ================= +Key* ptrsKeys_L0[] = { &s_a, &s_b }; +Row_uC row_L0(0, readPins, READ_PIN_COUNT, ptrsKeys_L0); + +Key* ptrsKeys_L1[] = { &s_c, &s_d }; +Row_uC row_L1(1, readPins, READ_PIN_COUNT, ptrsKeys_L1); + +// ================= RIGHT ROWS ================ +/* +Key* ptrsKeys_R[] = { &s_0, &s_z, &s_z, &s_z, + &s_4, &s_z, &s_z, &s_z, + &s_8, &s_z, &s_z, &s_z }; //the s_z are place holders and should not print +*/ +Key* ptrsKeys_R[] = { &s_0, &s_1, &s_2, &s_3, + &s_4, &s_5, &s_6, &s_7 }; //the most that 8-bit send() can handle +const uint8_t KEY_COUNT = sizeof(ptrsKeys_R)/sizeof(*ptrsKeys_R); +//Row_ShiftRegisters row_R(9, 2, ptrsKeys_R, KEY_COUNT); +Row_ShiftRegisters row_R(9, 1, ptrsKeys_R, KEY_COUNT); //1 byte + +// ################### MAIN #################### +void setup() +{ + Keyboard.begin(); + SPI.begin(); + row_R.begin(); + + //delay(1000); //time for OS to detect USB before printing + Keyboard.print(F("activeState.ino ")); + debug.print_free_RAM(); + + //Wire.begin(); +/* Teensy LC on 1*bb 6/13/16 copied to: activeState_MCP23018_begin_hangs.no +RowPort_MCP23018::begin() hangs +ColPort_MCP23018::begin() sometimes hangs, sometimes prints after 6 seconds +PCA9655E::begin()s works on 4*bb +maybe hangs if IOE is not attached because endTransmission() waiting for confirmation?? + +trouble shooting MCP23018::begin()s + checked wiring against datasheets + measured power and ground, 3.3 volts checks out +!! next things to check could take days: + test MCP23018 with Teensy 2.0, because it worked last year + set Teensy 2.0 to 3.3 volts and test again + try with PCA9655E instead of MCP23018 (works on 4*bb) + might be solder joints on LC (I soldered it), try using other Teensy LC + test MCP23018 on simple demo sketch /home/wolfv/Documents/Arduino/demo_keep/mcp23018_../ + test MCP23018 with signal analyzer + + //rowPort.begin(); //this breaks sketch, does not print "activeState.ino ", kb unresponsive + //colPort.begin(RowScanner_PinsBitwise::activeHigh); //hanges for 6 seconds + Keyboard.println(F(" after Port.begin()")); +*/ +} + +//uint16_t next = 0; +//elapsedMillis elapsed; + +void loop() +{ + row_L0.process(); + row_L1.process(); + + row_R.process(); + + //row_R0.process(); + //row_R1.process(); + +/* used this when debugging MCP23018::begin() hangs +if ( (next < 10) && (elapsed > 1000 * next) ) +{ + Keyboard.print(next); + Keyboard.print(F(" ")); + next++; +} +*/ +//delay(100); +//Keyboard.println(""); +} diff --git a/examples/tests_regression/keybrd/keybrd_MCP23018_begin_hangs.no b/examples/tests_regression/keybrd/keybrd_MCP23018_begin_hangs.no new file mode 100644 index 0000000..2c509b5 --- /dev/null +++ b/examples/tests_regression/keybrd/keybrd_MCP23018_begin_hangs.no @@ -0,0 +1,129 @@ +/* this works on Teensy LC 1*bb, active low and active high +MCP23018::begin() hangs, details in setup() + +| Layout | **0** | **1** | +|:------:|-------|-------| +| **0** | a | b | +| **1** | c | d | +*/ +// ################## GLOBAL ################### +// ================= INCLUDES ================== +#include + +//IOE Ports +#include "IOExpanderPort.h" +#include +#include + +//Codes +#include + +//Matrix +#include +#include + +// =============== CONFIGURATION =============== +const unsigned int RowBase::DELAY_MICROSECONDS = 500; + +//activeLow has diode cathode (band) on row +//activeHigh has diode cathode (band) on col, and pull down resistors on cols +//0=active low, 1= active high +const bool RowScanner_PinsArray::activeHigh = 0; +const bool RowScanner_PinsBitwise::activeHigh = 0; + +Debug debug; + +// ================= uC PINS ================= +uint8_t readPins[] = {14, 15}; +uint8_t READ_PIN_COUNT = sizeof(readPins)/sizeof(*readPins); + +// ================ IOE PORTS ================ +const uint8_t IOExpanderPort::ADDR = 0x20; + +IOExpanderPort portA(0, 0); +RowPort_MCP23018 rowPort(portA); + +IOExpanderPort portB(1, 0); +ColPort_MCP23018 colPort(portB, 1<<0 | 1<<1 ); + +// =================== 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_0(KEY_0); +Code_Sc s_1(KEY_1); +Code_Sc s_2(KEY_2); +Code_Sc s_3(KEY_3); + +// ================= LEFT ROWS ================= +Key* ptrsKeys_L0[] = { &s_a, &s_b }; +Row_uC row_L0(0, readPins, READ_PIN_COUNT, ptrsKeys_L0); + +Key* ptrsKeys_L1[] = { &s_c, &s_d }; +Row_uC row_L1(1, readPins, READ_PIN_COUNT, ptrsKeys_L1); + +// ================= RIGHT ROWS ================ +Key* ptrsKeys_R0[] = { &s_0, &s_1 }; +Row_IOE row_R0(rowPort, 1<<0, colPort, ptrsKeys_R0); + +Key* ptrsKeys_R1[] = { &s_2, &s_3 }; +Row_IOE row_R1(rowPort, 1<<1, colPort, ptrsKeys_R1); + +// ################### MAIN #################### +void setup() +{ + Keyboard.begin(); + Wire.begin(); //Wire.begin() must be called before rowPort.begin() colPort.begin() + + //delay(1000); //time for OS to detect USB before printing + Keyboard.print(F("activeState.ino ")); + debug.print_free_RAM(); + +/* Teensy LC on 1*bb +RowPort_MCP23018::begin() hangs +ColPort_MCP23018::begin() sometimes hangs, sometimes prints after 6 seconds +PCA9655E::begin()s works on 4*bb +maybe hangs if IOE is not attached because endTransmission() waiting for confirmation?? + +trouble shooting MCP23018::begin()s + checked wiring against datasheets + measured power and ground, 3.3 volts checks out +!! next things to check could take days: + test MCP23018 with Teensy 2.0, because it worked last year + set Teensy 2.0 to 3.3 volts and test again + try with PCA9655E instead of MCP23018 (works on 4*bb) + might be solder joints on LC (I soldered it), try using other Teensy LC + test MCP23018 on simple demo sketch /home/wolfv/Documents/Arduino/demo_keep/mcp23018_../ + test MCP23018 with signal analyzer + + //rowPort.begin(); //this breaks sketch, does not print "activeState.ino ", kb unresponsive + //colPort.begin(RowScanner_PinsBitwise::activeHigh); //hanges for 6 seconds + Keyboard.println(F(" after Port.begin()")); +*/ +} + + +uint16_t next = 0; +elapsedMillis elapsed; + +void loop() +{ + row_L0.process(); + row_L1.process(); + + //row_R0.process(); + //row_R1.process(); + +/* used this when debugging MCP23018::begin() hangs +if ( (next < 10) && (elapsed > 1000 * next) ) +{ + Keyboard.print(next); + Keyboard.print(F(" ")); + next++; +} +*/ +//delay(500); +//Keyboard.println(""); +} diff --git a/examples/tests_regression/keybrd_AVR/classes b/examples/tests_regression/keybrd_AVR/classes new file mode 120000 index 0000000..9046554 --- /dev/null +++ b/examples/tests_regression/keybrd_AVR/classes @@ -0,0 +1 @@ +../classes \ No newline at end of file diff --git a/examples/tests_regression/keybrd_AVR/keybrd_AVR.ino b/examples/tests_regression/keybrd_AVR/keybrd_AVR.ino new file mode 100644 index 0000000..3558833 --- /dev/null +++ b/examples/tests_regression/keybrd_AVR/keybrd_AVR.ino @@ -0,0 +1,113 @@ +/* this works on DH 4*bb, top-left buttons +demo RowScanner_PinsBitwise + +| Left | **0** | **1** | | Right | **0** | **1** | +|:-----:|-------|-------| |:-----:|-------|-------| +| **0** | a | b | | **0** | 0 | 1 | +| **1** | c | d | | **1** | 2 | 3 | +*/ +// ################## GLOBAL ################### +// ================= INCLUDES ================== +#include + +//Ports +#include +#include + +//LEDs +#include +#include +#include "classes/Code_Sc_LED.h" //symlink: ln -s ../classes classes + +//Codes +#include + +//Matrix +#include +#include + +// =============== CONFIGURATION =============== +const unsigned int RowBase::DELAY_MICROSECONDS = 500; + +const bool RowScanner_PinsBitwise::activeHigh = 1; + +Debug debug; + +// ================ LEFT PORTS ================= +RowPort_AVR_Optic rowPort_L(DDRF, PORTF); +ColPort_AVR colPort_L(DDRB, PORTB, PINB, 1<<0 | 1<<1 ); + +//LED +LED_AVR LED_L1(PORTB, 1<<6); //green + +// ================ RIGHT PORTS ================ +#include +#include +#include + +const uint8_t IOExpanderPort::ADDR = 0x18; + +//row port +IOExpanderPort port1(1, 0); +RowPort_PCA9655E rowPort_R(port1); + +//column and pin numbers on schematic_switch_matrix.png and schematic_pca9655_pin_assignments.png +//col port +IOExpanderPort port0(0, 0); +ColPort_PCA9655E colPort_R(port0, 1<<0 | 1<<1 ); + +//LED +LED_PCA9655E LED_R1(port1, 1<<5); //blue + +// =================== 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_LED s_d(KEY_D, LED_L1); + +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_LED s_3(KEY_3, LED_R1); + +// ================= LEFT ROWS ================= +Key* const ptrsKeys_L0[] = { &s_a, &s_b }; +Row_IOE row_L0(rowPort_L, 1<<0, colPort_L, ptrsKeys_L0); //strobe F0 + +Key* const ptrsKeys_L1[] = { &s_c, &s_d }; +Row_IOE row_L1(rowPort_L, 1<<1, colPort_L, ptrsKeys_L1); //strobe F1 + +// ================= RIGHT ROWS ================ +Key* ptrsKeys_R0[] = { &s_0, &s_1 }; +Row_IOE row_R0(rowPort_R, 1<<0, colPort_R, ptrsKeys_R0); + +Key* ptrsKeys_R1[] = { &s_2, &s_3 }; +Row_IOE row_R1(rowPort_R, 1<<1, colPort_R, ptrsKeys_R1); + +// ################### MAIN #################### +void setup() +{ + Keyboard.begin(); + Wire.begin(); + + delay(1000); //time for OS to detect USB before printing + Keyboard.print(F("activeState_AVR.ino ")); + debug.print_free_RAM(); + + rowPort_R.begin(); + colPort_R.begin(); +} + +void loop() +{ + row_L0.process(); + row_L1.process(); + + row_R0.process(); + row_R1.process(); + +//delay(500); +//Keyboard.println(F("")); +} diff --git a/src/Code_Sc.h b/src/Code_Sc.h index cd571a7..598654e 100644 --- a/src/Code_Sc.h +++ b/src/Code_Sc.h @@ -4,7 +4,7 @@ #include #include -/* Class Code_Sc is composed of one scancode, which it sends when press() or release() is called. +/* Class Code_Sc_LED sends a scancode when press() or release() is called. "S" stands for Scancode. */ class Code_Sc : public Code diff --git a/src/ColPort_MCP23018.cpp b/src/ColPort_MCP23018.cpp index 2b5e35d..ec1ac33 100644 --- a/src/ColPort_MCP23018.cpp +++ b/src/ColPort_MCP23018.cpp @@ -4,25 +4,26 @@ configures column port's IODIR, GPIO, and GPPU. */ ColPort_MCP23018::ColPort_MCP23018(IOExpanderPort& port, const uint8_t colPins) - : ColPort(colPins), port(port), IODIR(port.num), GPIO(port.num + 0x12), GPPU(port.num + 0x0C) + : ColPort(colPins), port(port), IODIR(port.num), GPIO(port.num + 0x12), GPPU(port.num + 0x0C) {} -void ColPort_MCP23018::begin() +void ColPort_MCP23018::begin(uint8_t activeHigh) { -//Wire.begin() should only be called once https://www.arduino.cc/en/Reference/WireBegin -#ifndef WIRE_BEGIN -#define WIRE_BEGIN - Wire.begin(); -#endif - Wire.beginTransmission(port.ADDR); Wire.write(IODIR); - Wire.write(colPins); //0=configure as output (for LED), 1=configure as input (for read) + Wire.write(colPins); //0=configure as output (for LED), 1=configure as input (for read) Wire.endTransmission(); Wire.beginTransmission(port.ADDR); Wire.write(GPPU); - Wire.write(colPins); //0=pull-up disabled (for LED), 1=pull-up enabled (for read) + if (activeHigh) + { + Wire.write(0); //0=pull-up disabled for activeHigh //todo not tested yet + } + else + { + Wire.write(colPins); //0=pull-up disabled (for LED), 1=pull-up enabled (for read) + } Wire.endTransmission(); } diff --git a/src/ColPort_MCP23018.h b/src/ColPort_MCP23018.h index 9333e03..1b16c68 100644 --- a/src/ColPort_MCP23018.h +++ b/src/ColPort_MCP23018.h @@ -38,7 +38,7 @@ class ColPort_MCP23018 : public ColPort public: //The constructor initialization list is in .cpp ColPort_MCP23018(IOExpanderPort& port, const uint8_t colPins); - void begin(); + void begin(uint8_t activeHigh); //read port and store result in portState virtual void read(); diff --git a/src/ColPort_PCA9655E.cpp b/src/ColPort_PCA9655E.cpp index 4fbb564..ed71571 100644 --- a/src/ColPort_PCA9655E.cpp +++ b/src/ColPort_PCA9655E.cpp @@ -10,12 +10,6 @@ ColPort_PCA9655E::ColPort_PCA9655E void ColPort_PCA9655E::begin() { -//Wire.begin() should only be called once https://www.arduino.cc/en/Reference/WireBegin -#ifndef WIRE_BEGIN -#define WIRE_BEGIN - Wire.begin(); -#endif - Wire.beginTransmission(port.ADDR); Wire.write(configurationByteCommand); Wire.write(colPins); //0=configure as output (for LED), 1=configure as input (for read) diff --git a/src/ColPort_PCA9655E.h b/src/ColPort_PCA9655E.h index faa8a1b..ca8a565 100644 --- a/src/ColPort_PCA9655E.h +++ b/src/ColPort_PCA9655E.h @@ -7,6 +7,7 @@ #include "IOExpanderPort.h" /* One PCA9655E I/O expander port connected to matrix columns. +PCA9655E does not have internal pull-up resistors (PCA9535E does). Instantiation ------------ diff --git a/src/LED_AVR.h b/src/LED_AVR.h index 815eef1..7656f45 100644 --- a/src/LED_AVR.h +++ b/src/LED_AVR.h @@ -15,9 +15,7 @@ class LED_AVR: public LED public: LED_AVR(volatile unsigned char& PORTx, const uint8_t pin): PORT(PORTx), pin(pin) {} - virtual void on(); - virtual void off(); }; #endif diff --git a/src/LED_PinNumber.cpp b/src/LED_PinNumber.cpp new file mode 100644 index 0000000..452801b --- /dev/null +++ b/src/LED_PinNumber.cpp @@ -0,0 +1,11 @@ +#include "LED_PinNumber.h" + +void LED_PinNumber::on() +{ + digitalWrite(pin, HIGH); +} + +void LED_PinNumber::off() +{ + digitalWrite(pin, LOW); +} diff --git a/src/LED_PinNumber.h b/src/LED_PinNumber.h new file mode 100644 index 0000000..5dc8301 --- /dev/null +++ b/src/LED_PinNumber.h @@ -0,0 +1,22 @@ +#ifndef LED_PINNUMBER_H +#define LED_PINNUMBER_H +#include +#include +#include + +/* A LED_PinNumber object is an Aduino pin that is used to power an LED on and off. +*/ +class LED_PinNumber: public LED +{ + private: + const uint8_t pin; + + public: + LED_PinNumber(const uint8_t pin): pin(pin) + { + pinMode(pin, OUTPUT); + } + virtual void on(); + virtual void off(); +}; +#endif diff --git a/src/RowBase.cpp b/src/RowBase.cpp index 45ac658..2b12d23 100644 --- a/src/RowBase.cpp +++ b/src/RowBase.cpp @@ -21,6 +21,8 @@ This version of wait() is very simple. More sophisticated versions can override For fastest response time, wait() should be placed before scan() or after pressRelease() (waiting between strobe and send would unnecessarily delay send). +DELAY_MICROSECONDS explained +---------------------------- A keyboard with a faster scan rate responds faster. Follow these step to tune DELAY_MICROSECONDS for maximum scan rate for a given SAMPLE_COUNT: Initialize DELAY_MICROSECONDS in your sketch: @@ -58,7 +60,7 @@ void RowBase::pressRelease(const uint16_t rowEnd, const uint8_t debouncedChanged { uint8_t isFallingEdge; //1 means falling edge uint8_t isRisingEdge; //1 means rising edge - uint8_t rowMask; //bitwise, active col bit is 1 + uint16_t rowMask; //bitwise, active col bit is 1 (same type as rowEnd) uint8_t col; //index for ptrsKeys[col] array //bit=1 if last debounced changed from 1 to 0, else bit=0 diff --git a/src/RowPort_MCP23018.cpp b/src/RowPort_MCP23018.cpp index 9e5b00c..4adb2fd 100644 --- a/src/RowPort_MCP23018.cpp +++ b/src/RowPort_MCP23018.cpp @@ -9,12 +9,6 @@ RowPort_MCP23018::RowPort_MCP23018(IOExpanderPort& port) void RowPort_MCP23018::begin() { -//Wire.begin() should only be called once https://www.arduino.cc/en/Reference/WireBegin -#ifndef WIRE_BEGIN -#define WIRE_BEGIN - Wire.begin(); -#endif - Wire.beginTransmission(port.ADDR); Wire.write(IODIR); Wire.write(0); //0=configure as output (for strobe pins and LED pins) diff --git a/src/RowPort_PCA9655E.cpp b/src/RowPort_PCA9655E.cpp index c5d52a6..cddd20e 100644 --- a/src/RowPort_PCA9655E.cpp +++ b/src/RowPort_PCA9655E.cpp @@ -9,12 +9,6 @@ RowPort_PCA9655E::RowPort_PCA9655E(IOExpanderPort& port) void RowPort_PCA9655E::begin() { -//Wire.begin() should only be called once https://www.arduino.cc/en/Reference/WireBegin -#ifndef WIRE_BEGIN -#define WIRE_BEGIN - Wire.begin(); -#endif - Wire.beginTransmission(port.ADDR); Wire.write(configurationByteCommand); Wire.write(0); //0=configure as output (for strobe pins and LED) diff --git a/src/RowPort_PCA9655E.h b/src/RowPort_PCA9655E.h index 32aa5f7..f2c6e3c 100644 --- a/src/RowPort_PCA9655E.h +++ b/src/RowPort_PCA9655E.h @@ -23,6 +23,8 @@ Example instantiation for row port 1: Diode orientation ---------------- +PCA9655E does not have internal pull-up resistors, external pull-down resistors are required. + Rows, columns, and diode orientation are explained in Matrix.h PCA9655E data sheet diff --git a/src/RowScanner_PinsArray.cpp b/src/RowScanner_PinsArray.cpp index e2d9341..55709c5 100644 --- a/src/RowScanner_PinsArray.cpp +++ b/src/RowScanner_PinsArray.cpp @@ -1,11 +1,43 @@ #include "RowScanner_PinsArray.h" -/* -Strobes the row and reads the columns. +/* constructor +*/ +RowScanner_PinsArray::RowScanner_PinsArray(const uint8_t strobePin, + const uint8_t readPins[], const uint8_t READ_PIN_COUNT) + : strobePin(strobePin), readPins(readPins), READ_PIN_COUNT(READ_PIN_COUNT) +{ + uint8_t mode; + + //configure row + pinMode(strobePin, OUTPUT); + + if (activeHigh) + { + mode = INPUT; //requires external pull-down resistor + } + else + { + mode = INPUT_PULLUP; //uses internal pull-up resistor + } + + //configure cols + for (uint8_t i=0; i < READ_PIN_COUNT; i++) + { + pinMode(readPins[i], mode); + } +} + +/* scan() Strobes the row and reads the columns. Sets rowEnd and returns rowState. rowEnd is a bitwise row mask, one col per bit, where active col bit is 1. At end of function, 1 bit marks place immediatly after last key of row. rowEnd is a larger type than portMask so that it can not overflow. + +https://www.arduino.cc/en/Tutorial/DigitalPins +https://www.arduino.cc/en/Reference/PinMode +https://www.arduino.cc/en/Reference/DigitalWrite +https://www.arduino.cc/en/Reference/DigitalRead +https://www.arduino.cc/en/Reference/Constants > Digital Pins modes: INPUT, INPUT_PULLUP, and OUTPUT */ uint8_t RowScanner_PinsArray::scan(uint16_t& rowEnd) { diff --git a/src/RowScanner_PinsArray.h b/src/RowScanner_PinsArray.h index 5b14baa..d6b5dda 100644 --- a/src/RowScanner_PinsArray.h +++ b/src/RowScanner_PinsArray.h @@ -6,7 +6,7 @@ #include #include -/* RowScanner_PinsArray class uses Arduino pin numbers (no port name). +/* RowScanner_PinsArray class uses Arduino pin numbers (not port pin numbers). */ class RowScanner_PinsArray : public RowScannerInterface { @@ -17,18 +17,7 @@ class RowScanner_PinsArray : public RowScannerInterface const uint8_t READ_PIN_COUNT; //number of read pins public: RowScanner_PinsArray(const uint8_t strobePin, - const uint8_t readPins[], const uint8_t READ_PIN_COUNT) - : strobePin(strobePin), readPins(readPins), READ_PIN_COUNT(READ_PIN_COUNT) - { - //row - pinMode(strobePin, OUTPUT); - - //cols - for (uint8_t i=0; i < READ_PIN_COUNT; i++) - { - pinMode(readPins[i], INPUT_PULLUP); - } - } + const uint8_t readPins[], const uint8_t READ_PIN_COUNT); virtual uint8_t scan(uint16_t& rowEnd); uint8_t getRowState(uint16_t& rowEnd); }; diff --git a/src/RowScanner_PinsBitwise.cpp b/src/RowScanner_PinsBitwise.cpp index 42d0a7f..cc641b9 100644 --- a/src/RowScanner_PinsBitwise.cpp +++ b/src/RowScanner_PinsBitwise.cpp @@ -69,7 +69,6 @@ uint8_t RowScanner_PinsBitwise::getRowState(uint16_t& rowEnd) rowEnd <<= 1; //shift rowEnd to next key } } -//todo Keyboard.print(rowState); return rowState; } diff --git a/src/RowScanner_PinsBitwise.h b/src/RowScanner_PinsBitwise.h index 8aaf805..f7a3fcf 100644 --- a/src/RowScanner_PinsBitwise.h +++ b/src/RowScanner_PinsBitwise.h @@ -11,7 +11,6 @@ class RowScanner_PinsBitwise : public RowScannerInterface { private: - static const bool activeHigh; //logic level of strobe pin: 0=activeLow, 1=activeHigh RowPort& refRowPort; //this row's IC port const uint8_t strobePin; //bitwise, 1 indicates IC pin connected to this row ColPort& refColPort; @@ -20,6 +19,7 @@ class RowScanner_PinsBitwise : public RowScannerInterface ColPort& refColPort) : refRowPort(refRowPort), strobePin(strobePin), refColPort(refColPort) {} + static const bool activeHigh; //logic level of strobe pin: 0=activeLow, 1=activeHigh virtual uint8_t scan(uint16_t& rowEnd); uint8_t getRowState(uint16_t& rowEnd); }; diff --git a/src/RowScanner_SPIShiftRegisters.cpp b/src/RowScanner_SPIShiftRegisters.cpp new file mode 100644 index 0000000..5adb215 --- /dev/null +++ b/src/RowScanner_SPIShiftRegisters.cpp @@ -0,0 +1,23 @@ +#include "RowScanner_SPIShiftRegisters.h" + +void RowScanner_SPIShiftRegisters::begin() +{ + pinMode (SS, OUTPUT); + digitalWrite (SS, HIGH); +} + +/* +Sets rowEnd and returns rowState. +*/ +uint8_t RowScanner_SPIShiftRegisters::scan(uint16_t& rowEnd) +{ +//todo rowEnd, rowState, return int size depend on BYTE_COUNT, like in send() + uint8_t rowState; + + digitalWrite(SS, LOW); + digitalWrite(SS, HIGH); + SPI.transfer(&rowState, BYTE_COUNT); + rowEnd = 1 << KEY_COUNT; + return rowState; +} + diff --git a/src/RowScanner_SPIShiftRegisters.h b/src/RowScanner_SPIShiftRegisters.h new file mode 100644 index 0000000..c590cb4 --- /dev/null +++ b/src/RowScanner_SPIShiftRegisters.h @@ -0,0 +1,25 @@ +#ifndef ROWSCANNER_SPI_SHIFTREGISTERS_H +#define ROWSCANNER_SPI_SHIFTREGISTERS_H +#include +#include +#include +#include +#include +#include + +/* RowScanner_SPIShiftRegisters reads all shift registers in a daisy chain. +//todo delete: Assumes only one row of shift registers is connected (no Slave Select). +*/ +class RowScanner_SPIShiftRegisters : public RowScannerInterface +{ + private: + const uint8_t SS; //Slave Select, pin on master + const uint8_t BYTE_COUNT; //number of shift registers + const uint8_t KEY_COUNT; //number of keys in row + public: + RowScanner_SPIShiftRegisters(const uint8_t SS, uint8_t BYTE_COUNT, uint16_t KEY_COUNT) + : SS(SS), BYTE_COUNT(BYTE_COUNT), KEY_COUNT(KEY_COUNT) {} + virtual uint8_t scan(uint16_t& rowEnd); + void begin(); +}; +#endif diff --git a/src/Row_IOE.h b/src/Row_IOE.h index 3fc651a..47c019d 100644 --- a/src/Row_IOE.h +++ b/src/Row_IOE.h @@ -7,13 +7,15 @@ /* Row_DH_IOE is a row connected to an Input/Output Expander. -Configuration -------------- -Define and initilize DELAY_MICROSECONDS in sketch. Detailed how to is in RowBase.cpp. - Instantiation ------------- +Definition of DELAY_MICROSECONDS is explained in RowBase.cpp. +Definition of activeHigh is explained in RowScanner_Interface.h Example instantiation of a row: + + const unsigned int RowBase::DELAY_MICROSECONDS = 1000; + const bool RowScanner_PinsArray::activeHigh = 0; + const uint8_t IOExpanderPort::ADDR = 0x18; IOExpanderPort port1(1, 0); diff --git a/src/Row_ShiftRegisters.cpp b/src/Row_ShiftRegisters.cpp new file mode 100644 index 0000000..4d59a0c --- /dev/null +++ b/src/Row_ShiftRegisters.cpp @@ -0,0 +1,16 @@ +#include "Row_ShiftRegisters.h" + +void Row_ShiftRegisters::begin() +{ + scanner.begin(); +} + +uint8_t Row_ShiftRegisters::scan(uint16_t& rowEnd) +{ + return scanner.scan(rowEnd); +} + +uint8_t Row_ShiftRegisters::debounce(const uint8_t rowState, uint8_t& debounced) +{ + return debouncer.debounce(rowState, debounced); +} diff --git a/src/Row_ShiftRegisters.h b/src/Row_ShiftRegisters.h new file mode 100644 index 0000000..51f9b11 --- /dev/null +++ b/src/Row_ShiftRegisters.h @@ -0,0 +1,38 @@ +#ifndef ROW_SHIFTREGISTERS_H +#define ROW_SHIFTREGISTERS_H + +#include +#include +#include + +/* Row_DH_IOE is a row connected to an Input/Output Expander. + +Instantiation +------------- +Definition of DELAY_MICROSECONDS is explained in RowBase.cpp. +Example instantiation of a row: + + const unsigned int RowBase::DELAY_MICROSECONDS = 1000; + + todo + + Key* const ptrsKeys_0[] = { &k_00, &k_01, &k_02, &k_03, &k_04, &k_05 }; + Row_ShiftRegisters row_0(uint8_t BYTE_COUNT, ptrsKeys_0); + +Number of pins in colPort0 should equal number of keys in ptrsKeys_0[] array. + if a pin is missing, a key will be unresposive + if a Key pointer is missing, the keyboard will fail in an unprdictable way +*/ +class Row_ShiftRegisters : public RowBase +{ + private: + RowScanner_SPIShiftRegisters scanner; + Debouncer_4Samples debouncer; + public: + Row_ShiftRegisters(const uint8_t SS, uint8_t BYTE_COUNT, Key *const ptrsKeys[], uint16_t KEY_COUNT) + : RowBase(ptrsKeys), scanner(SS, BYTE_COUNT, KEY_COUNT) { } + void begin(); + uint8_t scan(uint16_t& rowEnd); + uint8_t debounce(const uint8_t rowState, uint8_t& debounced); +}; +#endif diff --git a/src/Row_uC.h b/src/Row_uC.h index bac33ac..ca3e3cd 100644 --- a/src/Row_uC.h +++ b/src/Row_uC.h @@ -6,14 +6,16 @@ #include /* Row_uC is a row connected to a micro controller. -Configuration -------------- -Define and initilize DELAY_MICROSECONDS in sketch. Detailed how to is in RowBase.cpp. Instantiation ------------- +Definition of DELAY_MICROSECONDS is explained in RowBase.cpp. +todo Definition of activeHigh is explained in RowScanner_Interface.h Example instantiation of a row: + const unsigned int RowBase::DELAY_MICROSECONDS = 1000; + const bool RowScanner_PinsArray::activeHigh = 0; + const uint8_t colPins[] = {0,1,2,3,7,8}; const uint8_t COL_PIN_COUNT = sizeof(colPins)/sizeof(*colPins); diff --git a/tutorials/breadboard_keyboard_supplies.ods b/tutorials/breadboard_keyboard_supplies.ods index 95e1288..f4bcbea 100644 Binary files a/tutorials/breadboard_keyboard_supplies.ods and b/tutorials/breadboard_keyboard_supplies.ods differ diff --git a/tutorials/tutorial_1_breadboard_keyboard.md b/tutorials/tutorial_1_breadboard_keyboard.md index 76a37ad..0df7231 100644 --- a/tutorials/tutorial_1_breadboard_keyboard.md +++ b/tutorials/tutorial_1_breadboard_keyboard.md @@ -7,7 +7,8 @@ All the tutorial example sketches run on breadboard keyboards that have 2 to 8 k Breadboard keyboards have row-column matrices and diodes just like the big keyboards. A breadboard is the easiest way to learn keyboard electronics. -Electronics are fickle, and you won't get everything right the first time. +It's easy to get some detail wrong with electronics. +You won't get everything right the first time. There is a learning curve. Compared to PCBs, breadboard keyboards are easier to learn on because: * Mistakes are easily corrected; no soldering and desoldering @@ -22,12 +23,13 @@ Breadboard keyboards are useful for: ## Breadboard keyboard starter kit The parts needed to build all the tutorial Breadboard Keyboards are listed in [breadboard_keyboard_supplies.ods](breadboard_keyboard_supplies.ods). -Wire cutters (or nail clipper) is the only required tool. -A multi-meter is useful for trouble shooting. +You will need two tools: +* Wire cutters (or nail clipper) +* A multi-meter for trouble shooting ## How a breadboard works To understand the breadboard keyboard you will need to know the internal parts of a breadboard: -* power rail +* bus strip * terminal strip These are explained in [How to Use a Breadboard](https://learn.sparkfun.com/tutorials/how-to-use-a-breadboard) @@ -42,7 +44,7 @@ The basic breadboard has 4 switches and a microcontroller. ![breadboard keyboard with 2 rows and 2 columns](images/breadboard_keyboard_2x2_labeled.jpg "2x2 breadboard keyboard") The key matrix has two rows and two columns. -Breadboard power rails are repurposed as matrix rows. +Breadboard bus strips are used as matrix rows. Short bare wires connect terminal strips into matrix columns. Switch-diode pairs connect rows to columns. @@ -63,7 +65,7 @@ Breadboard keyboard assembly instructions: * Teensy LC on the terminal strip labeled 1 * switch leads oriented so that they will connect diodes to columns * diode cut offs connect terminal strips into columns - * diodes are orient with cathode (banded end) towards the row (power strip) + * diodes are orient with cathode (banded end) towards the row (bus strip) 3. Insert jumper wires connecting Teensy2 to the matrix rows and columns. * follow pin connections table (below) and consult pinout diagram in [close-up pic shows switch way half out, to show lead orientation] diff --git a/tutorials/tutorial_4_split_keyboard_with_IOE.md b/tutorials/tutorial_4_split_keyboard_with_IOE.md index 68110f5..d97fec0 100644 --- a/tutorials/tutorial_4_split_keyboard_with_IOE.md +++ b/tutorials/tutorial_4_split_keyboard_with_IOE.md @@ -6,10 +6,8 @@ When you finish this tutorial you will be able to be able to modify a 2-matrix k 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") -There is a total of 4 matrix rows, each on a breadboard power rail. - -The right matrix is connected to a microcontroller. -The left matrix is connected to a I/O expander. +The breadboard has four bus strips used as rows. +Two rows connected to a microcontroller, and two rows connected to a I/O expander. The I/O expander has a small notch on one end, which identifies the end with pin 1. In the picture, pin 1 is on the right end.