/* keybrd_PCA9655E.ino | /* keybrd_PCA9655E.ino | ||||
Teensy 2.0 controller PCA9655E I/O expander | |||||
Controller I/O expander | |||||
| Left | **0** | **1** | | Right | **0** | **1** | | | Left | **0** | **1** | | Right | **0** | **1** | | ||||
|:-----:|-------|-------| |:-----:|-------|-------| | |:-----:|-------|-------| |:-----:|-------|-------| | ||||
| **1** | 1 | 2 | | **1** | 3 | 4 | | | **1** | 1 | 2 | | **1** | 3 | 4 | | ||||
#include <Scanner_uC.h> | #include <Scanner_uC.h> | ||||
//right matrix | //right matrix | ||||
#include <PortIOE.h> | |||||
#include <PortWrite_PCA9655E.h> | |||||
#include <PortRead_PCA9655E.h> | |||||
#include <PortPCA9655E.h> | |||||
#include <Scanner_IOE.h> | #include <Scanner_IOE.h> | ||||
// ============ SPEED CONFIGURATION ============ | // ============ SPEED CONFIGURATION ============ | ||||
ScanDelay scanDelay(9000); | ScanDelay scanDelay(9000); | ||||
// ================ LEFT SCANNER =============== | // ================ LEFT SCANNER =============== | ||||
uint8_t readPins_L[] = {0, 1}; | |||||
uint8_t READPIN_COUNT_L = sizeof(readPins_L)/sizeof(*readPins_L); | |||||
uint8_t readPins[] = {0, 1}; | |||||
uint8_t readPinCount = sizeof(readPins)/sizeof(*readPins); | |||||
Scanner_uC scanner_L(HIGH, readPins_L, READPIN_COUNT_L); | |||||
Scanner_uC scanner_L(HIGH, readPins, readPinCount); | |||||
// =============== RIGHT SCANNER =============== | // =============== RIGHT SCANNER =============== | ||||
const uint8_t PortIOE::DEVICE_ADDR = 0x18; | |||||
const uint8_t IOE_ADDR = 0x18; | |||||
PortIOE port_1(1); | |||||
PortWrite_PCA9655E portWrite_1(port_1); | |||||
PortPCA9655E port1(IOE_ADDR, 1, 0); //for strobe | |||||
PortPCA9655E port0(IOE_ADDR, 0, 1<<0 | 1<<1 ); //for read | |||||
PortIOE port_0(0); | |||||
//PortWrite_PCA9655E portWrite_R0(port_0); //for LEDs | |||||
PortRead_PCA9655E portRead_0(port_0, 1<<0 | 1<<1 ); | |||||
Scanner_IOE scanner_R(HIGH, portWrite_1, portRead_0); | |||||
Scanner_IOE scanner_R(HIGH, port1, port0); | |||||
// =================== CODES =================== | // =================== CODES =================== | ||||
Code_Sc s_a(KEY_A); | Code_Sc s_a(KEY_A); |
#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 <PortPCA9655E.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 PortPCA9655E.begin() and PortRead_PCA9655E.begin(). | |||||
*/ | |||||
class LED_PCA9655E: public LED | |||||
{ | |||||
private: | |||||
PortPCA9655E& refPort; | |||||
const uint8_t pin; //bit pattern, IOE pin to LED | |||||
public: | |||||
LED_PCA9655E(PortPCA9655E& refPort, const uint8_t pin) : refPort(refPort), pin(pin) {} | |||||
virtual void on(); | |||||
virtual void off(); | |||||
}; | |||||
#endif |
#include "PortPCA9655E.h" | |||||
/* begin() is called from Scanner_IOE::begin(). Initiates I2C bus. | |||||
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 PortPCA9655E::beginProtocol() | |||||
{ | |||||
Wire.begin(); //initiate I2C bus to 100 kHz | |||||
//Wire.setClock(400000L); //set I2C bus to 400 kHz (have not tested 400 kHz) | |||||
} | |||||
/* begin() is called from Scanner_IOE::begin(). | |||||
Configures read pins to input. | |||||
strobeOn is not used because PCA9655E has no pull-up resistors. | |||||
*/ | |||||
void PortPCA9655E::begin(const uint8_t strobeOn) | |||||
{ | |||||
Wire.beginTransmission(deviceAddr); | |||||
Wire.write(portNum + 6); //configuration byte command | |||||
Wire.write(readPins); //0=output (for strobe and LED), 1=input (for read) | |||||
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 PortPCA9655E::write(const uint8_t pin, const bool logicLevel) | |||||
{ | |||||
if (logicLevel == LOW) | |||||
{ | |||||
outputVal &= ~pin; //set pin output to low | |||||
} | |||||
else | |||||
{ | |||||
outputVal |= pin; //set pin output to high | |||||
} | |||||
Wire.beginTransmission(deviceAddr); | |||||
Wire.write(portNum + 2); //output Byte command | |||||
Wire.write(outputVal); | |||||
Wire.endTransmission(); | |||||
} | |||||
/* read() returns portState. | |||||
Only portState bits of readPins are valid. | |||||
*/ | |||||
uint8_t PortPCA9655E::read() | |||||
{ | |||||
Wire.beginTransmission(deviceAddr); | |||||
Wire.write(portNum); //input byte command | |||||
Wire.endTransmission(false); //PCA9655E needs false to send a restart | |||||
Wire.requestFrom(deviceAddr, 1u); //request one byte from input port | |||||
return Wire.read(); | |||||
} |
#ifndef PORTPCA9655E_H | |||||
#define PORTPCA9655E_H | |||||
#include <Arduino.h> | |||||
#include <inttypes.h> | |||||
#include <Wire.h> | |||||
#include <PortInterface.h> | |||||
/* | |||||
write pins are connected to matrix Row (strobe pin) or LED. | |||||
readPins are connected to matrix column to read which keys are pressed. | |||||
Be careful with the deviceAddr. | |||||
Table 6 in PCA9655E datasheet lists 8-bit versions of I2C addresses. | |||||
The Arduino Wire library uses 7-bit addresses throughout, so drop the low bit. | |||||
For example, I2C address with AD2=GND AD1=SCL AD0=SCL, | |||||
Table 6 lists 8-bit DEVICE_ADDR = 0x30 (b 00110000) | |||||
while Arduino uses 7-bit DEVICE_ADDR = 0x18 (b 00011000) | |||||
http://playground.arduino.cc/Main/WireLibraryDetailedReference | |||||
Instantiation | |||||
------------ | |||||
Example instantiation: | |||||
const uint8_t IOE_ADDR = 0x20; //PCA9655E address, all 3 ADDR pins are grounded | |||||
PortPCA9655E portB(IOE_ADDR, 1, 0); //all pins are set to output for strobes and LEDs | |||||
PortPCA9655E portA(IOE_ADDR, 0, 1<<0 | 1<<1 ); //first two pins 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 | |||||
PCA9655E data sheet | |||||
---------------- | |||||
http://www.onsemi.com/pub_link/Collateral/PCA9655E-D.PDF | |||||
*/ | |||||
class PortPCA9655E : public PortInterface | |||||
{ | |||||
private: | |||||
const uint8_t deviceAddr; | |||||
const uint8_t portNum; //port identification number | |||||
uint8_t outputVal; //bit pattern for strobe and LEDs | |||||
const uint8_t readPins; //bit pattern, IODIR 0=output, 1=input | |||||
public: | |||||
PortPCA9655E(const uint8_t deviceAddr, const uint8_t portNum, const uint8_t readPins) | |||||
: deviceAddr(deviceAddr), portNum(portNum), outputVal(0), readPins(readPins) {} | |||||
void beginProtocol(); | |||||
void begin(const uint8_t strobeOn); | |||||
virtual void write(const uint8_t pin, const bool logicLevel); | |||||
virtual uint8_t read(); | |||||
}; | |||||
#endif |
This layout table shows left and right matrices: | This layout table shows left and right matrices: | ||||
Controller I/O expander | |||||
| Left | **0** | **1** | | Right | **0** | **1** | | | Left | **0** | **1** | | Right | **0** | **1** | | ||||
|:-----:|-------|-------|-|:-----:|-------|-------| | |:-----:|-------|-------|-|:-----:|-------|-------| | ||||
| **1** |CapsLck| a 1 | | **1** | b 2 | c 3 | | | **1** |CapsLck| a 1 | | **1** | b 2 | c 3 | |