void loop() | void loop() | ||||
{ | { | ||||
//left matrix | |||||
row_L0.process(); | row_L0.process(); | ||||
row_L1.process(); | row_L1.process(); | ||||
//right matrix | |||||
row_R0.process(); | row_R0.process(); | ||||
row_R1.process(); | row_R1.process(); | ||||
#include "ColPort_AVR.h" | |||||
/* | |||||
configures column port's DDRx and PORTx. | |||||
*/ | |||||
ColPort_AVR::ColPort_AVR(volatile unsigned char& DDRx, volatile unsigned char& PORTx, | |||||
volatile unsigned char& PINx, const uint8_t colPins) | |||||
: ColPort(colPins), DDR(DDRx = ~colPins), PORT(PORTx = colPins), PIN(PINx) | |||||
{} | |||||
/* | |||||
Saves all port-pin values to portState. | |||||
*/ | |||||
void ColPort_AVR::read() | |||||
{ | |||||
portState = PIN; | |||||
} |
#ifndef COLPORT_AVR_H | |||||
#define COLPORT_AVR_H | |||||
#include <Arduino.h> | |||||
#include <inttypes.h> | |||||
#include <ColPort.h> | |||||
/* One AVR microcontroller port connected to matrix columns. | |||||
Instantiation | |||||
------------ | |||||
The constructor configures column's DDRx and PORTx. | |||||
The 'x' in parameters DDRx, PORTx, and PINx should all be the same letter. | |||||
colPins is port's bitwise pin configuration | |||||
1=configure as input (for pins connected to column) | |||||
0=configure as output (for LED or not connected to a column) | |||||
Example instantiation on column port B, with pins 2 and 3 connected to columns: | |||||
ColPort_AVR colPortB(DDRB, PORTB, PINB, 1<<2 | 1<<3 ); | |||||
colPins are read from pin 0 on up. | |||||
Diode orientation | |||||
---------------- | |||||
Rows, columns, and diode orientation are explained in Matrix.h | |||||
*/ | |||||
class ColPort_AVR : public ColPort | |||||
{ | |||||
private: | |||||
const volatile unsigned char& DDR; //Data Direction Register, Direction: 0=Input | |||||
const volatile unsigned char& PORT; //PORT register | |||||
const volatile unsigned char& PIN; //PIN read register | |||||
public: | |||||
//The constructor initialization list is in .cpp | |||||
ColPort_AVR(volatile unsigned char& DDRx, volatile unsigned char& PORTx, | |||||
volatile unsigned char& PINx, const uint8_t colPins); | |||||
//read port and store result in portState | |||||
virtual void read(); | |||||
}; | |||||
#endif |
#include "ColPort_MCP23018.h" | |||||
/* | |||||
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) | |||||
{} | |||||
void ColPort_MCP23018::begin(uint8_t activeHigh) | |||||
{ | |||||
Wire.beginTransmission(port.ADDR); | |||||
Wire.write(IODIR); | |||||
Wire.write(colPins); //0=configure as output (for LED), 1=configure as input (for read) | |||||
Wire.endTransmission(); | |||||
Wire.beginTransmission(port.ADDR); | |||||
Wire.write(GPPU); | |||||
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(); | |||||
} | |||||
/* | |||||
Saves all port-pin values to portState. | |||||
*/ | |||||
void ColPort_MCP23018::read() | |||||
{ | |||||
Wire.beginTransmission(port.ADDR); | |||||
Wire.write(GPIO); //GPIO immediately before requestFrom | |||||
Wire.endTransmission(); | |||||
Wire.requestFrom(port.ADDR, 1u); //request one byte from input port | |||||
portState = Wire.read(); | |||||
} |
#ifndef COLPORT_MCP23018_H | |||||
#define COLPORT_MCP23018_H | |||||
#include <Arduino.h> | |||||
#include <inttypes.h> | |||||
#include <Wire.h> | |||||
#include <ColPort.h> | |||||
#include "IOExpanderPort.h" | |||||
/* One MCP23018 I/O expander port connected to matrix columns. | |||||
Instantiation | |||||
------------ | |||||
colPins parameter is port's bitwise pin configuration | |||||
1=configure as input (for read) | |||||
0=configure as output (for LED or not connected to a column) | |||||
example instantiation for column port A, with pins 2 and 3 connected to columnss: | |||||
IOExpanderPort portA(0, ~0); | |||||
ColPort_MCP23018 colPortA(portA, 1<<2 | 1<<3 ); | |||||
example instantiation for column port B, with pins 2 and 3 connected to columns: | |||||
IOExpanderPort portB(1, ~0); | |||||
ColPort_MCP23018 colPortB(portB, 1<<2 | 1<<3 ); | |||||
colPins are read from pin 0 on up. | |||||
Diode orientation | |||||
---------------- | |||||
Rows, columns, and diode orientation are explained in Matrix.h | |||||
*/ | |||||
class ColPort_MCP23018 : public ColPort | |||||
{ | |||||
private: | |||||
IOExpanderPort& port; | |||||
const uint8_t IODIR; //Input/Ouput Direction register | |||||
const uint8_t GPIO; //General Purpose Input/Ouput register | |||||
const uint8_t GPPU; //General Purpose Pullup register | |||||
public: | |||||
//The constructor initialization list is in .cpp | |||||
ColPort_MCP23018(IOExpanderPort& port, const uint8_t colPins); | |||||
void begin(uint8_t activeHigh); | |||||
//read port and store result in portState | |||||
virtual void read(); | |||||
}; | |||||
#endif |
#include "LED_AVR.h" | |||||
void LED_AVR::on() | |||||
{ | |||||
PORT |= pin; //set pin high | |||||
} | |||||
void LED_AVR::off() | |||||
{ | |||||
PORT &= ~pin; //set pin low | |||||
} |
#ifndef LED_AVR_H | |||||
#define LED_AVR_H | |||||
#include <Arduino.h> | |||||
#include <inttypes.h> | |||||
#include <LED.h> | |||||
/* A LED_AVR object is an AVR pin that is used to power an LED on and off. | |||||
DDRx Data Direction Register is configured as output in RowPort_AVR constructor. | |||||
*/ | |||||
class LED_AVR: public LED | |||||
{ | |||||
private: | |||||
volatile unsigned char& PORT; | |||||
const uint8_t pin; //bitwise pin to LED | |||||
public: | |||||
LED_AVR(volatile unsigned char& PORTx, const uint8_t pin): PORT(PORTx), pin(pin) {} | |||||
virtual void on(); | |||||
virtual void off(); | |||||
}; | |||||
#endif |
#include "LED_MCP23018.h" | |||||
void LED_MCP23018::on() | |||||
{ | |||||
Wire.beginTransmission(port.ADDR); | |||||
Wire.write(GPIO); | |||||
Wire.write(port.outputVal &= ~pin); //set pin low (sink) | |||||
Wire.endTransmission(); | |||||
} | |||||
void LED_MCP23018::off() | |||||
{ | |||||
Wire.beginTransmission(port.ADDR); | |||||
Wire.write(GPIO); | |||||
Wire.write(port.outputVal |= pin); //set pin high (sink off) | |||||
Wire.endTransmission(); | |||||
} |
#ifndef LED_MCP23018_H | |||||
#define LED_MCP23018_H | |||||
#include <Arduino.h> | |||||
#include <inttypes.h> | |||||
#include <Wire.h> | |||||
#include <LED.h> | |||||
#include "IOExpanderPort.h" | |||||
/* Class LED_MCP23018 uses a MCP23018 I/O expander pin to turn a LED on and off. | |||||
Connect the LED in series with the resistor: | |||||
determin resistor value needed (Internet search: LED resistor value) | |||||
Connect the LED's (-) ground to the AVR output pin | |||||
connect LED's (+) to power | |||||
Never connect a LED directly from ground to power. Doing so would destroy the LED. | |||||
MCP23018 ouput is open drain. The output acts like a switch to ground. | |||||
It cannot produce a high signal by itself. | |||||
*/ | |||||
class LED_MCP23018: public LED | |||||
{ | |||||
private: | |||||
IOExpanderPort& port; | |||||
const uint8_t GPIO; //General Purpose Input/Ouput register address | |||||
const uint8_t pin; //bitwise pin to LED | |||||
public: | |||||
LED_MCP23018(IOExpanderPort& port, const uint8_t pin) | |||||
: port(port), GPIO(port.num + 0x12), pin(pin) {} | |||||
virtual void on(); | |||||
virtual void off(); | |||||
}; | |||||
#endif |
#include "Matrix.h" | |||||
/* | |||||
scan every row of matrix one time | |||||
*/ | |||||
void Matrix::scan() | |||||
{ | |||||
for (uint8_t i=0; i < rowCount; i++) | |||||
{ | |||||
ptrsRows[i]->process(); | |||||
} | |||||
} |
#ifndef MATRIX_H | |||||
#define MATRIX_H | |||||
#include <Arduino.h> | |||||
#include <inttypes.h> | |||||
#include "RowBase.h" | |||||
/* | |||||
Diode orientation | |||||
---------------- | |||||
A keyboard's physically matrix is composed of rows and columns. | |||||
The rows and columns are physically connected to the keys. | |||||
The rows and columns are distinguishable by diode orientation (not horizontal/vertical). | |||||
For active low diode orientation is: | |||||
cathodes on rows | |||||
anodes on columns | |||||
For active high diode orientation is reversed: | |||||
anodes on rows | |||||
cathodes on columns | |||||
Pull-down resistors | |||||
------------------- | |||||
If Matrix uses active low, IC requires one pull-up resistor on each ColPort::colPins. | |||||
If Matrix uses active high, IC requires one pull-down resistor on each ColPort::colPins. | |||||
External pull-down resistors should have a value between 10k Ohms and 2.2k Ohms. | |||||
*/ | |||||
class Matrix | |||||
{ | |||||
private: | |||||
RowBase *const *const ptrsRows; //array of row pointers | |||||
const uint8_t rowCount; | |||||
public: | |||||
Matrix( RowBase *const ptrsRows[], const uint8_t rowCount) | |||||
: ptrsRows(ptrsRows), rowCount(rowCount) {} | |||||
void scan(); | |||||
}; | |||||
#endif |
#include "RowPort_AVR_Optic.h" | |||||
/* | |||||
configures row port's DDRx and PORTx pins as output. | |||||
*/ | |||||
RowPort_AVR_Optic::RowPort_AVR_Optic | |||||
(volatile unsigned char& DDRx, volatile unsigned char& PORTx) | |||||
: DDR(DDRx = ~0), PORT(PORTx) | |||||
{} | |||||
/* | |||||
activePin is a port mask, where active pin is 1. | |||||
*/ | |||||
void RowPort_AVR_Optic::setActivePinLow(const uint8_t activePin) | |||||
{ | |||||
PORT &= ~activePin; | |||||
} | |||||
/* | |||||
activePin is port mask, where active pin is 1. | |||||
The delayMicroseconds() is for DodoHand keyboard's optic switches. | |||||
Strobe needs to be turned on for >= 300µs before the columns are read. | |||||
During this time the state of the columns are settling into their actual values. | |||||
Seems to be necessary in order to allow the phototransistors to turn completely off. | |||||
(delay is not need for I/O expander because time between I2C Transmissions) | |||||
(Teensy2 ATMEGA32U4 16 MHz is a 0.0625 µs period) | |||||
*/ | |||||
void RowPort_AVR_Optic::setActivePinHigh(const uint8_t activePin) | |||||
{ | |||||
//strobe row on | |||||
PORT |= activePin; | |||||
delayMicroseconds(300); //wait for the column value to stabilize after strobe | |||||
} |
#ifndef ROWPORT_AVR_OPTIC_H | |||||
#define ROWPORT_AVR_OPTIC_H | |||||
#include <Arduino.h> | |||||
#include <inttypes.h> | |||||
#include <RowPort.h> | |||||
/* One AVR microcontroller port connected to matrix rows. | |||||
setActivePinHigh() has a delay to allow phototransistors time to sense strobe | |||||
(DodoHand has optic switches with phototransistors). | |||||
Instantiation | |||||
------------ | |||||
The constructor configures all pins of port as output (for strobe pins and LED). | |||||
The 'x' in parameters DDRx, PORTx, and PINx should all be the same letter. | |||||
Example instantiation for row port F: | |||||
RowPort_AVR_Optic rowPortF(DDRF, PORTF); | |||||
Diode orientation | |||||
---------------- | |||||
Rows, columns, and diode orientation are explained in Matrix.h | |||||
*/ | |||||
class RowPort_AVR_Optic : public RowPort | |||||
{ | |||||
private: | |||||
const volatile unsigned char& DDR; //Data Direction Register | |||||
protected: | |||||
volatile unsigned char& PORT; //PORT register | |||||
public: | |||||
//The constructor initialization list is in .cpp | |||||
RowPort_AVR_Optic(volatile unsigned char& DDRx, volatile unsigned char& PORTx); | |||||
virtual void setActivePinLow(const uint8_t activePin); //activePin is a port mask | |||||
virtual void setActivePinHigh(const uint8_t activePin); | |||||
}; | |||||
#endif |
#include "RowPort_MCP23018.h" | |||||
/* | |||||
configures column port's IODIR, GPIO. | |||||
*/ | |||||
RowPort_MCP23018::RowPort_MCP23018(IOExpanderPort& port) | |||||
: port(port), IODIR(port.num), GPIO(port.num + 0x12) | |||||
{} | |||||
void RowPort_MCP23018::begin() | |||||
{ | |||||
Wire.beginTransmission(port.ADDR); | |||||
Wire.write(IODIR); | |||||
Wire.write(0); //0=configure as output (for strobe pins and LED pins) | |||||
Wire.endTransmission(); | |||||
} | |||||
/* | |||||
sets activePin pin output to low. | |||||
activePin is port mask, where active-low pin is 1. | |||||
*/ | |||||
void RowPort_MCP23018::setActivePinLow(const uint8_t activePin) | |||||
{ | |||||
Wire.beginTransmission(port.ADDR); | |||||
Wire.write(GPIO); | |||||
Wire.write(port.outputVal &= ~activePin); | |||||
Wire.endTransmission(); | |||||
} | |||||
/* | |||||
sets activePin pin output to high, does not reset the other pins because they might be used by LEDs. | |||||
activePin is port mask, where active-high pin is 1. | |||||
*/ | |||||
void RowPort_MCP23018::setActivePinHigh(const uint8_t activePin) | |||||
{ | |||||
Wire.beginTransmission(port.ADDR); | |||||
Wire.write(GPIO); | |||||
Wire.write(port.outputVal |= activePin); | |||||
Wire.endTransmission(); | |||||
} |
#ifndef ROWPORT_MCP23018_H | |||||
#define ROWPORT_MCP23018_H | |||||
#include <Arduino.h> | |||||
#include <inttypes.h> | |||||
#include <Wire.h> | |||||
#include <RowPort.h> | |||||
#include "IOExpanderPort.h" | |||||
/* One MCP23018 I/O expander port connected to matrix rows. | |||||
begin() configures column port's IODIR, GPIO. | |||||
This should normally be called only once. | |||||
Instantiation | |||||
------------ | |||||
Example instantiation for row port A: | |||||
IOExpanderPort portA(0, ~0); | |||||
RowPort_MCP23018 rowPortA(portA); | |||||
Example instantiation for row port B: | |||||
IOExpanderPort portB(1, ~0); | |||||
RowPort_MCP23018 rowPortB(portB); | |||||
Diode orientation | |||||
---------------- | |||||
Rows, columns, and diode orientation are explained in Matrix.h | |||||
MCP23018 data sheet | |||||
------------------ | |||||
http://ww1.microchip.com/downloads/en/DeviceDoc/22103a.pdf | |||||
*/ | |||||
class RowPort_MCP23018 : public RowPort | |||||
{ | |||||
private: | |||||
IOExpanderPort& port; | |||||
const uint8_t IODIR; //Input/Ouput Direction register address | |||||
const uint8_t GPIO; //General Purpose Input/Ouput register address | |||||
public: | |||||
//The constructor initialization list is in .cpp | |||||
RowPort_MCP23018(IOExpanderPort& port); | |||||
void begin(); | |||||
virtual void setActivePinLow(const uint8_t activePin); //activePin is a port mask | |||||
virtual void setActivePinHigh(const uint8_t activePin); | |||||
}; | |||||
#endif |
For RowScanner_SPIShiftRegisters, RowScanner_SPIShiftRegisters::KEY_COUNT | For RowScanner_SPIShiftRegisters, RowScanner_SPIShiftRegisters::KEY_COUNT | ||||
For RowScanner_PinsBitwise, cover the last 1 bit in RowScanner_PinsBitwise::strobePin | For RowScanner_PinsBitwise, cover the last 1 bit in RowScanner_PinsBitwise::strobePin | ||||
*/ | */ | ||||
//typedef uint8_t read_pins_t; | |||||
typedef uint8_t read_pins_t; | |||||
//typedef uint16_t read_pins_t; | //typedef uint16_t read_pins_t; | ||||
typedef uint32_t read_pins_t; | |||||
//typedef uint32_t read_pins_t; | |||||
/* read_pins_mask_t is only used for rowMask and rowEnd, which extends one bit beyond the last col pin. | /* read_pins_mask_t is only used for rowMask and rowEnd, which extends one bit beyond the last col pin. | ||||
uncomment typedef that covers one bit beyond the last col pin. | uncomment typedef that covers one bit beyond the last col pin. | ||||
This could be the same typedef as read_pins_t, or the next larger typedef. | This could be the same typedef as read_pins_t, or the next larger typedef. | ||||
*/ | */ | ||||
//typedef uint8_t read_pins_mask_t; | |||||
typedef uint8_t read_pins_mask_t; | |||||
//typedef uint16_t read_pins_mask_t; | //typedef uint16_t read_pins_mask_t; | ||||
typedef uint32_t read_pins_mask_t; | |||||
//typedef uint32_t read_pins_mask_t; | |||||
/* SAMPLE_COUNT = 4 is very reliable for a keyboard. | /* SAMPLE_COUNT = 4 is very reliable for a keyboard. | ||||
Split keyboards with a long connecting wire or in environment with | Split keyboards with a long connecting wire or in environment with |