diff --git a/plugins/Kaleidoscope-Hardware-Keyboardio-Alto/examples/Devices/Keyboardio/Alto/Alto.ino b/plugins/Kaleidoscope-Hardware-Keyboardio-Alto/examples/Devices/Keyboardio/Alto/Alto.ino new file mode 100644 index 0000000000..ab31f7eac7 --- /dev/null +++ b/plugins/Kaleidoscope-Hardware-Keyboardio-Alto/examples/Devices/Keyboardio/Alto/Alto.ino @@ -0,0 +1,386 @@ +// -*- mode: c++ -*- +// Copyright 2016-2022 Keyboardio, inc. +// See "LICENSE" for license details + +/** + * These #include directives pull in the Kaleidoscope firmware core, + * as well as the Kaleidoscope plugins we use in the Alto's Firmware + */ + +// The Kaleidoscope core +#include "Kaleidoscope.h" + +// Support for storing the keymap in EEPROM +#include "Kaleidoscope-EEPROM-Settings.h" +#include "Kaleidoscope-EEPROM-Keymap.h" + +// Support for communicating with the host via a simple Serial protocol +#include "Kaleidoscope-FocusSerial.h" + +// Support for querying the firmware version via Focus +#include "Kaleidoscope-FirmwareVersion.h" + +// Support for keys that move the mouse +#include "Kaleidoscope-MouseKeys.h" + +// Support for macros +#include "Kaleidoscope-Macros.h" + +// Support for host power management (suspend & wakeup) +#include "Kaleidoscope-HostPowerManagement.h" + +// Support for magic combos (key chords that trigger an action) +#include "Kaleidoscope-MagicCombo.h" + +// Support for USB quirks, like changing the key state report protocol +#include "Kaleidoscope-USB-Quirks.h" + +// Support for secondary actions on keys +#include "Kaleidoscope-Qukeys.h" + +// Support for one-shot modifiers and layer keys +#include "Kaleidoscope-OneShot.h" +#include "Kaleidoscope-Escape-OneShot.h" + +// Support for dynamic, Chrysalis-editable macros +#include "Kaleidoscope-DynamicMacros.h" + +// Support for SpaceCadet keys +#include "Kaleidoscope-SpaceCadet.h" + +// Support for editable layer names +#include "Kaleidoscope-LayerNames.h" + +// Support for the GeminiPR Stenography protocol +#include "Kaleidoscope-Steno.h" + +/** This 'enum' is a list of all the macros used by the Alto's firmware + * The names aren't particularly important. What is important is that each + * is unique. + * + * These are the names of your macros. They'll be used in two places. + * The first is in your keymap definitions. There, you'll use the syntax + * `M(MACRO_NAME)` to mark a specific keymap position as triggering `MACRO_NAME` + * + * The second usage is in the 'switch' statement in the `macroAction` function. + * That switch statement actually runs the code associated with a macro when + * a macro key is pressed. + */ + +enum { + MACRO_VERSION_INFO, + MACRO_ANY, +}; + + +/** The Alto's key layouts are defined as 'keymaps'. By default, there are three + * keymaps: The standard QWERTY keymap, the "Function layer" keymap and the "Numpad" + * keymap. + * + * Each keymap is defined as a list using the 'KEYMAP' macro, built + * of first the left hand's layout, followed by the right hand's layout. + * + * Keymaps typically consist mostly of `Key_` definitions. There are many, many keys + * defined as part of the USB HID Keyboard specification. You can find the names + * (if not yet the explanations) for all the standard `Key_` defintions offered by + * Kaleidoscope in these files: + * https://github.com/keyboardio/Kaleidoscope/blob/master/src/kaleidoscope/key_defs/keyboard.h + * https://github.com/keyboardio/Kaleidoscope/blob/master/src/kaleidoscope/key_defs/consumerctl.h + * https://github.com/keyboardio/Kaleidoscope/blob/master/src/kaleidoscope/key_defs/sysctl.h + * https://github.com/keyboardio/Kaleidoscope/blob/master/src/kaleidoscope/key_defs/keymaps.h + * + * Additional things that should be documented here include + * using ___ to let keypresses fall through to the previously active layer + * using XXX to mark a keyswitch as 'blocked' on this layer + * using ShiftToLayer() and LockLayer() keys to change the active keymap. + * keeping NUM and FN consistent and accessible on all layers + * + * The PROG key is special, since it is how you indicate to the board that you + * want to flash the firmware. However, it can be remapped to a regular key. + * When the keyboard boots, it first looks to see whether the PROG key is held + * down; if it is, it simply awaits further flashing instructions. If it is + * not, it continues loading the rest of the firmware and the keyboard + * functions normally, with whatever binding you have set to PROG. More detail + * here: https://community.keyboard.io/t/how-the-prog-key-gets-you-into-the-bootloader/506/8 + * + * The "keymaps" data structure is a list of the keymaps compiled into the firmware. + * The order of keymaps in the list is important, as the ShiftToLayer(#) and LockLayer(#) + * macros switch to key layers based on this list. + * + * + + * A key defined as 'ShiftToLayer(FUNCTION)' will switch to FUNCTION while held. + * Similarly, a key defined as 'LockLayer(NUMPAD)' will switch to NUMPAD when tapped. + */ + +/** + * Layers are "0-indexed" -- That is the first one is layer 0. The second one is layer 1. + * The third one is layer 2. + * This 'enum' lets us use names like QWERTY, FUNCTION, and NUMPAD in place of + * the numbers 0, 1 and 2. + * + */ + +enum { + PRIMARY, +}; // layers + + +/** + * To change your keyboard's layout from QWERTY to DVORAK or COLEMAK, comment out the line + * + * #define PRIMARY_KEYMAP_QWERTY + * + * by changing it to + * + * // #define PRIMARY_KEYMAP_QWERTY + * + * Then uncomment the line corresponding to the layout you want to use. + * + */ \ +#define PRIMARY_KEYMAP_QWERTY // #define PRIMARY_KEYMAP_DVORAK +// #define PRIMARY_KEYMAP_COLEMAK +// #define PRIMARY_KEYMAP_CUSTOM + + +/* This comment temporarily turns off astyle's indent enforcement + * so we can make the keymaps actually resemble the physical key layout better + */ +// clang-format off +KEYMAPS( [PRIMARY] = KEYMAP ( +Key_Esc, Key_1, Key_2, Key_3, Key_4, Key_5, Key_6, Key_7, Key_8, Key_9, Key_0, Key_Minus, Key_Equals, Key_Backslash, Key_DownArrow, Key_Delete, +Key_Tab, Key_Q, Key_W, Key_E, Key_R, Key_T, Key_Y, Key_U, Key_I, Key_O, Key_P, Key_LeftBracket, Key_RightBracket, Key_LeftArrow, Key_Backspace, Key_F1, +Key_LeftControl, Key_A, Key_S, Key_D, Key_F, Key_G, Key_H, Key_J, Key_K, Key_L, Key_Semicolon, Key_Quote, Key_Enter, Key_F2, +Key_F4, Key_LeftShift, Key_Z, Key_X, Key_C, Key_V, Key_B, Key_N, Key_M, Key_Comma, Key_Period, Key_Slash, Key_RightShift, Key_F3, + Key_Space) + + + + +) // KEYMAPS( +/* Re-enable astyle's indent enforcement */ +// clang-format on + +/** versionInfoMacro handles the 'firmware version info' macro + * When a key bound to the macro is pressed, this macro + * prints out the firmware build information as virtual keystrokes + */ + +static void versionInfoMacro(uint8_t key_state) { + if (keyToggledOn(key_state)) { + Macros.type(PSTR("Keyboardio Alto - Firmware version ")); + Macros.type(PSTR(KALEIDOSCOPE_FIRMWARE_VERSION)); + } +} + +/** anyKeyMacro is used to provide the functionality of the 'Any' key. + * + * When the 'any key' macro is toggled on, a random alphanumeric key is + * selected. While the key is held, the function generates a synthetic + * keypress event repeating that randomly selected key. + * + */ + +static void anyKeyMacro(KeyEvent &event) { + if (keyToggledOn(event.state)) { + event.key.setKeyCode(Key_A.getKeyCode() + (uint8_t)(millis() % 36)); + event.key.setFlags(0); + } +} + + +/** macroAction dispatches keymap events that are tied to a macro + to that macro. It takes two uint8_t parameters. + + The first is the macro being called (the entry in the 'enum' earlier in this file). + The second is the state of the keyswitch. You can use the keyswitch state to figure out + if the key has just been toggled on, is currently pressed or if it's just been released. + + The 'switch' statement should have a 'case' for each entry of the macro enum. + Each 'case' statement should call out to a function to handle the macro in question. + + */ + +const macro_t *macroAction(uint8_t macro_id, KeyEvent &event) { + switch (macro_id) { + + case MACRO_VERSION_INFO: + versionInfoMacro(event.state); + break; + + case MACRO_ANY: + anyKeyMacro(event); + break; + } + return MACRO_NONE; +} + + +/** This 'enum' is a list of all the magic combos used by the Alto's + * firmware The names aren't particularly important. What is important is that + * each is unique. + * + * These are the names of your magic combos. They will be used by the + * `USE_MAGIC_COMBOS` call below. + */ +enum { + // Toggle between Boot (6-key rollover; for BIOSes and early boot) and NKRO + // mode. + COMBO_TOGGLE_NKRO_MODE, +}; + +/** Wrappers, to be used by MagicCombo. **/ + +/** + * This simply toggles the keyboard protocol via USBQuirks, and wraps it within + * a function with an unused argument, to match what MagicCombo expects. + */ +static void toggleKeyboardProtocol(uint8_t combo_index) { + USBQuirks.toggleKeyboardProtocol(); +} + +/** + * Toggles between using the built-in keymap, and the EEPROM-stored one. + */ +static void toggleKeymapSource(uint8_t combo_index) { + if (Layer.getKey == Layer.getKeyFromPROGMEM) { + Layer.getKey = EEPROMKeymap.getKey; + } else { + Layer.getKey = Layer.getKeyFromPROGMEM; + } +} + + +/** Magic combo list, a list of key combo and action pairs the firmware should + * recognise. + */ +USE_MAGIC_COMBOS({.action = toggleKeyboardProtocol, + // Left Fn + Prog + LED + .keys = {R3C6, R0C0, R0C6}}, + {.action = toggleKeymapSource, + // Left Fn + Prog + Shift + .keys = {R3C6, R0C0, R3C7}}); + +// First, tell Kaleidoscope which plugins you want to use. +// The order can be important. For example, LED effects are +// added in the order they're listed here. +KALEIDOSCOPE_INIT_PLUGINS( + // The EEPROMSettings & EEPROMKeymap plugins make it possible to have an + // editable keymap in EEPROM. + EEPROMSettings, + EEPROMKeymap, + + // SpaceCadet can turn your shifts into parens on tap, while keeping them as + // Shifts when held. SpaceCadetConfig lets Chrysalis configure some aspects of + // the plugin. + SpaceCadet, + SpaceCadetConfig, + + // Focus allows bi-directional communication with the host, and is the + // interface through which the keymap in EEPROM can be edited. + Focus, + + // FocusSettingsCommand adds a few Focus commands, intended to aid in + // changing some settings of the keyboard, such as the default layer (via the + // `settings.defaultLayer` command) + FocusSettingsCommand, + + // FocusEEPROMCommand adds a set of Focus commands, which are very helpful in + // both debugging, and in backing up one's EEPROM contents. + FocusEEPROMCommand, + + // The macros plugin adds support for macros + Macros, + + // The MouseKeys plugin lets you add keys to your keymap which move the mouse. + // The MouseKeysConfig plugin lets Chrysalis configure some aspects of the + // plugin. + MouseKeys, + MouseKeysConfig, + + // The HostPowerManagement plugin allows us to turn LEDs off when then host + // goes to sleep, and resume them when it wakes up. + HostPowerManagement, + + // The MagicCombo plugin lets you use key combinations to trigger custom + // actions - a bit like Macros, but triggered by pressing multiple keys at the + // same time. + MagicCombo, + + // The USBQuirks plugin lets you do some things with USB that we aren't + // comfortable - or able - to do automatically, but can be useful + // nevertheless. Such as toggling the key report protocol between Boot (used + // by BIOSes) and Report (NKRO). + USBQuirks, + + // The Qukeys plugin enables the "Secondary action" functionality in + // Chrysalis. Keys with secondary actions will have their primary action + // performed when tapped, but the secondary action when held. + Qukeys, + + // Enables the "Sticky" behavior for modifiers, and the "Layer shift when + // held" functionality for layer keys. + OneShot, + OneShotConfig, + EscapeOneShot, + EscapeOneShotConfig, + + // Enables dynamic, Chrysalis-editable macros. + DynamicMacros, + + // The FirmwareVersion plugin lets Chrysalis query the version of the firmware + // programmatically. + FirmwareVersion, + + // The LayerNames plugin allows Chrysalis to display - and edit - custom layer + // names, to be shown instead of the default indexes. + LayerNames, + + // Enables the GeminiPR Stenography protocol. Unused by default, but with the + // plugin enabled, it becomes configurable - and then usable - via Chrysalis. + GeminiPR); + +/** The 'setup' function is one of the two standard Arduino sketch functions. + * It's called when your keyboard first powers up. This is where you set up + * Kaleidoscope and any plugins. + */ +void setup() { + // First, call Kaleidoscope's internal setup function + Kaleidoscope.setup(); + + // To make the keymap editable without flashing new firmware, we store + // additional layers in EEPROM. For now, we reserve space for eight layers. If + // one wants to use these layers, just set the default layer to one in EEPROM, + // by using the `settings.defaultLayer` Focus command, or by using the + // `keymap.onlyCustom` command to use EEPROM layers only. + EEPROMKeymap.setup(8); + + // For Dynamic Macros, we need to reserve storage space for the editable + // macros. A kilobyte is a reasonable default. + DynamicMacros.reserve_storage(1024); + + // If there's a default layer set in EEPROM, we should set that as the default + // here. + Layer.move(EEPROMSettings.default_layer()); + + // To avoid any surprises, SpaceCadet is turned off by default. However, it + // can be permanently enabled via Chrysalis, so we should only disable it if + // no configuration exists. + SpaceCadetConfig.disableSpaceCadetIfUnconfigured(); + + // Editable layer names are stored in EEPROM too, and we reserve 16 bytes per + // layer for them. We need one extra byte per layer for bookkeeping, so we + // reserve 17 / layer in total. + LayerNames.reserve_storage(17 * 8); +} + +/** loop is the second of the standard Arduino sketch functions. + * As you might expect, it runs in a loop, never exiting. + * + * For Kaleidoscope-based keyboard firmware, you usually just want to + * call Kaleidoscope.loop(); and not do anything custom here. + */ + +void loop() { + Kaleidoscope.loop(); +} diff --git a/plugins/Kaleidoscope-Hardware-Keyboardio-Alto/examples/Devices/Keyboardio/Alto/Makefile b/plugins/Kaleidoscope-Hardware-Keyboardio-Alto/examples/Devices/Keyboardio/Alto/Makefile new file mode 100644 index 0000000000..19019b31db --- /dev/null +++ b/plugins/Kaleidoscope-Hardware-Keyboardio-Alto/examples/Devices/Keyboardio/Alto/Makefile @@ -0,0 +1,48 @@ +# This makefile for a Kaleidoscope sketch pulls in all the targets +# required to build the example + + + + +ifneq ($(KALEIDOSCOPE_DIR),) +search_path += $(KALEIDOSCOPE_DIR) +endif + +ifneq ($(ARDUINO_DIRECTORIES_USER),) +search_path += $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/avr/libraries/Kaleidoscope +endif + +ifeq ($(shell uname -s),Darwin) +search_path += $(HOME)/Documents/Arduino/hardware/keyboardio/avr/libraries/Kaleidoscope +else +search_path += $(HOME)/Arduino/hardware/keyboardio/avr/libraries/Kaleidoscope +endif + +sketch_makefile := etc/makefiles/sketch.mk + +$(foreach candidate, $(search_path), $(if $(wildcard $(candidate)/$(sketch_makefile)), $(eval ks_dir ?= $(candidate)))) + +ifneq ($(ks_dir),) + +$(info Using Kaleidoscope from $(ks_dir)) + +export KALEIDOSCOPE_DIR := $(ks_dir) +include $(ks_dir)/$(sketch_makefile) + +else + +$(info I can't find your Kaleidoscope installation.) +$(info ) +$(info I tried looking in:) +$(info ) +$(foreach candidate, $(search_path), $(info $(candidate))) +$(info ) +$(info The easiest way to fix this is to set the 'KALEIDOSCOPE_DIR' environment) +$(info variable to the location of your Kaleidoscope directory.) + +endif + + +null-target: + $(info You should never see this message) + @: diff --git a/plugins/Kaleidoscope-Hardware-Keyboardio-Alto/examples/Devices/Keyboardio/Alto/sketch.json b/plugins/Kaleidoscope-Hardware-Keyboardio-Alto/examples/Devices/Keyboardio/Alto/sketch.json new file mode 100644 index 0000000000..0a5a268bb1 --- /dev/null +++ b/plugins/Kaleidoscope-Hardware-Keyboardio-Alto/examples/Devices/Keyboardio/Alto/sketch.json @@ -0,0 +1,6 @@ +{ + "cpu": { + "fqbn": "keyboardio:gd32:keyboardio_alto", + "port": "" + } +} diff --git a/plugins/Kaleidoscope-Hardware-Keyboardio-Alto/library.properties b/plugins/Kaleidoscope-Hardware-Keyboardio-Alto/library.properties new file mode 100644 index 0000000000..5050156cf2 --- /dev/null +++ b/plugins/Kaleidoscope-Hardware-Keyboardio-Alto/library.properties @@ -0,0 +1,7 @@ +name=Kaleidoscope-Hardware-Keyboardio-Alto +version=0.0.0 +sentence=Keyboardio Alto hardware support for Kaleidoscope +maintainer=Kaleidoscope's Developers +url=https://github.com/keyboardio/Kaleidoscope +author=Keyboardio +paragraph= diff --git a/plugins/Kaleidoscope-Hardware-Keyboardio-Alto/src/Kaleidoscope-Hardware-Keyboardio-Alto.h b/plugins/Kaleidoscope-Hardware-Keyboardio-Alto/src/Kaleidoscope-Hardware-Keyboardio-Alto.h new file mode 100644 index 0000000000..d21433fbc1 --- /dev/null +++ b/plugins/Kaleidoscope-Hardware-Keyboardio-Alto/src/Kaleidoscope-Hardware-Keyboardio-Alto.h @@ -0,0 +1,20 @@ +/* -*- mode: c++ -*- + * Kaleidoscope-Hardware-Keyboardio-Alto -- Keyboardio Alto hardware support for Kaleidoscope + * Copyright (C) 2021 Keyboard.io, Inc + * + * 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, version 3. + * + * 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 "kaleidoscope/device/keyboardio/Alto.h" // IWYU pragma: export diff --git a/plugins/Kaleidoscope-Hardware-Keyboardio-Alto/src/kaleidoscope/device/keyboardio/Alto.cpp b/plugins/Kaleidoscope-Hardware-Keyboardio-Alto/src/kaleidoscope/device/keyboardio/Alto.cpp new file mode 100644 index 0000000000..a0435e1215 --- /dev/null +++ b/plugins/Kaleidoscope-Hardware-Keyboardio-Alto/src/kaleidoscope/device/keyboardio/Alto.cpp @@ -0,0 +1,71 @@ +/* -*- mode: c++ -*- + * Kaleidoscope-Hardware-Alto -- Keyboardio Alto hardware support for Kaleidoscope + * Copyright (C) 2021 Keyboard.io, Inc + * + * 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, version 3. + * + * 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 . + */ + +#ifdef ARDUINO_keyboardio_alto + +#include "kaleidoscope/driver/keyscanner/Base_Impl.h" // For Base<> + +#include "kaleidoscope/device/keyboardio/Alto.h" +#include "kaleidoscope/Runtime.h" + + +// Here, we set up aliases to the device's KeyScanner and KeyScannerProps +// in the global namespace within the scope of this file. We'll use these +// aliases to simplify some template initialization code below. +using KeyScannerProps = typename kaleidoscope::device::keyboardio::AltoProps::KeyScannerProps; +using KeyScanner = typename kaleidoscope::device::keyboardio::AltoProps::KeyScanner; + + +namespace kaleidoscope { +namespace device { +namespace keyboardio { + +// `KeyScannerProps` here refers to the alias set up above. We do not need to +// prefix the `matrix_rows` and `matrix_columns` names within the array +// declaration, because those are resolved within the context of the class, so +// the `matrix_rows` in `KeyScannerProps::matrix_row_pins[matrix_rows]` gets +// resolved as `KeyScannerProps::matrix_rows`. +const uint8_t KeyScannerProps::matrix_rows; +const uint8_t KeyScannerProps::matrix_columns; +constexpr uint8_t KeyScannerProps::matrix_row_pins[matrix_rows]; +constexpr uint8_t KeyScannerProps::matrix_col_pins[matrix_columns]; +// +//// `KeyScanner` here refers to the alias set up above, just like in the +//// `KeyScannerProps` case above. +template<> +KeyScanner::row_state_t KeyScanner::matrix_state_[KeyScannerProps::matrix_rows] = {}; + +template<> +uint32_t KeyScanner::next_scan_at_ = 0; + + +#ifndef KALEIDOSCOPE_VIRTUAL_BUILD + +/********* Hardware plugin *********/ + +void Alto::rebootBootloader() { + USBCore().disconnect(); + NVIC_SystemReset(); +} + +#endif + +} // namespace keyboardio +} // namespace device +} // namespace kaleidoscope + +#endif diff --git a/plugins/Kaleidoscope-Hardware-Keyboardio-Alto/src/kaleidoscope/device/keyboardio/Alto.h b/plugins/Kaleidoscope-Hardware-Keyboardio-Alto/src/kaleidoscope/device/keyboardio/Alto.h new file mode 100644 index 0000000000..e040a210e3 --- /dev/null +++ b/plugins/Kaleidoscope-Hardware-Keyboardio-Alto/src/kaleidoscope/device/keyboardio/Alto.h @@ -0,0 +1,124 @@ +/* -*- mode: c++ -*- + * Kaleidoscope-Hardware-Alto -- Keyboardio Alto hardware support for Kaleidoscope + * Copyright (C) 2017-2021 Keyboard.io, Inc + * + * 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, version 3. + * + * 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 + +#ifdef ARDUINO_keyboardio_alto + +#include + +#define CRGB(r, g, b) \ + (cRGB) { \ + b, g, r \ + } + +struct cRGB { + uint8_t b; + uint8_t g; + uint8_t r; +}; + + +#include "kaleidoscope/device/Base.h" +#include "kaleidoscope/driver/bootloader/gd32/Base.h" +#include "kaleidoscope/driver/hid/Base.h" +#include "kaleidoscope/driver/hid/Keyboardio.h" +#include "kaleidoscope/driver/keyscanner/GD32.h" +#include "kaleidoscope/driver/led/Base.h" +#include "kaleidoscope/driver/mcu/GD32.h" +#include "kaleidoscope/driver/storage/GD32Flash.h" + + +namespace kaleidoscope { +namespace device { +namespace keyboardio { + +struct AltoStorageProps : public kaleidoscope::driver::storage::GD32FlashProps { + static constexpr uint16_t length = 16384; +}; + +struct AltoKeyScannerProps : public kaleidoscope::driver::keyscanner::GD32Props { + + static constexpr uint8_t matrix_rows = 4; + static constexpr uint8_t matrix_columns = 16; + typedef MatrixAddr KeyAddr; +#ifndef KALEIDOSCOPE_VIRTUAL_BUILD + static constexpr uint8_t matrix_row_pins[matrix_rows] = {PB15, PB14, PB13, PB12}; + static constexpr uint8_t matrix_col_pins[matrix_columns] = {PB11, PB10, PB9, PB8, PB7, PB6, PB5, PC15, PC14, PC13, PA0, PA1, PA2, PA3, PA4, PA5}; + +#endif // ifndef KALEIDOSCOPE_VIRTUAL_BUILD +}; + + +// If we need to override HID props: +struct AltoHIDProps : public kaleidoscope::driver::hid::KeyboardioProps { + //typedef kaleidoscope::driver::hid::base::AbsoluteMouseProps AbsoluteMouseProps; + //typedef kaleidoscope::driver::hid::base::AbsoluteMouse AbsoluteMouse; +}; + + +struct AltoProps : public kaleidoscope::device::BaseProps { + typedef AltoHIDProps HIDProps; + typedef kaleidoscope::driver::hid::Keyboardio HID; + + typedef AltoKeyScannerProps KeyScannerProps; + typedef kaleidoscope::driver::keyscanner::GD32 KeyScanner; + + typedef AltoStorageProps StorageProps; + typedef kaleidoscope::driver::storage::GD32Flash Storage; + + typedef kaleidoscope::driver::bootloader::gd32::Base Bootloader; + static constexpr const char *short_name = "alto"; + + typedef kaleidoscope::driver::mcu::GD32Props MCUProps; + typedef kaleidoscope::driver::mcu::GD32 MCU; +}; + +#ifndef KALEIDOSCOPE_VIRTUAL_BUILD + +class Alto : public kaleidoscope::device::Base { + public: + auto serialPort() -> decltype(Serial) & { + return Serial; + } + static void rebootBootloader(); +}; + +#endif // ifndef KALEIDOSCOPE_VIRTUAL_BUILD + +} // namespace keyboardio +} // namespace device + +EXPORT_DEVICE(kaleidoscope::device::keyboardio::Alto) + +} // namespace kaleidoscope + +// clang-format off + + +#define PER_KEY_DATA(dflt, \ + r0c0, r0c1, r0c2, r0c3, r0c4, r0c5, r0c6, r0c7, r0c8, r0c9, r0c10, r0c11, r0c12, r0c13, r0c14, r0c15, \ + r1c0, r1c1, r1c2, r1c3, r1c4, r1c5, r1c6, r1c7, r1c8, r1c9, r1c10, r1c11, r1c12, r1c13, r1c14, r1c15, \ + r2c0, r2c1, r2c2, r2c3, r2c4, r2c5, r2c6, r2c7, r2c8, r2c9, r2c10, r2c11, r2c12, r2c14, \ + r3c0, r3c1, r3c2, r3c3, r3c4, r3c5, r3c6, r3c8, r3c9, r3c10, r3c11, r3c12, r3c13, r3c14, \ + r3c7, ...)\ + r0c0, r0c1, r0c2, r0c3, r0c4, r0c5, r0c6, r0c7, r0c8, r0c9, r0c10, r0c11, r0c12, r0c13, r0c14, r0c15, \ + r1c0, r1c1, r1c2, r1c3, r1c4, r1c5, r1c6, r1c7, r1c8, r1c9, r1c10, r1c11, r1c12, r1c13, r1c14, r1c15, \ + r2c0, r2c1, r2c2, r2c3, r2c4, r2c5, r2c6, r2c7, r2c8, r2c9, r2c10, r2c11, r2c12, XXX, r2c14, XXX, \ + r3c0, r3c1, r3c2, r3c3, r3c4, r3c5, r3c6, r3c7, r3c8, r3c9, r3c10, r3c11, r3c12, r3c13, RESTRICT_ARGS_COUNT((r3c14), 61, KEYMAP, ##__VA_ARGS__) + +#endif diff --git a/plugins/Kaleidoscope-Hardware-Keyboardio-Model100u/examples/Devices/Keyboardio/Model100u/Makefile b/plugins/Kaleidoscope-Hardware-Keyboardio-Model100u/examples/Devices/Keyboardio/Model100u/Makefile new file mode 100644 index 0000000000..19019b31db --- /dev/null +++ b/plugins/Kaleidoscope-Hardware-Keyboardio-Model100u/examples/Devices/Keyboardio/Model100u/Makefile @@ -0,0 +1,48 @@ +# This makefile for a Kaleidoscope sketch pulls in all the targets +# required to build the example + + + + +ifneq ($(KALEIDOSCOPE_DIR),) +search_path += $(KALEIDOSCOPE_DIR) +endif + +ifneq ($(ARDUINO_DIRECTORIES_USER),) +search_path += $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/avr/libraries/Kaleidoscope +endif + +ifeq ($(shell uname -s),Darwin) +search_path += $(HOME)/Documents/Arduino/hardware/keyboardio/avr/libraries/Kaleidoscope +else +search_path += $(HOME)/Arduino/hardware/keyboardio/avr/libraries/Kaleidoscope +endif + +sketch_makefile := etc/makefiles/sketch.mk + +$(foreach candidate, $(search_path), $(if $(wildcard $(candidate)/$(sketch_makefile)), $(eval ks_dir ?= $(candidate)))) + +ifneq ($(ks_dir),) + +$(info Using Kaleidoscope from $(ks_dir)) + +export KALEIDOSCOPE_DIR := $(ks_dir) +include $(ks_dir)/$(sketch_makefile) + +else + +$(info I can't find your Kaleidoscope installation.) +$(info ) +$(info I tried looking in:) +$(info ) +$(foreach candidate, $(search_path), $(info $(candidate))) +$(info ) +$(info The easiest way to fix this is to set the 'KALEIDOSCOPE_DIR' environment) +$(info variable to the location of your Kaleidoscope directory.) + +endif + + +null-target: + $(info You should never see this message) + @: diff --git a/plugins/Kaleidoscope-Hardware-Keyboardio-Model100u/examples/Devices/Keyboardio/Model100u/Model100u.ino b/plugins/Kaleidoscope-Hardware-Keyboardio-Model100u/examples/Devices/Keyboardio/Model100u/Model100u.ino new file mode 100644 index 0000000000..3805a531f1 --- /dev/null +++ b/plugins/Kaleidoscope-Hardware-Keyboardio-Model100u/examples/Devices/Keyboardio/Model100u/Model100u.ino @@ -0,0 +1,488 @@ +// -*- mode: c++ -*- +// Copyright 2016-2022 Keyboardio, inc. +// See "LICENSE" for license details + +/** + * These #include directives pull in the Kaleidoscope firmware core, + * as well as the Kaleidoscope plugins we use in the Model 100's firmware + */ + +// The Kaleidoscope core +#include "Kaleidoscope.h" + +// Support for storing the keymap in EEPROM +#include "Kaleidoscope-EEPROM-Settings.h" +#include "Kaleidoscope-EEPROM-Keymap.h" + +// Support for communicating with the host via a simple Serial protocol +#include "Kaleidoscope-FocusSerial.h" + +// Support for querying the firmware version via Focus +#include "Kaleidoscope-FirmwareVersion.h" + +// Support for keys that move the mouse +#include "Kaleidoscope-MouseKeys.h" + +// Support for macros +#include "Kaleidoscope-Macros.h" + +// Support for host power management (suspend & wakeup) +#include "Kaleidoscope-HostPowerManagement.h" + +// Support for magic combos (key chords that trigger an action) +#include "Kaleidoscope-MagicCombo.h" + +// Support for USB quirks, like changing the key state report protocol +#include "Kaleidoscope-USB-Quirks.h" + +// Support for secondary actions on keys +#include "Kaleidoscope-Qukeys.h" + +// Support for one-shot modifiers and layer keys +#include "Kaleidoscope-OneShot.h" +#include "Kaleidoscope-Escape-OneShot.h" + +// Support for dynamic, Chrysalis-editable macros +#include "Kaleidoscope-DynamicMacros.h" + +// Support for SpaceCadet keys +#include "Kaleidoscope-SpaceCadet.h" + +// Support for editable layer names +#include "Kaleidoscope-LayerNames.h" + +// Support for the GeminiPR Stenography protocol +#include "Kaleidoscope-Steno.h" + +/** This 'enum' is a list of all the macros used by the Model 100's firmware + * The names aren't particularly important. What is important is that each + * is unique. + * + * These are the names of your macros. They'll be used in two places. + * The first is in your keymap definitions. There, you'll use the syntax + * `M(MACRO_NAME)` to mark a specific keymap position as triggering `MACRO_NAME` + * + * The second usage is in the 'switch' statement in the `macroAction` function. + * That switch statement actually runs the code associated with a macro when + * a macro key is pressed. + */ + +enum { + MACRO_VERSION_INFO, + MACRO_ANY, +}; + + +/** The Model 100's key layouts are defined as 'keymaps'. By default, there are three + * keymaps: The standard QWERTY keymap, the "Function layer" keymap and the "Numpad" + * keymap. + * + * Each keymap is defined as a list using the 'KEYMAP_STACKED' macro, built + * of first the left hand's layout, followed by the right hand's layout. + * + * Keymaps typically consist mostly of `Key_` definitions. There are many, many keys + * defined as part of the USB HID Keyboard specification. You can find the names + * (if not yet the explanations) for all the standard `Key_` defintions offered by + * Kaleidoscope in these files: + * https://github.com/keyboardio/Kaleidoscope/blob/master/src/kaleidoscope/key_defs/keyboard.h + * https://github.com/keyboardio/Kaleidoscope/blob/master/src/kaleidoscope/key_defs/consumerctl.h + * https://github.com/keyboardio/Kaleidoscope/blob/master/src/kaleidoscope/key_defs/sysctl.h + * https://github.com/keyboardio/Kaleidoscope/blob/master/src/kaleidoscope/key_defs/keymaps.h + * + * Additional things that should be documented here include + * using ___ to let keypresses fall through to the previously active layer + * using XXX to mark a keyswitch as 'blocked' on this layer + * using ShiftToLayer() and LockLayer() keys to change the active keymap. + * keeping NUM and FN consistent and accessible on all layers + * + * The PROG key is special, since it is how you indicate to the board that you + * want to flash the firmware. However, it can be remapped to a regular key. + * When the keyboard boots, it first looks to see whether the PROG key is held + * down; if it is, it simply awaits further flashing instructions. If it is + * not, it continues loading the rest of the firmware and the keyboard + * functions normally, with whatever binding you have set to PROG. More detail + * here: https://community.keyboard.io/t/how-the-prog-key-gets-you-into-the-bootloader/506/8 + * + * The "keymaps" data structure is a list of the keymaps compiled into the firmware. + * The order of keymaps in the list is important, as the ShiftToLayer(#) and LockLayer(#) + * macros switch to key layers based on this list. + * + * + + * A key defined as 'ShiftToLayer(FUNCTION)' will switch to FUNCTION while held. + * Similarly, a key defined as 'LockLayer(NUMPAD)' will switch to NUMPAD when tapped. + */ + +/** + * Layers are "0-indexed" -- That is the first one is layer 0. The second one is layer 1. + * The third one is layer 2. + * This 'enum' lets us use names like QWERTY, FUNCTION, and NUMPAD in place of + * the numbers 0, 1 and 2. + * + */ + +enum { + PRIMARY, + NUMPAD, + FUNCTION, +}; // layers + + +/** + * To change your keyboard's layout from QWERTY to DVORAK or COLEMAK, comment out the line + * + * #define PRIMARY_KEYMAP_QWERTY + * + * by changing it to + * + * // #define PRIMARY_KEYMAP_QWERTY + * + * Then uncomment the line corresponding to the layout you want to use. + * + */ + +#define PRIMARY_KEYMAP_QWERTY +// #define PRIMARY_KEYMAP_DVORAK +// #define PRIMARY_KEYMAP_COLEMAK +// #define PRIMARY_KEYMAP_CUSTOM + + +/* This comment temporarily turns off astyle's indent enforcement + * so we can make the keymaps actually resemble the physical key layout better + */ +// clang-format off + +KEYMAPS( + +#if defined (PRIMARY_KEYMAP_QWERTY) + [PRIMARY] = KEYMAP_STACKED + (___, Key_1, Key_2, Key_3, Key_4, Key_5, ___, + Key_Backtick, Key_Q, Key_W, Key_E, Key_R, Key_T, Key_Tab, + Key_PageUp, Key_A, Key_S, Key_D, Key_F, Key_G, + Key_PageDown, Key_Z, Key_X, Key_C, Key_V, Key_B, Key_Escape, + Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift, + ShiftToLayer(FUNCTION), + + M(MACRO_ANY), Key_6, Key_7, Key_8, Key_9, Key_0, LockLayer(NUMPAD), + Key_Enter, Key_Y, Key_U, Key_I, Key_O, Key_P, Key_Equals, + Key_H, Key_J, Key_K, Key_L, Key_Semicolon, Key_Quote, + Key_RightAlt, Key_N, Key_M, Key_Comma, Key_Period, Key_Slash, Key_Minus, + Key_RightShift, Key_LeftAlt, Key_Spacebar, Key_RightControl, + ShiftToLayer(FUNCTION)), + +#elif defined (PRIMARY_KEYMAP_DVORAK) + + [PRIMARY] = KEYMAP_STACKED + (___, Key_1, Key_2, Key_3, Key_4, Key_5, ___, + Key_Backtick, Key_Quote, Key_Comma, Key_Period, Key_P, Key_Y, Key_Tab, + Key_PageUp, Key_A, Key_O, Key_E, Key_U, Key_I, + Key_PageDown, Key_Semicolon, Key_Q, Key_J, Key_K, Key_X, Key_Escape, + Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift, + ShiftToLayer(FUNCTION), + + M(MACRO_ANY), Key_6, Key_7, Key_8, Key_9, Key_0, LockLayer(NUMPAD), + Key_Enter, Key_F, Key_G, Key_C, Key_R, Key_L, Key_Slash, + Key_D, Key_H, Key_T, Key_N, Key_S, Key_Minus, + Key_RightAlt, Key_B, Key_M, Key_W, Key_V, Key_Z, Key_Equals, + Key_RightShift, Key_LeftAlt, Key_Spacebar, Key_RightControl, + ShiftToLayer(FUNCTION)), + +#elif defined (PRIMARY_KEYMAP_COLEMAK) + + [PRIMARY] = KEYMAP_STACKED + (___, Key_1, Key_2, Key_3, Key_4, Key_5, ___, + Key_Backtick, Key_Q, Key_W, Key_F, Key_P, Key_B, Key_Tab, + Key_PageUp, Key_A, Key_R, Key_S, Key_T, Key_G, + Key_PageDown, Key_Z, Key_X, Key_C, Key_D, Key_V, Key_Escape, + Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift, + ShiftToLayer(FUNCTION), + + M(MACRO_ANY), Key_6, Key_7, Key_8, Key_9, Key_0, LockLayer(NUMPAD), + Key_Enter, Key_J, Key_L, Key_U, Key_Y, Key_Semicolon, Key_Equals, + Key_M, Key_N, Key_E, Key_I, Key_O, Key_Quote, + Key_RightAlt, Key_K, Key_H, Key_Comma, Key_Period, Key_Slash, Key_Minus, + Key_RightShift, Key_LeftAlt, Key_Spacebar, Key_RightControl, + ShiftToLayer(FUNCTION)), + +#elif defined (PRIMARY_KEYMAP_CUSTOM) + // Edit this keymap to make a custom layout + [PRIMARY] = KEYMAP_STACKED + (___, Key_1, Key_2, Key_3, Key_4, Key_5, ___, + Key_Backtick, Key_Q, Key_W, Key_E, Key_R, Key_T, Key_Tab, + Key_PageUp, Key_A, Key_S, Key_D, Key_F, Key_G, + Key_PageDown, Key_Z, Key_X, Key_C, Key_V, Key_B, Key_Escape, + Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift, + ShiftToLayer(FUNCTION), + + M(MACRO_ANY), Key_6, Key_7, Key_8, Key_9, Key_0, LockLayer(NUMPAD), + Key_Enter, Key_Y, Key_U, Key_I, Key_O, Key_P, Key_Equals, + Key_H, Key_J, Key_K, Key_L, Key_Semicolon, Key_Quote, + Key_RightAlt, Key_N, Key_M, Key_Comma, Key_Period, Key_Slash, Key_Minus, + Key_RightShift, Key_LeftAlt, Key_Spacebar, Key_RightControl, + ShiftToLayer(FUNCTION)), + +#else + +#error "No default keymap defined. You should make sure that you have a line like '#define PRIMARY_KEYMAP_QWERTY' in your sketch" + +#endif + + + + [NUMPAD] = KEYMAP_STACKED + (___, ___, ___, ___, ___, ___, ___, + ___, ___, ___, ___, ___, ___, ___, + ___, ___, ___, ___, ___, ___, + ___, ___, ___, ___, ___, ___, ___, + ___, ___, ___, ___, + ___, + + M(MACRO_VERSION_INFO), ___, Key_7, Key_8, Key_9, Key_KeypadSubtract, ___, + ___, ___, Key_4, Key_5, Key_6, Key_KeypadAdd, ___, + ___, Key_1, Key_2, Key_3, Key_Equals, ___, + ___, ___, Key_0, Key_Period, Key_KeypadMultiply, Key_KeypadDivide, Key_Enter, + ___, ___, ___, ___, + ___), + + [FUNCTION] = KEYMAP_STACKED + (___, Key_F1, Key_F2, Key_F3, Key_F4, Key_F5, Key_CapsLock, + Key_Tab, ___, Key_mouseUp, ___, Key_mouseBtnR, Key_mouseWarpEnd, Key_mouseWarpNE, + Key_Home, Key_mouseL, Key_mouseDn, Key_mouseR, Key_mouseBtnL, Key_mouseWarpNW, + Key_End, Key_PrintScreen, Key_Insert, ___, Key_mouseBtnM, Key_mouseWarpSW, Key_mouseWarpSE, + ___, Key_Delete, ___, ___, + ___, + + Consumer_ScanPreviousTrack, Key_F6, Key_F7, Key_F8, Key_F9, Key_F10, Key_F11, + Consumer_PlaySlashPause, Consumer_ScanNextTrack, Key_LeftCurlyBracket, Key_RightCurlyBracket, Key_LeftBracket, Key_RightBracket, Key_F12, + Key_LeftArrow, Key_DownArrow, Key_UpArrow, Key_RightArrow, ___, ___, + Key_PcApplication, Consumer_Mute, Consumer_VolumeDecrement, Consumer_VolumeIncrement, ___, Key_Backslash, Key_Pipe, + ___, ___, Key_Enter, ___, + ___) +) // KEYMAPS( + +/* Re-enable astyle's indent enforcement */ +// clang-format on + +/** versionInfoMacro handles the 'firmware version info' macro + * When a key bound to the macro is pressed, this macro + * prints out the firmware build information as virtual keystrokes + */ + +static void versionInfoMacro(uint8_t key_state) { + if (keyToggledOn(key_state)) { + Macros.type(PSTR("Keyboardio Model 100 - Firmware version ")); + Macros.type(PSTR(KALEIDOSCOPE_FIRMWARE_VERSION)); + } +} + +/** anyKeyMacro is used to provide the functionality of the 'Any' key. + * + * When the 'any key' macro is toggled on, a random alphanumeric key is + * selected. While the key is held, the function generates a synthetic + * keypress event repeating that randomly selected key. + * + */ + +static void anyKeyMacro(KeyEvent &event) { + if (keyToggledOn(event.state)) { + event.key.setKeyCode(Key_A.getKeyCode() + (uint8_t)(millis() % 36)); + event.key.setFlags(0); + } +} + + +/** macroAction dispatches keymap events that are tied to a macro + to that macro. It takes two uint8_t parameters. + + The first is the macro being called (the entry in the 'enum' earlier in this file). + The second is the state of the keyswitch. You can use the keyswitch state to figure out + if the key has just been toggled on, is currently pressed or if it's just been released. + + The 'switch' statement should have a 'case' for each entry of the macro enum. + Each 'case' statement should call out to a function to handle the macro in question. + + */ + +const macro_t *macroAction(uint8_t macro_id, KeyEvent &event) { + switch (macro_id) { + + case MACRO_VERSION_INFO: + versionInfoMacro(event.state); + break; + + case MACRO_ANY: + anyKeyMacro(event); + break; + } + return MACRO_NONE; +} + + +/** This 'enum' is a list of all the magic combos used by the Model 100's + * firmware The names aren't particularly important. What is important is that + * each is unique. + * + * These are the names of your magic combos. They will be used by the + * `USE_MAGIC_COMBOS` call below. + */ +enum { + // Toggle between Boot (6-key rollover; for BIOSes and early boot) and NKRO + // mode. + COMBO_TOGGLE_NKRO_MODE, +}; + +/** Wrappers, to be used by MagicCombo. **/ + +/** + * This simply toggles the keyboard protocol via USBQuirks, and wraps it within + * a function with an unused argument, to match what MagicCombo expects. + */ +static void toggleKeyboardProtocol(uint8_t combo_index) { + USBQuirks.toggleKeyboardProtocol(); +} + +/** + * Toggles between using the built-in keymap, and the EEPROM-stored one. + */ +static void toggleKeymapSource(uint8_t combo_index) { + if (Layer.getKey == Layer.getKeyFromPROGMEM) { + Layer.getKey = EEPROMKeymap.getKey; + } else { + Layer.getKey = Layer.getKeyFromPROGMEM; + } +} + + +/** Magic combo list, a list of key combo and action pairs the firmware should + * recognise. + */ +USE_MAGIC_COMBOS({.action = toggleKeyboardProtocol, + // Left Fn + Prog + LED + .keys = {R3C6, R0C0, R0C6}}, + {.action = toggleKeymapSource, + // Left Fn + Prog + Shift + .keys = {R3C6, R0C0, R3C7}}); + +// First, tell Kaleidoscope which plugins you want to use. +// The order can be important. For example, LED effects are +// added in the order they're listed here. +KALEIDOSCOPE_INIT_PLUGINS( + // The EEPROMSettings & EEPROMKeymap plugins make it possible to have an + // editable keymap in EEPROM. + EEPROMSettings, + EEPROMKeymap, + + // SpaceCadet can turn your shifts into parens on tap, while keeping them as + // Shifts when held. SpaceCadetConfig lets Chrysalis configure some aspects of + // the plugin. + SpaceCadet, + SpaceCadetConfig, + + // Focus allows bi-directional communication with the host, and is the + // interface through which the keymap in EEPROM can be edited. + Focus, + + // FocusSettingsCommand adds a few Focus commands, intended to aid in + // changing some settings of the keyboard, such as the default layer (via the + // `settings.defaultLayer` command) + FocusSettingsCommand, + + // FocusEEPROMCommand adds a set of Focus commands, which are very helpful in + // both debugging, and in backing up one's EEPROM contents. + FocusEEPROMCommand, + + // The macros plugin adds support for macros + Macros, + + // The MouseKeys plugin lets you add keys to your keymap which move the mouse. + // The MouseKeysConfig plugin lets Chrysalis configure some aspects of the + // plugin. + MouseKeys, + MouseKeysConfig, + + // The HostPowerManagement plugin allows us to turn LEDs off when then host + // goes to sleep, and resume them when it wakes up. + HostPowerManagement, + + // The MagicCombo plugin lets you use key combinations to trigger custom + // actions - a bit like Macros, but triggered by pressing multiple keys at the + // same time. + MagicCombo, + + // The USBQuirks plugin lets you do some things with USB that we aren't + // comfortable - or able - to do automatically, but can be useful + // nevertheless. Such as toggling the key report protocol between Boot (used + // by BIOSes) and Report (NKRO). + USBQuirks, + + // The Qukeys plugin enables the "Secondary action" functionality in + // Chrysalis. Keys with secondary actions will have their primary action + // performed when tapped, but the secondary action when held. + Qukeys, + + // Enables the "Sticky" behavior for modifiers, and the "Layer shift when + // held" functionality for layer keys. + OneShot, + OneShotConfig, + EscapeOneShot, + EscapeOneShotConfig, + + // Enables dynamic, Chrysalis-editable macros. + DynamicMacros, + + // The FirmwareVersion plugin lets Chrysalis query the version of the firmware + // programmatically. + FirmwareVersion, + + // The LayerNames plugin allows Chrysalis to display - and edit - custom layer + // names, to be shown instead of the default indexes. + LayerNames, + + // Enables the GeminiPR Stenography protocol. Unused by default, but with the + // plugin enabled, it becomes configurable - and then usable - via Chrysalis. + GeminiPR); + +/** The 'setup' function is one of the two standard Arduino sketch functions. + * It's called when your keyboard first powers up. This is where you set up + * Kaleidoscope and any plugins. + */ +void setup() { + // First, call Kaleidoscope's internal setup function + Kaleidoscope.setup(); + + // To make the keymap editable without flashing new firmware, we store + // additional layers in EEPROM. For now, we reserve space for eight layers. If + // one wants to use these layers, just set the default layer to one in EEPROM, + // by using the `settings.defaultLayer` Focus command, or by using the + // `keymap.onlyCustom` command to use EEPROM layers only. + EEPROMKeymap.setup(8); + + // For Dynamic Macros, we need to reserve storage space for the editable + // macros. A kilobyte is a reasonable default. + DynamicMacros.reserve_storage(1024); + + // If there's a default layer set in EEPROM, we should set that as the default + // here. + Layer.move(EEPROMSettings.default_layer()); + + // To avoid any surprises, SpaceCadet is turned off by default. However, it + // can be permanently enabled via Chrysalis, so we should only disable it if + // no configuration exists. + SpaceCadetConfig.disableSpaceCadetIfUnconfigured(); + + // Editable layer names are stored in EEPROM too, and we reserve 16 bytes per + // layer for them. We need one extra byte per layer for bookkeeping, so we + // reserve 17 / layer in total. + LayerNames.reserve_storage(17 * 8); +} + +/** loop is the second of the standard Arduino sketch functions. + * As you might expect, it runs in a loop, never exiting. + * + * For Kaleidoscope-based keyboard firmware, you usually just want to + * call Kaleidoscope.loop(); and not do anything custom here. + */ + +void loop() { + Kaleidoscope.loop(); +} diff --git a/plugins/Kaleidoscope-Hardware-Keyboardio-Model100u/examples/Devices/Keyboardio/Model100u/sketch.json b/plugins/Kaleidoscope-Hardware-Keyboardio-Model100u/examples/Devices/Keyboardio/Model100u/sketch.json new file mode 100644 index 0000000000..2b73ff01d9 --- /dev/null +++ b/plugins/Kaleidoscope-Hardware-Keyboardio-Model100u/examples/Devices/Keyboardio/Model100u/sketch.json @@ -0,0 +1,6 @@ +{ + "cpu": { + "fqbn": "keyboardio:gd32:keyboardio_model_100u", + "port": "" + } +} diff --git a/plugins/Kaleidoscope-Hardware-Keyboardio-Model100u/library.properties b/plugins/Kaleidoscope-Hardware-Keyboardio-Model100u/library.properties new file mode 100644 index 0000000000..c16311292c --- /dev/null +++ b/plugins/Kaleidoscope-Hardware-Keyboardio-Model100u/library.properties @@ -0,0 +1,7 @@ +name=Kaleidoscope-Hardware-Keyboardio-Model100u +version=0.0.0 +sentence=Keyboardio Model100u hardware support for Kaleidoscope +maintainer=Kaleidoscope's Developers +url=https://github.com/keyboardio/Kaleidoscope +author=Keyboardio +paragraph= diff --git a/plugins/Kaleidoscope-Hardware-Keyboardio-Model100u/src/Kaleidoscope-Hardware-Keyboardio-Model100u.h b/plugins/Kaleidoscope-Hardware-Keyboardio-Model100u/src/Kaleidoscope-Hardware-Keyboardio-Model100u.h new file mode 100644 index 0000000000..a4445eb379 --- /dev/null +++ b/plugins/Kaleidoscope-Hardware-Keyboardio-Model100u/src/Kaleidoscope-Hardware-Keyboardio-Model100u.h @@ -0,0 +1,20 @@ +/* -*- mode: c++ -*- + * Kaleidoscope-Hardware-Keyboardio-Model100u -- Keyboardio Model100 hardware support for Kaleidoscope + * Copyright (C) 2021 Keyboard.io, Inc + * + * 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, version 3. + * + * 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 "kaleidoscope/device/keyboardio/Model100u.h" // IWYU pragma: export diff --git a/plugins/Kaleidoscope-Hardware-Keyboardio-Model100u/src/kaleidoscope/device/keyboardio/Model100u.cpp b/plugins/Kaleidoscope-Hardware-Keyboardio-Model100u/src/kaleidoscope/device/keyboardio/Model100u.cpp new file mode 100644 index 0000000000..9e56929da6 --- /dev/null +++ b/plugins/Kaleidoscope-Hardware-Keyboardio-Model100u/src/kaleidoscope/device/keyboardio/Model100u.cpp @@ -0,0 +1,92 @@ +/* -*- mode: c++ -*- + * Kaleidoscope-Hardware-Model100u -- Keyboardio Model 100 hardware support for Kaleidoscope + * Copyright (C) 2021 Keyboard.io, Inc + * + * 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, version 3. + * + * 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 . + */ + +#ifdef ARDUINO_keyboardio_model_100u + +#include "kaleidoscope/device/keyboardio/Model100u.h" + +#include // for PROGMEM + +#include "kaleidoscope/driver/keyscanner/Base_Impl.h" // For Base<> + +namespace kaleidoscope { +namespace device { +namespace keyboardio { + + +#ifndef KALEIDOSCOPE_VIRTUAL_BUILD + + +/********* Key scanner *********/ + + +void Model100uKeyScanner::setup() { +} + +void Model100uKeyScanner::readMatrix() { + //scan the Keyboard matrix looking for connections +} + +void Model100uKeyScanner::actOnMatrixScan() { +} + + +void Model100uKeyScanner::scanMatrix() { + readMatrix(); + actOnMatrixScan(); +} + +void Model100uKeyScanner::setKeyscanInterval(uint8_t interval) { +} + +bool Model100uKeyScanner::isKeyswitchPressed(KeyAddr key_addr) { +} + +bool Model100uKeyScanner::wasKeyswitchPressed(KeyAddr key_addr) { +} + +uint8_t Model100uKeyScanner::pressedKeyswitchCount() { +} + +uint8_t Model100uKeyScanner::previousPressedKeyswitchCount() { +} + +/********* Hardware plugin *********/ + +void Model100u::setup() { + Model100uKeyScanner::setup(); + kaleidoscope::device::Base::setup(); +} + +void Model100u::enableHardwareTestMode() { + // Toggle the programming LEDS on + // TODO(anyone): PORTD |= (1 << 5); + // TOOD(anyone): PORTB |= (1 << 0); +} + +void Model100u::rebootBootloader() { + USBCore().disconnect(); + NVIC_SystemReset(); +} + +#endif + +} // namespace keyboardio +} // namespace device +} // namespace kaleidoscope + +#endif diff --git a/plugins/Kaleidoscope-Hardware-Keyboardio-Model100u/src/kaleidoscope/device/keyboardio/Model100u.h b/plugins/Kaleidoscope-Hardware-Keyboardio-Model100u/src/kaleidoscope/device/keyboardio/Model100u.h new file mode 100644 index 0000000000..51b5a76f43 --- /dev/null +++ b/plugins/Kaleidoscope-Hardware-Keyboardio-Model100u/src/kaleidoscope/device/keyboardio/Model100u.h @@ -0,0 +1,167 @@ +/* -*- mode: c++ -*- + * Kaleidoscope-Hardware-Model100u -- Keyboardio Model100 hardware support for Kaleidoscope + * Copyright (C) 2017-2021 Keyboard.io, Inc + * + * 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, version 3. + * + * 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 + +#ifdef ARDUINO_keyboardio_model_100u + +#include + +#define CRGB(r, g, b) \ + (cRGB) { \ + b, g, r \ + } + +struct cRGB { + uint8_t b; + uint8_t g; + uint8_t r; +}; + + +#include "kaleidoscope/device/Base.h" +#include "kaleidoscope/driver/bootloader/gd32/Base.h" +#include "kaleidoscope/driver/hid/Base.h" +#include "kaleidoscope/driver/hid/Keyboardio.h" +#include "kaleidoscope/driver/keyscanner/Base.h" +#include "kaleidoscope/driver/led/Base.h" +#include "kaleidoscope/driver/mcu/GD32.h" +#include "kaleidoscope/driver/storage/GD32Flash.h" + + +namespace kaleidoscope { +namespace device { +namespace keyboardio { + +struct Model100uStorageProps : public kaleidoscope::driver::storage::GD32FlashProps { + static constexpr uint16_t length = 16384; +}; + + +struct Model100uKeyScannerProps : public kaleidoscope::driver::keyscanner::BaseProps { + static constexpr uint8_t matrix_rows = 4; + static constexpr uint8_t matrix_columns = 16; + typedef MatrixAddr KeyAddr; +}; + +#ifndef KALEIDOSCOPE_VIRTUAL_BUILD +class Model100uKeyScanner : public kaleidoscope::driver::keyscanner::Base { + private: + typedef Model100uKeyScanner ThisType; + + public: + static void setup(); + static void scanMatrix(); + static void readMatrix(); + static void actOnMatrixScan(); + + static bool isKeyswitchPressed(KeyAddr key_addr); + static uint8_t pressedKeyswitchCount(); + + static bool wasKeyswitchPressed(KeyAddr key_addr); + static uint8_t previousPressedKeyswitchCount(); + + static void setKeyscanInterval(uint8_t interval); + + protected: +}; +#else // ifndef KALEIDOSCOPE_VIRTUAL_BUILD +class Model100uKeyScanner; +#endif // ifndef KALEIDOSCOPE_VIRTUAL_BUILD + + +// If we need to override HID props: +struct Model100uHIDProps : public kaleidoscope::driver::hid::KeyboardioProps { + //typedef kaleidoscope::driver::hid::base::AbsoluteMouseProps AbsoluteMouseProps; + //typedef kaleidoscope::driver::hid::base::AbsoluteMouse AbsoluteMouse; +}; + + +struct Model100uProps : public kaleidoscope::device::BaseProps { + typedef Model100uHIDProps HIDProps; + typedef kaleidoscope::driver::hid::Keyboardio HID; + + typedef Model100uKeyScannerProps KeyScannerProps; + typedef Model100uKeyScanner KeyScanner; + + typedef Model100uStorageProps StorageProps; + typedef kaleidoscope::driver::storage::GD32Flash Storage; + + typedef kaleidoscope::driver::bootloader::gd32::Base Bootloader; + static constexpr const char *short_name = "kbio100u"; + + typedef kaleidoscope::driver::mcu::GD32Props MCUProps; + typedef kaleidoscope::driver::mcu::GD32 MCU; +}; + +#ifndef KALEIDOSCOPE_VIRTUAL_BUILD + +class Model100u : public kaleidoscope::device::Base { + public: + void setup(); + + auto serialPort() -> decltype(Serial) & { + return Serial; + } + static void rebootBootloader(); + static void enableHardwareTestMode(); +}; + +#endif // ifndef KALEIDOSCOPE_VIRTUAL_BUILD + +} // namespace keyboardio +} // namespace device + +EXPORT_DEVICE(kaleidoscope::device::keyboardio::Model100u) + +} // namespace kaleidoscope + +// clang-format off + +#define PER_KEY_DATA_STACKED(dflt, \ + r0c0, r0c1, r0c2, r0c3, r0c4, r0c5, r0c6, \ + r1c0, r1c1, r1c2, r1c3, r1c4, r1c5, r1c6, \ + r2c0, r2c1, r2c2, r2c3, r2c4, r2c5, \ + r3c0, r3c1, r3c2, r3c3, r3c4, r3c5, r2c6, \ + r0c7, r1c7, r2c7, r3c7, \ + r3c6, \ + \ + r0c9, r0c10, r0c11, r0c12, r0c13, r0c14, r0c15, \ + r1c9, r1c10, r1c11, r1c12, r1c13, r1c14, r1c15, \ + r2c10, r2c11, r2c12, r2c13, r2c14, r2c15, \ + r2c9, r3c10, r3c11, r3c12, r3c13, r3c14, r3c15, \ + r3c8, r2c8, r1c8, r0c8, \ + r3c9, ...) \ + \ + r0c0, r0c1, r0c2, r0c3, r0c4, r0c5, r0c6, r0c7, r0c8, r0c9, r0c10, r0c11, r0c12, r0c13, r0c14, r0c15, \ + r1c0, r1c1, r1c2, r1c3, r1c4, r1c5, r1c6, r1c7, r1c8, r1c9, r1c10, r1c11, r1c12, r1c13, r1c14, r1c15, \ + r2c0, r2c1, r2c2, r2c3, r2c4, r2c5, r2c6, r2c7, r2c8, r2c9, r2c10, r2c11, r2c12, r2c13, r2c14, r2c15, \ + r3c0, r3c1, r3c2, r3c3, r3c4, r3c5, r3c6, r3c7, r3c8, r3c9, r3c10, r3c11, r3c12, r3c13, r3c14, RESTRICT_ARGS_COUNT((r3c15), 64, KEYMAP_STACKED, ##__VA_ARGS__) + +#define PER_KEY_DATA(dflt, \ + r0c0, r0c1, r0c2, r0c3, r0c4, r0c5, r0c6, r0c9, r0c10, r0c11, r0c12, r0c13, r0c14, r0c15, \ + r1c0, r1c1, r1c2, r1c3, r1c4, r1c5, r1c6, r1c9, r1c10, r1c11, r1c12, r1c13, r1c14, r1c15, \ + r2c0, r2c1, r2c2, r2c3, r2c4, r2c5, r2c10, r2c11, r2c12, r2c13, r2c14, r2c15, \ + r3c0, r3c1, r3c2, r3c3, r3c4, r3c5, r2c6, r2c9, r3c10, r3c11, r3c12, r3c13, r3c14, r3c15, \ + r0c7, r1c7, r2c7, r3c7, r3c8, r2c8, r1c8, r0c8, \ + r3c6, r3c9, ...) \ + r0c0, r0c1, r0c2, r0c3, r0c4, r0c5, r0c6, r0c7, r0c8, r0c9, r0c10, r0c11, r0c12, r0c13, r0c14, r0c15, \ + r1c0, r1c1, r1c2, r1c3, r1c4, r1c5, r1c6, r1c7, r1c8, r1c9, r1c10, r1c11, r1c12, r1c13, r1c14, r1c15, \ + r2c0, r2c1, r2c2, r2c3, r2c4, r2c5, r2c6, r2c7, r2c8, r2c9, r2c10, r2c11, r2c12, r2c13, r2c14, r2c15, \ + r3c0, r3c1, r3c2, r3c3, r3c4, r3c5, r3c6, r3c7, r3c8, r3c9, r3c10, r3c11, r3c12, r3c13, r3c14, RESTRICT_ARGS_COUNT((r3c15), 64, KEYMAP, ##__VA_ARGS__) + +#endif diff --git a/src/kaleidoscope/driver/keyscanner/GD32.h b/src/kaleidoscope/driver/keyscanner/GD32.h new file mode 100644 index 0000000000..0482daaaf8 --- /dev/null +++ b/src/kaleidoscope/driver/keyscanner/GD32.h @@ -0,0 +1,215 @@ +/* -*- mode: c++ -*- + * kaleidoscope::driver::keyscanner::GD32 -- Keyscanner for GD32 microcontrollers + * Copyright (C) 2018-2023 Keyboard.io, Inc + * + * 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, version 3. + * + * 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 // for uint16_t, uint8_t, uint32_t +#include "kaleidoscope/driver/keyscanner/Base.h" // for BaseProps +#include "kaleidoscope/driver/keyscanner/None.h" // for None + + +namespace kaleidoscope { +namespace driver { +namespace keyscanner { + + +struct GD32Props : kaleidoscope::driver::keyscanner::BaseProps { + static const uint32_t keyscan_interval_micros = 1500; + typedef uint16_t RowState; + + /* + * The following two lines declare an empty array. Both of these must be + * shadowed by the descendant keyscanner description class. + */ + static constexpr uint8_t matrix_row_pins[] = {}; + static constexpr uint8_t matrix_col_pins[] = {}; +}; + + +#ifndef KALEIDOSCOPE_VIRTUAL_BUILD +template +class GD32 : public kaleidoscope::driver::keyscanner::Base<_KeyScannerProps> { + protected: + /* + each of these variables are storing the state for a row of keys + + so for key 0, the counter is represented by db0[0] and db1[0] + and the state in debounced_state[0]. + */ + struct debounce_t { + typename _KeyScannerProps::RowState db0; // counter bit 0 + typename _KeyScannerProps::RowState db1; // counter bit 1 + typename _KeyScannerProps::RowState debounced_state; // debounced state + }; + + struct row_state_t { + typename _KeyScannerProps::RowState previous; + typename _KeyScannerProps::RowState current; + debounce_t debouncer; + }; + + private: + typedef GD32<_KeyScannerProps> ThisType; + typedef _KeyScannerProps KeyScannerProps_; + static row_state_t matrix_state_[_KeyScannerProps::matrix_rows]; + static uint32_t next_scan_at_; + + public: + void setup() { + static_assert( + sizeof(_KeyScannerProps::matrix_row_pins) > 0, + "The key scanner description has an empty array of matrix row pins."); + static_assert( + sizeof(_KeyScannerProps::matrix_col_pins) > 0, + "The key scanner description has an empty array of matrix column pins."); + + + for (uint8_t i = 0; i < _KeyScannerProps::matrix_columns; i++) { + pinMode(_KeyScannerProps::matrix_col_pins[i], INPUT_PULLUP); + } + + for (uint8_t i = 0; i < _KeyScannerProps::matrix_rows; i++) { + pinMode(_KeyScannerProps::matrix_row_pins[i], OUTPUT); + digitalWrite(_KeyScannerProps::matrix_row_pins[i], HIGH); + } + } + + + __attribute__((optimize(3))) void readMatrix(void) { + typename _KeyScannerProps::RowState any_debounced_changes = 0; + + for (uint8_t current_row = 0; current_row < _KeyScannerProps::matrix_rows; current_row++) { + digitalWrite(_KeyScannerProps::matrix_row_pins[current_row], LOW); + typename _KeyScannerProps::RowState hot_pins = readCols(); + digitalWrite(_KeyScannerProps::matrix_row_pins[current_row], HIGH); + + + any_debounced_changes |= debounce(hot_pins, &matrix_state_[current_row].debouncer); + + if (any_debounced_changes) { + for (uint8_t current_row = 0; current_row < _KeyScannerProps::matrix_rows; current_row++) { + matrix_state_[current_row].current = matrix_state_[current_row].debouncer.debounced_state; + } + } + } + } + void scanMatrix() { + uint32_t current_micros_ = micros(); + if (current_micros_ >= next_scan_at_) { + next_scan_at_ = current_micros_ + _KeyScannerProps::keyscan_interval_micros; + readMatrix(); + } + actOnMatrixScan(); + } + + void __attribute__((optimize(3))) actOnMatrixScan() { + for (uint8_t row = 0; row < _KeyScannerProps::matrix_rows; row++) { + for (uint8_t col = 0; col < _KeyScannerProps::matrix_columns; col++) { + uint8_t keyState = (bitRead(matrix_state_[row].previous, col) << 0) | (bitRead(matrix_state_[row].current, col) << 1); + if (keyState) { + ThisType::handleKeyswitchEvent(Key_NoKey, typename _KeyScannerProps::KeyAddr(row, col), keyState); + } + } + matrix_state_[row].previous = matrix_state_[row].current; + } + } + + uint8_t pressedKeyswitchCount() { + uint8_t count = 0; + + for (int8_t r = 0; r < _KeyScannerProps::matrix_rows; r++) { + count += __builtin_popcount(matrix_state_[r].current); + } + return count; + } + bool isKeyswitchPressed(typename _KeyScannerProps::KeyAddr key_addr) { + return (bitRead(matrix_state_[key_addr.row()].current, key_addr.col()) != 0); + } + + uint8_t previousPressedKeyswitchCount() { + uint8_t count = 0; + + for (int8_t r = 0; r < _KeyScannerProps::matrix_rows; r++) { + count += __builtin_popcount(matrix_state_[r].previous); + } + return count; + } + bool wasKeyswitchPressed(typename _KeyScannerProps::KeyAddr key_addr) { + return (bitRead(matrix_state_[key_addr.row()].previous, + key_addr.col()) != 0); + } + + + private: + /* + * This function has loop unrolling disabled on purpose: we want to give the + * hardware enough time to produce stable PIN reads for us. If we unroll the + * loop, we will not have that, because even with the NOP, the codepath is too + * fast. If we don't have stable reads, then entire rows or columns will behave + * erratically. + * + * For this reason, we ask the compiler to not unroll our loop, which in turn, + * gives hardware enough time to produce stable reads, at the cost of a little + * bit of speed. + * + * Do not remove the attribute! + * TODO : see if this is true on ARM. Jesse thinks it was an AVRism + */ + // __attribute__((optimize("no-unroll-loops"))) + typename _KeyScannerProps::RowState + readCols() { + typename _KeyScannerProps::RowState hot_pins = 0; + for (uint8_t i = 0; i < _KeyScannerProps::matrix_columns; i++) { + // asm("NOP"); // We need to pause a beat before reading or we may read before the pin is hot + hot_pins |= (!digitalRead(_KeyScannerProps::matrix_col_pins[i]) << i); + } + + return hot_pins; + } + + static inline typename _KeyScannerProps::RowState debounce( + typename _KeyScannerProps::RowState sample, debounce_t *debouncer) { + typename _KeyScannerProps::RowState delta, changes; + + // Use xor to detect changes from last stable state: + // if a key has changed, it's bit will be 1, otherwise 0 + delta = sample ^ debouncer->debounced_state; + + // Increment counters and reset any unchanged bits: + // increment bit 1 for all changed keys + debouncer->db1 = ((debouncer->db1) ^ (debouncer->db0)) & delta; + // increment bit 0 for all changed keys + debouncer->db0 = ~(debouncer->db0) & delta; + + // Calculate returned change set: if delta is still true + // and the counter has wrapped back to 0, the key is changed. + + changes = ~(~delta | (debouncer->db0) | (debouncer->db1)); + // Update state: in this case use xor to flip any bit that is true in changes. + debouncer->debounced_state ^= changes; + + return changes; + } +}; +#else // ifndef KALEIDOSCOPE_VIRTUAL_BUILD +template +class GD32 : public keyscanner::None {}; +#endif // ifndef KALEIDOSCOPE_VIRTUAL_BUILD + +} // namespace keyscanner +} // namespace driver +} // namespace kaleidoscope