This commit is contained in:
silvinor 2025-07-23 13:39:25 -07:00 committed by GitHub
commit 0f968e5d7c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 526 additions and 0 deletions

View File

@ -0,0 +1,193 @@
// Copyright 2023 binepad (@binepad)
// SPDX-License-Identifier: GPL-2.0-or-later
// Portions of this code are based on [Andrew Kannan](https://github.com/awkannan1)'s
// work on the Satisfaction75
#include "quantum.h"
#include "keymap_introspection.h"
#ifdef OLED_ENABLE
static char *get_enc_mode(uint8_t encoder) {
# ifdef ENCODER_MAP_ENABLE
static char s_u_d[4] = {24, 38, 25, 0}; // Up and down
static char s_l_r[4] = {27, 38, 26, 0}; // Left and right
uint8_t layer = get_highest_layer(layer_state);
uint16_t keycode = KC_TRNS;
while (keycode == KC_TRNS) {
keycode = keycode_at_encodermap_location(layer, encoder, true); // only reads ENCODER_CW
if (keycode == KC_TRNS) {
if (layer > 0) {
layer--;
} else {
keycode = KC_NO;
}
}
}
switch (keycode) {
case KC_NO:
return " - ";
break;
case KC_WH_U:
case KC_WH_D:
return "MWL";
break;
case KC_VOLD:
case KC_VOLU:
return "VOL";
break;
case KC_MEDIA_NEXT_TRACK:
case KC_MEDIA_PREV_TRACK:
return "MED";
break;
case KC_UP:
case KC_DOWN:
return (char *)&s_u_d;
break;
case KC_LEFT:
case KC_RIGHT:
return (char *)&s_l_r;
break;
default:
return "Usr";
break;
}
# else
return "Err";
# endif
}
static void __draw_line_h(uint8_t x, uint8_t y, uint8_t len, bool on) {
for (uint8_t i = 0; i < len; i++) {
oled_write_pixel(i + x, y, on);
}
}
static void __draw_line_v(uint8_t x, uint8_t y, uint8_t len, bool on) {
for (uint8_t i = 0; i < len; i++) {
oled_write_pixel(x, i + y, on);
}
}
// Weak so that user keymaps can make their own
__attribute__((weak)) bool candypad_render_default_user(void) {
return false; // Return true if your user keymap renders its own
}
__attribute__((weak)) bool candypad_render_default_kb(void) {
if (candypad_render_default_user()) {
return true; // Was handled by user code
}
bool on;
// --- Show Layer ---
oled_set_cursor(0, 1);
oled_write_P(PSTR("LAYER"), false);
oled_set_cursor(6, 1);
oled_write_char(get_highest_layer(layer_state) + 0x30, true);
__draw_line_h((OLED_FONT_WIDTH * 6) - 1, OLED_FONT_HEIGHT - 1, OLED_FONT_WIDTH + 1, true);
__draw_line_v((OLED_FONT_WIDTH * 6) - 1, OLED_FONT_HEIGHT, OLED_FONT_HEIGHT, true);
// --- Show Encoder 1 ---
oled_set_cursor(13, 1);
oled_write_P(PSTR("ENC1"), false);
oled_set_cursor(18, 1);
oled_write(get_enc_mode(0), true);
__draw_line_h((OLED_FONT_WIDTH * 18) - 1, OLED_FONT_HEIGHT - 1, (OLED_FONT_WIDTH * 3) + 1, true);
__draw_line_v((OLED_FONT_WIDTH * 18) - 1, OLED_FONT_HEIGHT, OLED_FONT_HEIGHT, true);
// --- Show Encoder 2 ---
oled_set_cursor(13, 3);
oled_write_P(PSTR("ENC2"), false);
oled_set_cursor(18, 3);
oled_write(get_enc_mode(1), true);
__draw_line_h((OLED_FONT_WIDTH * 18) - 1, (OLED_FONT_HEIGHT * 3) - 1, (OLED_FONT_WIDTH * 3) + 1, true);
__draw_line_v((OLED_FONT_WIDTH * 18) - 1, OLED_FONT_HEIGHT * 3, OLED_FONT_HEIGHT, true);
// --- Keyboard Modifiers ---
led_t led_state = host_keyboard_led_state();
oled_set_cursor(4, 3);
if (led_state.caps_lock && !led_state.num_lock) { // Caps-Lock transcends keyboards, so show it if on if num-lock is off
oled_write_P(PSTR("CAP"), true);
} else {
oled_write_P(PSTR("NUM"), led_state.num_lock);
}
on = led_state.num_lock || led_state.caps_lock;
__draw_line_h((OLED_FONT_WIDTH * 4) - 1, (OLED_FONT_HEIGHT * 3) - 1, (OLED_FONT_WIDTH * 3) + 1, on);
__draw_line_v((OLED_FONT_WIDTH * 4) - 1, OLED_FONT_HEIGHT * 3, OLED_FONT_HEIGHT, on);
// --- Show keymap matrix ---
# define MXDS_X 2
# define MXDS_Y (OLED_FONT_HEIGHT * 3)
// matrix
for (uint8_t x = 0; x < MATRIX_ROWS - 1; x++) {
for (uint8_t y = 0; y < MATRIX_COLS; y++) {
on = (matrix_get_row(x) & (1 << y)) > 0;
oled_write_pixel(MXDS_X + y, MXDS_Y + x + 1, on);
}
}
on = (matrix_get_row(1) & (1 << 3)) > 0; // `+`
oled_write_pixel(MXDS_X + 3, MXDS_Y + 1 + 2, on);
on = (matrix_get_row(3) & (1 << 3)) > 0; // `Enter`
oled_write_pixel(MXDS_X + 3, MXDS_Y + 3 + 2, on);
on = (matrix_get_row(4) & (1 << 0)) > 0; // `0`
oled_write_pixel(MXDS_X + 0 + 1, MXDS_Y + 4 + 1, on);
on = (matrix_get_row(5) & (1 << 0)) > 0; // Enc 1 press
oled_write_pixel(MXDS_X + 2, MXDS_Y, on);
on = (matrix_get_row(5) & (1 << 1)) > 0; // Enc 2 press
oled_write_pixel(MXDS_X + 3, MXDS_Y, on);
// outline
__draw_line_h(MXDS_X - 2, MXDS_Y - 2, 8, true);
__draw_line_h(MXDS_X - 2, MXDS_Y + 7, 8, true);
__draw_line_v(MXDS_X - 2, MXDS_Y - 1, 8, true);
__draw_line_v(MXDS_X + 5, MXDS_Y - 1, 8, true);
return true; // Was handled here
}
void oled_request_repaint(void) {
oled_clear();
}
bool oled_task_kb(void) {
if (!oled_task_user()) {
return false;
}
candypad_render_default_kb();
return false;
}
#endif // OLED_ENABLE

View File

@ -0,0 +1,33 @@
// Copyright 2023 binepad (@binepad)
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
/* ----- Encoder ----- */
#define ENCODER_DEFAULT_POS 0x3 // enable 1:1 resolution
/* ----- OLED ----- */
#ifdef OLED_ENABLE
/* I2C (for OLED) */
# define I2C1_SCL_PIN GP23
# define I2C1_SDA_PIN GP22
# define I2C_DRIVER I2CD1
/* Configure oled driver for the 128x32 oled */
# define OLED_TIMEOUT (2 * 60 * 1000) // 2 minutes
# define OLED_UPDATE_INTERVAL 33 // ~30fps
#endif // OLED_ENABLE
/* ---- Direct pins, for use in (custom) `matrix.c` (please read that file) ----- */
// !! : DIRECT_PINS_CUSTOM must match the MATRIX_ROWS / MATRIX_COLS matrix structure
// clang-format off
#define DIRECT_PINS_CUSTOM \
{ \
{NO_PIN, NO_PIN, NO_PIN, NO_PIN}, \
{NO_PIN, NO_PIN, NO_PIN, NO_PIN}, \
{NO_PIN, NO_PIN, NO_PIN, NO_PIN}, \
{NO_PIN, NO_PIN, NO_PIN, NO_PIN}, \
{NO_PIN, NO_PIN, NO_PIN, NO_PIN}, \
{GP26, GP3, NO_PIN, NO_PIN} \
}
// clang-format on

View File

@ -0,0 +1,59 @@
{
"manufacturer": "binepad",
"keyboard_name": "CandyPad",
"maintainer": "binepad",
"bootloader": "rp2040",
"diode_direction": "COL2ROW",
"encoder": {
"rotary": [
{"pin_a": "GP28", "pin_b": "GP27"},
{"pin_a": "GP1", "pin_b": "GP2"}
]
},
"features": {
"bootmagic": true,
"encoder": true,
"extrakey": true,
"mousekey": true,
"nkro": true,
"oled": true
},
"matrix_pins": {
"custom": true,
"custom_lite": true,
"cols": ["GP15", "GP14", "GP13", "GP10"],
"rows": ["GP12", "GP9", "GP7", "GP6", "GP5", null]
},
"processor": "RP2040",
"url": "https://candykeys.com/product/candypad-keyboard",
"usb": {
"device_version": "1.0.0",
"pid": "0x4350",
"vid": "0x4249"
},
"layouts": {
"LAYOUT": {
"layout": [
{"matrix": [5, 0], "x": 2, "y": 0, "encoder":0},
{"matrix": [5, 1], "x": 3, "y": 0, "encoder":1},
{"label": "Num Lock", "matrix": [0, 0], "x": 0, "y": 1},
{"label": "/", "matrix": [0, 1], "x": 1, "y": 1},
{"label": "*", "matrix": [0, 2], "x": 2, "y": 1},
{"label": "-", "matrix": [0, 3], "x": 3, "y": 1},
{"label": "7", "matrix": [1, 0], "x": 0, "y": 2},
{"label": "8", "matrix": [1, 1], "x": 1, "y": 2},
{"label": "9", "matrix": [1, 2], "x": 2, "y": 2},
{"label": "+", "matrix": [1, 3], "x": 3, "y": 2, "h": 2},
{"label": "4", "matrix": [2, 0], "x": 0, "y": 3},
{"label": "5", "matrix": [2, 1], "x": 1, "y": 3},
{"label": "6", "matrix": [2, 2], "x": 2, "y": 3},
{"label": "1", "matrix": [3, 0], "x": 0, "y": 4},
{"label": "2", "matrix": [3, 1], "x": 1, "y": 4},
{"label": "3", "matrix": [3, 2], "x": 2, "y": 4},
{"label": "Enter", "matrix": [3, 3], "x": 3, "y": 4, "h": 2},
{"label": "0", "matrix": [4, 0], "x": 0, "y": 5, "w": 2},
{"label": ".", "matrix": [4, 2], "x": 2, "y": 5}
]
}
}
}

View File

@ -0,0 +1,33 @@
{
"config": {
"features": {
"encoder_map": true
}
},
"encoders": [
[
{
"ccw": "KC_WH_U",
"cw": "KC_WH_D"
},
{
"ccw": "KC_VOLD",
"cw": "KC_VOLU"
}
]
],
"keyboard": "binepad/candypad",
"keymap": "default",
"layers": [
[
"MO(1)", "KC_MUTE",
"KC_NUM", "KC_PSLS", "KC_PAST", "KC_PMNS",
"KC_P7", "KC_P8", "KC_P9", "KC_PPLS",
"KC_P4", "KC_P5", "KC_P6",
"KC_P1", "KC_P2", "KC_P3", "KC_PENT",
"KC_P0", "KC_PDOT"
]
],
"layout": "LAYOUT",
"version": 1
}

View File

@ -0,0 +1,162 @@
// Copyright 2023 Silvino R. (@silvinor)
// Copyright 2012-2018 Jun Wako, Jack Humbert, Yiancar [portions from `qantum/matrix.c`]
// SPDX-License-Identifier: GPL-2.0-or-later
/**
* This file is mostly a copy of `matrix.c` from the QMK core, however
* it assumes the matrix is always COL2ROW and that the KB has BOTH
* matrix and direct pins.
* For direct pins define:
* `DIRECT_PINS_CUSTOM`
* This stucture must match exactly the MATRIX_ROWS / MATRIX_COLS matrix.
*
* NB!!: The ROW that supports direct pins **must** have the row set
* to NO_PIN in the matrix array.
*/
#include "quantum.h"
#include "matrix.h"
#include "atomic_util.h"
// ---------- Interal defs and vars ----------
#ifndef MATRIX_INPUT_PRESSED_STATE
# define MATRIX_INPUT_PRESSED_STATE 0
#endif
static pin_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS;
static pin_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS;
static pin_t direct_pins[MATRIX_ROWS][MATRIX_COLS] = DIRECT_PINS_CUSTOM;
// ---------- Interal functions ----------
// .......... Helpers ..........
static inline void gpio_atomic_set_pin_output_low(pin_t pin) {
ATOMIC_BLOCK_FORCEON {
gpio_set_pin_output(pin);
gpio_write_pin_low(pin);
}
}
static inline void gpio_atomic_set_pin_input_high(pin_t pin) {
ATOMIC_BLOCK_FORCEON {
gpio_set_pin_input_high(pin);
}
}
static bool select_row(uint8_t row) {
pin_t pin = row_pins[row];
if (pin != NO_PIN) {
gpio_atomic_set_pin_output_low(pin);
return true;
}
return false;
}
static void unselect_row(uint8_t row) {
pin_t pin = row_pins[row];
if (pin != NO_PIN) {
gpio_atomic_set_pin_input_high(pin);
}
}
static void unselect_rows(void) {
for (uint8_t x = 0; x < MATRIX_ROWS; x++) {
unselect_row(x);
}
}
// .......... Inits ..........
void matrix_init_matrix_pins(void) {
unselect_rows();
for (uint8_t x = 0; x < MATRIX_COLS; x++) {
if (col_pins[x] != NO_PIN) {
gpio_atomic_set_pin_input_high(col_pins[x]);
}
}
}
void matrix_init_direct_pins(void) {
for (int row = 0; row < MATRIX_ROWS; row++) {
for (int col = 0; col < MATRIX_COLS; col++) {
pin_t pin = direct_pins[row][col];
if (pin != NO_PIN) {
gpio_atomic_set_pin_input_high(pin);
}
}
}
}
// .......... Scanners ..........
static inline uint8_t readMatrixPin(pin_t pin) {
if (pin != NO_PIN) {
return (gpio_read_pin(pin) == MATRIX_INPUT_PRESSED_STATE) ? 0 : 1;
} else {
return 1;
}
}
void matrix_scan_matrix_row(matrix_row_t row_buffer[], uint8_t row_to_scan) {
matrix_row_t scan_row_value = 0;
if (!select_row(row_to_scan)) {
return;
}
matrix_output_select_delay();
matrix_row_t row_shifter = MATRIX_ROW_SHIFTER;
for (uint8_t col_to_scan = 0; col_to_scan < MATRIX_COLS; col_to_scan++, row_shifter <<= 1) {
uint8_t pin_state = readMatrixPin(col_pins[col_to_scan]);
// Populate the matrix row with the state of the col pin
scan_row_value |= pin_state ? 0 : row_shifter;
}
unselect_row(row_to_scan);
matrix_output_unselect_delay(row_to_scan, scan_row_value != 0);
row_buffer[row_to_scan] = scan_row_value;
}
void matrix_scan_direct_row(matrix_row_t row_buffer[], uint8_t row_to_scan) {
matrix_row_t scan_row_value = 0;
matrix_row_t row_shifter = MATRIX_ROW_SHIFTER;
for (uint8_t col_to_scan = 0; col_to_scan < MATRIX_COLS; col_to_scan++, row_shifter <<= 1) {
pin_t pin = direct_pins[row_to_scan][col_to_scan];
if (pin != NO_PIN) {
scan_row_value |= readMatrixPin(pin) ? 0 : row_shifter;
}
}
row_buffer[row_to_scan] = scan_row_value;
}
// ========== Core functions required by lite matrix driver ==========
void matrix_init_custom(void) {
matrix_init_matrix_pins();
matrix_init_direct_pins();
}
bool matrix_scan_custom(matrix_row_t current_matrix[]) {
matrix_row_t buff_matrix[MATRIX_ROWS] = {0};
for (uint8_t scan_row = 0; scan_row < MATRIX_ROWS; scan_row++) {
// if the row is a NO_PIN then assume it's on a direct pins matrix, else assume COL2ROW matrix
if (row_pins[scan_row] == NO_PIN) {
matrix_scan_direct_row(buff_matrix, scan_row);
} else {
matrix_scan_matrix_row(buff_matrix, scan_row);
}
}
bool matrix_has_changed = memcmp(current_matrix, buff_matrix, sizeof(buff_matrix)) != 0;
if (matrix_has_changed) {
memcpy(current_matrix, buff_matrix, sizeof(buff_matrix));
}
return matrix_has_changed;
}

View File

@ -0,0 +1,12 @@
// Copyright 2023 binepad (@binepad)
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include_next <mcuconf.h>
#undef RP_I2C_USE_I2C0
#define RP_I2C_USE_I2C0 FALSE
#undef RP_I2C_USE_I2C1
#define RP_I2C_USE_I2C1 TRUE

View File

@ -0,0 +1,29 @@
# CandyPad Number Pad
![CandyPad Keyboard](https://i.imgur.com/W9cfNOm.png)
*A numeric pad with 2x rotary encoders and OLED display, a joint venture by <b>Candy</b>Keys &times; Bine<b>Pad</b>*
* Keyboard Maintainer: [binepad](https://github.com/binepad)
* Hardware Supported: CandyPad Keyboard
* Hardware Availability:
- [candykeys.com](https://candykeys.com/product/candypad-keyboard)
- [binepad.com](https://binepad.com/products/candypad)
Make example for this keyboard (after setting up your build environment):
make binepad/candypad:default
Flashing example for this keyboard:
make binepad/candypad:default:flash
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).
## Bootloader
Enter the bootloader in 3 ways:
* **Bootmagic reset**: Hold down the key at (0:0) in the matrix (the top left key) and plug in the keyboard
* **Physical reset button**: Briefly press the PCB button located on the back of the PCB
* **Keycode in layout**: Press the key mapped to `QK_BOOT` if it is available

View File

@ -0,0 +1,5 @@
# Copyright 2023 binepad (@binepad)
# SPDX-License-Identifier: GPL-2.0-or-later
SRC += matrix.c \
candypad_oled.c