Adds support for JacoBurge's TouchPad (#4186)
* add touchpad * progress * working with leds and vibrations * adds readme * Update keyboards/touchpad/readme.md Co-Authored-By: jackhumbert <jack.humb@gmail.com> * updatesmaster
parent
3cf179be61
commit
d28684da90
|
@ -179,6 +179,9 @@ i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16
|
|||
status = i2c_write(regaddr, timeout);
|
||||
if (status) return status;
|
||||
|
||||
status = i2c_stop(timeout);
|
||||
if (status) return status;
|
||||
|
||||
status = i2c_start(devaddr | 0x01, timeout);
|
||||
if (status) return status;
|
||||
|
||||
|
@ -217,4 +220,4 @@ i2c_status_t i2c_stop(uint16_t timeout)
|
|||
}
|
||||
|
||||
return I2C_STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
Copyright 2018 Jack Humbert <jack.humb@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "config_common.h"
|
||||
|
||||
/* USB Device descriptor parameter */
|
||||
#define VENDOR_ID 0x16D0
|
||||
#define PRODUCT_ID 0x0DB8
|
||||
#define DEVICE_VER 0x0001
|
||||
#define MANUFACTURER JacoBurge
|
||||
#define PRODUCT TouchPad
|
||||
#define DESCRIPTION A capacitive touchpad
|
||||
|
||||
/* key matrix size */
|
||||
#define MATRIX_ROWS 6
|
||||
#define MATRIX_COLS 6
|
||||
|
||||
|
||||
/* define if matrix has ghost */
|
||||
//#define MATRIX_HAS_GHOST
|
||||
|
||||
/* number of backlight levels */
|
||||
#define BACKLIGHT_LEVELS 3
|
||||
|
||||
/* Set 0 if debouncing isn't needed */
|
||||
#define DEBOUNCING_DELAY 5
|
||||
|
||||
/* Mechanical locking support. Use KC_LCAP, KC_LNUM or KC_LSCR instead in keymap */
|
||||
#define LOCKING_SUPPORT_ENABLE
|
||||
/* Locking resynchronize hack */
|
||||
#define LOCKING_RESYNC_ENABLE
|
||||
|
||||
/* key combination for command */
|
||||
#define IS_COMMAND() ( \
|
||||
keyboard_report->mods == (MOD_BIT(KC_LSHIFT) | MOD_BIT(KC_RSHIFT)) \
|
||||
)
|
||||
|
||||
/*
|
||||
* Feature disable options
|
||||
* These options are also useful to firmware size reduction.
|
||||
*/
|
||||
|
||||
/* disable debug print */
|
||||
//#define NO_DEBUG
|
||||
|
||||
/* disable print */
|
||||
//#define NO_PRINT
|
||||
|
||||
/* disable action features */
|
||||
//#define NO_ACTION_LAYER
|
||||
//#define NO_ACTION_TAPPING
|
||||
//#define NO_ACTION_ONESHOT
|
||||
//#define NO_ACTION_MACRO
|
||||
//#define NO_ACTION_FUNCTION
|
|
@ -0,0 +1,30 @@
|
|||
/* Copyright 2018 Jack Humbert <jack.humb@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include QMK_KEYBOARD_H
|
||||
|
||||
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
|
||||
|
||||
[0] = {
|
||||
{ KC_A, KC_B, KC_C, KC_D, KC_E, KC_F },
|
||||
{ KC_A, KC_B, KC_C, KC_D, KC_E, KC_F },
|
||||
{ KC_A, KC_B, KC_C, KC_D, KC_E, KC_F },
|
||||
{ KC_A, KC_B, KC_C, KC_D, KC_E, KC_F },
|
||||
{ KC_A, KC_B, KC_C, KC_D, KC_E, KC_F },
|
||||
{ KC_A, KC_B, KC_C, KC_D, KC_E, KC_F }
|
||||
}
|
||||
|
||||
};
|
|
@ -0,0 +1,291 @@
|
|||
/*
|
||||
MIT License
|
||||
Copyright (c) 2018, JacoBurge
|
||||
Adapted for QMK by Jack Humbert in 2018
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "matrix.h"
|
||||
#include "i2c_master.h"
|
||||
#include "quantum.h"
|
||||
|
||||
#define VIBRATE_LENGTH 50 //Defines number of interrupts motor will vibrate for, must be bigger than 8 for correct operation
|
||||
volatile uint8_t vibrate = 0; //Trigger vibration in interrupt
|
||||
|
||||
static matrix_row_t matrix[MATRIX_ROWS];
|
||||
|
||||
const uint8_t SENr[6] = {1, 2, 3, 5, 6, 7};//Maps capacitive pads to pins
|
||||
const uint8_t SENc[6] = {0, 4, 8, 9, 10, 11};
|
||||
|
||||
volatile uint8_t LEDs[6][6] = {{0}};//Stores current LED values
|
||||
|
||||
//Read data from the cap touch IC
|
||||
uint8_t readDataFromTS(uint8_t reg) {
|
||||
uint8_t rx[1] = { 0 };
|
||||
if (i2c_readReg(0x1C << 1, reg, rx, 1, 100) == 0) {
|
||||
return rx[0];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//Write data to cap touch IC
|
||||
uint8_t writeDataToTS(uint8_t reg, uint8_t data) {
|
||||
uint8_t tx[2] = { reg, data };
|
||||
if (i2c_transmit(0x1C << 1, tx, 2, 100) == 0) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint8_t checkTSPres(void) {
|
||||
return (readDataFromTS(0x00) == 0x3E);
|
||||
}
|
||||
|
||||
uint8_t capSetup(void) {
|
||||
|
||||
uint8_t temp_return = checkTSPres();
|
||||
|
||||
if (temp_return == 1) {
|
||||
// Perform measurements every 16ms
|
||||
writeDataToTS(0x08, 1);
|
||||
|
||||
// Increase detection integrator value
|
||||
writeDataToTS(0x0B, 1);
|
||||
|
||||
// Oversample to gain two bits for columns
|
||||
writeDataToTS(0x28, 0x42);
|
||||
writeDataToTS(0x29, 0x00);
|
||||
writeDataToTS(0x2A, 0x00);
|
||||
writeDataToTS(0x2B, 0x00);
|
||||
writeDataToTS(0x2C, 0x42);
|
||||
writeDataToTS(0x2D, 0x00);
|
||||
writeDataToTS(0x2E, 0x00);
|
||||
writeDataToTS(0x2F, 0x00);
|
||||
writeDataToTS(0x30, 0x42);
|
||||
writeDataToTS(0x31, 0x42);
|
||||
writeDataToTS(0x32, 0x42);
|
||||
writeDataToTS(0x33, 0x42);
|
||||
|
||||
// Recalibration if touch detected for more than 8 seconds n*0.16s
|
||||
writeDataToTS(0x0C, 50);
|
||||
|
||||
// Enable keys and set key groups
|
||||
writeDataToTS(0x1C, 0x00 | 0x04);
|
||||
writeDataToTS(0x1D, 0x00 | 0x08);
|
||||
writeDataToTS(0x1E, 0x00 | 0x08);
|
||||
writeDataToTS(0x1F, 0x00 | 0x08);
|
||||
writeDataToTS(0x20, 0x00 | 0x04);
|
||||
writeDataToTS(0x21, 0x00 | 0x08);
|
||||
writeDataToTS(0x22, 0x00 | 0x08);
|
||||
writeDataToTS(0x23, 0x00 | 0x08);
|
||||
writeDataToTS(0x24, 0x00 | 0x04);
|
||||
writeDataToTS(0x25, 0x00 | 0x04);
|
||||
writeDataToTS(0x26, 0x00 | 0x04);
|
||||
writeDataToTS(0x27, 0x00 | 0x04);
|
||||
|
||||
}
|
||||
return temp_return;
|
||||
}
|
||||
|
||||
__attribute__ ((weak))
|
||||
void matrix_init_user(void) {}
|
||||
|
||||
__attribute__ ((weak))
|
||||
void matrix_scan_user(void) {}
|
||||
|
||||
__attribute__ ((weak))
|
||||
void matrix_init_kb(void) {
|
||||
matrix_init_user();
|
||||
}
|
||||
|
||||
__attribute__ ((weak))
|
||||
void matrix_scan_kb(void) {
|
||||
matrix_scan_user();
|
||||
}
|
||||
|
||||
void matrix_init(void) {
|
||||
|
||||
i2c_init();
|
||||
|
||||
//Motor enable
|
||||
setPinOutput(E6);
|
||||
//Motor PWM
|
||||
setPinOutput(D7);
|
||||
|
||||
//Power LED
|
||||
setPinOutput(B7);
|
||||
writePinHigh(B7);
|
||||
|
||||
//LEDs Columns
|
||||
setPinOutput(F7);
|
||||
setPinOutput(F6);
|
||||
setPinOutput(F5);
|
||||
setPinOutput(F4);
|
||||
setPinOutput(F1);
|
||||
setPinOutput(F0);
|
||||
|
||||
//LEDs Rows
|
||||
setPinOutput(D6);
|
||||
setPinOutput(B4);
|
||||
setPinOutput(B5);
|
||||
setPinOutput(B6);
|
||||
setPinOutput(C6);
|
||||
setPinOutput(C7);
|
||||
|
||||
//Capacitive Interrupt
|
||||
setPinInput(D2);
|
||||
|
||||
capSetup();
|
||||
writeDataToTS(0x06, 0x12); //Calibrate capacitive touch IC
|
||||
|
||||
memset(matrix, 0, MATRIX_ROWS * sizeof(matrix_row_t));
|
||||
|
||||
matrix_init_quantum();
|
||||
}
|
||||
|
||||
|
||||
uint16_t touchDetectionRoutine(void) {
|
||||
uint16_t data;
|
||||
uint8_t temp1, temp2;
|
||||
|
||||
temp1 = readDataFromTS(0x04);
|
||||
temp2 = readDataFromTS(0x03);
|
||||
data = temp1;
|
||||
data = (data << 8) | temp2;
|
||||
return data;
|
||||
|
||||
}
|
||||
|
||||
//Process raw capacitive data, map pins to rows and columns
|
||||
void decodeArray(uint16_t dataIn, uint8_t *column, uint8_t *row) {
|
||||
uint8_t i1 = 20, i2 = 20;
|
||||
for (uint8_t i = 0; i < 12; i++) {
|
||||
if ((dataIn & 0b1) == 1) {
|
||||
if (i1 == 20) {
|
||||
i1 = i;
|
||||
} else if (i2 == 20) {
|
||||
i2 = i;
|
||||
}
|
||||
}
|
||||
dataIn = dataIn >> 1;
|
||||
}
|
||||
|
||||
for (uint8_t j = 0; j < 6; j++) {
|
||||
if (SENr[j] == i1 || SENr[j] == i2) {
|
||||
*row = j;
|
||||
}
|
||||
if (SENc[j] == i1 || SENc[j] == i2) {
|
||||
*column = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void touchClearCurrentDetections(void) {
|
||||
readDataFromTS(0x05);
|
||||
readDataFromTS(0x02);
|
||||
readDataFromTS(0x03);
|
||||
readDataFromTS(0x04);
|
||||
}
|
||||
|
||||
//Check interrupt pin
|
||||
uint8_t isTouchChangeDetected(void) {
|
||||
return !readPin(D2);
|
||||
}
|
||||
|
||||
uint8_t matrix_scan(void) {
|
||||
if (isTouchChangeDetected()) {
|
||||
uint16_t dataIn = touchDetectionRoutine();
|
||||
if ((dataIn & 0b111100010001) > 0 && (dataIn & 0b000011101110) > 0) {
|
||||
uint8_t column = 10, row = 10;
|
||||
decodeArray(dataIn, &column, &row);
|
||||
if (column != 10 && row != 10) {
|
||||
vibrate = VIBRATE_LENGTH; //Trigger vibration
|
||||
matrix[row] = _BV(column);
|
||||
} else {
|
||||
memset(matrix, 0, MATRIX_ROWS * sizeof(matrix_row_t));
|
||||
}
|
||||
} else {
|
||||
memset(matrix, 0, MATRIX_ROWS * sizeof(matrix_row_t));
|
||||
}
|
||||
touchClearCurrentDetections();
|
||||
}
|
||||
|
||||
for (uint8_t c = 0; c < 6; c++) {
|
||||
for (uint8_t r = 0; r < 6; r++) {
|
||||
switch (r) {
|
||||
case 0: writePin(D6, matrix_is_on(r, c)); break;
|
||||
case 1: writePin(B4, matrix_is_on(r, c)); break;
|
||||
case 2: writePin(B5, matrix_is_on(r, c)); break;
|
||||
case 3: writePin(B6, matrix_is_on(r, c)); break;
|
||||
case 4: writePin(C6, matrix_is_on(r, c)); break;
|
||||
case 5: writePin(C7, matrix_is_on(r, c)); break;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case 0: writePin(F5, !matrix_is_on(r, c)); break;
|
||||
case 1: writePin(F4, !matrix_is_on(r, c)); break;
|
||||
case 2: writePin(F1, !matrix_is_on(r, c)); break;
|
||||
case 3: writePin(F0, !matrix_is_on(r, c)); break;
|
||||
case 4: writePin(F6, !matrix_is_on(r, c)); break;
|
||||
case 5: writePin(F7, !matrix_is_on(r, c)); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (vibrate == VIBRATE_LENGTH) {
|
||||
writePinHigh(E6);
|
||||
writePinHigh(D7);
|
||||
vibrate--;
|
||||
} else if (vibrate > 0) {
|
||||
vibrate--;
|
||||
} else if (vibrate == 0) {
|
||||
writePinLow(D7);
|
||||
writePinLow(E6);
|
||||
}
|
||||
|
||||
matrix_scan_quantum();
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
bool matrix_is_on(uint8_t row, uint8_t col) {
|
||||
return (matrix[row] & (1<<col));
|
||||
}
|
||||
|
||||
matrix_row_t matrix_get_row(uint8_t row) {
|
||||
return matrix[row];
|
||||
}
|
||||
|
||||
void matrix_print(void) {
|
||||
printf("\nr/c 01234567\n");
|
||||
for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
|
||||
printf("%X0: ", row);
|
||||
matrix_row_t data = matrix_get_row(row);
|
||||
for (int col = 0; col < MATRIX_COLS; col++) {
|
||||
if (data & (1<<col))
|
||||
printf("1");
|
||||
else
|
||||
printf("0");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
# Touchpad
|
||||
|
||||
![TouchPad](https://static1.squarespace.com/static/561b7180e4b05a82a1747f0b/59d8985903596eb5953e1803/5a43f205e4966b67c55878a2/1521104083905/IMG_2183.jpg?format=2500w)
|
||||
|
||||
A small capacitive 6x6 touchpad for launching programs. [More info](https://jacoburge.co.uk/touch-pad/)
|
||||
|
||||
Keyboard Maintainer: QMK Community
|
||||
Hardware Supported: TouchPad PCB
|
||||
Hardware Availability: [JaboBurge's Shop](https://jacoburge.co.uk/shop)
|
||||
|
||||
Make example for this keyboard (after setting up your build environment):
|
||||
|
||||
make touchpad:default
|
||||
|
||||
See the [build environment setup](https://docs.qmk.fm/#/getting_started_build_tools) and the [make instructions](https://docs.qmk.fm/#/getting_started_make_guide) for more information. Brand new to QMK? Start with our [Complete Newbs Guide](https://docs.qmk.fm/#/newbs).
|
|
@ -0,0 +1,67 @@
|
|||
# MCU name
|
||||
MCU = atmega32u4
|
||||
|
||||
SRC = matrix.c i2c_master.c
|
||||
|
||||
# Processor frequency.
|
||||
# This will define a symbol, F_CPU, in all source code files equal to the
|
||||
# processor frequency in Hz. You can then use this symbol in your source code to
|
||||
# calculate timings. Do NOT tack on a 'UL' at the end, this will be done
|
||||
# automatically to create a 32-bit value in your source code.
|
||||
#
|
||||
# This will be an integer division of F_USB below, as it is sourced by
|
||||
# F_USB after it has run through any CPU prescalers. Note that this value
|
||||
# does not *change* the processor frequency - it should merely be updated to
|
||||
# reflect the processor speed set externally so that the code can use accurate
|
||||
# software delays.
|
||||
F_CPU = 16000000
|
||||
|
||||
#
|
||||
# LUFA specific
|
||||
#
|
||||
# Target architecture (see library "Board Types" documentation).
|
||||
ARCH = AVR8
|
||||
|
||||
# Input clock frequency.
|
||||
# This will define a symbol, F_USB, in all source code files equal to the
|
||||
# input clock frequency (before any prescaling is performed) in Hz. This value may
|
||||
# differ from F_CPU if prescaling is used on the latter, and is required as the
|
||||
# raw input clock is fed directly to the PLL sections of the AVR for high speed
|
||||
# clock generation for the USB and other AVR subsections. Do NOT tack on a 'UL'
|
||||
# at the end, this will be done automatically to create a 32-bit value in your
|
||||
# source code.
|
||||
#
|
||||
# If no clock division is performed on the input clock inside the AVR (via the
|
||||
# CPU clock adjust registers or the clock division fuses), this will be equal to F_CPU.
|
||||
F_USB = $(F_CPU)
|
||||
|
||||
# Bootloader
|
||||
# This definition is optional, and if your keyboard supports multiple bootloaders of
|
||||
# different sizes, comment this out, and the correct address will be loaded
|
||||
# automatically (+60). See bootloader.mk for all options.
|
||||
BOOTLOADER = caterina
|
||||
|
||||
# Interrupt driven control endpoint task(+60)
|
||||
OPT_DEFS += -DINTERRUPT_CONTROL_ENDPOINT
|
||||
|
||||
# Build Options
|
||||
# change to "no" to disable the options, or define them in the Makefile in
|
||||
# the appropriate keymap folder that will get included automatically
|
||||
#
|
||||
BOOTMAGIC_ENABLE = no # Virtual DIP switch configuration(+1000)
|
||||
MOUSEKEY_ENABLE = no # Mouse keys(+4700)
|
||||
EXTRAKEY_ENABLE = yes # Audio control and System control(+450)
|
||||
CONSOLE_ENABLE = yes # Console for debug(+400)
|
||||
COMMAND_ENABLE = no # Commands for debug and configuration
|
||||
NKRO_ENABLE = yes # Nkey Rollover - if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work
|
||||
BACKLIGHT_ENABLE = no # Enable keyboard backlight functionality
|
||||
MIDI_ENABLE = no # MIDI controls
|
||||
AUDIO_ENABLE = no # Audio output on port C6
|
||||
UNICODE_ENABLE = no # Unicode
|
||||
BLUETOOTH_ENABLE = no # Enable Bluetooth with the Adafruit EZ-Key HID
|
||||
RGBLIGHT_ENABLE = no # Enable WS2812 RGB underlight.
|
||||
API_SYSEX_ENABLE = no
|
||||
CUSTOM_MATRIX = yes
|
||||
|
||||
# Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE
|
||||
SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend
|
|
@ -0,0 +1 @@
|
|||
#include "touchpad.h"
|
|
@ -0,0 +1,2 @@
|
|||
#pragma once
|
||||
#include "quantum.h"
|
Loading…
Reference in New Issue