We need to know what improvements to the keybrd library would help you create your keyboard design. | We need to know what improvements to the keybrd library would help you create your keyboard design. | ||||
Before requesting an improvement, please check [planned_features list](doc/planned_features.md) | Before requesting an improvement, please check [planned_features list](doc/planned_features.md) | ||||
Submit improvement suggestions to [GitHub issues](https://github.com/wolfv6/Keybrd/issues). | |||||
* The issue title should start with "suggestion:" followed by a descriptive title | |||||
Submit improvement suggestions to [GitHub issues](https://github.com/wolfv6/Keybrd/issues) | |||||
or [geekhack thread](https://geekhack.org/index.php?topic=83599.0). | |||||
<!-- * The issue title should start with "suggestion:" followed by a descriptive title --> | |||||
* Provide a use case | * Provide a use case | ||||
* Explain why the improvement is useful | * Explain why the improvement is useful | ||||
* Site other product examples where this improvement exists | * Site other product examples where this improvement exists | ||||
Bug reports | Bug reports | ||||
----------- | ----------- | ||||
A bug report is the first step in making the keybrd library work the way it's supposed to work. | A bug report is the first step in making the keybrd library work the way it's supposed to work. | ||||
Submit bug reports to [GitHub issues](https://github.com/wolfv6/Keybrd/issues) | |||||
or [geekhack thread](https://geekhack.org/index.php?topic=83599.0). | |||||
Please provide enough information so we can reproduce the bug behaviour! | Please provide enough information so we can reproduce the bug behaviour! | ||||
* Complete sketch (copy & paste, attachment, or a link to the code) | * Complete sketch (copy & paste, attachment, or a link to the code) | ||||
* Documentation - Suggest a clarification, simplification, correction, or other improvement. | * Documentation - Suggest a clarification, simplification, correction, or other improvement. | ||||
We need the perspective of people new to the project to see these things. | We need the perspective of people new to the project to see these things. | ||||
Sometimes just changing a word or two makes a big difference. | Sometimes just changing a word or two makes a big difference. | ||||
* [Current user contributions](https://geekhack.org/index.php?topic=83599.0) highlights contributions that are needed for the keybrd project's current stage of development. | |||||
* [Current user contributions](https://geekhack.org/index.php?topic=83599.msg2223776#msg2223776) highlights contributions that are needed for the keybrd project's current stage of development. | |||||
Text file documentation style guide: | Text file documentation style guide: | ||||
* Use Markdown with a .md suffix. | * Use Markdown with a .md suffix. |
Row_Ext_uC and Row_Ext_ShiftRegisters are a custom classes composed of stock keybrd library classes.<br> | Row_Ext_uC and Row_Ext_ShiftRegisters are a custom classes composed of stock keybrd library classes.<br> | ||||
Row_Ext_uC uses Scanner_uC to scan the primary matrix.<br> | Row_Ext_uC uses Scanner_uC to scan the primary matrix.<br> | ||||
Row_Ext_ShiftRegisters uses Scanner_ShiftRegs74HC165 to scan the secondary matrix. | |||||
Row_Ext_ShiftRegisters uses Scanner_ShiftRegs74HC165 to scan the peripheral matrix. | |||||
Class inheritance diagram | Class inheritance diagram | ||||
``` | ``` | ||||
``` | ``` | ||||
________ Row ___________ | ________ Row ___________ | ||||
/ | \ | / | \ | ||||
Row_uC Row_ShiftRegisters Row_IOE (to be added) | |||||
Row_uC Row_ShiftRegisters Row_IOE (todo to be added) | |||||
Scanner_uC Scanner_Port Scanner_ShiftRegs74HC165 | Scanner_uC Scanner_Port Scanner_ShiftRegs74HC165 | ||||
PortWrite | PortWrite | ||||
| | | | ||||
PortWrite_PCA9655E (one PortWrite class for each IOE type) | |||||
PortWrite_PCA9655E (one PortWrite class for each IOE type) | |||||
PortRead | PortRead | ||||
| | | | ||||
| | | | | | | | ||||
| Code_LayeredScSc Code_LayeredCodeSc | | Code_LayeredScSc Code_LayeredCodeSc | ||||
| | | | ||||
|__________________________________________ | |||||
\ \ \ \ | |||||
Code_Sc Code_Shift Code_AutoShift Code_LEDLock | |||||
|_________________________________________________________ | |||||
\ \ \ \ \ | |||||
Code_Sc Code_Shift Code_AutoShift Code_LEDLock Code_Null | |||||
/ \ | / \ | ||||
Code_ScS Code_ScNS | Code_ScS Code_ScNS | ||||
``` | ``` | ||||
Dependency diagram of example secondary matrix with shift registers | |||||
Dependency diagram of example peripheral matrix with shift registers | |||||
``` | ``` | ||||
Row_ShiftRegisters[1..*] | Row_ShiftRegisters[1..*] | ||||
/ \ \ | / \ \ | ||||
``` | ``` | ||||
Dependency diagram of example secondary matrix with I/O Expander and LEDs | |||||
Dependency diagram of example peripheral matrix with I/O Expander and LEDs | |||||
``` | ``` | ||||
___ Row_IOE[1..*] _________ | |||||
/ \ \ | |||||
_ Scanner_Port[1] _ Debouncer[1] Keys[1..*] __ | |||||
/ | \ | \ | |||||
PortWrite[1] RowPin[1] PortRead[1] Code[1..*] Code_LEDLock[1..*] | |||||
\ / \ | | |||||
\ / ColPins[1..*] LED[1] | |||||
\ / | |||||
PortIOE[0..*] | |||||
_____ Row_IOE[1..*] _________ | |||||
/ \ \ | |||||
__ Scanner_Port[1] __ Debouncer[1] Keys[1..*] __ | |||||
/ | \ | \ | |||||
PortWrite[1] strobePin[1] PortRead[1] Code[1..*] Code_LEDLock[1..*] | |||||
\ / \ | | |||||
\ / ColPins[1..*] LED[1] | |||||
\ / | |||||
PortIOE[0..*] | |||||
``` | ``` | ||||
Trace of keybrd scan | Trace of keybrd scan | ||||
-------------------- | -------------------- | ||||
Arduino does not have a debugger. | Arduino does not have a debugger. | ||||
So here is the next best thing; a list of functions in the order that they are called. | |||||
So here is a list of functions in the order that they are called. | |||||
The trace is of a one-row single-layer keybrd scan. | The trace is of a one-row single-layer keybrd scan. | ||||
Refer to it like a table of contents while reading the keybrd library. | Refer to it like a table of contents while reading the keybrd library. | ||||
class PortRead | class PortRead | ||||
{ | { | ||||
protected: | protected: | ||||
const uint8_t readPins; //bitwise pin configuration, 1 means read column | |||||
const uint8_t readPins; //bitwise pin configuration, 1 means read pin | |||||
public: | public: | ||||
PortRead(const uint8_t readPins): readPins(readPins) {} | PortRead(const uint8_t readPins): readPins(readPins) {} | ||||
virtual uint8_t read()=0; | virtual uint8_t read()=0; |
#include "PortRead_MCP23S17.h" | |||||
/* | |||||
PortRead_MCP23S17::begin() is not needed because port direction is already configured to input by default. | |||||
SPI bus is configured in PortWrite_MCP23S17::begin(). | |||||
*/ | |||||
/* | |||||
returns port value | |||||
*/ | |||||
uint8_t PortRead_MCP23S17::read() | |||||
{ | |||||
uint8_t portState; //bit wise | |||||
//slower clock | |||||
digitalWrite(SS, LOW); //enable Slave Select | |||||
SPI.transfer(port.ADDR << 1 | 1); //read command | |||||
SPI.transfer(port.num + 0x12); //register address to read data from | |||||
portState = SPI.transfer(0); //save the data (0 is dummy data to send) | |||||
digitalWrite(SS, HIGH); //disable Slave Select | |||||
return portState; | |||||
} |
#ifndef PORTREAD_MCP23S17_H | |||||
#define PORTREAD_MCP23S17_H | |||||
#include <Arduino.h> | |||||
#include <inttypes.h> | |||||
#include <SPI.h> | |||||
#include <PortRead.h> | |||||
#include "PortIOE.h" | |||||
/* One MCP23S17 I/O expander port connected to matrix columns. | |||||
MCP23S17 does not have internal pull-up resistors (PCA9535E does). | |||||
Instantiation | |||||
------------ | |||||
readPins parameter 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 for column port 0, with pins 2 and 3 connected to columns: | |||||
PortIOE port0(0, 0); | |||||
PortRead_MCP23S17 colPort0(port0, 2<<0 | 1<<3 ); | |||||
Example instantiation for column port 1, with pins 2 and 3 connected to columns: | |||||
PortIOE port1(1, 0); | |||||
PortRead_MCP23S17 colPort1(port1, 2<<0 | 1<<3 ); | |||||
readPins are read from pin 0 on up. | |||||
*/ | |||||
class PortRead_MCP23S17 : public PortRead | |||||
{ | |||||
private: | |||||
PortIOE& port; | |||||
public: | |||||
/* | |||||
todo not all PortRead_ classes need a readPins | |||||
move PortRead::readPins from PortRead to PortRead_PCA9655E | |||||
remove PortRead(0) initialization from this constructor | |||||
*/ | |||||
//The constructor initialization list is in .cpp | |||||
PortRead_MCP23S17(PortIOE& port) : PortRead(0), port(port) {} | |||||
virtual uint8_t read(); | |||||
}; | |||||
#endif |
#include "PortWrite_MCP23S17.h" | |||||
void PortWrite_MCP23S17::writePort(const uint8_t registerAddr, const uint8_t data) | |||||
{ | |||||
//slower clock | |||||
//SPI.beginTransaction(SPISettings (SPI_CLOCK_DIV8, MSBFIRST, SPI_MODE0)); //control SPI bus todo move to begin() | |||||
digitalWrite(SS, LOW); //enable Slave Select | |||||
SPI.transfer(port.ADDR << 1); //write command | |||||
SPI.transfer(registerAddr); //register address to write data to | |||||
SPI.transfer(data); //data | |||||
digitalWrite(SS, HIGH); //disable Slave Select | |||||
//SPI.endTransaction(); //release the SPI bus | |||||
} | |||||
/* | |||||
If PortRead_MCP23S17 is instantiated on the same port, do NOT use PortWrite_MCP23S17::begin(). | |||||
Otherwise readPins could be overwritten. | |||||
Output pins can be used for strobe pins and LEDs. | |||||
SPI.endTransaction() is not called because keyboard only has one SPI device, so no need to release the SPI bus | |||||
*/ | |||||
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 (SPI_CLOCK_DIV8, MSBFIRST, SPI_MODE0)); //control SPI bus | |||||
writePort(port.num, 0); //configure port direction (port.num) to output (0) | |||||
} | |||||
/* | |||||
pin is bitwise, where pin being strobed is 1. | |||||
strobe is HIGH or LOW (for active high or active low). | |||||
port.outputVal can be shared by LEDs. | |||||
The functions does not reset the other pins so that they can be used for LEDs. | |||||
*/ | |||||
void PortWrite_MCP23S17::write(const uint8_t pin, const bool strobe) | |||||
{ | |||||
if (strobe == LOW) //if active low | |||||
{ | |||||
port.outputVal &= ~pin; //set pin output to low | |||||
} | |||||
else //if active high | |||||
{ | |||||
port.outputVal |= pin; //set pin output to high | |||||
} | |||||
writePort(port.num + 0x12, port.outputVal); //set GPIO port pins for stobe and LEDs | |||||
} |
#ifndef PORTWRITE_MCP23S17_H | |||||
#define PORTWRITE_MCP23S17_H | |||||
#include <Arduino.h> | |||||
#include <inttypes.h> | |||||
#include <SPI.h> | |||||
#include <PortWrite.h> | |||||
#include "PortIOE.h" | |||||
/* One MCP23S17 I/O expander port connected to matrix rows. | |||||
MCP23S17 does not have internal pull-up resistors (PCA9535E does). | |||||
begin() configures column port's configuration and output. | |||||
This should normally be called once in sketch's setup(). | |||||
If PortRead_MCP23S17 is instantiated on the same port, do NOT use PortWrite_MCP23S17::begin(). | |||||
Otherwise readPins could be overwritten. | |||||
Instantiation | |||||
------------ | |||||
Example instantiation for row port 0: | |||||
PortIOE port0(0, 0); | |||||
PortWrite_MCP23S17 rowPort0(port0); | |||||
Example instantiation for row port 1: | |||||
PortIOE port1(1, 0); | |||||
PortWrite_MCP23S17 rowPort1(port1); | |||||
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 PortWrite | |||||
{ | |||||
private: | |||||
PortIOE& port; | |||||
void writePort(const uint8_t registerAddr, const uint8_t data); | |||||
public: | |||||
PortWrite_MCP23S17(PortIOE& port) : port(port) {} | |||||
void begin(); | |||||
virtual void write(const uint8_t pin, const bool level); | |||||
}; | |||||
#endif |
Key *const *const ptrsKeys; //array of Key pointers | Key *const *const ptrsKeys; //array of Key pointers | ||||
virtual void keyWasPressed(); | virtual void keyWasPressed(); | ||||
protected: | protected: | ||||
read_pins_t debounced; //bitwise state of keys after debouncing | |||||
// 1 means pressed, 0 means released | |||||
read_pins_t debounced; //bitwise state of keys after debouncing, 1=pressed, 0=released | |||||
void send(const uint8_t readPinCount, const read_pins_t debouncedChanged); | void send(const uint8_t readPinCount, const read_pins_t debouncedChanged); | ||||
public: | public: | ||||
Row(Key* const ptrsKeys[]) : ptrsKeys(ptrsKeys), debounced(0) { } | Row(Key* const ptrsKeys[]) : ptrsKeys(ptrsKeys), debounced(0) { } |
#include "Row_IOE.h" | |||||
void Row_IOE::process() | |||||
{ | |||||
//these variables are all bitwise, one bit per key | |||||
uint8_t readState; //1 means pressed, 0 means released | |||||
uint8_t debouncedChanged; //1 means debounced changed | |||||
readState = scanner.scan(); | |||||
debouncedChanged = debouncer.debounce(readState, debounced); | |||||
send(readPinCount, debouncedChanged); | |||||
} |
#ifndef ROW_IOE_H | |||||
#define ROW_IOE_H | |||||
#include <Row.h> | |||||
#include <Scanner_Port.h> | |||||
#include <Debouncer_Samples.h> | |||||
class PortWrite; | |||||
class PortRead; | |||||
/* Row_IOE is a row connected to an Input/Output Expander. | |||||
Configuration and Instantiation instructions are in keybrd/src/Row_IOE.h | |||||
*/ | |||||
class Row_IOE : public Row | |||||
{ | |||||
private: | |||||
Scanner_Port scanner; | |||||
Debouncer_Samples debouncer; | |||||
const uint8_t readPinCount; //number of read pins | |||||
public: | |||||
Row_IOE(PortWrite& refPortWrite, const uint8_t strobePin, | |||||
PortRead& refPortRead, const uint8_t readPinCount, Key *const ptrsKeys[]) | |||||
: Row(ptrsKeys), scanner(refPortWrite, strobePin, refPortRead), | |||||
readPinCount(readPinCount) { } | |||||
void process(); | |||||
}; | |||||
#endif |
Upto 4 shift registers can be in a daisy chained for a total of 32 read pins. | Upto 4 shift registers can be in a daisy chained for a total of 32 read pins. | ||||
For active low: | For active low: | ||||
Shift register parallel input pins have 10k pull-down resistors powered | |||||
Shift register parallel input pins have 10k pull-up resistors powered | |||||
Orient diodes with cathode (banded end) towards the write pins (row) | 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 | Controller's MISO pin is connected to shift register's complementary serial output (/QH) pin | ||||
Use these two lines in the sketch: | Use these two lines in the sketch: |
Key* ptrsKeys_0[] = { &s_1, &s_a }; | Key* ptrsKeys_0[] = { &s_1, &s_a }; | ||||
Row_uC row_0(0, readPins, READ_PIN_COUNT, ptrsKeys_0); | Row_uC row_0(0, readPins, READ_PIN_COUNT, ptrsKeys_0); | ||||
Key* ptrsKeys_1[] = { &s_b, &s_c }; | |||||
Key* ptrsKeys_1[] = { &s_b, &s_c }; | |||||
Row_uC row_1(1, readPins, READ_PIN_COUNT, ptrsKeys_1); | Row_uC row_1(1, readPins, READ_PIN_COUNT, ptrsKeys_1); | ||||
// ################### MAIN #################### | // ################### MAIN #################### |
/* tutorial_4a_split_keyboard_with_shift_registers.ino | |||||
Tested on Teensy LC and two 74HC165 shift registers. | |||||
The right matrix has 2 shift registers daisy chained. | |||||
Layout Layout | |||||
| Left |**0** | Right |**0**|**1**|**2**|**3**|**4**|**5**|**6**|**7**| | |||||
|:-----:|-----| |:-----:|-----|-----|-----|-----|-----|-----|-----|-----| | |||||
| **0** | x | | **0** | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | | |||||
| **1** | y | | **1** | a | b | c | d | e | f | g | h | | |||||
*/ | |||||
// ################## GLOBAL ################### | |||||
// ================= INCLUDES ================== | |||||
#include <ScanDelay.h> | |||||
#include <Code_Sc.h> | |||||
//Left matrix | |||||
#include <Row_uC.h> | |||||
//Right matrix | |||||
#include <SPI.h> | |||||
#include <Row_ShiftRegisters.h> | |||||
// =============== CONFIGURATION =============== | |||||
ScanDelay scanDelay(9000); | |||||
// =================== 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_e(KEY_E); | |||||
Code_Sc s_f(KEY_F); | |||||
Code_Sc s_g(KEY_G); | |||||
Code_Sc s_x(KEY_X); | |||||
Code_Sc s_y(KEY_Y); | |||||
Code_Sc s_0(KEY_0); | |||||
Code_Sc s_1(KEY_1); | |||||
Code_Sc s_2(KEY_2); | |||||
Code_Sc s_3(KEY_3); | |||||
Code_Sc s_4(KEY_4); | |||||
Code_Sc s_5(KEY_5); | |||||
Code_Sc s_6(KEY_6); | |||||
// =============== LEFT MATRIX ================= | |||||
//set left matrix for active low | |||||
const bool Scanner_uC::STROBE_ON = LOW; | |||||
const bool Scanner_uC::STROBE_OFF = HIGH; | |||||
//column pin | |||||
uint8_t readPins[] = {14}; | |||||
uint8_t READ_PIN_COUNT = sizeof(readPins)/sizeof(*readPins); | |||||
//rows | |||||
Key* ptrsKeys_L0[] = { &s_x }; | |||||
Row_uC row_L0(0, readPins, READ_PIN_COUNT, ptrsKeys_L0); | |||||
Key* ptrsKeys_L1[] = { &s_y }; | |||||
Row_uC row_L1(1, readPins, READ_PIN_COUNT, ptrsKeys_L1); | |||||
// =============== RIGHT MATRIX ================ | |||||
//set matrix to active high | |||||
const bool Scanner_ShiftRegs74HC165::STROBE_ON = HIGH; | |||||
const bool Scanner_ShiftRegs74HC165::STROBE_OFF = LOW; | |||||
//chip select pin | |||||
const uint8_t Scanner_ShiftRegs74HC165::SHIFT_LOAD = 10; | |||||
//rows | |||||
Key* ptrsKeys_R0[] = { &s_6, &s_5, &s_4, &s_3, //shift regiser on right | |||||
&s_c, &s_d, &s_e, &s_f, | |||||
&s_2, &s_1, &s_0, &s_g, //shift regiser on left | |||||
&s_a, &s_b }; //unused input pins are grounded | |||||
Row_ShiftRegisters row_R0(0, sizeof(ptrsKeys_R0)/sizeof(*ptrsKeys_R0), ptrsKeys_R0); | |||||
// ################### MAIN #################### | |||||
void setup() | |||||
{ | |||||
Keyboard.begin(); | |||||
SPI.begin(); | |||||
row_R0.begin(); | |||||
} | |||||
void loop() | |||||
{ | |||||
//left matrix | |||||
row_L0.process(); | |||||
row_L1.process(); | |||||
//right matrix | |||||
row_R0.process(); | |||||
scanDelay.delay(); | |||||
} |
/* keybrd_4c_split_with_IOE_annotated.ino | |||||
| Left | **0** | **1** | | Right | **0** | **1** | | |||||
|:-----:|-------|-------| |:-----:|-------|-------| | |||||
| **0** | a | b | | **0** | 1 | 2 | | |||||
| **1** | shift | c | | **1** | 3 | shift | | |||||
| Left | **0** | **1** | | Right | **0** | **1** | todo | |||||
|:-----:|-------|-------| |:-----:|-------|-------| | |||||
| **1** | 1 | 2 | | **1** | 3 | 4 | | |||||
| **0** | a | b | | **0** | c | d | | |||||
*/ | |||||
// ################## GLOBAL ################### | |||||
// ================= INCLUDES ================== | |||||
#include <ScanDelay.h> | |||||
#include <Code_Sc.h> | |||||
//left matrix | |||||
#include <Row_uC.h> | |||||
//right matrix | |||||
#include <Row_IOE.h> | |||||
#include <PortIOE.h> | |||||
#include <PortWrite_MCP23S17.h> | |||||
#include <PortRead_MCP23S17.h> | |||||
// ============ SPEED CONFIGURATION ============ | |||||
ScanDelay scanDelay(9000); | |||||
// ================ LEFT MATRIX ================ | |||||
// ---------------- ACTIVE STATE --------------- | |||||
const bool Scanner_uC::STROBE_ON = LOW; //active low | |||||
const bool Scanner_uC::STROBE_OFF = HIGH; | |||||
// ------------------- PINS -------------------- | |||||
uint8_t readPins[] = {14, 15}; | |||||
// ================ RIGHT MATRIX =============== | |||||
const bool Scanner_Port::STROBE_ON = HIGH; //active high | |||||
const bool Scanner_Port::STROBE_OFF = LOW; | |||||
const uint8_t PortIOE::ADDR = 0x18; | |||||
// ------------------ PORT 1 ------------------- | |||||
PortIOE port1_R(1, 0); | |||||
PortWrite_MCP23S17 portWrite1_R(port1_R); | |||||
// ------------------ PORT 0 ------------------- | |||||
PortIOE port0_R(0, 0); | |||||
PortWrite_MCP23S17 portWrite0_R(port0_R); | |||||
PortRead_MCP23S17 portRead0_R(port0_R, 1<<0 | 1<<1 ); | |||||
// =================== CODES =================== | |||||
Code_Sc s_shiftL(MODIFIERKEY_LEFT_SHIFT); | |||||
Code_Sc s_shiftR(MODIFIERKEY_RIGHT_SHIFT); | |||||
Code_Sc s_a(KEY_A); | |||||
Code_Sc s_b(KEY_B); | |||||
Code_Sc s_c(KEY_C); | |||||
Code_Sc s_1(KEY_1); | |||||
Code_Sc s_2(KEY_2); | |||||
Code_Sc s_3(KEY_3); | |||||
// =================== ROWS ==================== | |||||
// ---------------- LEFT ROWS ------------------ | |||||
Key* ptrsKeys_L0[] = { &s_a, &s_b }; | |||||
const uint8_t KEY_COUNT_L0 = sizeof(ptrsKeys_L0)/sizeof(*ptrsKeys_L0); | |||||
Row_uC row_L0(0, readPins, KEY_COUNT_L0, ptrsKeys_L0); | |||||
Key* ptrsKeys_L1[] = { &s_c, &s_shiftL }; | |||||
const uint8_t KEY_COUNT_L1 = sizeof(ptrsKeys_L1)/sizeof(*ptrsKeys_L1); | |||||
Row_uC row_L1(1, readPins, KEY_COUNT_L1, ptrsKeys_L1); | |||||
// ---------------- RIGHT ROWS ----------------- | |||||
Key* ptrsKeys_R0[] = { &s_1, &s_2 }; | |||||
const uint8_t KEY_COUNT_R0 = sizeof(ptrsKeys_R0)/sizeof(*ptrsKeys_R0); | |||||
Row_IOE row_R0(portWrite1_R, 1<<0, portRead0_R, KEY_COUNT_R0, ptrsKeys_R0); | |||||
Key* ptrsKeys_R1[] = { &s_3, &s_shiftR }; | |||||
const uint8_t KEY_COUNT_R1 = sizeof(ptrsKeys_R1)/sizeof(*ptrsKeys_R1); | |||||
Row_IOE row_R1(portWrite1_R, 1<<1, portRead0_R, KEY_COUNT_R1, ptrsKeys_R1); | |||||
// ################### MAIN #################### | |||||
void setup() | |||||
{ | |||||
Keyboard.begin(); | |||||
Wire.begin(); //Wire.begin() must be called before port begin() | |||||
portWrite1_R.begin(); | |||||
portRead0_R.begin(); | |||||
} | |||||
void loop() | |||||
{ | |||||
//left matrix | |||||
row_L0.process(); | |||||
row_L1.process(); | |||||
//right matrix | |||||
row_R0.process(); | |||||
row_R1.process(); | |||||
scanDelay.delay(); | |||||
//debug.print_scans_per_second(); | |||||
//debug.print_microseconds_per_scan(); | |||||
} |
Tutorial 10 - writing IOE Port classes | |||||
=========================================== | |||||
Tutorial 10 - writing new IOE Port classes | |||||
========================================== | |||||
Port classes are the keybrd library's interface to I/O expander ports. | Port classes are the keybrd library's interface to I/O expander ports. | ||||
To write your own Port class: | |||||
To write a new Port class: | |||||
1. Get a copy of the I/O expander datasheet. | |||||
2. Study other keybrd Port classes. | |||||
For example, the keybrd_DH library uses these keybrd classes for its PCA9655E I/O expander: | |||||
* PortWrite_PCA9655E | |||||
* PortRead_PCA9655E | |||||
* LED_PCA9655E | |||||
Debugging I/O expander code is hard because SPI or I2C protocol adds a level of indirection. | |||||
If you haven't written Arduino code for an I/O expander before, learn from an Arduiono I/O expander tutorial before attempting it here. | |||||
1. Get a copy of the I/O expander's datasheet. | |||||
2. An I/O expander will use one of two communication protocols: [http://www.byteparadigm.com/applications/introduction-to-i2c-and-spi-protocols/](SPI or I2C). | |||||
Refer to the [Arduino SPI](https://www.arduino.cc/en/Reference/SPI) | |||||
or [Arduino Wire (I2C)](https://www.arduino.cc/en/Reference/Wire) library | |||||
3. Get familiar with your I/O expander. | |||||
Different I/O expanders use different commands (a.k.a. operation codes). | |||||
Refer to your I/O expander's datasheet for read and write commands. | |||||
Search for Arduino sketch examples containing your I/O expander | |||||
([sumotoy](https://github.com/sumotoy/gpio_expander) has a large gpio expander library). | |||||
Write very simple read and write examples for your I/O expander. | |||||
Simple SPI I/O expander examples: | |||||
todo link, pictures | |||||
/home/wolfv/Documents/Arduino/demo/IOE_MCP23S17_read/ | |||||
/home/wolfv/Documents/Arduino/demo/IOE_MCP23S17_write/ | |||||
Simple I2C I/O expander examples: | |||||
todo link, pictures | |||||
read | |||||
write | |||||
4. Study other keybrd Port classes. | |||||
Port classes for SPI MCP23S17 I/O expander: | |||||
*todo | |||||
* | |||||
* | |||||
Port classes for I2C PCA9655E I/O expander: | |||||
* PortWrite_PCA9655E todo link | |||||
* PortRead_PCA9655E | |||||
* LED_PCA9655E | |||||
5. Write similar Port classes for your I/O expander. | |||||
Debugging I/O expander code is hard because SPI or I2C protocol adds a level of indirection. | |||||
<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>. |
Tutorial 8b - creating and publishing your own keybrd extension library | |||||
======================================================================= | |||||
Tutorial 8b - sharing your keybrd extension library | |||||
=================================================== | |||||
Publishing and listing your keybrd extension library allows others to find and install your library. | Publishing and listing your keybrd extension library allows others to find and install your library. | ||||
The keybrd extension library name should start with "keybrd_" so that it is easy for other people to find. | The keybrd extension library name should start with "keybrd_" so that it is easy for other people to find. | ||||
There are two ways to publish and list an Arduino library. | There are two ways to publish and list an Arduino library. | ||||
Publishing anywhere with listing on Arduino Playground LibraryList | |||||
------------------------------------------------------------------ | |||||
Publish anywhere and list on Arduino Playground | |||||
----------------------------------------------- | |||||
Publishing your keybrd extension library with the following directory structure makes it easy for others to understand. | Publishing your keybrd extension library with the following directory structure makes it easy for others to understand. | ||||
keybrd_MyKeyboard/ | keybrd_MyKeyboard/ | ||||
instantiations_codes.h | instantiations_codes.h | ||||
instantiations_rows.h | instantiations_rows.h | ||||
When your ready to list your keybrd extension library, go to the [Arduino Playground keybrd page](http://playground.arduino.cc/Main/keybrd). | |||||
When your ready to list your keybrd extension library, | |||||
add a link and short description of your keybrd extension library to the [Arduino Playground keybrd page](http://playground.arduino.cc/Main/keybrd) under "keybrd extension libraries". | |||||
Arduino playground is a wiki. | Arduino playground is a wiki. | ||||
Links on how to edit the wiki are on the bottom left under "Participate". | Links on how to edit the wiki are on the bottom left under "Participate". | ||||
You can also add a picture of a keyboard that uses your keybrd extension library. | |||||
Uploading files to the Playground is not allowed for standard users. | |||||
So if you want to add a picture, it will need to be hosted somewhere else. | |||||
Publishing on GitHub with listing on Arduino Library-Manager and Arduino Playground LibraryList | |||||
----------------------------------------------------------------------------------------------- | |||||
Publish on GitHub and list on Arduino Library-Manager and Arduino Playground | |||||
---------------------------------------------------------------------------- | |||||
The advantage of using GitHub is that users can submit pull requests. | The advantage of using GitHub is that users can submit pull requests. | ||||
The advantage of using Arduino Library-Manager is that users can easily find and install your library through the Arduino IDE. | The advantage of using Arduino Library-Manager is that users can easily find and install your library through the Arduino IDE. | ||||
Instructions for listing a library on Arduino Library Manager are at: | Instructions for listing a library on Arduino Library Manager are at: | ||||
https://github.com/arduino/Arduino/wiki/Library-Manager-FAQ | https://github.com/arduino/Arduino/wiki/Library-Manager-FAQ | ||||
After it has been accepted into the Arduino IDE Library Manager, add your keybrd extension library to the [Arduino Playground keybrd page](http://playground.arduino.cc/Main/keybrd). | |||||
After it has been accepted into the Arduino IDE Library Manager, | |||||
add a link and short description of your keybrd extension library to the [Arduino Playground keybrd page](http://playground.arduino.cc/Main/keybrd) under "keybrd extension libraries". | |||||
Arduino playground is a wiki. | Arduino playground is a wiki. | ||||
Links on how to edit the wiki are on the bottom left under "Participate". | Links on how to edit the wiki are on the bottom left under "Participate". | ||||
You can also add a picture of a keyboard that uses your keybrd extension library. | |||||
Uploading files to the Playground is not allowed for standard users. | |||||
So if you want to add a picture, it will need to be hosted somewhere else. | |||||
To publish a new release of a library that is already listed on Arduino Library Manager | To publish a new release of a library that is already listed on Arduino Library Manager | ||||
1. Update the version in your library.properties file: | 1. Update the version in your library.properties file: |
Tutorial 8c - sharing your keybrd sketch | |||||
======================================== | |||||
keybrd sketches that use a keybrd extension library should be published in the extension library's examples directory. | |||||
keybrd sketches that do not use a keybrd extension library can be published anywhere. | |||||
Publishing and listing your keybrd sketch allows others to find your sketch. | |||||
Publish anywhere and list on Arduino Playground | |||||
----------------------------------------------- | |||||
Publish your sketch anywhere. Some free places are: | |||||
* GitHub repository | |||||
* [GitHub Gist](https://help.github.com/categories/gists/) | |||||
* [geekhack Making Stuff Together!](https://geekhack.org/index.php?board=117.0) | |||||
Then add a link and short description of your keybrd sketch to the [Arduino Playground keybrd page](http://playground.arduino.cc/Main/keybrd) under "keybrd sketches". | |||||
Arduino playground is a wiki. | |||||
Links on how to edit the wiki are on the bottom left under "Participate". |
/* this works | |||||
The setup is an MCP23S17 I/O expander on a Teensy LC controller. | |||||
MCP23S17 port B pins are alternately grounded and energized. | |||||
portBState is a bitwise reading of port B. | |||||
output is: 10101010 | |||||
posted on http://arduino.stackexchange.com/questions/tagged/spi | |||||
http://arduino.stackexchange.com/questions/28792/reading-an-mcp23s17-i-o-expander-port-with-the-arduino-spi-library | |||||
*/ | |||||
#include <SPI.h> | |||||
const uint8_t ADDR = 0x20; //MCP23S17 address, all 3 ADDR pins are grounded | |||||
const uint8_t OPCODE_READ = (ADDR << 1 | 0x01); //MCP23S17 opcode read has LSB set | |||||
const uint8_t IODIRB = 0x01; | |||||
const uint8_t GPIOB = 0x13; | |||||
uint8_t portBState = 0; //bit wise | |||||
void setup() | |||||
{ | |||||
Serial.begin(9600); | |||||
delay(1000); | |||||
pinMode(SS, OUTPUT); //configure controller's Slave Select pin to output | |||||
digitalWrite(SS, HIGH); //disable Slave Select | |||||
SPI.begin(); | |||||
//IODIRB register is already configured to input by default | |||||
SPI.beginTransaction(SPISettings (SPI_CLOCK_DIV8, MSBFIRST, SPI_MODE0)); //gain control of SPI bus | |||||
digitalWrite(SS, LOW); //enable Slave Select | |||||
SPI.transfer(OPCODE_READ); //read command | |||||
SPI.transfer(GPIOB); //register address to read data from | |||||
portBState = SPI.transfer(0); //save the data (0 is dummy data to send) | |||||
digitalWrite(SS, HIGH); //disable Slave Select | |||||
SPI.endTransaction(); //release the SPI bus | |||||
Serial.println(portBState, BIN); //should print 10101010 | |||||
} | |||||
void loop() { } |
/* this works with volt meter (MCP23S17 on 3.3v does not output enough power for LEDs) | |||||
LED lights w/o resistor, not light with 56 ohm resistor | |||||
blink LED on MCP23S17 port A pin | |||||
from Example 41.1 - Microchip MCP23017 with Arduino | |||||
http://tronixstuff.com/tutorials > chapter 41 | |||||
http://tronixstuff.com/2011/08/26/tutorial-maximising-your-arduinos-io-ports/ | |||||
John Boxall | CC by-sa-nc | |||||
from http://69.5.26.215/forum/?id=10945&page=3 #35 | |||||
modified to test MCP23S17 (SPI) using syntax from | |||||
http://arduino.stackexchange.com/questions/16348/how-do-you-use-spi-on-an-arduino | |||||
> | |||||
SPISettings from http://arduino.stackexchange.com/questions/14191/mcp23s17-programming-iodirx-register-works-in-loop-but-not-in-setup | |||||
*/ | |||||
#include <SPI.h> | |||||
const uint8_t ADDR = 0x20; //MCP23S17 address, all ADDR pins are grounded | |||||
const uint8_t OPCODE_WRITE = (ADDR << 1); //MCP23S17 opcode write has LSB clear | |||||
const uint8_t IODIRA = 0x00; //LEDs are on port A | |||||
const uint8_t GPIOA = 0x12; | |||||
uint8_t LED_state = 0; //bit wise | |||||
void IOEWrite(const uint8_t registerAddr, const uint8_t data) | |||||
{ | |||||
SPI.beginTransaction(SPISettings (SPI_CLOCK_DIV8, MSBFIRST, SPI_MODE0)); //slower clock | |||||
digitalWrite(SS, LOW); //enable Slave Select | |||||
SPI.transfer(OPCODE_WRITE); //write command | |||||
SPI.transfer(registerAddr); //register address to write data to | |||||
SPI.transfer(data); //data | |||||
digitalWrite(SS, HIGH); //disable Slave Select | |||||
SPI.endTransaction(); //release the SPI bus | |||||
} | |||||
void setup() | |||||
{ | |||||
Serial.begin(9600); | |||||
pinMode(SS, OUTPUT); //configure controller's Slave Select pin to output | |||||
digitalWrite(SS, HIGH); //disable Slave Select | |||||
SPI.begin(); | |||||
IOEWrite(IODIRA, 0x00); //configure IODIRA register to output | |||||
} | |||||
void loop() | |||||
{ | |||||
IOEWrite(GPIOA, LED_state); //set all GPIOA pins | |||||
delay(2000); | |||||
//Serial.println(LED_state, BIN); //prints alternating 0 and 11111111 | |||||
LED_state = ~LED_state; //toggle LED on/off | |||||
} |
/* unit test for PortRead_MCP23S17 | |||||
The setup is an MCP23S17 I/O expander on a Teensy LC controller. | |||||
MCP23S17 port-B pins are alternately grounded and energized. | |||||
portBState is a bitwise reading of port B. | |||||
output is: 10101010 | |||||
posted on http://arduino.stackexchange.com/questions/tagged/spi | |||||
http://arduino.stackexchange.com/questions/28792/reading-an-mcp23s17-i-o-expander-port-with-the-arduino-spi-library | |||||
*/ | |||||
#include "PortIOE.h" | |||||
#include "PortRead_MCP23S17.h" | |||||
#include "PortWrite_MCP23S17.h" | |||||
const uint8_t PortIOE::ADDR = 0x20; //MCP23S17 address, all 3 ADDR pins are grounded | |||||
PortIOE portB(1, 0); | |||||
PortRead_MCP23S17 portBRead(portB); | |||||
PortWrite_MCP23S17 portBWrite(portB); //PortBWrite needed for begin() | |||||
void setup() | |||||
{ | |||||
uint8_t portBState; //bit wise | |||||
delay(6000); | |||||
portBWrite.begin(); | |||||
portBState = portBRead.read(); | |||||
Keyboard.print("portBState = "); | |||||
Keyboard.println(portBState, BIN); //should print 10101010 | |||||
} | |||||
void loop() { } |
/* unit test for PortRead_MCP23S17 | |||||
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 ouputs. | |||||
MCP23S17 on 3.3v does not output enough power to reliable light LEDs | |||||
LED lights w/o resistor | |||||
LED not light with 56 ohm resistor | |||||
*/ | |||||
#include "PortIOE.h" | |||||
#include "PortWrite_MCP23S17.h" | |||||
const uint8_t PortIOE::ADDR = 0x20; //MCP23S17 address, all 3 ADDR pins are grounded | |||||
PortIOE portA(0, 0); | |||||
PortWrite_MCP23S17 portAWrite(portA); //PortAWrite needed for begin() | |||||
const uint8_t GPIOA = 0x12; //LEDs are on port A | |||||
void setup() | |||||
{ | |||||
delay(6000); | |||||
portAWrite.begin(); | |||||
//Keyboard.print("start blinking"); | |||||
} | |||||
void loop() | |||||
{ | |||||
portAWrite.write(~0, HIGH); //set all GPIOA pins HIGH | |||||
delay(2000); | |||||
portAWrite.write(~0, LOW); //set all GPIOA pins LOW | |||||
delay(2000); | |||||
} |