diff --git a/Makefile b/Makefile index 53c63dc1..0daf2b81 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ endif DEFAULT_GOAL: smoke-sketches -setup: $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/avr/boards.txt $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/virtual/boards.txt $(ARDUINO_CLI_PATH) $(ARDUINO_DIRECTORIES_DATA)/arduino-cli.yaml install-arduino-core-avr +setup: $(ARDUINO_CLI_PATH) $(ARDUINO_DIRECTORIES_DATA)/arduino-cli.yaml install-arduino-core-avr install-arduino-core-kaleidoscope $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/avr/boards.txt $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/virtual/boards.txt @: @@ -27,9 +27,22 @@ $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/virtual/boards.txt: $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/avr/boards.txt: - git clone -c core.symlinks=true --recurse-submodules=":(exclude)avr/libraries/Kaleidoscope" --recurse-submodules=build-tools --recurse-submodules=toolchain --recurse-submodules=avr/libraries/ git://github.com/keyboardio/Kaleidoscope-Bundle-Keyboardio $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio - rm -d $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/avr/libraries/Kaleidoscope + git clone -c core.symlinks=true \ + --recurse-submodules=":(exclude)avr/libraries/Kaleidoscope" \ + --recurse-submodules=":(exclude)gd32/libraries/Kaleidoscope" \ + --recurse-submodules=avr/libraries/ \ + --recurse-submodules=gd32/ \ + --recurse-submodules=gd32/libraries/ \ + git://github.com/keyboardio/Kaleidoscope-Bundle-Keyboardio \ + $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio + -rm -d $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/avr/libraries/Kaleidoscope ln -s $(KALEIDOSCOPE_DIR) $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/avr/libraries/Kaleidoscope + git clone -c core.symlinks=true \ + --recurse-submodules=":(exclude)libraries/Kaleidoscope" \ + --recurse-submodules=libraries/ \ + git://github.com/keyboardio/ArduinoCore-GD32-Keyboardio $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/gd32 + -rm -d $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/gd32/libraries/Kaleidoscope + ln -s $(KALEIDOSCOPE_DIR) $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/gd32/libraries/Kaleidoscope simulator-tests: $(MAKE) -C tests all diff --git a/etc/makefiles/arduino-cli.mk b/etc/makefiles/arduino-cli.mk index a618dfe4..337dfa37 100644 --- a/etc/makefiles/arduino-cli.mk +++ b/etc/makefiles/arduino-cli.mk @@ -164,6 +164,7 @@ arduino-update-cores: install-arduino-core-kaleidoscope: arduino-update-cores $(QUIET) $(ARDUINO_CLI) core install "keyboardio:avr" + $(QUIET) $(ARDUINO_CLI) core install "keyboardio:gd32" install-arduino-core-avr: arduino-update-cores $(QUIET) $(ARDUINO_CLI) core install "arduino:avr" diff --git a/etc/makefiles/sketch.mk b/etc/makefiles/sketch.mk index 27fdb7cb..7d158dfc 100644 --- a/etc/makefiles/sketch.mk +++ b/etc/makefiles/sketch.mk @@ -209,12 +209,21 @@ ensure-device-port-defined: echo ;\ exit -1;fi -flash: ensure-device-port-defined +ifneq ($(FQBN),) +fqbn_arg = --fqbn $(FQBN) +endif + +ifneq ($(KALEIDOSCOPE_DEVICE_PORT),) +port_arg = --port $(KALEIDOSCOPE_DEVICE_PORT) +endif + + +flash: $(info $(unescaped_flashing_instructions)) $(info ) $(info When you're ready to proceed, press 'Enter'.) $(info ) @$(shell read _) - $(QUIET) $(ARDUINO_CLI) upload --fqbn $(FQBN) \ + $(QUIET) $(ARDUINO_CLI) upload $(fqbn_arg) \ --input-dir "${OUTPUT_PATH}" \ - --port $(KALEIDOSCOPE_DEVICE_PORT) $(ARDUINO_VERBOSE) + $(port_arg) $(ARDUINO_VERBOSE) diff --git a/examples/Devices/Keyboardio/Model100/Makefile b/examples/Devices/Keyboardio/Model100/Makefile new file mode 100644 index 00000000..19019b31 --- /dev/null +++ b/examples/Devices/Keyboardio/Model100/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/examples/Devices/Keyboardio/Model100/Model100.ino b/examples/Devices/Keyboardio/Model100/Model100.ino new file mode 100644 index 00000000..8d9650c4 --- /dev/null +++ b/examples/Devices/Keyboardio/Model100/Model100.ino @@ -0,0 +1,571 @@ +// -*- mode: c++ -*- +// Copyright 2016 Keyboardio, inc. +// See "LICENSE" for license details + +#ifndef BUILD_INFORMATION +#define BUILD_INFORMATION "locally built on " __DATE__ " at " __TIME__ +#endif + + +/** + * 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 keys that move the mouse +#include "Kaleidoscope-MouseKeys.h" + +// Support for macros +#include "Kaleidoscope-Macros.h" + +// Support for controlling the keyboard's LEDs +#include "Kaleidoscope-LEDControl.h" + +// Support for "Numpad" mode, which is mostly just the Numpad specific LED mode +#include "Kaleidoscope-NumPad.h" + +// Support for the "Boot greeting" effect, which pulses the 'LED' button for 10s +// when the keyboard is connected to a computer (or that computer is powered on) +#include "Kaleidoscope-LEDEffect-BootGreeting.h" + +// Support for LED modes that set all LEDs to a single color +#include "Kaleidoscope-LEDEffect-SolidColor.h" + +// Support for an LED mode that makes all the LEDs 'breathe' +#include "Kaleidoscope-LEDEffect-Breathe.h" + +// Support for an LED mode that makes a red pixel chase a blue pixel across the keyboard +#include "Kaleidoscope-LEDEffect-Chase.h" + +// Support for LED modes that pulse the keyboard's LED in a rainbow pattern +#include "Kaleidoscope-LEDEffect-Rainbow.h" + +// Support for an LED mode that lights up the keys as you press them +#include "Kaleidoscope-LED-Stalker.h" + +// Support for an LED mode that prints the keys you press in letters 4px high +#include "Kaleidoscope-LED-AlphaSquare.h" + +// Support for shared palettes for other plugins, like Colormap below +#include "Kaleidoscope-LED-Palette-Theme.h" + +// Support for an LED mode that lets one configure per-layer color maps +#include "Kaleidoscope-Colormap.h" + +// Support for Keyboardio's internal keyboard testing mode +#include "Kaleidoscope-HardwareTestMode.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" + +/** 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 + */ +// *INDENT-OFF* + +KEYMAPS( + +#if defined (PRIMARY_KEYMAP_QWERTY) + [PRIMARY] = KEYMAP_STACKED + (___, Key_1, Key_2, Key_3, Key_4, Key_5, Key_LEDEffectNext, + 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_LEDEffectNext, + 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_LEDEffectNext, + Key_Backtick, Key_Q, Key_W, Key_F, Key_P, Key_G, Key_Tab, + Key_PageUp, Key_A, Key_R, Key_S, Key_T, Key_D, + 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_J, Key_L, Key_U, Key_Y, Key_Semicolon, Key_Equals, + Key_H, Key_N, Key_E, Key_I, Key_O, Key_Quote, + Key_RightAlt, Key_K, Key_M, 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_LEDEffectNext, + 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 */ +// *INDENT-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 - Kaleidoscope ")); + Macros.type(PSTR(BUILD_INFORMATION)); + } +} + +/** 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; +} + + + +// These 'solid' color effect definitions define a rainbow of +// LED color modes calibrated to draw 500mA or less on the +// Keyboardio Model 100. + + +static kaleidoscope::plugin::LEDSolidColor solidRed(160, 0, 0); +static kaleidoscope::plugin::LEDSolidColor solidOrange(140, 70, 0); +static kaleidoscope::plugin::LEDSolidColor solidYellow(130, 100, 0); +static kaleidoscope::plugin::LEDSolidColor solidGreen(0, 160, 0); +static kaleidoscope::plugin::LEDSolidColor solidBlue(0, 70, 130); +static kaleidoscope::plugin::LEDSolidColor solidIndigo(0, 0, 170); +static kaleidoscope::plugin::LEDSolidColor solidViolet(130, 0, 120); + +/** toggleLedsOnSuspendResume toggles the LEDs off when the host goes to sleep, + * and turns them back on when it wakes up. + */ +void toggleLedsOnSuspendResume(kaleidoscope::plugin::HostPowerManagement::Event event) { + switch (event) { + case kaleidoscope::plugin::HostPowerManagement::Suspend: + LEDControl.disable(); + break; + case kaleidoscope::plugin::HostPowerManagement::Resume: + LEDControl.enable(); + break; + case kaleidoscope::plugin::HostPowerManagement::Sleep: + break; + } +} + +/** hostPowerManagementEventHandler dispatches power management events (suspend, + * resume, and sleep) to other functions that perform action based on these + * events. + */ +void hostPowerManagementEventHandler(kaleidoscope::plugin::HostPowerManagement::Event event) { + toggleLedsOnSuspendResume(event); +} + +/** 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, + // Enter test mode + COMBO_ENTER_TEST_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(); +} + +/** + * This enters the hardware test mode + */ +static void enterHardwareTestMode(uint8_t combo_index) { + HardwareTestMode.runTests(); +} + + +/** Magic combo list, a list of key combo and action pairs the firmware should + * recognise. + */ +USE_MAGIC_COMBOS({.action = toggleKeyboardProtocol, + // Left Fn + Esc + Shift + .keys = { R3C6, R2C6, R3C7 } +}, { + .action = enterHardwareTestMode, + // Left Fn + Prog + LED + .keys = { R3C6, R0C0, R0C6 } +}); + +// 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, + + // 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 boot greeting effect pulses the LED button for 10 seconds after the + // keyboard is first connected + BootGreetingEffect, + + // The hardware test mode, which can be invoked by tapping Prog, LED and the + // left Fn button at the same time. + HardwareTestMode, + + // LEDControl provides support for other LED modes + LEDControl, + + // We start with the LED effect that turns off all the LEDs. + LEDOff, + + // The rainbow effect changes the color of all of the keyboard's keys at the same time + // running through all the colors of the rainbow. + LEDRainbowEffect, + + // The rainbow wave effect lights up your keyboard with all the colors of a rainbow + // and slowly moves the rainbow across your keyboard + LEDRainbowWaveEffect, + + // The chase effect follows the adventure of a blue pixel which chases a red pixel across + // your keyboard. Spoiler: the blue pixel never catches the red pixel + LEDChaseEffect, + + // These static effects turn your keyboard's LEDs a variety of colors + solidRed, solidOrange, solidYellow, solidGreen, solidBlue, solidIndigo, solidViolet, + + // The breathe effect slowly pulses all of the LEDs on your keyboard + LEDBreatheEffect, + + // The AlphaSquare effect prints each character you type, using your + // keyboard's LEDs as a display + AlphaSquareEffect, + + // The stalker effect lights up the keys you've pressed recently + StalkerEffect, + + // The LED Palette Theme plugin provides a shared palette for other plugins, + // like Colormap below + LEDPaletteTheme, + + // The Colormap effect makes it possible to set up per-layer colormaps + ColormapEffect, + + // The numpad plugin is responsible for lighting up the 'numpad' mode + // with a custom LED effect + NumPad, + + // The macros plugin adds support for macros + Macros, + + // The MouseKeys plugin lets you add keys to your keymap which move the mouse. + MouseKeys, + + // 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 '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(); + + // While we hope to improve this in the future, the NumPad plugin + // needs to be explicitly told which keymap layer is your numpad layer + NumPad.numPadLayer = NUMPAD; + + // We configure the AlphaSquare effect to use RED letters + AlphaSquare.color = CRGB(255, 0, 0); + + // We set the brightness of the rainbow effects to 150 (on a scale of 0-255) + // This draws more than 500mA, but looks much nicer than a dimmer effect + LEDRainbowEffect.brightness(255); + LEDRainbowWaveEffect.brightness(255); + + // Set the action key the test mode should listen for to Left Fn + HardwareTestMode.setActionKey(R3C6); + + // The LED Stalker mode has a few effects. The one we like is called + // 'BlazingTrail'. For details on other options, see + // https://github.com/keyboardio/Kaleidoscope/blob/master/docs/plugins/LED-Stalker.md + StalkerEffect.variant = STALKER(BlazingTrail); + + // We want to make sure that the firmware starts with LED effects off + // This avoids over-taxing devices that don't have a lot of power to share + // with USB devices + LEDOff.activate(); + + // To make the keymap editable without flashing new firmware, we store + // additional layers in EEPROM. For now, we reserve space for five 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(5); + + // We need to tell the Colormap plugin how many layers we want to have custom + // maps for. To make things simple, we set it to five layers, which is how + // many editable layers we have (see above). + //ColormapEffect.max_layers(5); +} + +/** 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/examples/Devices/Keyboardio/Model100/sketch.json b/examples/Devices/Keyboardio/Model100/sketch.json new file mode 100644 index 00000000..e00344e9 --- /dev/null +++ b/examples/Devices/Keyboardio/Model100/sketch.json @@ -0,0 +1,6 @@ +{ + "cpu": { + "fqbn": "keyboardio:gd32:keyboardio_model_100", + "port": "" + } +} diff --git a/plugins/Kaleidoscope-Hardware-Keyboardio-Model100/src/kaleidoscope/device/keyboardio/Model100.cpp b/plugins/Kaleidoscope-Hardware-Keyboardio-Model100/src/kaleidoscope/device/keyboardio/Model100.cpp index 9ccb38d9..e4c5db78 100644 --- a/plugins/Kaleidoscope-Hardware-Keyboardio-Model100/src/kaleidoscope/device/keyboardio/Model100.cpp +++ b/plugins/Kaleidoscope-Hardware-Keyboardio-Model100/src/kaleidoscope/device/keyboardio/Model100.cpp @@ -15,16 +15,16 @@ * this program. If not, see . */ -#ifdef ARDUINO_GD32_keyboardio_model_100 +#ifdef ARDUINO_keyboardio_model_100 #include "Arduino.h" // for PROGMEM #include "kaleidoscope/device/keyboardio/Model100.h" // for Model100LEDDriver... #include "kaleidoscope/key_events.h" #include "kaleidoscope/driver/keyscanner/Base_Impl.h" +#include "Wire.h" + #ifndef KALEIDOSCOPE_VIRTUAL_BUILD -#include -#include #endif // ifndef KALEIDOSCOPE_VIRTUAL_BUILD namespace kaleidoscope { @@ -48,14 +48,13 @@ driver::keyboardio::Model100Side Model100Hands::leftHand(0); driver::keyboardio::Model100Side Model100Hands::rightHand(3); void Model100Hands::setup(void) { - // This lets the keyboard pull up to 1.6 amps from the host. - // That violates the USB spec. But it sure is pretty looking - DDRE |= _BV(6); - PORTE &= ~_BV(6); + delay(100); + pinMode(PB9, OUTPUT_OPEN_DRAIN); + digitalWrite(PB9, LOW); + delay(105); // TODO remove this when we remove it from the attiny code + Wire.begin(); + Wire.setClock(400000); - // Set B4, the overcurrent check to an input with an internal pull-up - DDRB &= ~_BV(4); // set bit, input - PORTB &= ~_BV(4); // set bit, enable pull-up resistor } /********* LED Driver *********/ @@ -103,7 +102,6 @@ cRGB Model100LEDDriver::getCrgbAt(uint8_t i) { void Model100LEDDriver::syncLeds() { if (!isLEDChanged) return; - // LED Data is stored in four "banks" for each side // We send it all at once to make it look nicer. // We alternate left and right hands because otherwise @@ -125,10 +123,6 @@ void Model100LEDDriver::syncLeds() { isLEDChanged = false; } -boolean Model100LEDDriver::ledPowerFault() { - // TODO remove - obsolete -} - /********* Key scanner *********/ driver::keyboardio::keydata_t Model100KeyScanner::leftHandState; @@ -137,16 +131,26 @@ driver::keyboardio::keydata_t Model100KeyScanner::previousLeftHandState; driver::keyboardio::keydata_t Model100KeyScanner::previousRightHandState; void Model100KeyScanner::enableScannerPower(void) { +// Turn on the switched 5V network. +// make sure this happens at least 100ms after USB connect +// to satisfy inrush limits + // + pinMode(PB9, OUTPUT_OPEN_DRAIN); + digitalWrite(PB9, LOW); +} + +void Model100KeyScanner::disableScannerPower(void) { // Turn on power to the 5V net // - pinMode(PC13, OUTPUT_OPEN_DRAIN); - digitalWrite(PC13, LOW); + pinMode(PB9, OUTPUT_OPEN_DRAIN); + digitalWrite(PB9, HIGH); } + + void Model100KeyScanner::setup() { - wdt_disable(); - delay(100); enableScannerPower(); + delay(250); } void Model100KeyScanner::readMatrix() { @@ -228,6 +232,7 @@ uint8_t Model100KeyScanner::previousPressedKeyswitchCount() { /********* Hardware plugin *********/ void Model100::setup() { + Model100KeyScanner::setup(); Model100Hands::setup(); kaleidoscope::device::Base::setup(); } diff --git a/plugins/Kaleidoscope-Hardware-Keyboardio-Model100/src/kaleidoscope/device/keyboardio/Model100.h b/plugins/Kaleidoscope-Hardware-Keyboardio-Model100/src/kaleidoscope/device/keyboardio/Model100.h index 606f3e93..817f53cd 100644 --- a/plugins/Kaleidoscope-Hardware-Keyboardio-Model100/src/kaleidoscope/device/keyboardio/Model100.h +++ b/plugins/Kaleidoscope-Hardware-Keyboardio-Model100/src/kaleidoscope/device/keyboardio/Model100.h @@ -17,7 +17,11 @@ #pragma once -#ifdef ARDUINO_GD32_keyboardio_model_100 +#ifdef ARDUINO_keyboardio_model_100 + +#ifndef EEPROM_EMULATION_SIZE +#define EEPROM_EMULATION_SIZE 4096 +#endif #include @@ -29,17 +33,24 @@ struct cRGB { uint8_t r; }; -#include "kaleidoscope/device/ATmega32U4Keyboard.h" #include "kaleidoscope/driver/keyscanner/Base.h" +#include "kaleidoscope/driver/storage/GD32Flash.h" #include "kaleidoscope/driver/keyboardio/Model100Side.h" #include "kaleidoscope/driver/led/Base.h" +#include "kaleidoscope/device/Base.h" +#include "kaleidoscope/driver/hid/Keyboardio.h" #include "kaleidoscope/driver/bootloader/gd32/Base.h" namespace kaleidoscope { namespace device { namespace keyboardio { +struct Model100StorageProps: public kaleidoscope::driver::storage::GD32FlashProps { + static constexpr uint16_t length = EEPROM_EMULATION_SIZE; +}; + + struct Model100LEDDriverProps : public kaleidoscope::driver::led::BaseProps { static constexpr uint8_t led_count = 64; static constexpr uint8_t key_led_map[] PROGMEM = { @@ -60,7 +71,6 @@ class Model100LEDDriver : public kaleidoscope::driver::led::Base HID; + typedef Model100LEDDriverProps LEDDriverProps; typedef Model100LEDDriver LEDDriver; typedef Model100KeyScannerProps KeyScannerProps; typedef Model100KeyScanner KeyScanner; + typedef Model100StorageProps StorageProps; + typedef kaleidoscope::driver::storage::GD32Flash Storage; + typedef kaleidoscope::driver::bootloader::gd32::Base BootLoader; static constexpr const char *short_name = "kbio100"; }; diff --git a/plugins/Kaleidoscope-Hardware-Keyboardio-Model100/src/kaleidoscope/driver/keyboardio/Model100Side.cpp b/plugins/Kaleidoscope-Hardware-Keyboardio-Model100/src/kaleidoscope/driver/keyboardio/Model100Side.cpp index 8d4c6e31..2f278f96 100644 --- a/plugins/Kaleidoscope-Hardware-Keyboardio-Model100/src/kaleidoscope/driver/keyboardio/Model100Side.cpp +++ b/plugins/Kaleidoscope-Hardware-Keyboardio-Model100/src/kaleidoscope/driver/keyboardio/Model100Side.cpp @@ -24,10 +24,8 @@ #include #include "Model100Side.h" - -extern "C" { -#include "kaleidoscope/device/keyboardio/twi.h" -} +#include +#include #include "kaleidoscope/driver/color/GammaCorrection.h" @@ -43,9 +41,7 @@ uint8_t twi_uninitialized = 1; Model100Side::Model100Side(byte setAd01) { ad01 = setAd01; addr = SCANNER_I2C_ADDR_BASE | ad01; - if (twi_uninitialized--) { - twi_init(); - } + markDeviceUnavailable(); } // Returns the relative controller addresss. The expected range is 0-3 @@ -72,8 +68,7 @@ uint8_t Model100Side::controllerAddress() { // https://www.arduino.cc/en/Reference/WireEndTransmission byte Model100Side::setKeyscanInterval(byte delay) { uint8_t data[] = {TWI_CMD_KEYSCAN_INTERVAL, delay}; - uint8_t result = twi_writeTo(addr, data, ELEMENTS(data), 1, 0); - + uint8_t result = writeData(data, ELEMENTS(data)); return result; } @@ -103,34 +98,84 @@ int Model100Side::readLEDSPIFrequency() { // https://www.arduino.cc/en/Reference/WireEndTransmission byte Model100Side::setLEDSPIFrequency(byte frequency) { uint8_t data[] = {TWI_CMD_LED_SPI_FREQUENCY, frequency}; - uint8_t result = twi_writeTo(addr, data, ELEMENTS(data), 1, 0); + uint8_t result = writeData(data, ELEMENTS(data)); return result; } +// GD32 I2C implements timeouts which will cause a stall when a device does not answer. +// This method will verify that the device is around and ready to talk. +bool Model100Side::isDeviceAvailable() { + return true; + // if the counter is zero, that's the special value that means "we know it's there" + if (unavailable_device_check_countdown_ == 0) { + return true; + } -int Model100Side::readRegister(uint8_t cmd) { + // if the time to check counter is 1, check for the device + + else if (--unavailable_device_check_countdown_ == 0) { + uint8_t wire_result; + Wire.beginTransmission(addr); + wire_result = Wire.endTransmission(); + //if the check succeeds + if (wire_result == 0) { + // unavailable_device_check_countdown_ = 0; // TODO this is already true + return true; + } else { + // set the time to check counter to max + unavailable_device_check_countdown_ = UNAVAILABLE_DEVICE_COUNTDOWN_MAX; + return false; + } + } else { + // we've decremented the counter, but it's not time to probe for the device yet. + return false; + } - byte return_value = 0; +} - uint8_t data[] = {cmd}; - uint8_t result = twi_writeTo(addr, data, ELEMENTS(data), 1, 0); +void Model100Side::markDeviceUnavailable() { + unavailable_device_check_countdown_ = 1; // We think there was a comms problem. Check on the next cycle +} +uint8_t Model100Side::writeData(uint8_t *data, uint8_t length) { + if (isDeviceAvailable() == false) { + return 1; + } + Wire.beginTransmission(addr); + Wire.write(data, length); + uint8_t result = Wire.endTransmission(); + if (result) { + markDeviceUnavailable(); + } + return result; +} + +int Model100Side::readRegister(uint8_t cmd) { + byte return_value = 0; + uint8_t data[] = {cmd}; + uint8_t result = writeData(data, ELEMENTS(data)); + // If the setup failed, return. This means there was a problem asking for the register + if (result) { + return -1; + } - delayMicroseconds(15); // We may be able to drop this in the future + delayMicroseconds(50); // TODO We may be able to drop this in the future // but will need to verify with correctly // sized pull-ups on both the left and right // hands' i2c SDA and SCL lines - uint8_t rxBuffer[1]; + uint8_t rxBuffer[1] = {0}; // perform blocking read into buffer - uint8_t read = twi_readFrom(addr, rxBuffer, ELEMENTS(rxBuffer), true); - if (read > 0) { - return rxBuffer[0]; + + Wire.requestFrom(addr, 1); // request 1 byte from the keyscanner + if (Wire.available()) { + return Wire.read(); } else { + markDeviceUnavailable(); return -1; } @@ -139,20 +184,28 @@ int Model100Side::readRegister(uint8_t cmd) { // gives information on the key that was just pressed or released. bool Model100Side::readKeys() { + if (isDeviceAvailable() == false) { + return false; + } - uint8_t rxBuffer[5]; - + uint8_t row_counter = 0; // perform blocking read into buffer - uint8_t read = twi_readFrom(addr, rxBuffer, ELEMENTS(rxBuffer), true); - if (rxBuffer[0] == TWI_REPLY_KEYDATA) { - keyData.rows[0] = rxBuffer[1]; - keyData.rows[1] = rxBuffer[2]; - keyData.rows[2] = rxBuffer[3]; - keyData.rows[3] = rxBuffer[4]; - return true; - } else { + uint8_t read = 0; + uint8_t bytes_returned = 0; + bytes_returned = Wire.requestFrom(addr, 5); // request 5 bytes from the keyscanner + if (bytes_returned < 5) { return false; } + if (Wire.available()) { + read = Wire.read(); + if (TWI_REPLY_KEYDATA == read) { + while (Wire.available()) { + keyData.rows[row_counter++] = Wire.read(); + } + return true; + } + } + return false; } keydata_t Model100Side::getKeyData() { @@ -166,7 +219,6 @@ void Model100Side::sendLEDData() { } } -auto constexpr gamma8 = kaleidoscope::driver::color::gamma_correction; void Model100Side::sendLEDBank(byte bank) { uint8_t data[LED_BYTES_PER_BANK + 1]; @@ -182,28 +234,28 @@ void Model100Side::sendLEDBank(byte bank) { else c = 0; - data[i + 1] = pgm_read_byte(&gamma8[c]); + data[i + 1] = c; } - uint8_t result = twi_writeTo(addr, data, ELEMENTS(data), 1, 0); + uint8_t result = writeData(data, ELEMENTS(data)); } void Model100Side::setAllLEDsTo(cRGB color) { uint8_t data[] = {TWI_CMD_LED_SET_ALL_TO, - pgm_read_byte(&gamma8[color.b]), - pgm_read_byte(&gamma8[color.g]), - pgm_read_byte(&gamma8[color.r]) + color.b, + color.g, + color.r }; - uint8_t result = twi_writeTo(addr, data, ELEMENTS(data), 1, 0); + uint8_t result = writeData(data, ELEMENTS(data)); } void Model100Side::setOneLEDTo(byte led, cRGB color) { uint8_t data[] = {TWI_CMD_LED_SET_ONE_TO, led, - pgm_read_byte(&gamma8[color.b]), - pgm_read_byte(&gamma8[color.g]), - pgm_read_byte(&gamma8[color.r]) + color.b, + color.g, + color.r }; - uint8_t result = twi_writeTo(addr, data, ELEMENTS(data), 1, 0); + uint8_t result = writeData(data, ELEMENTS(data)); } diff --git a/plugins/Kaleidoscope-Hardware-Keyboardio-Model100/src/kaleidoscope/driver/keyboardio/Model100Side.h b/plugins/Kaleidoscope-Hardware-Keyboardio-Model100/src/kaleidoscope/driver/keyboardio/Model100Side.h index 1841095b..2d7838e4 100644 --- a/plugins/Kaleidoscope-Hardware-Keyboardio-Model100/src/kaleidoscope/driver/keyboardio/Model100Side.h +++ b/plugins/Kaleidoscope-Hardware-Keyboardio-Model100/src/kaleidoscope/driver/keyboardio/Model100Side.h @@ -78,9 +78,12 @@ class Model100Side { void setAllLEDsTo(cRGB color); keydata_t getKeyData(); bool readKeys(); + LEDData_t ledData; - uint8_t controllerAddress(); + uint8_t controllerAddress(); + bool isDeviceAvailable(); + void markDeviceUnavailable(); void setBrightness(uint8_t brightness) { brightness_adjustment_ = 255 - brightness; } @@ -93,9 +96,14 @@ class Model100Side { int addr; int ad01; keydata_t keyData; + // a value of 0 is "device seen" - anything else is how many cycles before we should + // check for the device + uint16_t unavailable_device_check_countdown_ = 0; + static const uint16_t UNAVAILABLE_DEVICE_COUNTDOWN_MAX = 0x00FFU; byte nextLEDBank = 0; void sendLEDBank(byte bank); int readRegister(uint8_t cmd); + uint8_t writeData(uint8_t* data, uint8_t length); }; #else // ifndef KALEIDOSCOPE_VIRTUAL_BUILD class Model100Side;