diff --git a/keyboards/ploopyco/common/as5600.c b/keyboards/ploopyco/common/as5600.c new file mode 100644 index 00000000000..4ded20c7f3d --- /dev/null +++ b/keyboards/ploopyco/common/as5600.c @@ -0,0 +1,69 @@ +/* Copyright 2025 Colin Lam, Ploopy Corporation (contact@ploopy.co) + * + * 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 . + */ + +#include "as5600.h" +#include "print.h" + +void as5600_init(void) { + i2c_init(); +} + +uint16_t get_rawangle(void) { + uint8_t data[] = {0, 0}; + i2c_status_t s = i2c_read_register(AS5600_I2C_ADDRESS, REG_RAWANGLE, data, 2, 100); + if (s == I2C_STATUS_TIMEOUT) { + printf("Timeout on get_rawangle()\n"); + } else if (s == I2C_STATUS_ERROR) { + printf("Error on get_rawangle()\n"); + } else { + ; + } + uint16_t rawangle = data[0] << 8 | data[1]; + return rawangle; +} + +bool is_magnet_too_high(void) { + uint8_t data[] = {0}; + i2c_read_register(AS5600_I2C_ADDRESS, REG_STATUS, data, 1, 100); + uint8_t v = (data[0] >> 3) & 0x1; + if (v == 1) { + return true; + } else { + return false; + } +} + +bool is_magnet_too_low(void) { + uint8_t data[] = {0}; + i2c_read_register(AS5600_I2C_ADDRESS, REG_STATUS, data, 1, 100); + uint8_t v = (data[0] >> 4) & 0x1; + if (v == 1) { + return true; + } else { + return false; + } +} + +bool is_magnet_present(void) { + uint8_t data[] = {0}; + i2c_read_register(AS5600_I2C_ADDRESS, REG_STATUS, data, 1, 100); + uint8_t v = (data[0] >> 5) & 0x1; + if (v == 1) { + return true; + } else { + return false; + } +} diff --git a/keyboards/ploopyco/common/as5600.h b/keyboards/ploopyco/common/as5600.h new file mode 100644 index 00000000000..608d272072d --- /dev/null +++ b/keyboards/ploopyco/common/as5600.h @@ -0,0 +1,54 @@ +/* Copyright 2025 Colin Lam, Ploopy Corporation (contact@ploopy.co) + * + * 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 . + */ + +#pragma once + +#include +#include +#include "i2c_master.h" + +#define POINTING_DEVICE_AS5600_TICK_COUNT 128 + +// 12 was found to be a good value experimentally, balancing good +// responsiveness with low backlash. +#define POINTING_DEVICE_AS5600_DEADZONE 12 + +// The speed divisor decreases the speed. 1 is base speed; 2 is divided by 2, +// 3 is divided by 3, and so forth. For best results, make sure that +// POINTING_DEVICE_AS5600_SPEED_DIV is an integer divisor of +// POINTING_DEVICE_AS5600_DEADZONE (i.e. 3 is an integer divisor of 12, but +// 5 is not). +#define POINTING_DEVICE_AS5600_SPEED_DIV 2 + +#define AS5600_I2C_ADDRESS (0x36 << 1) + +#define REG_ZMCO 0x00 +#define REG_ZPOS 0x01 +#define REG_MPOS 0x03 +#define REG_MANG 0x05 +#define REG_CONF 0x07 +#define REG_RAWANGLE 0x0c +#define REG_ANGLE 0x0e +#define REG_STATUS 0x0b +#define REG_AGC 0x1a +#define REG_MAGNITUDE 0x1b +#define REG_BURN 0xff + +void as5600_init(void); +uint16_t get_rawangle(void); +bool is_magnet_too_high(void); +bool is_magnet_too_low(void); +bool is_magnet_present(void); diff --git a/keyboards/ploopyco/knob/config.h b/keyboards/ploopyco/knob/config.h new file mode 100644 index 00000000000..9b7d9f5c1a2 --- /dev/null +++ b/keyboards/ploopyco/knob/config.h @@ -0,0 +1,29 @@ +/* Copyright 2025 Colin Lam (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 . + */ + +#pragma once + +#define POINTING_DEVICE_HIRES_SCROLL_ENABLE 0 +#define POINTING_DEVICE_AS5600_ENABLE true + +#define I2C_DRIVER I2CD1 +#define I2C1_SDA_PIN GP22 +#define I2C1_SCL_PIN GP23 + +#define UNUSABLE_PINS \ + { GP0, GP1, GP2, GP3, GP4, GP5, GP6, GP7, GP8, GP9, GP10, GP11, GP12, \ + GP13, GP14, GP15, GP16, GP17, GP18, GP19, GP20, GP21, GP24, GP25, \ + GP26, GP27, GP29 } diff --git a/keyboards/ploopyco/knob/info.json b/keyboards/ploopyco/knob/info.json new file mode 100644 index 00000000000..b1641f5772b --- /dev/null +++ b/keyboards/ploopyco/knob/info.json @@ -0,0 +1,28 @@ +{ + "keyboard_name": "Ploopy Knob", + "url": "www.ploopy.co", + "maintainer": "ploopyco", + "manufacturer": "Ploopy Corporation", + "processor": "RP2040", + "bootloader": "rp2040", + "usb": { + "vid": "0x5043", + "pid": "0x63C3", + "max_power": 100 + }, + "features": { + "extrakey": true, + "mousekey": true, + "nkro": true, + "pointing_device": true, + "console": true, + "os_detection": true + }, + "layouts": { + "LAYOUT": { + "layout": [ + {"x": 0, "y": 0, "matrix": [0, 0]} + ] + } + } +} diff --git a/keyboards/ploopyco/knob/keymaps/default/keymap.c b/keyboards/ploopyco/knob/keymaps/default/keymap.c new file mode 100644 index 00000000000..93c23584e1f --- /dev/null +++ b/keyboards/ploopyco/knob/keymaps/default/keymap.c @@ -0,0 +1,27 @@ +/* Copyright 2023 Colin Lam (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 . + */ +#include QMK_KEYBOARD_H + +// Dummy +const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {{{ KC_NO }}}; + +void keyboard_post_init_user(void) { + // Customise these values to desired behaviour + // debug_enable=true; + // debug_matrix=true; + // debug_keyboard=true; + // debug_mouse=true; +} diff --git a/keyboards/ploopyco/knob/post_rules.mk b/keyboards/ploopyco/knob/post_rules.mk new file mode 100644 index 00000000000..15a5cab6548 --- /dev/null +++ b/keyboards/ploopyco/knob/post_rules.mk @@ -0,0 +1,4 @@ +VPATH += keyboards/ploopyco/common +SRC += as5600.c +I2C_DRIVER_REQUIRED = yes +POINTING_DEVICE_DRIVER = custom diff --git a/keyboards/ploopyco/knob/readme.md b/keyboards/ploopyco/knob/readme.md new file mode 100644 index 00000000000..0489ba44197 --- /dev/null +++ b/keyboards/ploopyco/knob/readme.md @@ -0,0 +1,31 @@ +# Ploopyco Knob + +It's a DIY, QMK-powered knob! + +* Keyboard Maintainer: [PloopyCo](https://github.com/ploopyco) +* Hardware Supported: RP2040 +* Hardware Availability: [Store](https://ploopy.co/knob), [GitHub](https://github.com/ploopyco) + +Make example for this keyboard (after setting up your build environment): + + qmk compile -kb ploopyco/knob/rev1_001 -km default + +# Building Firmware + +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). + +# Triggering the Bootloader + +[Do you see those two golden holes in the board](https://ploopy.co/wp-content/uploads/2025/06/knob-vias.jpg)? Those are called **vias**. They act exactly like a switch does. Right now, that switch is OFF. However, if you take a paperclip or a pair of metal tweezers and touch those two vias, the two vias will form an electrical connection. Effectively, that switch turns ON. + +Go ahead and connect the two vias, and then (while the vias are connected) plug in the Knob board into your computer. + +The computer should recognise that a mass storage device was just plugged in. Once this is done, you should be able to drag and drop files onto the Knob board, as if the board was a USB drive. Feel free to remove the tweezers or paperclip at this point. + +If you want to upload a new firmware file (a ".uf2" file, like "knob_awesome_version.uf2" or something), just drag it into the folder, and it'll automatically install on the Knob board and restart itself, in normal operating mode. You're done! + +**TIP**: If your firmware is in some kind of strange state and uploading new firmware isn't fixing it, try uploading [a flash nuke](https://learn.adafruit.com/getting-started-with-raspberry-pi-pico-circuitpython/circuitpython#flash-resetting-uf2-3083182) to the Knob board before flashing the new firmware. It wipes the memory of the Knob board completely clean, which can help clear a few types of errors. + +# Customizing your Ploopy Knob + +You can find customziation options [here](../readme.md). diff --git a/keyboards/ploopyco/knob/rev1_001/keyboard.json b/keyboards/ploopyco/knob/rev1_001/keyboard.json new file mode 100644 index 00000000000..8c148ea974f --- /dev/null +++ b/keyboards/ploopyco/knob/rev1_001/keyboard.json @@ -0,0 +1,10 @@ +{ + "usb": { + "device_version": "1.0.0" + }, + "matrix_pins": { + "direct": [ + [null] + ] + } +} diff --git a/keyboards/ploopyco/knob/rev1_001/readme.md b/keyboards/ploopyco/knob/rev1_001/readme.md new file mode 100644 index 00000000000..0c23bf73eda --- /dev/null +++ b/keyboards/ploopyco/knob/rev1_001/readme.md @@ -0,0 +1,3 @@ +This is the R1.001 version of the Knob. Future versions may have other features. + +See the [main readme](../readme.md) for more details. diff --git a/keyboards/ploopyco/ploopyco.c b/keyboards/ploopyco/ploopyco.c index 57f2a26b8c3..72fad42aba0 100644 --- a/keyboards/ploopyco/ploopyco.c +++ b/keyboards/ploopyco/ploopyco.c @@ -19,6 +19,7 @@ #include "ploopyco.h" #include "analog.h" #include "opt_encoder.h" +#include "as5600.h" // for legacy support #if defined(OPT_DEBOUNCE) && !defined(PLOOPY_SCROLL_DEBOUNCE) @@ -58,6 +59,10 @@ # define ENCODER_BUTTON_COL 0 #endif +#ifdef POINTING_DEVICE_AS5600_ENABLE +uint16_t current_position = 0; +#endif + keyboard_config_t keyboard_config; uint16_t dpi_array[] = PLOOPY_DPI_OPTIONS; #define DPI_OPTION_SIZE ARRAY_SIZE(dpi_array) @@ -163,6 +168,38 @@ report_mouse_t pointing_device_task_kb(report_mouse_t mouse_report) { mouse_report.y = 0; } +#ifdef POINTING_DEVICE_AS5600_ENABLE + // Get AS5600 rawangle + uint16_t ra = get_rawangle(); + int16_t delta = (int16_t)(ra - current_position); + + // Wrap into [-2048, 2047] to get shortest direction + if (delta > 2048) { + delta -= 4096; + } else if (delta < -2048) { + delta += 4096; + } + + if (detected_host_os() == OS_WINDOWS || detected_host_os() == OS_LINUX) { + // Establish a deadzone to prevent spurious inputs + if (delta > POINTING_DEVICE_AS5600_DEADZONE || delta < -POINTING_DEVICE_AS5600_DEADZONE) { + current_position = ra; + mouse_report.v = delta / POINTING_DEVICE_AS5600_SPEED_DIV; + } + } else { + // Certain operating systems, like MacOS, don't play well with the + // high-res scrolling implementation. For more details, see: + // https://github.com/qmk/qmk_firmware/issues/17585#issuecomment-2325248167 + if (delta >= POINTING_DEVICE_AS5600_TICK_COUNT) { + current_position = ra; + mouse_report.v = 1; + } else if (delta <= -POINTING_DEVICE_AS5600_TICK_COUNT) { + current_position = ra; + mouse_report.v = -1; + } + } +#endif + return pointing_device_task_user(mouse_report); } @@ -204,7 +241,7 @@ bool process_record_kb(uint16_t keycode, keyrecord_t* record) { void keyboard_pre_init_kb(void) { // debug_enable = true; // debug_matrix = true; - // debug_mouse = true; + //debug_mouse = true; // debug_encoder = true; /* Ground all output pins connected to ground. This provides additional @@ -237,6 +274,14 @@ void pointing_device_init_kb(void) { pointing_device_set_cpi(dpi_array[keyboard_config.dpi_config]); } +#ifdef POINTING_DEVICE_AS5600_ENABLE +void keyboard_post_init_kb(void) { + // Init the AS5600 controlling the Dial + as5600_init(); + current_position = get_rawangle(); +} +#endif + void eeconfig_init_kb(void) { keyboard_config.dpi_config = PLOOPY_DPI_DEFAULT; eeconfig_update_kb(keyboard_config.raw);