/* simulate one scan of a key martix, on open-drain active-low I/O expander | |||||
BREADBOARD SETUP ******************************* | |||||
Teensy LC connected to MCP23018 I/O expander via I2C | |||||
4.7k Ohm pullup resistors on SDA and SCL | |||||
use volt meter to measure pin voltages | |||||
MCP23018 has open-drain outputs (open-drain can only sink current) | |||||
MCP23018 PIN DIAGRAM *************************** | |||||
write port B read port A | |||||
GND VSS 1 28 NC | |||||
NC 2 27 GPA7 | |||||
jumper0 GPB0 3 26 GPA6 | |||||
GPB1 4 25 GPA5 | |||||
GPB2 5 24 GPA4 jumper4 | |||||
GPB3 6 23 GPA3 | |||||
jumper4 GPB4 7 22 GPA2 | |||||
GPB5 8 21 GPA1 | |||||
GPB6 9 20 GPA0 jumper0 | |||||
GPB7 10 19 INTA | |||||
power VDD 11 18 INTB | |||||
SCL SCL 12 17 NC | |||||
SDA SDA 13 16 RESET power | |||||
NC 14 15 ADDR GND | |||||
*/ | |||||
#include "Wire.h" | |||||
const uint8_t ADDR = 0x20; //MCP23018 I2C address with ADDR pin grounded | |||||
void setup() | |||||
{ | |||||
delay(1000); //time for Serial print to work | |||||
// ================= configure ================ | |||||
Serial.print("config "); | |||||
Wire.begin(); | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(0x01); //IODIRB Configure | |||||
Wire.write(0); //as output | |||||
Wire.endTransmission(); | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(0x00); //IODIRA Configuration | |||||
Wire.write(~0); //as input | |||||
Wire.endTransmission(); | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(0x0C); //GPPUA pull-up | |||||
Wire.write(~0); //pull-up enabled | |||||
Wire.endTransmission(); | |||||
// =================== scan =================== | |||||
Serial.println("scan"); | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(0x13); //GPIOB output | |||||
Wire.write(B00001111); //pins 0-3 off, pins 4-7 sink on (strobe, LED on) | |||||
Wire.endTransmission(); | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(0x12); //GPIOA (immediately before requestFrom) | |||||
Wire.endTransmission(); | |||||
Wire.requestFrom(ADDR, static_cast<uint8_t>(1)); //request one byte from GPIOA read | |||||
Serial.print("portA="); | |||||
Serial.println(Wire.read(), BIN); //prints portA=11101111 | |||||
} | |||||
void loop() { } |
/* simulate one scan of a key martix, on source active-high I/O expander | |||||
BREADBOARD SETUP ******************************* | |||||
Teensy LC connected to MCP23017 I/O expander via I2C | |||||
10k Ohm external pulldown resistors on port A | |||||
4.7k Ohm pullup resistors on SDA and SCL | |||||
use volt meter to measure pin voltages | |||||
MCP23017 PIN DIAGRAM *************************** | |||||
write port B read port A | |||||
jumper0 GPB0 1 26 GPA7 pulldown | |||||
GPB1 2 25 GPA6 pulldown | |||||
GPB2 3 24 GPA5 pulldown | |||||
GPB3 4 23 GPA4 pulldown jumper4 | |||||
jumper4 GPB4 5 22 GPA3 pulldown | |||||
GPB5 6 21 GPA2 pulldown | |||||
GPB6 7 20 GPA1 pulldown | |||||
GPB7 8 19 GPA0 pulldown jumper0 | |||||
power VDD 9 18 INTA | |||||
GND VSS 10 28 INTB | |||||
NC 11 27 RESET power | |||||
SCL SCL 12 17 A2 gnd | |||||
SDA SDA 13 16 A1 gnd | |||||
NC 14 15 A0 gnd | |||||
*/ | |||||
#include "Wire.h" | |||||
const uint8_t ADDR = 0x20; //MCP23017 I2C address with all ADDR pins grounded | |||||
void setup() | |||||
{ | |||||
delay(1000); //time for Serial print to work | |||||
// ================= configure ================ | |||||
Serial.print("config "); | |||||
Wire.begin(); | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(0x01); //IODIRB Configure | |||||
Wire.write(0); //as output | |||||
Wire.endTransmission(); | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(0x00); //IODIRA Configuration | |||||
Wire.write(~0); //as input | |||||
Wire.endTransmission(); | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(0x0C); //GPPUA pull-up | |||||
Wire.write(0); //pull-up disabled | |||||
Wire.endTransmission(); | |||||
// =================== scan =================== | |||||
Serial.println("scan"); | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(0x13); //GPIOB output | |||||
Wire.write(B11110000); //pins 0-3 ground, pins 4-7 power (strobe, LED on) | |||||
Wire.endTransmission(); | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(0x12); //GPIOA (immediately before requestFrom) | |||||
Wire.endTransmission(); | |||||
Wire.requestFrom(ADDR, static_cast<uint8_t>(1)); //request one byte from GPIOA read | |||||
Serial.print("portA="); | |||||
Serial.println(Wire.read(), BIN); //prints portA=00010000 | |||||
} | |||||
void loop() { } |
/* simulate one scan of a key martix, on source active-low I/O expander | |||||
BREADBOARD SETUP ******************************* | |||||
Teensy LC connected to MCP23017 I/O expander via I2C | |||||
4.7k Ohm pullup resistors on SDA and SCL | |||||
use volt meter to measure pin voltages | |||||
MCP23017 PIN DIAGRAM *************************** | |||||
write port B read port A | |||||
jumper0 GPB0 1 26 GPA7 | |||||
GPB1 2 25 GPA6 | |||||
GPB2 3 24 GPA5 | |||||
GPB3 4 23 GPA4 jumper4 | |||||
jumper4 GPB4 5 22 GPA3 | |||||
GPB5 6 21 GPA2 | |||||
GPB6 7 20 GPA1 | |||||
GPB7 8 19 GPA0 jumper0 | |||||
power VDD 9 18 INTA | |||||
GND VSS 10 28 INTB | |||||
NC 11 27 RESET power | |||||
SCL SCL 12 17 A2 gnd | |||||
SDA SDA 13 16 A1 gnd | |||||
NC 14 15 A0 gnd | |||||
*/ | |||||
#include "Wire.h" | |||||
const uint8_t ADDR = 0x20; //MCP23017 I2C address with all ADDR pins grounded | |||||
void setup() | |||||
{ | |||||
delay(1000); //time for Serial print to work | |||||
// ================= configure ================ | |||||
Serial.print("config "); | |||||
Wire.begin(); | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(0x01); //IODIRB Configure | |||||
Wire.write(0); //as output | |||||
Wire.endTransmission(); | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(0x00); //IODIRA Configuration | |||||
Wire.write(~0); //as input | |||||
Wire.endTransmission(); | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(0x0C); //GPPUA pull-up | |||||
Wire.write(~0); //pull-up enabled | |||||
Wire.endTransmission(); | |||||
// =================== scan =================== | |||||
Serial.println("scan"); | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(0x13); //GPIOB output | |||||
Wire.write(B00001111); //pins 0-3 power (strobe, LED on), pins 4-7 ground | |||||
Wire.endTransmission(); | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(0x12); //GPIOA (immediately before requestFrom) | |||||
Wire.endTransmission(); | |||||
Wire.requestFrom(ADDR, static_cast<uint8_t>(1)); //request one byte from GPIOA read | |||||
Serial.print("portA="); | |||||
Serial.println(Wire.read(), BIN); //prints portA=11101111 | |||||
} | |||||
void loop() { } |
/* PCA9655E_1_write_read.ino | |||||
set port 1 pins | |||||
read and print value of port 0 | |||||
measure port 1 pin voltages with a multimeter | |||||
DESTINATION PIN PIN_NUMBER PIN DESTINATION | |||||
x INT 1 24 VDD Teensy LC 3.3V | |||||
SCL AD1 2 23 SDA Teensy LC 18 | |||||
GND AD2 3 22 SCL Teensy LC 19 | |||||
GND IO0_0 4 21 AD0 SCL | |||||
GND IO0_1 5 20 IO1_6 x | |||||
VDD IO0_2 6 19 IO1_5 x | |||||
VDD IO0_3 7 18 IO1_4 x | |||||
GND IO0_4 8 17 IO1_4 x | |||||
GND IO0_5 9 16 IO1_3 x | |||||
x IO0_6 10 15 IO1_2 x | |||||
x IO0_7 11 14 IO1_1 x | |||||
GND VSS 12 13 IO1_0 x | |||||
*/ | |||||
#include "Wire.h" | |||||
const uint8_t ADDR = 0x18; //I2C address with AD2=GND AD1=SCL AD0=SCL | |||||
void setup() | |||||
{ | |||||
delay(1000); | |||||
Serial.print("PCA9655E_read.ino"); | |||||
Wire.begin(); | |||||
//Configure port 1 to output | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(7); //configure direction | |||||
Wire.write(0); //0=output | |||||
Wire.endTransmission(); | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(3); //command byte 3 = Output Port 1 | |||||
Wire.write( 1<<2 | 1<<3); //1=high | |||||
Wire.endTransmission(); | |||||
//Configure port 0 to input | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(6); //command byte 6 = Configuration dir Port 0 | |||||
Wire.write(~0); //1=input | |||||
Wire.endTransmission(); | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(0); //command byte 0 = Input Port 0 | |||||
Wire.endTransmission(false); //PCA9655E needs false to send a restart | |||||
Wire.requestFrom(ADDR, 1u); //request one byte from input port | |||||
Serial.print(" port0_val= "); | |||||
uint8_t port0_val = Wire.read(); | |||||
Serial.print(port0_val, BIN); //expect xx001100 | |||||
} | |||||
void loop() { } |
/* PCA9655E_2_scan.ino | |||||
power IO0_2 and read IO1_1 (similar to strobe and read on key matrix) | |||||
DESTINATION PIN PIN_NUMBER PIN DESTINATION | |||||
x INT 1 24 VDD Teensy LC 3.3V | |||||
SCL AD1 2 23 SDA Teensy LC 18 | |||||
GND AD2 3 22 SCL Teensy LC 19 | |||||
GND IO0_0 4 21 AD0 SCL | |||||
GND IO0_1 5 20 IO1_6 x | |||||
IO1_1 IO0_2 6 19 IO1_5 x | |||||
GND IO0_3 7 18 IO1_4 x | |||||
GND IO0_4 8 17 IO1_4 x | |||||
GND IO0_5 9 16 IO1_3 x | |||||
x IO0_6 10 15 IO1_2 x | |||||
x IO0_7 11 14 IO1_1 IO0_2 | |||||
GND VSS 12 13 IO1_0 x | |||||
*/ | |||||
#include "Wire.h" | |||||
const uint8_t ADDR = 0x18; //I2C address with AD2=GND AD1=SCL AD0=SCL | |||||
void setup() | |||||
{ | |||||
delay(1000); | |||||
Serial.print("PCA9655E_read.ino"); | |||||
Wire.begin(); | |||||
//Configure port 1 to output | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(7); //command byte 7 = Configuration dir Port 1 | |||||
Wire.write(0); //0=output | |||||
Wire.endTransmission(); | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(3); //command byte 3 = Output Port 1 | |||||
Wire.write( 1<<1 ); //1=high | |||||
Wire.endTransmission(); | |||||
//Configure port 0 to input | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(6); //command byte 6 = Configuration dir Port 0 | |||||
Wire.write(~0); //1=input | |||||
Wire.endTransmission(); | |||||
Wire.beginTransmission(ADDR); | |||||
Wire.write(0); //command byte 0 = Input Port 0 | |||||
Wire.endTransmission(false); //PCA9655E needs false to send a restart | |||||
Wire.requestFrom(ADDR, 1u); //request one byte from input port | |||||
Serial.print(" port0_val= "); | |||||
uint8_t port0_val = Wire.read(); | |||||
Serial.print(port0_val, BIN); //expect xx000100 | |||||
} | |||||
void loop() { } |
/* PCA9655E_3_scan_lib.ino | |||||
does same as PCA9655E_2_scan.ino, but using keybrd library classes | |||||
pictures in PCA9655E_2_scan/ | |||||
set port 1 pins | |||||
read and print value of port 0 | |||||
measure port 1 pin voltages with a multimeter | |||||
DESTINATION PIN PIN_NUMBER PIN DESTINATION | |||||
x INT 1 24 VDD Teensy LC 3.3V | |||||
SCL AD1 2 23 SDA Teensy LC 18 | |||||
GND AD2 3 22 SCL Teensy LC 19 | |||||
GND IO0_0 4 21 AD0 SCL | |||||
GND IO0_1 5 20 IO1_6 x | |||||
IO1_1 IO0_2 6 19 IO1_5 x | |||||
GND IO0_3 7 18 IO1_4 x | |||||
GND IO0_4 8 17 IO1_4 x | |||||
GND IO0_5 9 16 IO1_3 x | |||||
x IO0_6 10 15 IO1_2 x | |||||
x IO0_7 11 14 IO1_1 IO0_2 strobe pin | |||||
GND VSS 12 13 IO1_0 x | |||||
*/ | |||||
#include <Port_PCA9655E.h> | |||||
#include <Scanner_IOE.h> | |||||
const uint8_t ADDR = 0x18; //I2C address with AD2=GND AD1=SCL AD0=SCL | |||||
Port_PCA9655E port0(ADDR, 0, ~0); //read all pins | |||||
Port_PCA9655E port1(ADDR, 1, 0); //for strobe | |||||
Scanner_IOE scanner_R(HIGH, port1, port0); | |||||
void setup() | |||||
{ | |||||
delay(6000); | |||||
Keyboard.print("PCA9655E_scan.ino"); | |||||
scanner_R.begin(); | |||||
Keyboard.print(" port0_val= "); | |||||
uint8_t port0_val = scanner_R.scan(1<<1); //strobe pin 1 | |||||
Keyboard.print(port0_val, BIN); //expect xx000100 | |||||
} | |||||
void loop() { } |
/* PCA9655E_4_scan_loop.ino | |||||
does same as PCA9655E_2_scan.ino, but using keybrd library classes | |||||
pictures in PCA9655E_2_scan/ | |||||
set port 1 pins | |||||
read and print value of port 0 | |||||
measure port 1 pin voltages with a multimeter | |||||
DESTINATION PIN PIN_NUMBER PIN DESTINATION | |||||
x INT 1 24 VDD Teensy LC 3.3V | |||||
SCL AD1 2 23 SDA Teensy LC 18 | |||||
GND AD2 3 22 SCL Teensy LC 19 | |||||
GND IO0_0 4 21 AD0 SCL | |||||
GND IO0_1 5 20 IO1_6 x | |||||
IO1_1 IO0_2 6 19 IO1_5 x | |||||
GND IO0_3 7 18 IO1_4 x | |||||
GND IO0_4 8 17 IO1_4 x | |||||
GND IO0_5 9 16 IO1_3 x | |||||
x IO0_6 10 15 IO1_2 x | |||||
x IO0_7 11 14 IO1_1 IO0_2 strobe pin | |||||
GND VSS 12 13 IO1_0 x | |||||
*/ | |||||
#include <Port_PCA9655E.h> | |||||
#include <Scanner_IOE.h> | |||||
const uint8_t ADDR = 0x18; //I2C address with AD2=GND AD1=SCL AD0=SCL | |||||
Port_PCA9655E port0(ADDR, 0, ~0); //read all pins | |||||
Port_PCA9655E port1(ADDR, 1, 0); //for strobe | |||||
Scanner_IOE scanner_R(HIGH, port1, port0); | |||||
void setup() | |||||
{ | |||||
delay(6000); | |||||
Keyboard.println("PCA9655E_scan_loop.ino"); | |||||
scanner_R.begin(); | |||||
} | |||||
uint8_t port0_val; //bit pattern | |||||
uint8_t errorCount = 0; | |||||
int loopCount = 0; | |||||
int wait = 0; //delayMicroseconds | |||||
void status() | |||||
{ | |||||
Keyboard.print(" loopCount="); | |||||
Keyboard.print(loopCount); | |||||
Keyboard.print(" wait="); | |||||
Keyboard.println(wait); | |||||
}; | |||||
void loop() | |||||
{ | |||||
port0_val = scanner_R.scan(1<<1); //strobe pin 1 | |||||
if ( (B00111111 & port0_val) != B00000100 ) //expect xx000100, where xx float | |||||
{ | |||||
Keyboard.print("port0_val="); | |||||
Keyboard.print(port0_val, BIN); | |||||
errorCount++; | |||||
status(); | |||||
} | |||||
loopCount++; | |||||
if (loopCount > 32000 || errorCount > 10) | |||||
{ | |||||
Keyboard.print("stop errorCount="); | |||||
Keyboard.print(errorCount); | |||||
status(); | |||||
while(1); | |||||
} | |||||
if (loopCount % 1000 == 0) | |||||
{ | |||||
Keyboard.print(" progress"); | |||||
status(); | |||||
} | |||||
if (loopCount > 22000) | |||||
{ | |||||
wait++; //test other delays | |||||
} | |||||
delayMicroseconds(wait); | |||||
} |
/* keybrd_PCA9655E.ino | /* keybrd_PCA9655E.ino | ||||
keyboard layout is same as top-left keys of DH matrices: | |||||
Controller I/O expander | Controller I/O expander | ||||
| Left | **0** | **1** | | Right | **0** | **1** | | |||||
| Left | **0** | **1** | | Right | **2** | **3** | | |||||
|:-----:|-------|-------| |:-----:|-------|-------| | |:-----:|-------|-------| |:-----:|-------|-------| | ||||
| **1** | 1 | 2 | | **1** | 3 | 4 | | |||||
| **0** | a | b | | **0** | c | d | | |||||
| **1** | q | w | | **1** | u | i | | |||||
| **0** | [ | b | | **0** | h | y | | |||||
PCA9655E pin assignments are compatible with this sketch and keybrd_DH.ino | |||||
DESTINATION PIN PIN_NUMBER PIN DESTINATION | |||||
x INT 1 24 VDD Teensy LC 3.3V | |||||
SCL AD1 2 23 SDA Teensy LC 18 | |||||
GND AD2 3 22 SCL Teensy LC 19 | |||||
GND IO0_0 4 21 AD0 SCL | |||||
GND IO0_1 5 20 IO1_6 x | |||||
col2 IO0_2 6 19 IO1_5 x | |||||
col3 IO0_3 7 18 IO1_4 x | |||||
GND IO0_4 8 17 IO1_4 x | |||||
GND IO0_5 9 16 IO1_3 x | |||||
x IO0_6 10 15 IO1_2 x | |||||
x IO0_7 11 14 IO1_1 row1 | |||||
GND VSS 12 13 IO1_0 row2 | |||||
*/ | */ | ||||
// ################## GLOBAL ################### | // ################## GLOBAL ################### | ||||
// ================= INCLUDES ================== | // ================= INCLUDES ================== | ||||
Scanner_uC scanner_L(HIGH, readPins, readPinCount); | Scanner_uC scanner_L(HIGH, readPins, readPinCount); | ||||
// =============== RIGHT SCANNER =============== | // =============== RIGHT SCANNER =============== | ||||
const uint8_t IOE_ADDR = 0x18; | |||||
const uint8_t IOE_ADDR = 0x18; //AD2=GND AD1=SCL AD0=SCL | |||||
Port_PCA9655E port1(IOE_ADDR, 1, 0); //for strobe | |||||
Port_PCA9655E port0(IOE_ADDR, 0, 1<<0 | 1<<1 ); //for read | |||||
Port_PCA9655E port0(IOE_ADDR, 0, 1<<0 | 1<<1 | 1<<2 | 1<<3 | 1<<4 | 1<<5 ); //for read | |||||
Port_PCA9655E port1(IOE_ADDR, 1, 0); //for strobe | |||||
Scanner_IOE scanner_R(HIGH, port1, port0); | Scanner_IOE scanner_R(HIGH, port1, port0); | ||||
// =================== CODES =================== | // =================== 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_B(KEY_B); | |||||
Code_Sc s_I(KEY_I); | |||||
Code_Sc s_H(KEY_H); | |||||
Code_Sc s_Q(KEY_Q); | |||||
Code_Sc s_W(KEY_W); | |||||
Code_Sc s_U(KEY_U); | |||||
Code_Sc s_Y(KEY_Y); | |||||
Code_Sc s_0(KEY_0); | |||||
Code_Sc s_1(KEY_1); | 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_leftBracket(KEY_LEFT_BRACE); | |||||
// =================== ROWS ==================== | // =================== ROWS ==================== | ||||
// ---------------- LEFT ROWS ------------------ | // ---------------- LEFT ROWS ------------------ | ||||
Key* ptrsKeys_L0[] = { &s_1, &s_2 }; | |||||
Key* ptrsKeys_L0[] = { &s_Q, &s_W }; | |||||
uint8_t KEY_COUNT_L0 = sizeof(ptrsKeys_L0)/sizeof(*ptrsKeys_L0); | uint8_t KEY_COUNT_L0 = sizeof(ptrsKeys_L0)/sizeof(*ptrsKeys_L0); | ||||
Row row_L0(scanner_L, 21, ptrsKeys_L0, KEY_COUNT_L0); | Row row_L0(scanner_L, 21, ptrsKeys_L0, KEY_COUNT_L0); | ||||
Key* ptrsKeys_L1[] = { &s_a, &s_b }; | |||||
Key* ptrsKeys_L1[] = { &s_leftBracket, &s_B }; | |||||
uint8_t KEY_COUNT_L1 = sizeof(ptrsKeys_L1)/sizeof(*ptrsKeys_L1); | uint8_t KEY_COUNT_L1 = sizeof(ptrsKeys_L1)/sizeof(*ptrsKeys_L1); | ||||
Row row_L1(scanner_L, 20, ptrsKeys_L1, KEY_COUNT_L1); | Row row_L1(scanner_L, 20, ptrsKeys_L1, KEY_COUNT_L1); | ||||
// ---------------- RIGHT ROWS ----------------- | // ---------------- RIGHT ROWS ----------------- | ||||
Key* ptrsKeys_R0[] = { &s_3, &s_4 }; | |||||
Key* ptrsKeys_R0[] = { &s_0, &s_1, &s_U, &s_I }; | |||||
uint8_t KEY_COUNT_R0 = sizeof(ptrsKeys_R0)/sizeof(*ptrsKeys_R0); | uint8_t KEY_COUNT_R0 = sizeof(ptrsKeys_R0)/sizeof(*ptrsKeys_R0); | ||||
Row row_R0(scanner_R, 1<<0, ptrsKeys_R0, KEY_COUNT_R0); | Row row_R0(scanner_R, 1<<0, ptrsKeys_R0, KEY_COUNT_R0); | ||||
Key* ptrsKeys_R1[] = { &s_c, &s_d }; | |||||
Key* ptrsKeys_R1[] = { &s_0, &s_1, &s_H, &s_Y }; | |||||
uint8_t KEY_COUNT_R1 = sizeof(ptrsKeys_R1)/sizeof(*ptrsKeys_R1); | uint8_t KEY_COUNT_R1 = sizeof(ptrsKeys_R1)/sizeof(*ptrsKeys_R1); | ||||
Row row_R1(scanner_R, 1<<1, ptrsKeys_R1, KEY_COUNT_R1); | Row row_R1(scanner_R, 1<<1, ptrsKeys_R1, KEY_COUNT_R1); | ||||
/* | |||||
*/ | |||||
// ################### MAIN #################### | // ################### MAIN #################### | ||||
void setup() | void setup() | ||||
{ | { | ||||
Keyboard.begin(); | |||||
delay(6000); | |||||
Keyboard.print("keybrd_PCA9655E.ino "); | |||||
//Keyboard.begin(); not needed ?? it's in DH mainSketch.cpp and keybrd_4c_split_keyboard_with_IOE.ino | |||||
scanner_R.begin(); | scanner_R.begin(); | ||||
} | } | ||||
void loop() | void loop() | ||||
{ | { | ||||
//left matrix | |||||
row_L0.process(); | |||||
row_L1.process(); | |||||
//left matrix (commented because keys are not connected) | |||||
//row_L0.process(); | |||||
//row_L1.process(); | |||||
//right matrix | //right matrix | ||||
row_R0.process(); | row_R0.process(); |
The series of sketches in this folder where used to develope the Port_PCA9655E class. | |||||
The folder numbers are ordered from fundamental to practical. | |||||
Each sketch was tested on a breadboard. Breadboards hold: | |||||
* Teensy LC controller | |||||
* PCA9655E I/O expander | |||||
Pictures of the breadboard are in the folders. |
Code_Sc s_3(KEY_3); | Code_Sc s_3(KEY_3); | ||||
Code_Sc s_4(KEY_4); | Code_Sc s_4(KEY_4); | ||||
Code_LEDLock o_capsLock(KEY_CAPS_LOCK, LED_capsLck);//todo was testing LED, restore s_4 when done | |||||
Code_LEDLock o_capsLock(KEY_CAPS_LOCK, LED_capsLck);//was testing LED, restore s_4 when done | |||||
/* =================== ROWS ==================== | /* =================== ROWS ==================== | ||||
Left row names contain the letter 'L', while right row names conatain the letter 'R'. | Left row names contain the letter 'L', while right row names conatain the letter 'R'. | ||||
//right matrix | //right matrix | ||||
row_R0.process(); | row_R0.process(); | ||||
//Keyboard.println(" 0"); | |||||
//delay(2000); | |||||
row_R1.process(); | row_R1.process(); | ||||
//Keyboard.println(" 1"); | |||||
//delay(2000); | |||||
scanDelay.delay(); | scanDelay.delay(); | ||||
//Keyboard.println(" end loop");//todo | |||||
//debug.print_scans_per_second(); | //debug.print_scans_per_second(); | ||||
//debug.print_microseconds_per_scan(); | //debug.print_microseconds_per_scan(); | ||||
} | } |
#include "LED_uC.h" | #include "LED_uC.h" | ||||
LED_uC::LED_uC(const uint8_t pin) : pin(pin) | |||||
{ | |||||
pinMode(pin, OUTPUT); | |||||
} | |||||
void LED_uC::on() | void LED_uC::on() | ||||
{ | { | ||||
digitalWrite(pin, HIGH); | digitalWrite(pin, HIGH); |
class LED_uC: public LEDInterface | class LED_uC: public LEDInterface | ||||
{ | { | ||||
private: | private: | ||||
const uint8_t pin; //Aduino pin that is connected to an LED | |||||
const uint8_t pin; //Aduino pin number connected to an LED | |||||
public: | public: | ||||
LED_uC(const uint8_t pin): pin(pin) | |||||
{ | |||||
pinMode(pin, OUTPUT);//todo move to .cpp file | |||||
} | |||||
LED_uC(const uint8_t pin); | |||||
virtual void on(); | virtual void on(); | ||||
virtual void off(); | virtual void off(); | ||||
}; | }; |
#include "Port_MCP23018.h" | #include "Port_MCP23018.h" | ||||
//todo add Port_MCP23018::write() like Port_MCP23S17::transer() ?? | |||||
/* beginProtocol() is called from Scanner_IOE::begin(). Initiates I2C bus. | /* beginProtocol() is called from Scanner_IOE::begin(). Initiates I2C bus. | ||||
void Port_MCP23018::beginProtocol() | void Port_MCP23018::beginProtocol() | ||||
{ | { | ||||
Wire.begin(); //initiate I2C bus to 100 kHz | 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(). | /* begin() is called from Scanner_IOE::begin(). | ||||
if (logicLevel == LOW) | if (logicLevel == LOW) | ||||
{ | { | ||||
outputVal &= ~pin; //set pin output to low | outputVal &= ~pin; //set pin output to low | ||||
//Keyboard.print(" low"); | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
outputVal |= pin; //set pin output to high | outputVal |= pin; //set pin output to high | ||||
//Keyboard.print(" high"); | |||||
} | } | ||||
//Keyboard.print(" outputVal=");//todo | |||||
//Keyboard.println(outputVal); | |||||
Wire.beginTransmission(deviceAddr); | Wire.beginTransmission(deviceAddr); | ||||
Wire.write(portNum + 0x12); //GPIO | Wire.write(portNum + 0x12); //GPIO | ||||
Wire.write(outputVal); | Wire.write(outputVal); | ||||
Wire.endTransmission(); | Wire.endTransmission(); | ||||
//delay(4000); | |||||
} | } | ||||
/* read() returns portState. | /* read() returns portState. |
/* begin() is called from Scanner_IOE::begin(). | /* begin() is called from Scanner_IOE::begin(). | ||||
Configures read pins to input. | Configures read pins to input. | ||||
strobeOn is not used because PCA9655E has no pull-up resistors. | |||||
strobeOn is not used because PCA9655E has no internal pull-up resistors. | |||||
*/ | */ | ||||
void Port_PCA9655E::begin(const uint8_t strobeOn) | void Port_PCA9655E::begin(const uint8_t strobeOn) | ||||
{ | { | ||||
Wire.beginTransmission(deviceAddr); | Wire.beginTransmission(deviceAddr); | ||||
Wire.write(portNum + 6); //configuration byte command | |||||
Wire.write(portNum + 6); //configure direction | |||||
Wire.write(readPins); //0=output (for strobe and LED), 1=input (for read) | Wire.write(readPins); //0=output (for strobe and LED), 1=input (for read) | ||||
Wire.endTransmission(); | Wire.endTransmission(); | ||||
} | } |
/* | /* | ||||
strobePin has one of two formats: | strobePin has one of two formats: | ||||
1. if strobe pin is on uC (Scanner_uC or Scanner_ShiftRegsRead), | |||||
* if strobe pin is on uC (strobe for Scanner_uC or Scanner_ShiftRegsRead), | |||||
then strobePin is an Arduino pin number connected to this row. | then strobePin is an Arduino pin number connected to this row. | ||||
2. if strobe pin is on I/O expander (Scanner_IOE), then strobePin is bit pattern, | |||||
* if strobe pin is on I/O expander (strobe for Scanner_IOE), then strobePin is bit pattern, | |||||
1 indicating the I/O expander pin connected to this row | 1 indicating the I/O expander pin connected to this row | ||||
todo instantiation examples - here or in Scanner? | |||||
*/ | */ | ||||
class Row | class Row | ||||
{ | { | ||||
protected: | protected: | ||||
const uint8_t keyCount; //number of read pins | const uint8_t keyCount; //number of read pins | ||||
//Debouncer_Samples debouncer; | //Debouncer_Samples debouncer; | ||||
Debouncer_Not debouncer; //todo | |||||
Debouncer_Not debouncer; //todo restore Debouncer_Samples after testing | |||||
read_pins_t debounced; //bit pattern, state of keys after debouncing, 1=pressed, 0=released | read_pins_t debounced; //bit pattern, state of keys after debouncing, 1=pressed, 0=released | ||||
public: | public: | ||||
Row(ScannerInterface& refScanner, const uint8_t strobePin, | Row(ScannerInterface& refScanner, const uint8_t strobePin, |
slaveSelect(slaveSelect), byte_count(byte_count) | slaveSelect(slaveSelect), byte_count(byte_count) | ||||
{ | { | ||||
pinMode(slaveSelect, OUTPUT); | pinMode(slaveSelect, OUTPUT); | ||||
SPI.begin(); | |||||
} | } | ||||
/* init() is called once for each row from Row constructor. | /* init() is called once for each row from Row constructor. | ||||
Configures controller to communicate with shift register matrix. | Configures controller to communicate with shift register matrix. | ||||
slaveSelect initialize not needed, only affects first scan, which is before USB is detected by OS. | |||||
digitalWrite(slaveSelect, HIGH); | |||||
*/ | */ | ||||
void Scanner_ShiftRegsReadStrobed::init(const uint8_t strobePin) | void Scanner_ShiftRegsReadStrobed::init(const uint8_t strobePin) | ||||
{ | { | ||||
pinMode(strobePin, OUTPUT); | pinMode(strobePin, OUTPUT); | ||||
} | } | ||||
/* begin() should be called once from sketch setup(). | |||||
Initializes shift register's shift/load pin. | |||||
*/ | |||||
void Scanner_ShiftRegsReadStrobed::begin() | |||||
{ | |||||
digitalWrite(slaveSelect, HIGH); //initialize ??only needed for first scan | |||||
SPI.begin(); //todo move this to constructor or init() | |||||
} | |||||
/* scan() strobes the row's strobePin and returns state of the shift register's input pins. | /* 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. | strobePin is Arduino pin number connected to this row. | ||||
Bit patterns are 1 bit per key. | Bit patterns are 1 bit per key. | ||||
{ | { | ||||
read_pins_t readState = 0; //bits, 1 means key is pressed, 0 means released | read_pins_t readState = 0; //bits, 1 means key is pressed, 0 means released | ||||
//strobe off here NOT release continuously | |||||
digitalWrite(strobePin, activeState); //strobe on | digitalWrite(strobePin, activeState); //strobe on | ||||
//SPI.beginTransaction( SPISettings(5000000, MSBFIRST, SPI_MODE0) ); //control SPI bus, 5 MHz | //SPI.beginTransaction( SPISettings(5000000, MSBFIRST, SPI_MODE0) ); //control SPI bus, 5 MHz | ||||
digitalWrite(slaveSelect, HIGH); //shift the data toward a serial output | digitalWrite(slaveSelect, HIGH); //shift the data toward a serial output | ||||
digitalWrite(strobePin, !activeState); //strobe off to preserv IR LED life | |||||
digitalWrite(strobePin, !activeState); //strobe off to preserve IR LED life | |||||
SPI.transfer(&readState, byte_count); | SPI.transfer(&readState, byte_count); | ||||
digitalWrite(slaveSelect, LOW); //load parallel inputs to registers | digitalWrite(slaveSelect, LOW); //load parallel inputs to registers | ||||
//strobe off here still releases continuously | |||||
return readState; | return readState; | ||||
} | } | ||||
Scanner_ShiftRegsReadStrobed(const bool activeState, | Scanner_ShiftRegsReadStrobed(const bool activeState, | ||||
const uint8_t slaveSelect, const uint8_t byte_count); | const uint8_t slaveSelect, const uint8_t byte_count); | ||||
virtual void init(const uint8_t strobePin); | virtual void init(const uint8_t strobePin); | ||||
virtual void begin(); | |||||
virtual read_pins_t scan(const uint8_t strobePin); | virtual read_pins_t scan(const uint8_t strobePin); | ||||
}; | }; | ||||
#endif | #endif |
"<<" (bit shift left) and "|" (OR) are bitwise operators. | "<<" (bit shift left) and "|" (OR) are bitwise operators. | ||||
Pin numbers to be read are to the right of "1<<" and delimited by "|". | Pin numbers to be read are to the right of "1<<" and delimited by "|". | ||||
*/ | */ | ||||
Port_MCP23S17 portA(IOE_ADDR, 0, 1<<0 | 1<<1 ); | |||||
Port_MCP23S17 portB(IOE_ADDR, 1, 0); | |||||
Port_MCP23S17 portA(IOE_ADDR, 0, 1<<0 | 1<<1 ); //for read | |||||
Port_MCP23S17 portB(IOE_ADDR, 1, 0); //for strobe | |||||
Scanner_IOE scanner_R(LOW, portB, portA); | Scanner_IOE scanner_R(LOW, portB, portA); | ||||
// =================== CODES =================== | // =================== CODES =================== | ||||
Row constructor parameters are: scanner, strobePin, ptrsKeys[], keyCount. | Row constructor parameters are: scanner, strobePin, ptrsKeys[], keyCount. | ||||
strobePin has one of two formats: | strobePin has one of two formats: | ||||
* if refScanner a Scanner_uC, then strobePin is an Arduino pin number connected to this row | |||||
* otherwise strobePin is a bit pattern, 1 indicating an IC pin connected to the row | |||||
* if strobe pin is on uC (strobe for Scanner_uC or Scanner_ShiftRegsRead), | |||||
then strobePin is an Arduino pin number connected to this row. | |||||
* if strobe pin is on I/O expander (strobe for Scanner_IOE), then strobePin is bit pattern, | |||||
1 indicating the I/O expander pin connected to this row | |||||
*/ | */ | ||||
/* ---------------- LEFT ROWS ------------------ | /* ---------------- LEFT ROWS ------------------ | ||||
The left rows have a Scanner_uC and Arduino pin numbers to strobe. | The left rows have a Scanner_uC and Arduino pin numbers to strobe. |
5. Study other keybrd port classes. | 5. Study other keybrd port classes. | ||||
* SPI I/O expander port classes: Port_MCP23S17 | * SPI I/O expander port classes: Port_MCP23S17 | ||||
* I2C I/O expander port classes: Port_PCA9655E | * I2C I/O expander port classes: Port_PCA9655E | ||||
6. Write the port classes for your I/O expander. | |||||
Debugging I/O expander code is hard because SPI or I2C protocol adds a level of indirection. | |||||
6. Write the port classes for your I/O expander. Debugging I/O expander code is hard because | |||||
SPI or I2C protocol, expander configuration, and expander commands. | |||||
<br> | <br> | ||||
<a rel="license" href="https://creativecommons.org/licenses/by/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://licensebuttons.net/l/by/4.0/88x31.png" /></a><br /><span xmlns:dct="http://purl.org/dc/terms/" property="dct:title">keybrd tutorial</span> by <a xmlns:cc="https://creativecommons.org/ns" href="https://github.com/wolfv6/keybrd" property="cc:attributionName" rel="cc:attributionURL">Wolfram Volpi</a> is licensed under a <a rel="license" href="https://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International License</a>.<br />Permissions beyond the scope of this license may be available at <a xmlns:cc="https://creativecommons.org/ns" href="https://github.com/wolfv6/keybrd/issues/new" rel="cc:morePermissions">https://github.com/wolfv6/keybrd/issues/new</a>. | <a rel="license" href="https://creativecommons.org/licenses/by/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://licensebuttons.net/l/by/4.0/88x31.png" /></a><br /><span xmlns:dct="http://purl.org/dc/terms/" property="dct:title">keybrd tutorial</span> by <a xmlns:cc="https://creativecommons.org/ns" href="https://github.com/wolfv6/keybrd" property="cc:attributionName" rel="cc:attributionURL">Wolfram Volpi</a> is licensed under a <a rel="license" href="https://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International License</a>.<br />Permissions beyond the scope of this license may be available at <a xmlns:cc="https://creativecommons.org/ns" href="https://github.com/wolfv6/keybrd/issues/new" rel="cc:morePermissions">https://github.com/wolfv6/keybrd/issues/new</a>. |
/* unit test for Port_MCP23018 | |||||
Picture of hardware is in unit_tests/PortMCP23018_read/PortMCP23018_bb.JPG todo | |||||
The setup is an MCP23018 I/O expander on a Teensy LC controller. | |||||
MCP23018 port-A GPIO pins are not connected to anything. | |||||
Port-A GPIO-pin ouputs alternate between 0 and 3.3 volts. | |||||
volt meter between pin A1 and power because | |||||
MCP23018 has open-drain outputs (open-drain can only sink current) | |||||
Use a volt meter to measure port-A GPIO-pin outputs or red LED. | |||||
*/ | |||||
#include "Port_MCP23018.h" | |||||
const uint8_t IOE_ADDR = 0x20; //MCP23018 address ADDR pin grounded | |||||
Port_MCP23018 portA(IOE_ADDR, 0, 0); | |||||
void setup() | |||||
{ | |||||
delay(6000); | |||||
Keyboard.println("PortMCP23018_write.ino"); | |||||
portA.beginProtocol(); | |||||
portA.begin(LOW); //HIGH or LOW, not matter if readPins=0 | |||||
} | |||||
void loop() | |||||
{ | |||||
portA.write(~0, HIGH); //set all GPIOA pins HIGH | |||||
Keyboard.print("+"); | |||||
delay(2000); | |||||
portA.write(~0, LOW); //set all GPIOA pins LOW | |||||
Keyboard.print("0"); | |||||
delay(2000); | |||||
} |
/* unit test for PortMCP23S17 | |||||
Picture of hardware is in unit_tests/PortMCP23S17_read/PortMCP23S17_bb.JPG | |||||
The setup is an MCP23S17 I/O expander on a Teensy LC controller. | |||||
MCP23S17 port-B pins are alternately grounded and energized. | |||||
output is: 10101010 | |||||
*/ | |||||
#include "Port_MCP23S17.h" | |||||
const uint8_t IOE_ADDR = 0x20; //MCP23S17 address, all 3 ADDR pins grounded | |||||
Port_MCP23S17 portB(IOE_ADDR, 1, ~0); //read all pins | |||||
void setup() | |||||
{ | |||||
uint8_t BitPattern; //reading of port B | |||||
delay(6000); | |||||
portB.begin(HIGH); //HIGH or LOW, does not matter | |||||
BitPattern = portB.read(); | |||||
Keyboard.print("BitPattern = "); | |||||
Keyboard.println(BitPattern, BIN); //prints 10101010 | |||||
} | |||||
void loop() { } |
/* unit test for Port_MCP23S17 | |||||
Picture of hardware is in unit_tests/PortMCP23S17_read/PortMCP23S17_bb.JPG | |||||
The setup is an MCP23S17 I/O expander on a Teensy LC controller. | |||||
MCP23S17 port-A GPIO pins are not connected to anything. | |||||
Port-A GPIO-pin ouputs alternate between 0 and 3.3 volts. | |||||
Use a volt meter to measure port-A GPIO-pin outputs or red LED. | |||||
*/ | |||||
#include "Port_MCP23S17.h" | |||||
const uint8_t IOE_ADDR = 0x20; //MCP23S17 address, all 3 ADDR pins grounded | |||||
Port_MCP23S17 portA(IOE_ADDR , 0, 0); //PortAWrite needed for begin() | |||||
void setup() | |||||
{ | |||||
delay(6000); | |||||
Keyboard.println("start setup"); | |||||
portA.begin(LOW); //HIGH or LOW, not matter if readPins=0 | |||||
Keyboard.println("start loop"); | |||||
} | |||||
void loop() | |||||
{ | |||||
portA.write(~0, HIGH); //set all GPIOA pins HIGH | |||||
delay(2000); | |||||
portA.write(~0, LOW); //set all GPIOA pins LOW | |||||
delay(2000); | |||||
} |