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>

* updates
daktil_manuform
Jack Humbert 2019-01-10 11:26:40 -05:00 committed by MechMerlin
parent 3cf179be61
commit d28684da90
8 changed files with 480 additions and 1 deletions

View File

@ -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;
}
}

View File

@ -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

View File

@ -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 }
}
};

View File

@ -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");
}
}

View File

@ -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).

View File

@ -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

View File

@ -0,0 +1 @@
#include "touchpad.h"

View File

@ -0,0 +1,2 @@
#pragma once
#include "quantum.h"