@@ -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(); | |||
} |
@@ -0,0 +1,21 @@ | |||
#ifndef CODE_SC_LED_H | |||
#define CODE_SC_LED_H | |||
#include <Arduino.h> | |||
#include <inttypes.h> | |||
#include <Code.h> | |||
#include <LED.h> | |||
/* 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 |
@@ -0,0 +1 @@ | |||
../classes |
@@ -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 <Debug.h> | |||
//LEDs | |||
#include <LED_PinNumber.h> | |||
#include "classes/Code_Sc_LED.h" //symlink: ln -s ../classes classes | |||
//IOE Ports | |||
#include "IOExpanderPort.h" | |||
#include <RowPort_MCP23018.h> | |||
#include <ColPort_MCP23018.h> | |||
//Codes | |||
#include <Code_Sc.h> | |||
//Matrix | |||
#include <Row_uC.h> | |||
#include <SPI.h> | |||
#include <Row_ShiftRegisters.h> | |||
// =============== 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(""); | |||
} |
@@ -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 <Debug.h> | |||
//IOE Ports | |||
#include "IOExpanderPort.h" | |||
#include <RowPort_MCP23018.h> | |||
#include <ColPort_MCP23018.h> | |||
//Codes | |||
#include <Code_Sc.h> | |||
//Matrix | |||
#include <Row_uC.h> | |||
#include <Row_IOE.h> | |||
// =============== 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(""); | |||
} |
@@ -0,0 +1 @@ | |||
../classes |
@@ -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 <Debug.h> | |||
//Ports | |||
#include <RowPort_AVR_Optic.h> | |||
#include <ColPort_AVR.h> | |||
//LEDs | |||
#include <LED_AVR.h> | |||
#include <LED_PCA9655E.h> | |||
#include "classes/Code_Sc_LED.h" //symlink: ln -s ../classes classes | |||
//Codes | |||
#include <Code_Sc.h> | |||
//Matrix | |||
#include <Row_IOE.h> | |||
#include <Matrix.h> | |||
// =============== 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 <IOExpanderPort.h> | |||
#include <RowPort_PCA9655E.h> | |||
#include <ColPort_PCA9655E.h> | |||
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("")); | |||
} |
@@ -4,7 +4,7 @@ | |||
#include <inttypes.h> | |||
#include <Code.h> | |||
/* 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 |
@@ -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(); | |||
} | |||
@@ -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(); |
@@ -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) |
@@ -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 | |||
------------ |
@@ -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 |
@@ -0,0 +1,11 @@ | |||
#include "LED_PinNumber.h" | |||
void LED_PinNumber::on() | |||
{ | |||
digitalWrite(pin, HIGH); | |||
} | |||
void LED_PinNumber::off() | |||
{ | |||
digitalWrite(pin, LOW); | |||
} |
@@ -0,0 +1,22 @@ | |||
#ifndef LED_PINNUMBER_H | |||
#define LED_PINNUMBER_H | |||
#include <Arduino.h> | |||
#include <inttypes.h> | |||
#include <LED.h> | |||
/* 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 |
@@ -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 |
@@ -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) |
@@ -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) |
@@ -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 |
@@ -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) | |||
{ |
@@ -6,7 +6,7 @@ | |||
#include <RowPort.h> | |||
#include <ColPort.h> | |||
/* 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); | |||
}; |
@@ -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; | |||
} |
@@ -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); | |||
}; |
@@ -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; | |||
} | |||
@@ -0,0 +1,25 @@ | |||
#ifndef ROWSCANNER_SPI_SHIFTREGISTERS_H | |||
#define ROWSCANNER_SPI_SHIFTREGISTERS_H | |||
#include <Arduino.h> | |||
#include <inttypes.h> | |||
#include <SPI.h> | |||
#include <RowScannerInterface.h> | |||
#include <RowPort.h> | |||
#include <ColPort.h> | |||
/* 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 |
@@ -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); |
@@ -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); | |||
} |
@@ -0,0 +1,38 @@ | |||
#ifndef ROW_SHIFTREGISTERS_H | |||
#define ROW_SHIFTREGISTERS_H | |||
#include <RowBase.h> | |||
#include <RowScanner_SPIShiftRegisters.h> | |||
#include <Debouncer_4Samples.h> | |||
/* 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 |
@@ -6,14 +6,16 @@ | |||
#include <Debouncer_4Samples.h> | |||
/* 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); | |||
@@ -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] |
@@ -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. |