diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3d18462..4bbc30e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,8 +7,9 @@ Improvement suggestions 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) -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). + * Provide a use case * Explain why the improvement is useful * Site other product examples where this improvement exists @@ -16,6 +17,8 @@ Submit improvement suggestions to [GitHub issues](https://github.com/wolfv6/Keyb Bug reports ----------- 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! * Complete sketch (copy & paste, attachment, or a link to the code) @@ -52,7 +55,7 @@ A healthy project needs the perspective of many people. * Documentation - Suggest a clarification, simplification, correction, or other improvement. 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. -* [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: * Use Markdown with a .md suffix. diff --git a/doc/keybrd_library_developer_guide.md b/doc/keybrd_library_developer_guide.md index b912204..4506c49 100644 --- a/doc/keybrd_library_developer_guide.md +++ b/doc/keybrd_library_developer_guide.md @@ -27,7 +27,7 @@ Row_Ext::keyWasPressed() overrides Row::keyWasPressed() which is used to unstick Row_Ext_uC and Row_Ext_ShiftRegisters are a custom classes composed of stock keybrd library classes.
Row_Ext_uC uses Scanner_uC to scan the primary matrix.
-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 ``` @@ -68,7 +68,7 @@ Keybrd library class inheritance diagram ``` ________ 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 @@ -78,7 +78,7 @@ Keybrd library class inheritance diagram PortWrite | - PortWrite_PCA9655E (one PortWrite class for each IOE type) + PortWrite_PCA9655E (one PortWrite class for each IOE type) PortRead | @@ -115,9 +115,9 @@ Keybrd library class inheritance diagram | | | | 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 @@ -149,7 +149,7 @@ Dependency diagram of example multi-layer keyboard with layer LEDs ``` -Dependency diagram of example secondary matrix with shift registers +Dependency diagram of example peripheral matrix with shift registers ``` Row_ShiftRegisters[1..*] / \ \ @@ -159,17 +159,17 @@ Dependency diagram of example secondary matrix with shift registers ``` -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..*] ``` @@ -243,7 +243,7 @@ Following the style guide makes it easier for the next programmer to understand Trace of keybrd scan -------------------- 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. Refer to it like a table of contents while reading the keybrd library. diff --git a/src/PortRead.h b/src/PortRead.h index 8293045..9f3da80 100644 --- a/src/PortRead.h +++ b/src/PortRead.h @@ -13,7 +13,7 @@ Details are in config_key.h class PortRead { protected: - const uint8_t readPins; //bitwise pin configuration, 1 means read column + const uint8_t readPins; //bitwise pin configuration, 1 means read pin public: PortRead(const uint8_t readPins): readPins(readPins) {} virtual uint8_t read()=0; diff --git a/src/PortRead_MCP23S17.cpp b/src/PortRead_MCP23S17.cpp new file mode 100644 index 0000000..199bd64 --- /dev/null +++ b/src/PortRead_MCP23S17.cpp @@ -0,0 +1,23 @@ +#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; +} diff --git a/src/PortRead_MCP23S17.h b/src/PortRead_MCP23S17.h new file mode 100644 index 0000000..97ab958 --- /dev/null +++ b/src/PortRead_MCP23S17.h @@ -0,0 +1,42 @@ +#ifndef PORTREAD_MCP23S17_H +#define PORTREAD_MCP23S17_H +#include +#include +#include +#include +#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 diff --git a/src/PortWrite_MCP23S17.cpp b/src/PortWrite_MCP23S17.cpp new file mode 100644 index 0000000..73fd1c5 --- /dev/null +++ b/src/PortWrite_MCP23S17.cpp @@ -0,0 +1,50 @@ +#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 +} diff --git a/src/PortWrite_MCP23S17.h b/src/PortWrite_MCP23S17.h new file mode 100644 index 0000000..7d2ea90 --- /dev/null +++ b/src/PortWrite_MCP23S17.h @@ -0,0 +1,46 @@ +#ifndef PORTWRITE_MCP23S17_H +#define PORTWRITE_MCP23S17_H +#include +#include +#include +#include +#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 diff --git a/src/Row.h b/src/Row.h index 7b70013..a0b797a 100644 --- a/src/Row.h +++ b/src/Row.h @@ -14,8 +14,7 @@ class Row Key *const *const ptrsKeys; //array of Key pointers virtual void keyWasPressed(); 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); public: Row(Key* const ptrsKeys[]) : ptrsKeys(ptrsKeys), debounced(0) { } diff --git a/src/Row_IOE.cpp b/src/Row_IOE.cpp new file mode 100644 index 0000000..066623a --- /dev/null +++ b/src/Row_IOE.cpp @@ -0,0 +1,12 @@ +#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); +} diff --git a/src/Row_IOE.h b/src/Row_IOE.h new file mode 100644 index 0000000..dd3b400 --- /dev/null +++ b/src/Row_IOE.h @@ -0,0 +1,26 @@ +#ifndef ROW_IOE_H +#define ROW_IOE_H + +#include +#include +#include +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 diff --git a/src/Scanner_ShiftRegs74HC165.h b/src/Scanner_ShiftRegs74HC165.h index 3f83f84..359f4b6 100644 --- a/src/Scanner_ShiftRegs74HC165.h +++ b/src/Scanner_ShiftRegs74HC165.h @@ -13,7 +13,7 @@ shift registers 74HC165 are Parallel-In-Serial-Out (PISO) Upto 4 shift registers can be in a daisy chained for a total of 32 read pins. 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) Controller's MISO pin is connected to shift register's complementary serial output (/QH) pin Use these two lines in the sketch: diff --git a/tutorials/breadboard_keyboard_supplies.ods b/tutorials/breadboard_keyboard_supplies.ods index 6acdd35..b185ce4 100644 Binary files a/tutorials/breadboard_keyboard_supplies.ods and b/tutorials/breadboard_keyboard_supplies.ods differ diff --git a/tutorials/keybrd_1_breadboard/keybrd_1_breadboard.ino b/tutorials/keybrd_1_breadboard/keybrd_1_breadboard.ino index ee185ec..398f3e9 100644 --- a/tutorials/keybrd_1_breadboard/keybrd_1_breadboard.ino +++ b/tutorials/keybrd_1_breadboard/keybrd_1_breadboard.ino @@ -32,7 +32,7 @@ Code_Sc s_c(KEY_C); Key* ptrsKeys_0[] = { &s_1, &s_a }; 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); // ################### MAIN #################### diff --git a/tutorials/keybrd_4b_split_keyboard_with_shift_registers/keybrd_4b_split_keyboard_with_shift_registers.ino b/tutorials/keybrd_4b_split_keyboard_with_shift_registers/keybrd_4b_split_keyboard_with_shift_registers.ino new file mode 100644 index 0000000..5831af4 --- /dev/null +++ b/tutorials/keybrd_4b_split_keyboard_with_shift_registers/keybrd_4b_split_keyboard_with_shift_registers.ino @@ -0,0 +1,96 @@ +/* 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 +#include + +//Left matrix +#include + +//Right matrix +#include +#include + +// =============== 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(); +} diff --git a/tutorials/keybrd_4c_split_with_IOE/keybrd_4c_split_with_IOE.ino b/tutorials/keybrd_4c_split_with_IOE/keybrd_4c_split_with_IOE.ino new file mode 100644 index 0000000..656f571 --- /dev/null +++ b/tutorials/keybrd_4c_split_with_IOE/keybrd_4c_split_with_IOE.ino @@ -0,0 +1,105 @@ +/* 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 +#include + +//left matrix +#include + +//right matrix +#include +#include +#include +#include + +// ============ 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(); +} diff --git a/tutorials/tutorial_10_writing_IOE_Port_classes.md b/tutorials/tutorial_10_writing_IOE_Port_classes.md index 1c34dfc..c3304dc 100644 --- a/tutorials/tutorial_10_writing_IOE_Port_classes.md +++ b/tutorials/tutorial_10_writing_IOE_Port_classes.md @@ -1,19 +1,38 @@ -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. -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.
Creative Commons License
keybrd tutorial by Wolfram Volpi is licensed under a Creative Commons Attribution 4.0 International License.
Permissions beyond the scope of this license may be available at https://github.com/wolfv6/keybrd/issues/new. diff --git a/tutorials/tutorial_8b_creating_and_publishing_your_own_keybrd_extension_library.md b/tutorials/tutorial_8b_sharing_your_keybrd_extension_library.md similarity index 77% rename from tutorials/tutorial_8b_creating_and_publishing_your_own_keybrd_extension_library.md rename to tutorials/tutorial_8b_sharing_your_keybrd_extension_library.md index 86b6793..01d5161 100644 --- a/tutorials/tutorial_8b_creating_and_publishing_your_own_keybrd_extension_library.md +++ b/tutorials/tutorial_8b_sharing_your_keybrd_extension_library.md @@ -1,12 +1,12 @@ -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. 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. -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. keybrd_MyKeyboard/ @@ -25,16 +25,13 @@ Publishing your keybrd extension library with the following directory structure instantiations_codes.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. 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 Arduino Library-Manager is that users can easily find and install your library through the Arduino IDE. @@ -78,14 +75,11 @@ Example library.properties file: Instructions for listing a library on Arduino Library Manager are at: 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. 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 1. Update the version in your library.properties file: diff --git a/tutorials/tutorial_8c_sharing_your_keybrd_sketch.md b/tutorials/tutorial_8c_sharing_your_keybrd_sketch.md new file mode 100644 index 0000000..97fd1c5 --- /dev/null +++ b/tutorials/tutorial_8c_sharing_your_keybrd_sketch.md @@ -0,0 +1,17 @@ +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". diff --git a/unit_tests/MCP23S17_read/MCP23S17_read.ino b/unit_tests/MCP23S17_read/MCP23S17_read.ino new file mode 100644 index 0000000..27911fd --- /dev/null +++ b/unit_tests/MCP23S17_read/MCP23S17_read.ino @@ -0,0 +1,42 @@ +/* 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 + +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() { } diff --git a/unit_tests/MCP23S17_write/MCP23S17_write.ino b/unit_tests/MCP23S17_write/MCP23S17_write.ino new file mode 100644 index 0000000..76dfc69 --- /dev/null +++ b/unit_tests/MCP23S17_write/MCP23S17_write.ino @@ -0,0 +1,54 @@ +/* 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 + +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 +} diff --git a/unit_tests/PortRead_MCP23S17/PortRead_MCP23S17.ino b/unit_tests/PortRead_MCP23S17/PortRead_MCP23S17.ino new file mode 100644 index 0000000..dd84376 --- /dev/null +++ b/unit_tests/PortRead_MCP23S17/PortRead_MCP23S17.ino @@ -0,0 +1,32 @@ +/* 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() { } diff --git a/unit_tests/PortWrite_MCP23S17/PortWrite_MCP23S17.ino b/unit_tests/PortWrite_MCP23S17/PortWrite_MCP23S17.ino new file mode 100644 index 0000000..2901f5e --- /dev/null +++ b/unit_tests/PortWrite_MCP23S17/PortWrite_MCP23S17.ino @@ -0,0 +1,35 @@ +/* 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); +}