* Diode orientation | * Diode orientation | ||||
* To validate keyboard hardware, modify the simple [keybrd_1_breadboard.ino](../tutorials/keybrd_1_breadboard/keybrd_1_breadboard.ino) sketch. | * To validate keyboard hardware, modify the simple [keybrd_1_breadboard.ino](../tutorials/keybrd_1_breadboard/keybrd_1_breadboard.ino) sketch. | ||||
Debugging: | |||||
Arduino doesn't have a debugger. You can print values like this: | |||||
Keyboard.print(" var="); Keyboard.print(var); | |||||
Keyboard.print(" bitPattern="); Keyboard.println(bitPattern, BIN); | |||||
delay(200); | |||||
The delay is so prints in a loop don't print too fast. | |||||
Keybrd nomenclature | Keybrd nomenclature | ||||
------------------- | ------------------- | ||||
**[scancode](http://en.wikipedia.org/wiki/Scancode)** - | **[scancode](http://en.wikipedia.org/wiki/Scancode)** - |
#include <inttypes.h> | #include <inttypes.h> | ||||
#include <Wire.h> | #include <Wire.h> | ||||
#include <LED.h> | #include <LED.h> | ||||
#include <PortWriteInterface.h> | |||||
#include <PortInterface.h> | |||||
/* A LED_IOE object is an I/O expander pin that is connected to an LED indicator light. | /* A LED_IOE object is an I/O expander pin that is connected to an LED indicator light. | ||||
Input/Ouput Direction configuration are set to ouput in PortWrite_*.begin() and PortRead_*.begin(). todo PortRead_*?? | Input/Ouput Direction configuration are set to ouput in PortWrite_*.begin() and PortRead_*.begin(). todo PortRead_*?? | ||||
*/ | */ | ||||
class LED_IOE: public LED | |||||
class LED_IOE : public LED | |||||
{ | { | ||||
private: | private: | ||||
PortWriteInterface& refPort; | |||||
PortInterface& refPort; | |||||
const uint8_t pin; //bit pattern, 1 is IOE pin to LED | const uint8_t pin; //bit pattern, 1 is IOE pin to LED | ||||
public: | public: | ||||
LED_IOE(PortWriteInterface& refPort, const uint8_t pin) | |||||
LED_IOE(PortInterface& refPort, const uint8_t pin) | |||||
: refPort(refPort), pin(pin) {} | : refPort(refPort), pin(pin) {} | ||||
virtual void on(); | virtual void on(); | ||||
virtual void off(); | virtual void off(); |
#include "LED_PCA9655E.h" | |||||
void LED_PCA9655E::on() | |||||
{ | |||||
refPort.write(pin, HIGH); | |||||
} | |||||
void LED_PCA9655E::off() | |||||
{ | |||||
refPort.write(pin, LOW); | |||||
} |
#ifndef LED_PCA9655E_H | |||||
#define LED_PCA9655E_H | |||||
#include <Arduino.h> | |||||
#include <inttypes.h> | |||||
#include <Wire.h> | |||||
#include <LED.h> | |||||
#include <PortWrite_PCA9655E.h> | |||||
/* A LED_PCA9655E object is an PCA9655E pin that is connected to an LED indicator light. | |||||
Input/Ouput Direction configuration are set to ouput in PortWrite_PCA9655E.begin() and PortRead_PCA9655E.begin(). | |||||
*/ | |||||
class LED_PCA9655E: public LED | |||||
{ | |||||
private: | |||||
//PortIOE& port; | |||||
//const uint8_t outputByteCommand; //General Purpose Input/Ouput register address | |||||
PortWrite_PCA9655E& refPort; | |||||
const uint8_t pin; //bit pattern, IOE pin to LED | |||||
public: | |||||
LED_PCA9655E(PortWrite_PCA9655E& refPort, const uint8_t pin) | |||||
: refPort(refPort), pin(pin) {} | |||||
virtual void on(); | |||||
virtual void off(); | |||||
}; | |||||
#endif |
#ifndef PORTREADINTERFACE_H | |||||
#define PORTREADINTERFACE_H | |||||
#ifndef PORTINTERFACE_H | |||||
#define PORTINTERFACE_H | |||||
#include <Arduino.h> | #include <Arduino.h> | ||||
#include <inttypes.h> | #include <inttypes.h> | ||||
/* | /* | ||||
Port classes are the keybrd library's interface to microcontroller ports or I/O expander ports. | Port classes are the keybrd library's interface to microcontroller ports or I/O expander ports. | ||||
*/ | */ | ||||
class PortReadInterface | |||||
class PortInterface | |||||
{ | { | ||||
public: | public: | ||||
virtual void begin(const uint8_t strobeOn)=0; | virtual void begin(const uint8_t strobeOn)=0; | ||||
virtual void write(const uint8_t strobePin, const bool pinLogicLevel)=0; | |||||
virtual uint8_t read()=0; | virtual uint8_t read()=0; | ||||
}; | }; | ||||
#endif | #endif |
return portState; | return portState; | ||||
} | } | ||||
/* begin() is called from Scanner_IOE::begin(). | |||||
Initiates SPI bus and configures I/O pins for read and write. | |||||
strobeOn is logic level of strobe on, HIGH or LOW | |||||
MCP23S17 SPI interface is 10 MHz max. | |||||
The electrical limitation to bus speed is bus capacitance and the length of the wires involved. | |||||
Longer wires require lower clock speeds. | |||||
*/ | |||||
void PortMCP23S17::begin(const uint8_t strobeOn) | |||||
{ | |||||
uint8_t pullUp; //bits, GPPU 0=pull-up disabled, 1=pull-up enabled | |||||
pinMode(SS, OUTPUT); //configure controller's Slave Select pin to output | |||||
digitalWrite(SS, HIGH); //disable Slave Select | |||||
SPI.begin(); | |||||
SPI.beginTransaction(SPISettings (5000000, MSBFIRST, SPI_MODE0)); //control SPI bus, 5 MHz | |||||
//SPI.endTransaction() not called to release SPI bus because keyboard only has one SPI device | |||||
if (strobeOn == LOW) //if active low, use internal pull-up resistors | |||||
{ | |||||
pullUp = readPins; | |||||
} | |||||
else //active high requires external pull-down resistors | |||||
{ | |||||
pullUp = 0; | |||||
} | |||||
//todo | |||||
Keyboard.print(" strobeOn="); Keyboard.print(strobeOn); | |||||
Keyboard.print(" readPins="); Keyboard.print(readPins, BIN); | |||||
Keyboard.print(" pullUp="); Keyboard.println(pullUp, BIN); | |||||
transfer(port.DEVICE_ADDR << 1, port.num, readPins); //configure IODIR | |||||
transfer(port.DEVICE_ADDR << 1, port.num + 0x0C, pullUp); //configure GPPU | |||||
} | |||||
/* write() sets pin output to logicLevel (useful for strobePin, one LED pin, or multiple pins). | |||||
pin is bit pattern, where pin being set is 1. | |||||
logicLevel is HIGH or LOW. | |||||
write() does not overwrite the other pins. | |||||
*/ | |||||
void PortMCP23S17::write(const uint8_t pin, const bool logicLevel) | |||||
{ | |||||
if (logicLevel == LOW) | |||||
{ | |||||
port.outputVal &= ~pin; //set pin output to low | |||||
} | |||||
else | |||||
{ | |||||
port.outputVal |= pin; //set pin output to high | |||||
} | |||||
//todo | |||||
//Keyboard.print(" readPins="); Keyboard.print(readPins, BIN); | |||||
Keyboard.print(" pin="); Keyboard.print(pin, BIN); | |||||
Keyboard.print(" logicLevel="); Keyboard.print(logicLevel); | |||||
Keyboard.print(" outputVal="); Keyboard.println(port.outputVal, BIN); | |||||
//Keyboard.print(" ="); Keyboard.print(); | |||||
//delay(200); | |||||
transfer(port.DEVICE_ADDR << 1, port.num + 0x12, port.outputVal); //set GPIO port to outputVal | |||||
} | |||||
/* read() returns portState. Only portState pins with pull resistors are valid. | |||||
*/ | |||||
uint8_t PortMCP23S17::read() | |||||
{ | |||||
return transfer( (port.DEVICE_ADDR << 1) | 1, port.num + 0x12, 0); //read from GPIO | |||||
} |
#include <Arduino.h> | #include <Arduino.h> | ||||
#include <inttypes.h> | #include <inttypes.h> | ||||
#include <SPI.h> | #include <SPI.h> | ||||
#include <PortIOE.h> | |||||
#include <PortInterface.h> | |||||
class PortMCP23S17 | |||||
/* | |||||
readPins are connected to matrix col | |||||
write pin is connected to matrix Row (strobe pin) or LED. | |||||
Slave Select is hardcoded to Arduino Pin 10. | |||||
Arduino Pin 10 avoids the speed penalty of digitalWrite. | |||||
Instantiation | |||||
------------ | |||||
readPins parameter is configures port's pins. | |||||
Example instantiation: | |||||
const uint8_t PortIOE::DEVICE_ADDR = 0x20; //MCP23S17 address, all 3 ADDR pins are grounded | |||||
PortIOE port_B(1); | |||||
Port_MCP23S17 portWrite_B(port_B, 0); //all pins are set to output for strobes and LEDs | |||||
PortIOE port_A(0); | |||||
Port_MCP23S17 portRead_A(port_A, 1<<0 | 1<<1 ); //pins 0,1 are set to input for reading, | |||||
//remaining pins can be used for LEDs | |||||
Diode orientation | |||||
---------------- | |||||
Diode orientation is explained in keybrd_library_user_guide.md > Diode orientation | |||||
MCP23S17 data sheet | |||||
------------------ | |||||
http://www.onsemi.com/pub_link/Collateral/MCP23S17-D.PDF | |||||
*/ | |||||
class PortMCP23S17 : public PortInterface | |||||
{ | { | ||||
protected: | |||||
private: | |||||
PortIOE& port; | |||||
const uint8_t readPins; //bits, IODIR 0=output, 1=input | |||||
uint8_t transfer(const uint8_t command, const uint8_t registerAddr, const uint8_t data); | uint8_t transfer(const uint8_t command, const uint8_t registerAddr, const uint8_t data); | ||||
public: | |||||
PortMCP23S17(PortIOE& port, const uint8_t readPins) : port(port), readPins(readPins) {} | |||||
void begin(const uint8_t strobeOn); | |||||
virtual void write(const uint8_t pin, const bool logicLevel); | |||||
virtual uint8_t read(); | |||||
}; | }; | ||||
#endif | #endif |
#include "PortRead_MCP23S17.h" | |||||
/* begin() is called from Scanner_IOE::begin(). | |||||
Configures read pins to input with pullup enabled. | |||||
*/ | |||||
void PortRead_MCP23S17::begin(const uint8_t strobeOn) | |||||
{ | |||||
if (strobeOn == LOW) //if active low, use internal pull-up resistors | |||||
{ | |||||
pullUp = readPins; | |||||
} | |||||
else //active high requires external pull-down resistors | |||||
{ | |||||
pullUp = 0; | |||||
} | |||||
transfer(port.DEVICE_ADDR << 1, port.num, readPins); //write, configure IODIR, 0=output, 1=input | |||||
transfer(port.DEVICE_ADDR << 1, port.num + 0x0C, pullUp); //write, configure GPPU, | |||||
//0=pull-up disabled, 1=pull-up enabled | |||||
} | |||||
/* read() returns portState. Only portState pins with pull resistors are valid. | |||||
*/ | |||||
uint8_t PortRead_MCP23S17::read() | |||||
{ | |||||
return transfer( (port.DEVICE_ADDR << 1) | 1, port.num + 0x12, 0); //read from GPIO | |||||
} |
#ifndef PORTREAD_MCP23S17_H | |||||
#define PORTREAD_MCP23S17_H | |||||
#include <Arduino.h> | |||||
#include <inttypes.h> | |||||
#include <SPI.h> | |||||
#include <PortReadInterface.h> | |||||
#include "PortMCP23S17.h" | |||||
#include "PortIOE.h" | |||||
#include "Scanner_IOE.h" | |||||
/* One MCP23S17 I/O expander port connected to matrix columns. | |||||
This class has Slave Select hardcoded to Arduino Pin 10. | |||||
Arduino Pin 10 avoids the speed penalty of digitalWrite. | |||||
Instantiation | |||||
------------ | |||||
readPins parameter is port's bit pattern pin configuration | |||||
1=configure as input (for read pins connected to column) | |||||
0=configure as output (for LED or not connected to a column) | |||||
readPins are read from pin 0 on up. | |||||
Example instantiation with port-A pins 0 and 1 connected to Scanner_IOE columns: | |||||
const uint8_t PortIOE::DEVICE_ADDR = 0x20; //MCP23S17 address, all 3 ADDR pins are grounded | |||||
PortIOE port_A(0); | |||||
PortRead_MCP23S17 portRead_A(port_A, 1<<0 | 1<<1 ); | |||||
Diode orientation | |||||
---------------- | |||||
Diode orientation is explained in keybrd_library_user_guide.md > Diode orientation | |||||
MCP23S17 data sheet | |||||
------------------ | |||||
http://www.onsemi.com/pub_link/Collateral/MCP23S17-D.PDF | |||||
*/ | |||||
class PortRead_MCP23S17 : public PortReadInterface, public PortMCP23S17 | |||||
{ | |||||
private: | |||||
PortIOE& port; | |||||
uint8_t pullUp; //bits, 1 means internal pull-up resistor enabled | |||||
const uint8_t readPins; //bits, 1 means internal pull-up resistor enabled | |||||
public: | |||||
PortRead_MCP23S17(PortIOE& port, const uint8_t readPins) | |||||
: port(port), readPins(readPins) {} | |||||
void begin(const uint8_t strobeOn); | |||||
virtual uint8_t read(); | |||||
}; | |||||
#endif |
#include "PortRead_PCA9655E.h" | |||||
/* begin() is called from Scanner_IOE::begin(). | |||||
Configures read pins to input. | |||||
*/ | |||||
void PortRead_PCA9655E::begin(const uint8_t strobeOn) | |||||
{ | |||||
Wire.beginTransmission(port.DEVICE_ADDR); | |||||
Wire.write(port.num + 6); //configuration byte command | |||||
Wire.write(readPins); //0=output (for LED), 1=input (for read) | |||||
Wire.endTransmission(); | |||||
} | |||||
/* read() returns portState. | |||||
Only portState bits of readPins are valid. | |||||
*/ | |||||
uint8_t PortRead_PCA9655E::read() | |||||
{ | |||||
Wire.beginTransmission(port.DEVICE_ADDR); | |||||
Wire.write(port.num); //input byte command | |||||
Wire.endTransmission(false); //PCA9655E needs false to send a restart | |||||
Wire.requestFrom(port.DEVICE_ADDR, 1u); //request one byte from input port | |||||
return Wire.read(); | |||||
} |
#ifndef PORTREAD_PCA9655E_H | |||||
#define PORTREAD_PCA9655E_H | |||||
#include <Arduino.h> | |||||
#include <inttypes.h> | |||||
#include <Wire.h> | |||||
#include <PortReadInterface.h> | |||||
#include "PortIOE.h" | |||||
/* One PCA9655E I/O expander port connected to matrix columns. | |||||
PCA9655E does not have internal pull-up resistors (PCA9535E does). | |||||
Instantiation | |||||
------------ | |||||
readPins parameter is bit pattern for port's pin configuration | |||||
1=configure as input (for pins connected to column) | |||||
0=configure as output (for LED or not connected to a column) | |||||
readPins are read from pin 0 on up. | |||||
Example instantiation for column port 1, with pins 2 and 3 connected to columns: | |||||
const uint8_t PortIOE::DEVICE_ADDR = 0x20; //PCA9655E address, all 3 ADDR pins are grounded | |||||
PortIOE port1(1); | |||||
PortRead_PCA9655E colPort1(port1, 1<<2 | 1<<3 ); | |||||
Diode orientation | |||||
---------------- | |||||
Diode orientation is explained in keybrd_library_user_guide.md > Diode orientation | |||||
PCA9655E data sheet | |||||
---------------- | |||||
http://www.onsemi.com/pub_link/Collateral/PCA9655E-D.PDF | |||||
*/ | |||||
class PortRead_PCA9655E : public PortReadInterface | |||||
{ | |||||
private: | |||||
PortIOE& port; | |||||
const uint8_t readPins; //bit pattern, pin configuration, 1 means read pin | |||||
public: | |||||
PortRead_PCA9655E (PortIOE& port, const uint8_t readPins) | |||||
: port(port), readPins(readPins) {} | |||||
void begin(const uint8_t strobeOn); | |||||
virtual uint8_t read(); | |||||
}; | |||||
#endif |
#ifndef PORTWRITEINTERFACE_H | |||||
#define PORTWRITEINTERFACE_H | |||||
#include <Arduino.h> | |||||
#include <inttypes.h> | |||||
/* | |||||
Port classes are the keybrd library's interface to microcontroller ports or I/O expander ports. | |||||
*/ | |||||
class PortWriteInterface | |||||
{ | |||||
public: | |||||
virtual void begin()=0; | |||||
virtual void write(const uint8_t strobePin, const bool pinLogicLevel)=0; | |||||
}; | |||||
#endif |
#include "PortWrite_MCP23S17.h" | |||||
/* begin() is called from Scanner_IOE::begin(). | |||||
Initiates SPI bus and configures port pins to output. | |||||
MCP23S17 SPI interface is 10 MHz max. | |||||
The electrical limitation to bus speed is bus capacitance and the length of the wires involved. | |||||
Longer wires require lower clock speeds. | |||||
*/ | |||||
void PortWrite_MCP23S17::begin() | |||||
{ | |||||
pinMode(SS, OUTPUT); //configure controller's Slave Select pin to output | |||||
digitalWrite(SS, HIGH); //disable Slave Select | |||||
SPI.begin(); | |||||
SPI.beginTransaction(SPISettings (5000000, MSBFIRST, SPI_MODE0)); //control SPI bus, 5 MHz | |||||
//SPI.endTransaction() not called to release SPI bus because keyboard only has one SPI device. | |||||
transfer(port.DEVICE_ADDR << 1, port.num, 0); //configure port direction (port.num) to output (0) | |||||
} | |||||
/* write() sets pin output to logicLevel. | |||||
pin is bit pattern, where pin being set is 1. | |||||
logicLevel is HIGH or LOW. | |||||
write() does not overwrite the other pins. | |||||
*/ | |||||
void PortWrite_MCP23S17::write(const uint8_t pin, const bool logicLevel) | |||||
{ | |||||
if (logicLevel == LOW) | |||||
{ | |||||
port.outputVal &= ~pin; //set pin output to low | |||||
} | |||||
else | |||||
{ | |||||
port.outputVal |= pin; //set pin output to high | |||||
} | |||||
transfer(port.DEVICE_ADDR << 1, port.num + 0x12, port.outputVal); //set GPIO port to outputVal | |||||
} |
#ifndef PORTWRITE_MCP23S17_H | |||||
#define PORTWRITE_MCP23S17_H | |||||
#include <Arduino.h> | |||||
#include <inttypes.h> | |||||
#include <SPI.h> | |||||
#include <PortWriteInterface.h> | |||||
#include "PortMCP23S17.h" | |||||
#include "PortIOE.h" | |||||
/* One MCP23S17 I/O expander port connected to matrix rows. | |||||
write() can output logiclevel to strobePin, one LED pin, or multiple pins. | |||||
This class has Slave Select hardcoded to Arduino Pin 10. | |||||
Arduino Pin 10 avoids the speed penalty of digitalWrite. | |||||
Instantiation | |||||
------------ | |||||
Example instantiation: | |||||
const uint8_t PortIOE::DEVICE_ADDR = 0x20; //MCP23S17 address, all 3 ADDR pins are grounded | |||||
PortIOE port_B(1); | |||||
PortWrite_MCP23S17 portWrite_B(port_B); | |||||
Diode orientation | |||||
---------------- | |||||
Diode orientation is explained in keybrd_library_user_guide.md > Diode orientation | |||||
MCP23S17 data sheet | |||||
------------------ | |||||
http://www.onsemi.com/pub_link/Collateral/MCP23S17-D.PDF | |||||
*/ | |||||
class PortWrite_MCP23S17 : public PortWriteInterface, public PortMCP23S17 | |||||
{ | |||||
private: | |||||
PortIOE& port; | |||||
public: | |||||
PortWrite_MCP23S17(PortIOE& port) : port(port) {} | |||||
void begin(); | |||||
virtual void write(const uint8_t pin, const bool logicLevel); | |||||
}; | |||||
#endif |
#include "PortWrite_PCA9655E.h" | |||||
/* begin() is called from Scanner_IOE::begin(). | |||||
Initiates I2C bus and configures port pins to output. | |||||
PCA9655E supports I2C SCL Clock Frequencies: 100 kHz, 400 kHz, 1000 kHz (Datasheet page 1 & 6) | |||||
The electrical limitation to bus speed is bus capacitance and the length of the wires involved. | |||||
Longer wires require lower clock speeds. | |||||
http://playground.arduino.cc/Main/WireLibraryDetailedReference > Wire.setclock() | |||||
*/ | |||||
void PortWrite_PCA9655E::begin() | |||||
{ | |||||
Wire.begin(); //initiate I2C bus to 100 kHz | |||||
//Wire.setClock(400000L); //set I2C bus to 400 kHz (have not tested 400 kHz) | |||||
Wire.beginTransmission(port.DEVICE_ADDR); | |||||
Wire.write(port.num + 6); //configuration byte command | |||||
Wire.write(0); //configure all pins as output | |||||
Wire.endTransmission(); | |||||
} | |||||
/* write() sets pin output to logicLevel. | |||||
pin is bit pattern, where pin being strobed is 1. | |||||
logicLevel is HIGH or LOW. | |||||
write() does not overwrite the other pins. | |||||
*/ | |||||
void PortWrite_PCA9655E::write(const uint8_t pin, const bool logicLevel) | |||||
{ | |||||
if (logicLevel == LOW) //if pin low | |||||
{ | |||||
port.outputVal &= ~pin; //set pin output to low | |||||
} | |||||
else //if strobestrobe high | |||||
{ | |||||
port.outputVal |= pin; //set pin output to high | |||||
} | |||||
Wire.beginTransmission(port.DEVICE_ADDR); | |||||
Wire.write(port.num + 2); //output Byte command | |||||
Wire.write(port.outputVal); | |||||
Wire.endTransmission(); | |||||
} |
#ifndef PORTWRITE_PCA9655E_H | |||||
#define PORTWRITE_PCA9655E_H | |||||
#include <Arduino.h> | |||||
#include <inttypes.h> | |||||
#include <Wire.h> | |||||
#include <PortWriteInterface.h> | |||||
#include "PortIOE.h" | |||||
/* One PCA9655E I/O expander port connected to matrix rows. | |||||
write() can output logiclevel to strobePin, one LED pin, or multiple pins. | |||||
Instantiation | |||||
------------ | |||||
Example instantiation: | |||||
const uint8_t PortIOE::DEVICE_ADDR = 0x20; //PCA9655E address, all 3 ADDR pins are grounded | |||||
PortIOE port0(0); | |||||
PortWrite_PCA9655E rowPort0(port0); | |||||
Diode orientation | |||||
---------------- | |||||
Diode orientation is explained in keybrd_library_user_guide.md > Diode orientation | |||||
PCA9655E data sheet | |||||
---------------- | |||||
http://www.onsemi.com/pub_link/Collateral/PCA9655E-D.PDF | |||||
*/ | |||||
class PortWrite_PCA9655E : public PortWriteInterface | |||||
{ | |||||
private: | |||||
PortIOE& port; | |||||
public: | |||||
PortWrite_PCA9655E(PortIOE& port) : port(port) {} | |||||
void begin(); | |||||
virtual void write(const uint8_t pin, const bool logicLevel); | |||||
}; | |||||
#endif |
*/ | */ | ||||
void Scanner_IOE::begin() | void Scanner_IOE::begin() | ||||
{ | { | ||||
refPortWrite.begin(); | |||||
refPortRead.begin(strobeOn); | refPortRead.begin(strobeOn); | ||||
} | } | ||||
{ | { | ||||
uint8_t readState; //bits, 1 means key is pressed, 0 means released | uint8_t readState; //bits, 1 means key is pressed, 0 means released | ||||
delay(2000);//todo | |||||
//strobe on | //strobe on | ||||
refPortWrite.write(strobePin, strobeOn); | refPortWrite.write(strobePin, strobeOn); | ||||
delayMicroseconds(3); //time to stabilize voltage | delayMicroseconds(3); //time to stabilize voltage | ||||
//delayMicroseconds(300); //todo | //delayMicroseconds(300); //todo | ||||
delay(2000); | |||||
//read the port pins | //read the port pins | ||||
readState = refPortRead.read(); | readState = refPortRead.read(); | ||||
#include <Arduino.h> | #include <Arduino.h> | ||||
#include <inttypes.h> | #include <inttypes.h> | ||||
#include <ScannerInterface.h> | #include <ScannerInterface.h> | ||||
#include <PortWriteInterface.h> | |||||
#include <PortReadInterface.h> | |||||
#include <PortInterface.h> | |||||
/* Scanner_IOE uses bit manipulation to read all pins of one port. | /* Scanner_IOE uses bit manipulation to read all pins of one port. | ||||
The maximum keys per row is 8, because ports have a maximum of 8 pins each. | The maximum keys per row is 8, because ports have a maximum of 8 pins each. | ||||
private: | private: | ||||
const bool strobeOn; //logic level of strobe on, HIGH or LOW | const bool strobeOn; //logic level of strobe on, HIGH or LOW | ||||
const bool strobeOff; //logic level of strobe off, complement of strobeOn | const bool strobeOff; //logic level of strobe off, complement of strobeOn | ||||
PortWriteInterface& refPortWrite; //the IC port containing the strobePin | |||||
PortReadInterface& refPortRead; //the IC's read port | |||||
PortInterface& refPortWrite; //the IC port containing the strobePin | |||||
PortInterface& refPortRead; //the IC's read port | |||||
public: | public: | ||||
Scanner_IOE(const bool strobeOn, | Scanner_IOE(const bool strobeOn, | ||||
PortWriteInterface &refPortWrite, PortReadInterface& refPortRead) | |||||
PortInterface &refPortWrite, PortInterface& refPortRead) | |||||
: strobeOn(strobeOn), strobeOff(!strobeOn), | : strobeOn(strobeOn), strobeOff(!strobeOn), | ||||
refPortWrite(refPortWrite), refPortRead(refPortRead) {} | refPortWrite(refPortWrite), refPortRead(refPortRead) {} | ||||
void init(const uint8_t strobePin); | void init(const uint8_t strobePin); |
#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> | |||||
#include <PortWriteInterface.h> | |||||
#include <PortReadInterface.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> | |||||
#include <PortWriteInterface.h> | |||||
#include <PortReadInterface.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 |
//right matrix | //right matrix | ||||
#include <PortIOE.h> | #include <PortIOE.h> | ||||
#include <PortWrite_MCP23S17.h> | |||||
#include <PortRead_MCP23S17.h> | |||||
#include <PortMCP23S17.h> | |||||
#include <Scanner_IOE.h> | #include <Scanner_IOE.h> | ||||
#include <LED_IOE.h> | #include <LED_IOE.h> | ||||
const uint8_t PortIOE::DEVICE_ADDR = 0x20; //MCP23S17 address with all 3 ADDR pins are grounded | const uint8_t PortIOE::DEVICE_ADDR = 0x20; //MCP23S17 address with all 3 ADDR pins are grounded | ||||
PortIOE port_A(0); | PortIOE port_A(0); | ||||
PortRead_MCP23S17 portRead(port_A, 1<<0 | 1<<1 ); | |||||
PortWrite_MCP23S17 portWriteA(port_A); //for LED | |||||
//todo portWriteA(port_A) instantiation would not be needed if PortRead_MCP23S17 had write() | |||||
// consider moving PortWrite_MCP23S17::write to Port_MCP23S17 (parent) | |||||
// and passing portRead to LED_IOE | |||||
// same for PCA9655E | |||||
PortMCP23S17 portRead(port_A, 1<<0 | 1<<1 ); | |||||
PortIOE port_B(1); | PortIOE port_B(1); | ||||
PortWrite_MCP23S17 portWrite(port_B); | |||||
PortMCP23S17 portWrite(port_B, 0); | |||||
Scanner_IOE scanner_R(LOW, portWrite, portRead); | Scanner_IOE scanner_R(LOW, portWrite, portRead); | ||||
// ================ RIGHT LEDs ================= | // ================ RIGHT LEDs ================= | ||||
LED_IOE LED_CapsLck(portWriteA, 1<<6); //tested LED on port A (read) | |||||
//LED_IOE LED_CapsLck(portRead, 1<<6); //tested LED on port A (read) | |||||
//LED_IOE LED_CapsLck(portWrite, 1<<6);//tested LED on port B (write) | //LED_IOE LED_CapsLck(portWrite, 1<<6);//tested LED on port B (write) | ||||
// =================== CODES =================== | // =================== CODES =================== | ||||
Code_Sc s_2(KEY_2); | Code_Sc s_2(KEY_2); | ||||
Code_Sc s_3(KEY_3); | Code_Sc s_3(KEY_3); | ||||
Code_LEDLock o_capsLock(KEY_CAPS_LOCK, LED_CapsLck); | |||||
Code_Sc o_capsLock(KEY_4); | |||||
//Code_LEDLock o_capsLock(KEY_CAPS_LOCK, LED_CapsLck); | |||||
// =================== ROWS ==================== | // =================== ROWS ==================== | ||||
// ---------------- LEFT ROWS ------------------ | // ---------------- LEFT ROWS ------------------ | ||||
void setup() | void setup() | ||||
{ | { | ||||
Keyboard.begin(); | Keyboard.begin(); | ||||
delay(7000); | |||||
scanner_R.begin(); | scanner_R.begin(); | ||||
} | } | ||||