summaryrefslogtreecommitdiffstats
path: root/keyboards/ploopyco
diff options
context:
space:
mode:
Diffstat (limited to 'keyboards/ploopyco')
-rw-r--r--keyboards/ploopyco/mouse/config.h73
-rw-r--r--keyboards/ploopyco/mouse/info.json21
-rw-r--r--keyboards/ploopyco/mouse/keymaps/default/keymap.c23
-rw-r--r--keyboards/ploopyco/mouse/keymaps/default/readme.md1
-rw-r--r--keyboards/ploopyco/mouse/keymaps/via/keymap.c26
-rw-r--r--keyboards/ploopyco/mouse/keymaps/via/rules.mk1
-rw-r--r--keyboards/ploopyco/mouse/mouse.c237
-rw-r--r--keyboards/ploopyco/mouse/mouse.h40
-rw-r--r--keyboards/ploopyco/mouse/readme.md68
-rw-r--r--keyboards/ploopyco/mouse/rules.mk30
-rw-r--r--keyboards/ploopyco/opt_encoder.c211
-rw-r--r--keyboards/ploopyco/opt_encoder.h66
-rw-r--r--keyboards/ploopyco/pmw3600.c222
-rw-r--r--keyboards/ploopyco/pmw3600.h103
-rw-r--r--keyboards/ploopyco/pmw3600_firmware.h300
-rw-r--r--keyboards/ploopyco/trackball/config.h69
-rw-r--r--keyboards/ploopyco/trackball/info.json18
-rw-r--r--keyboards/ploopyco/trackball/keymaps/default/keymap.c26
-rw-r--r--keyboards/ploopyco/trackball/keymaps/default/readme.md1
-rw-r--r--keyboards/ploopyco/trackball/keymaps/via/keymap.c26
-rw-r--r--keyboards/ploopyco/trackball/keymaps/via/rules.mk1
-rw-r--r--keyboards/ploopyco/trackball/readme.md68
-rw-r--r--keyboards/ploopyco/trackball/rules.mk30
-rw-r--r--keyboards/ploopyco/trackball/trackball.c237
-rw-r--r--keyboards/ploopyco/trackball/trackball.h40
25 files changed, 1938 insertions, 0 deletions
diff --git a/keyboards/ploopyco/mouse/config.h b/keyboards/ploopyco/mouse/config.h
new file mode 100644
index 0000000000..787eb9a85e
--- /dev/null
+++ b/keyboards/ploopyco/mouse/config.h
@@ -0,0 +1,73 @@
+/* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
+ * Copyright 2019 Sunjun Kim
+ * Copyright 2020 Ploopy Corporation
+ *
+ * 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 0x5043
+#define PRODUCT_ID 0x4D6F
+#define DEVICE_VER 0x0001
+#define MANUFACTURER Ploopyco
+#define PRODUCT Mouse
+
+/* key matrix size */
+#define MATRIX_ROWS 1
+#define MATRIX_COLS 8
+
+/*
+ * Keyboard Matrix Assignments
+ *
+ * Change this to how you wired your keyboard
+ * COLS: AVR pins used for columns, left to right
+ * ROWS: AVR pins used for rows, top to bottom
+ * DIODE_DIRECTION: COL2ROW = COL = Anode (+), ROW = Cathode (-, marked on diode)
+ * ROW2COL = ROW = Anode (+), COL = Cathode (-, marked on diode)
+ *
+ */
+#define DIRECT_PINS \
+ { \
+ { D4, D2, E6, B6, D7, C6, C7, B7 } \
+ }
+
+// These pins are not broken out, and cannot be used normally.
+// They are set as output and pulled high, by default
+#define UNUSED_PINS \
+ { B4, D6, F1, F5, F6, F7 }
+
+/* Debounce reduces chatter (unintended double-presses) - set 0 if debouncing is not needed */
+#define DEBOUNCE 5
+
+/* Much more so than a keyboard, speed matters for a mouse. So we'll go for as high
+ a polling rate as possible. */
+#define USB_POLLING_INTERVAL_MS 1
+
+/* define if matrix has ghost (lacks anti-ghosting diodes) */
+//#define MATRIX_HAS_GHOST
+
+/* disable action features */
+//#define NO_ACTION_LAYER
+//#define NO_ACTION_TAPPING
+//#define NO_ACTION_ONESHOT
+#define NO_ACTION_MACRO
+#define NO_ACTION_FUNCTION
+
+/* Bootmagic Lite key configuration */
+#define BOOTMAGIC_LITE_ROW 0
+#define BOOTMAGIC_LITE_COLUMN 3
diff --git a/keyboards/ploopyco/mouse/info.json b/keyboards/ploopyco/mouse/info.json
new file mode 100644
index 0000000000..6763838dcf
--- /dev/null
+++ b/keyboards/ploopyco/mouse/info.json
@@ -0,0 +1,21 @@
+{
+ "keyboard_name": "PloopyCo Mouse",
+ "url": "",
+ "maintainer": "drashna",
+ "width": 8,
+ "height": 3,
+ "layouts": {
+ "LAYOUT": {
+ "layout": [
+ {"x":1, "y":0, "h":2},
+ {"x":2, "y":0, "h":2},
+ {"x":3, "y":0.25, "h":1.25},
+ {"x":4, "y":0, "h":2},
+ {"x":5, "y":0, "h":2},
+ {"x":0, "y":0},
+ {"x":0, "y":1},
+ {"x":3, "y":1.5}
+ ]
+ }
+ }
+}
diff --git a/keyboards/ploopyco/mouse/keymaps/default/keymap.c b/keyboards/ploopyco/mouse/keymaps/default/keymap.c
new file mode 100644
index 0000000000..c02d23d2ab
--- /dev/null
+++ b/keyboards/ploopyco/mouse/keymaps/default/keymap.c
@@ -0,0 +1,23 @@
+/* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
+ * Copyright 2019 Sunjun Kim
+ * Copyright 2020 Ploopy Corporation
+ *
+ * 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] = LAYOUT(/* Base */
+ C(KC_C), KC_BTN1, KC_BTN3, KC_BTN2, C(KC_C), KC_BTN4, KC_BTN5, C(KC_Z)),
+};
diff --git a/keyboards/ploopyco/mouse/keymaps/default/readme.md b/keyboards/ploopyco/mouse/keymaps/default/readme.md
new file mode 100644
index 0000000000..f965ef3c32
--- /dev/null
+++ b/keyboards/ploopyco/mouse/keymaps/default/readme.md
@@ -0,0 +1 @@
+# The default keymap for Ploopyco Trackball
diff --git a/keyboards/ploopyco/mouse/keymaps/via/keymap.c b/keyboards/ploopyco/mouse/keymaps/via/keymap.c
new file mode 100644
index 0000000000..27a0384381
--- /dev/null
+++ b/keyboards/ploopyco/mouse/keymaps/via/keymap.c
@@ -0,0 +1,26 @@
+/* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
+ * Copyright 2019 Sunjun Kim
+ * Copyright 2020 Ploopy Corporation
+ *
+ * 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] = LAYOUT(/* Base */
+ C(KC_C), KC_BTN1, KC_BTN3, KC_BTN2, C(KC_C), KC_BTN4, KC_BTN5, C(KC_Z)),
+ [1] = LAYOUT(_______, _______, _______, _______, _______, _______, _______, _______),
+ [2] = LAYOUT(_______, _______, _______, _______, _______, _______, _______, _______),
+ [3] = LAYOUT(_______, _______, _______, _______, _______, _______, _______, _______),
+};
diff --git a/keyboards/ploopyco/mouse/keymaps/via/rules.mk b/keyboards/ploopyco/mouse/keymaps/via/rules.mk
new file mode 100644
index 0000000000..1e5b99807c
--- /dev/null
+++ b/keyboards/ploopyco/mouse/keymaps/via/rules.mk
@@ -0,0 +1 @@
+VIA_ENABLE = yes
diff --git a/keyboards/ploopyco/mouse/mouse.c b/keyboards/ploopyco/mouse/mouse.c
new file mode 100644
index 0000000000..6a9bffbffe
--- /dev/null
+++ b/keyboards/ploopyco/mouse/mouse.c
@@ -0,0 +1,237 @@
+/* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
+ * Copyright 2019 Sunjun Kim
+ * Copyright 2020 Ploopy Corporation
+ *
+ * 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
+
+#ifndef OPT_DEBOUNCE
+# define OPT_DEBOUNCE 5 // (ms) Time between scroll events
+#endif
+#ifndef SCROLL_BUTT_DEBOUNCE
+# define SCROLL_BUTT_DEBOUNCE 100 // (ms) Time between scroll events
+#endif
+#ifndef OPT_THRES
+# define OPT_THRES 150 // (0-1024) Threshold for actication
+#endif
+#ifndef OPT_SCALE
+# define OPT_SCALE 1 // Multiplier for wheel
+#endif
+
+// TODO: Implement libinput profiles
+// https://wayland.freedesktop.org/libinput/doc/latest/pointer-acceleration.html
+// Compile time accel selection
+// Valid options are ACC_NONE, ACC_LINEAR, ACC_CUSTOM, ACC_QUADRATIC
+
+// Trackball State
+bool is_scroll_clicked = false;
+bool BurstState = false; // init burst state for Trackball module
+uint16_t MotionStart = 0; // Timer for accel, 0 is resting state
+uint16_t lastScroll = 0; // Previous confirmed wheel event
+uint16_t lastMidClick = 0; // Stops scrollwheel from being read if it was pressed
+uint8_t OptLowPin = OPT_ENC1;
+bool debug_encoder = false;
+
+__attribute__((weak)) void process_wheel_user(report_mouse_t* mouse_report, int16_t h, int16_t v) {
+ mouse_report->h = h;
+ mouse_report->v = v;
+}
+
+__attribute__((weak)) void process_wheel(report_mouse_t* mouse_report) {
+ // TODO: Replace this with interrupt driven code, polling is S L O W
+ // Lovingly ripped from the Ploopy Source
+
+ // If the mouse wheel was just released, do not scroll.
+ if (timer_elapsed(lastMidClick) < SCROLL_BUTT_DEBOUNCE) {
+ return;
+ }
+
+ // Limit the number of scrolls per unit time.
+ if (timer_elapsed(lastScroll) < OPT_DEBOUNCE) {
+ return;
+ }
+
+ // Don't scroll if the middle button is depressed.
+ if (is_scroll_clicked) {
+#ifndef IGNORE_SCROLL_CLICK
+ return;
+#endif
+ }
+
+ lastScroll = timer_read();
+ uint16_t p1 = adc_read(OPT_ENC1_MUX);
+ uint16_t p2 = adc_read(OPT_ENC2_MUX);
+ if (debug_encoder) dprintf("OPT1: %d, OPT2: %d\n", p1, p2);
+
+ uint8_t dir = opt_encoder_handler(p1, p2);
+
+ if (dir == 0) return;
+ process_wheel_user(mouse_report, mouse_report->h, (int)(mouse_report->v + (dir * OPT_SCALE)));
+}
+
+__attribute__((weak)) void process_mouse_user(report_mouse_t* mouse_report, int16_t x, int16_t y) {
+ mouse_report->x = x;
+ mouse_report->y = y;
+}
+
+__attribute__((weak)) void process_mouse(report_mouse_t* mouse_report) {
+ report_pmw_t data = pmw_read_burst();
+ if (data.isOnSurface && data.isMotion) {
+ // Reset timer if stopped moving
+ if (!data.isMotion) {
+ if (MotionStart != 0) MotionStart = 0;
+ return;
+ }
+
+ // Set timer if new motion
+ if ((MotionStart == 0) && data.isMotion) {
+ if (debug_mouse) dprintf("Starting motion.\n");
+ MotionStart = timer_read();
+ }
+
+ if (debug_mouse) {
+ dprintf("Delt] d: %d t: %u\n", abs(data.dx) + abs(data.dy), MotionStart);
+ }
+ if (debug_mouse) {
+ dprintf("Pre ] X: %d, Y: %d\n", data.dx, data.dy);
+ }
+#if defined(PROFILE_LINEAR)
+ float scale = float(timer_elaspsed(MotionStart)) / 1000.0;
+ data.dx *= scale;
+ data.dy *= scale;
+#elif defined(PROFILE_INVERSE)
+ // TODO
+#else
+ // no post processing
+#endif
+ // apply multiplier
+ // data.dx *= mouse_multiplier;
+ // data.dy *= mouse_multiplier;
+
+ // Wrap to HID size
+ data.dx = constrain(data.dx, -127, 127);
+ data.dy = constrain(data.dy, -127, 127);
+ if (debug_mouse) dprintf("Cons] X: %d, Y: %d\n", data.dx, data.dy);
+ // dprintf("Elapsed:%u, X: %f Y: %\n", i, pgm_read_byte(firmware_data+i));
+
+ process_mouse_user(mouse_report, data.dx, data.dy);
+ }
+}
+
+bool process_record_kb(uint16_t keycode, keyrecord_t* record) {
+ if (debug_mouse) {
+ dprintf("KL: kc: %u, col: %u, row: %u, pressed: %u\n", keycode, record->event.key.col, record->event.key.row, record->event.pressed);
+ }
+
+ // Update Timer to prevent accidental scrolls
+ if ((record->event.key.col == 2) && (record->event.key.row == 0)) {
+ lastMidClick = timer_read();
+ is_scroll_clicked = record->event.pressed;
+ }
+
+/* If Mousekeys is disabled, then use handle the mouse button
+ * keycodes. This makes things simpler, and allows usage of
+ * the keycodes in a consistent manner. But only do this if
+ * Mousekeys is not enable, so it's not handled twice.
+ */
+#ifndef MOUSEKEY_ENABLE
+ if (IS_MOUSEKEY_BUTTON(keycode)) {
+ report_mouse_t currentReport = pointing_device_get_report();
+ if (record->event.pressed) {
+ if (keycode == KC_MS_BTN1)
+ currentReport.buttons |= MOUSE_BTN1;
+ else if (keycode == KC_MS_BTN2)
+ currentReport.buttons |= MOUSE_BTN2;
+ else if (keycode == KC_MS_BTN3)
+ currentReport.buttons |= MOUSE_BTN3;
+ else if (keycode == KC_MS_BTN4)
+ currentReport.buttons |= MOUSE_BTN4;
+ else if (keycode == KC_MS_BTN5)
+ currentReport.buttons |= MOUSE_BTN5;
+ } else {
+ if (keycode == KC_MS_BTN1)
+ currentReport.buttons &= ~MOUSE_BTN1;
+ else if (keycode == KC_MS_BTN2)
+ currentReport.buttons &= ~MOUSE_BTN2;
+ else if (keycode == KC_MS_BTN3)
+ currentReport.buttons &= ~MOUSE_BTN3;
+ else if (keycode == KC_MS_BTN4)
+ currentReport.buttons &= ~MOUSE_BTN4;
+ else if (keycode == KC_MS_BTN5)
+ currentReport.buttons &= ~MOUSE_BTN5;
+ }
+ pointing_device_set_report(currentReport);
+ }
+#endif
+
+ return process_record_user(keycode, record);
+}
+
+// Hardware Setup
+void keyboard_pre_init_kb(void) {
+ // debug_enable = true;
+ // debug_matrix = true;
+ // debug_mouse = true;
+ // debug_encoder = true;
+
+ setPinInput(OPT_ENC1);
+ setPinInput(OPT_ENC2);
+
+ // This is the debug LED.
+ setPinOutput(F7);
+ writePin(F7, debug_enable);
+
+ /* Ground all output pins connected to ground. This provides additional
+ * pathways to ground. If you're messing with this, know this: driving ANY
+ * of these pins high will cause a short. On the MCU. Ka-blooey.
+ */
+#ifdef UNUSED_PINS
+ const pin_t unused_pins[] = UNUSED_PINS;
+
+ for (uint8_t i = 0; i < (sizeof(unused_pins) / sizeof(pin_t)); i++) {
+ setPinOutput(unused_pins[i]);
+ writePinLow(unused_pins[i]);
+ }
+#endif
+ keyboard_pre_init_user();
+}
+
+void pointing_device_init(void) {
+ // initialize ball sensor
+ pmw_spi_init();
+ // initialize the scroll wheel's optical encoder
+ opt_encoder_init();
+}
+
+bool has_report_changed (report_mouse_t first, report_mouse_t second) {
+ return !(
+ (!first.buttons && first.buttons == second.buttons) &&
+ (!first.x && first.x == second.x) &&
+ (!first.y && first.y == second.y) &&
+ (!first.h && first.h == second.h) &&
+ (!first.v && first.v == second.v) );
+}
+
+void pointing_device_task(void) {
+ report_mouse_t mouse_report = pointing_device_get_report();
+ process_wheel(&mouse_report);
+ process_mouse(&mouse_report);
+
+ pointing_device_set_report(mouse_report);
+ if (has_report_changed(mouse_report, pointing_device_get_report())) {
+ pointing_device_send();
+ }
+}
diff --git a/keyboards/ploopyco/mouse/mouse.h b/keyboards/ploopyco/mouse/mouse.h
new file mode 100644
index 0000000000..3c89d64d8c
--- /dev/null
+++ b/keyboards/ploopyco/mouse/mouse.h
@@ -0,0 +1,40 @@
+/* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
+ * Copyright 2019 Sunjun Kim
+ * Copyright 2020 Ploopy Corporation
+ *
+ * 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 "quantum.h"
+#include "spi_master.h"
+#include "pmw3600.h"
+#include "analog.h"
+#include "opt_encoder.h"
+#include "pointing_device.h"
+
+// Sensor defs
+#define OPT_ENC1 F0
+#define OPT_ENC2 F4
+#define OPT_ENC1_MUX 0
+#define OPT_ENC2_MUX 4
+
+void process_mouse(report_mouse_t* mouse_report);
+void process_mouse_user(report_mouse_t* mouse_report, int16_t x, int16_t y);
+void process_wheel(report_mouse_t* mouse_report);
+void process_wheel_user(report_mouse_t* mouse_report, int16_t h, int16_t v);
+
+#define LAYOUT(BLL, BL, BM, BR, BRR, BF, BB, BDPI) \
+ { {BL, BM, BR, BF, BB, BRR, BLL, BDPI}, }
diff --git a/keyboards/ploopyco/mouse/readme.md b/keyboards/ploopyco/mouse/readme.md
new file mode 100644
index 0000000000..abfa643a44
--- /dev/null
+++ b/keyboards/ploopyco/mouse/readme.md
@@ -0,0 +1,68 @@
+# Ploopyco Mouse
+
+![Ploopyco Mouse](https://i.redd.it/bf7bkzqzeti51.jpg)
+
+It's a DIY, QMK Powered Trackball!!!!
+
+Everything works. However the scroll wheel has some issues and acts very odd.
+
+* Keyboard Maintainer: [PloopyCo](https://github.com/ploopyco), [Drashna Jael're](https://github.com/drashna/), [Germ](https://github.com/germ/)
+* Hardware Supported: ATMega32u4 8MHz(3.3v)
+* Hardware Availability: [Store](https://ploopy.co), [GitHub](https://github.com/ploopyco)
+
+Make example for this keyboard (after setting up your build environment):
+
+ make ploopyco/mouse:default:flash
+
+To jump to the bootloader, hold down "Button 4" (immediate right of the Mouse)
+
+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).
+
+# Customzing your PloopyCo Trackball
+
+While the defaults are designed so that it can be plugged in and used right away, there are a number of things that you may want to change. Such as adding DPI control, or to use the ball to scroll while holding a button. To allow for this sort of control, there is a callback for both the scroll wheel and the mouse censor.
+
+The default behavior for this is:
+
+```c
+void process_wheel_user(report_mouse_t* mouse_report, int16_t h, int16_t v) {
+ mouse_report->h = h;
+ mouse_report->v = v;
+}
+
+void process_mouse_user(report_mouse_t* mouse_report, int16_t x, int16_t y) {
+ mouse_report->x = x;
+ mouse_report->y = y;
+}
+```
+
+This should allow you to more heavily customize the behavior.
+
+Alternatively, the `process_wheel` and `process_mouse` functions can both be replaced too, to allow for even more functionality.
+
+Additionally, you can change the DPI/CPI or speed of the Mouse by calling `pmw_set_cpi` at any time. And tThe default can be changed by adding a define to the keymap's `config.h` file:
+
+ #define PMW_CPI 1600
+
+# Programming QMK-DFU onto the PloopyCo Mouse
+
+If you would rather have DFU on this board, you can use the QMK-DFU bootloader on the device. To do so, you want to run:
+
+ make ploopyco/trackball:default:production
+
+Once you have that, you'll need to [ISP Flash](https://docs.qmk.fm/#/isp_flashing_guide) the chip with the new bootloader hex file created (or the production hex), and set the fuses:
+
+
+| Fuse | Setting |
+|----------|------------------|
+| Low | `0xDF` |
+| High | `0xD8` or `0x98` |
+| Extended | `0xCB` |
+
+Original (Caterina) settings:
+
+| Fuse | Setting |
+|----------|------------------|
+| Low | `0xFF` |
+| High | `0xD8` |
+| Extended | `0xFE` |
diff --git a/keyboards/ploopyco/mouse/rules.mk b/keyboards/ploopyco/mouse/rules.mk
new file mode 100644
index 0000000000..d77431b676
--- /dev/null
+++ b/keyboards/ploopyco/mouse/rules.mk
@@ -0,0 +1,30 @@
+# MCU name
+MCU = atmega32u4
+
+# Processor frequency
+F_CPU = 8000000
+
+# Bootloader selection
+BOOTLOADER = caterina
+
+# Build Options
+# change yes to no to disable
+#
+BOOTMAGIC_ENABLE = lite # Virtual DIP switch configuration
+EXTRAKEY_ENABLE = yes # Audio control and System control
+CONSOLE_ENABLE = yes # Console for debug
+COMMAND_ENABLE = no # Commands for debug and configuration
+# Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE
+SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend
+# if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work
+NKRO_ENABLE = no # USB Nkey Rollover
+BACKLIGHT_ENABLE = no # Enable keyboard backlight functionality
+RGBLIGHT_ENABLE = no # Enable keyboard RGB underglow
+UNICODE_ENABLE = no # Unicode
+BLUETOOTH_ENABLE = no # Enable Bluetooth
+AUDIO_ENABLE = no # Audio output
+POINTING_DEVICE_ENABLE = yes
+MOUSEKEY_ENABLE = no # Mouse keys
+
+QUANTUM_LIB_SRC += analog.c spi_master.c
+SRC += pmw3600.c opt_encoder.c
diff --git a/keyboards/ploopyco/opt_encoder.c b/keyboards/ploopyco/opt_encoder.c
new file mode 100644
index 0000000000..44bcd5eb88
--- /dev/null
+++ b/keyboards/ploopyco/opt_encoder.c
@@ -0,0 +1,211 @@
+/* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
+ * Copyright 2020 Ploopy Corporation
+ *
+ * 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 "opt_encoder.h"
+
+/* Setup function for the scroll wheel. Initializes
+ the relevant variables. */
+void opt_encoder_init(void) {
+ state = HIHI;
+ lohif = false;
+ hilof = false;
+ lowA = 1023;
+ highA = 0;
+ cLowA = false;
+ cHighA = false;
+ lowIndexA = 0;
+ highIndexA = 0;
+ lowOverflowA = false;
+ highOverflowA = false;
+ lowB = 1023;
+ highB = 0;
+ cLowB = false;
+ cHighB = false;
+ lowIndexB = 0;
+ highIndexB = 0;
+ lowOverflowB = false;
+ highOverflowB = false;
+ scrollThresholdA = 0;
+ scrollThresholdB = 0;
+}
+
+int opt_encoder_handler(int curA, int curB) {
+ if (lowOverflowA == false || highOverflowA == false) calculateThresholdA(curA);
+ if (lowOverflowB == false || highOverflowB == false) calculateThresholdB(curB);
+
+ bool LO = false;
+ bool HI = true;
+ bool sA, sB;
+ int ret = 0;
+
+ if (curA < scrollThresholdA)
+ sA = LO;
+ else
+ sA = HI;
+
+ if (curB < scrollThresholdB)
+ sB = LO;
+ else
+ sB = HI;
+
+ if (state == HIHI) {
+ if (sA == LO && sB == HI) {
+ state = LOHI;
+ if (hilof) {
+ ret = 1;
+ hilof = false;
+ }
+ } else if (sA == HI && sB == LO) {
+ state = HILO;
+ if (lohif) {
+ ret = -1;
+ lohif = false;
+ }
+ }
+ }
+
+ else if (state == HILO) {
+ if (sA == HI && sB == HI) {
+ state = HIHI;
+ hilof = true;
+ lohif = false;
+ } else if (sA == LO && sB == LO) {
+ state = LOLO;
+ hilof = true;
+ lohif = false;
+ }
+ }
+
+ else if (state == LOLO) {
+ if (sA == HI && sB == LO) {
+ state = HILO;
+ if (lohif) {
+ ret = 1;
+ lohif = false;
+ }
+ } else if (sA == LO && sB == HI) {
+ state = LOHI;
+ if (hilof) {
+ ret = -1;
+ hilof = false;
+ }
+ }
+ }
+
+ else { // state must be LOHI
+ if (sA == HI && sB == HI) {
+ state = HIHI;
+ lohif = true;
+ hilof = false;
+ } else if (sA == LO && sB == LO) {
+ state = LOLO;
+ lohif = true;
+ hilof = false;
+ }
+ }
+
+ return ret;
+}
+
+void calculateThresholdA(int curA) { scrollThresholdA = calculateThreshold(curA, &lowA, &highA, &cLowA, &cHighA, arLowA, arHighA, &lowIndexA, &highIndexA, &lowOverflowA, &highOverflowA); }
+
+void calculateThresholdB(int curB) { scrollThresholdB = calculateThreshold(curB, &lowB, &highB, &cLowB, &cHighB, arLowB, arHighB, &lowIndexB, &highIndexB, &lowOverflowB, &highOverflowB); }
+
+int calculateThreshold(int cur, int* low, int* high, bool* cLow, bool* cHigh, int arLow[], int arHigh[], int* lowIndex, int* highIndex, bool* lowOverflow, bool* highOverflow) {
+ if (cur < *low) *low = cur;
+ if (cur > *high) *high = cur;
+
+ int curThresh = thresholdEquation(*low, *high);
+ int range = *high - *low;
+
+ // The range is enforced to be over a certain limit because noise
+ // can cause erroneous readings, making these calculations unstable.
+ if (range >= SCROLL_THRESH_RANGE_LIM) {
+ if (cur < curThresh) {
+ if (*cHigh == true) {
+ // We were just high, and now we crossed to low.
+ // high reflects a sample of a high reading.
+ arHigh[*highIndex] = *high;
+ incrementIndex(highIndex, highOverflow);
+ int midpoint = ((*high - *low) / 2) + *low;
+ *low = midpoint;
+ *high = midpoint;
+ *cLow = false;
+ *cHigh = false;
+ } else {
+ *cLow = true;
+ }
+ }
+ if (cur > curThresh) {
+ if (*cLow == true) {
+ // We were just low, and now we crossed to high.
+ // low reflects a sample of a low reading.
+ arLow[*lowIndex] = *low;
+ incrementIndex(lowIndex, lowOverflow);
+ int midpoint = ((*high - *low) / 2) + *low;
+ *low = midpoint;
+ *high = midpoint;
+ *cLow = false;
+ *cHigh = false;
+ } else {
+ *cHigh = true;
+ }
+ }
+ }
+
+ int calcHigh = 0;
+ if (*highOverflow == true) {
+ for (int i = 0; i < SCROLLER_AR_SIZE; i++) {
+ calcHigh += arHigh[i];
+ }
+ calcHigh = calcHigh / SCROLLER_AR_SIZE;
+ } else if (*highIndex != 0) {
+ for (int i = 0; i < *highIndex; i++) {
+ calcHigh += arHigh[i];
+ }
+ calcHigh = calcHigh / *highIndex;
+ } else {
+ calcHigh = *high;
+ }
+
+ int calcLow = 0;
+ if (*lowOverflow == true) {
+ for (int i = 0; i < SCROLLER_AR_SIZE; i++) {
+ calcLow += arLow[i];
+ }
+ calcLow = calcLow / SCROLLER_AR_SIZE;
+ } else if (*lowIndex != 0) {
+ for (int i = 0; i < *lowIndex; i++) {
+ calcLow += arLow[i];
+ }
+ calcLow = calcLow / *lowIndex;
+ } else {
+ calcLow = *low;
+ }
+
+ return thresholdEquation(calcLow, calcHigh);
+}
+
+int thresholdEquation(int lo, int hi) { return ((hi - lo) / 3) + lo; }
+
+void incrementIndex(int* index, bool* ovflw) {
+ if (*index < SCROLLER_AR_SIZE - 1)
+ (*index)++;
+ else {
+ *index = 0;
+ *ovflw = true;
+ }
+}
diff --git a/keyboards/ploopyco/opt_encoder.h b/keyboards/ploopyco/opt_encoder.h
new file mode 100644
index 0000000000..23a170d59f
--- /dev/null
+++ b/keyboards/ploopyco/opt_encoder.h
@@ -0,0 +1,66 @@
+/* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
+ * Copyright 2020 Ploopy Corporation
+ *
+ * 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 n