summaryrefslogtreecommitdiffstats
path: root/keyboards/ergodox_infinity
diff options
context:
space:
mode:
authorJack Humbert <jack.humb@gmail.com>2017-08-24 04:29:07 +0200
committerGitHub <noreply@github.com>2017-08-24 04:29:07 +0200
commitd2ff66a985b938e87fffe55c1d9f1dc55e356f91 (patch)
tree561b3e203033849573a17f5ac6248077ab687676 /keyboards/ergodox_infinity
parent7260fc3eef98fb7b0e2ed24d3d0d14cf2e613000 (diff)
downloadqmk_firmware-d2ff66a985b938e87fffe55c1d9f1dc55e356f91.tar.gz
qmk_firmware-d2ff66a985b938e87fffe55c1d9f1dc55e356f91.tar.xz
Creates a layouts/ folder for keymaps shared between keyboards (#1609)
* include variables and .h files as pp directives * start layout compilation * split ergodoxes up * don't compile all layouts for everything * might seg fault * reset layouts variable * actually reset layouts * include rules.mk instead * remove includes from rules.mk * update variable setting * load visualizer from path * adds some more examples * adds more layouts * more boards added * more boards added * adds documentation for layouts * use lowercase names for LAYOUT_ * add layout.json files for each layout * add community folder, default keymaps for layouts * touch-up default layouts * touch-up layouts, some keyboard rules.mk * update documentation for layouts * fix up serial/i2c switches
Diffstat (limited to 'keyboards/ergodox_infinity')
-rw-r--r--keyboards/ergodox_infinity/MEMO.txt385
-rw-r--r--keyboards/ergodox_infinity/Makefile3
-rw-r--r--keyboards/ergodox_infinity/board_is31fl3731c.h110
-rw-r--r--keyboards/ergodox_infinity/board_st7565.h113
-rw-r--r--keyboards/ergodox_infinity/bootloader_defs.h1
-rw-r--r--keyboards/ergodox_infinity/chconf.h524
-rw-r--r--keyboards/ergodox_infinity/config.h98
-rw-r--r--keyboards/ergodox_infinity/ergodox_infinity.c195
-rw-r--r--keyboards/ergodox_infinity/ergodox_infinity.h123
-rw-r--r--keyboards/ergodox_infinity/gfxconf.h27
-rw-r--r--keyboards/ergodox_infinity/halconf.h353
-rw-r--r--keyboards/ergodox_infinity/led.c26
-rw-r--r--keyboards/ergodox_infinity/matrix.c173
-rw-r--r--keyboards/ergodox_infinity/mcuconf.h74
-rw-r--r--keyboards/ergodox_infinity/readme.md45
-rw-r--r--keyboards/ergodox_infinity/rules.mk89
-rw-r--r--keyboards/ergodox_infinity/simple_visualizer.h123
-rw-r--r--keyboards/ergodox_infinity/visualizer.c329
18 files changed, 2791 insertions, 0 deletions
diff --git a/keyboards/ergodox_infinity/MEMO.txt b/keyboards/ergodox_infinity/MEMO.txt
new file mode 100644
index 000000000..e2886aa00
--- /dev/null
+++ b/keyboards/ergodox_infinity/MEMO.txt
@@ -0,0 +1,385 @@
+flabbergast's TMK/ChibiOS port
+==============================
+2015/10/16
+
+
+Build
+-----
+$ git clone -b chibios https://github.com/flabbergast/tmk_keyboard.git
+
+$ cd tmk_keyboard
+$ git submodule add -f -b kinetis https://github.com/flabbergast/ChibiOS.git tmk_core/tool/chibios/chibios
+or
+$ cd tmk_keyboard/tmk_core/tool/chibios
+$ git clone -b kinetis https://github.com/flabbergast/ChibiOS.git tmk_core/tool/chibios/chibios
+
+$ cd tmk_keyboard/keyboard/infinity_chibios
+$ make
+
+
+
+
+Chibios Configuration
+---------------------
+halconf.h: for HAL configuration
+ placed in project directory
+ read in chibios/os/hal/hal.mk
+ included in chibios/os/hal/include/hal.h
+mcuconf.h: for MCU configuration
+ placed in project directory
+ included in halconf.h
+
+
+Chibios Term
+------------
+PAL = Port Abstraction Layer
+ palWritePad
+ palReadPad
+ palSetPad
+ chibios/os/hal/include/pal.h
+
+LLD = Low Level Driver
+
+
+Makefile
+--------
+ # <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
+ MCU_FAMILY = KINETIS
+ MCU_SERIES = KL2x
+
+ # - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
+ # or <this_dir>/ld/
+ MCU_LDSCRIPT = MKL26Z64
+
+ # - it should exist in <chibios>/os/common/ports/ARMCMx/compilers/GCC/mk/
+ MCU_STARTUP = kl2x
+
+ # Board: it should exist either in <chibios>/os/hal/boards/
+ # or <this_dir>/boards
+ BOARD = PJRC_TEENSY_LC
+
+ MCU = cortex-m0
+
+ # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
+ ARMV = 6
+
+
+halconf.h
+---------
+
+
+mcuconf.h
+---------
+
+
+chconf.h
+--------
+
+
+ld script
+---------
+--- ../../tmk_core/tool/chibios/chibios/os/common/ports/ARMCMx/compilers/GCC/ld/MKL26Z64.ld 2015-10-15 09:08:58.732904304 +0900
++++ ld/MKL26Z64.ld 2015-10-15 08:48:06.430215496 +0900
+@@ -27,7 +27,8 @@
+ {
+ flash0 : org = 0x00000000, len = 0xc0
+ flashcfg : org = 0x00000400, len = 0x10
+- flash : org = 0x00000410, len = 64k - 0x410
++ flash : org = 0x00000410, len = 62k - 0x410
++ eeprom_emu : org = 0x0000F800, len = 2k
+ ram : org = 0x1FFFF800, len = 8k
+ }
+
+@@ -35,6 +36,10 @@
+ __ram_size__ = LENGTH(ram);
+ __ram_end__ = __ram_start__ + __ram_size__;
+
++__eeprom_workarea_start__ = ORIGIN(eeprom_emu);
++__eeprom_workarea_size__ = LENGTH(eeprom_emu);
++__eeprom_workarea_end__ = __eeprom_workarea_start__ + __eeprom_workarea_size__;
++
+ SECTIONS
+ {
+ . = 0;
+
+
+
+Configuration/Startup for Infinity 60%
+--------------------------------------
+Configuration:
+
+
+Clock:
+Inifinity
+ FEI(FLL Engaged Internal) mode with core clock:48MHz, bus clock:48MHz, flash clock:24MHz
+ Clock dividor:
+ SIM_CLKDIV1[OUTDIV1] = 0 divide-by-1 for core clock
+ SIM_CLKDIV1[OUTDIV2] = 0 divide-by-1 for bus clock
+ SIM_CLKDIV1[OUTDIV4] = 1 divide-by-2 for flash clock
+ Internal reference clock:
+ MCG_C1[IREFS] = 1 Internal Reference Select for clock source for FLL
+ MCG_C1[IRCLKEN] = 1 Internal Reference Clock Enable
+ FLL multipilication:
+ MCG_C4[DMX32] = 1
+ MCG_C4[DRST_DRS] = 01 FLL factor 1464 * 32.768kHz = 48MHz
+
+chibios/os/hal/ports/KINETIS/K20x/hal_lld.c
+ k20x_clock_init(): called in __early_init() defined in board.c
+ disable watchdog and configure clock
+
+ configurable macros:
+ KINETIS_NO_INIT: whether init or not
+ KINETIS_MCG_MODE: clock mode
+ KINETIS_MCG_MODE_FEI
+ KINETIS_MCG_MODE_PEE
+ hal/ports/KINETIS/K20x/hal_lld.h
+
+
+chibios/os/hal/boards/FREESCALE_FREEDOM_K20D50M/board.h
+ PALConfig pal_default_config
+ boardInit()
+ __early_init()
+ macro definitions for board infos, freq and mcu type
+
+chibios/os/hal/boards/FREESCALE_FREEDOM_K20D50M/board.c
+
+USB
+
+
+Startup
+-------
+ common/ports/ARMCMx/GCC/crt0_v[67]m.s
+ Reset_Handler: startup code
+ common/ports/ARMCMx/GCC/crt1.c
+ __core_init(): weak
+ __early_init(): weak
+ __late_init(): weak
+ __default_exit(): weak
+ called from Reset_Handler of crt0
+ common/ports/ARMCMx/GCC/vector.c
+ common/ports/ARMCMx/GCC/ld/*.ld
+
+chibios/os/common/ports/ARMCMx/compilers/GCC/
+├── crt0_v6m.s
+├── crt0_v7m.s
+├── crt1.c
+├── ld
+│   ├── MK20DX128BLDR3.ld
+│   ├── MK20DX128BLDR4.ld
+│   ├── MK20DX128.ld
+│   ├── MK20DX256.ld
+│   ├── MKL25Z128.ld
+│   ├── MKL26Z128.ld
+│   ├── MKL26Z64.ld
+│   └── STM32L476xG.ld
+├── mk
+│   ├── startup_k20x5.mk
+│   ├── startup_k20x7.mk
+│   ├── startup_k20x.mk
+│   ├── startup_kl2x.mk
+│   └── startup_stm32l4xx.mk
+├── rules.ld
+├── rules.mk
+└── vectors.c
+
+chibios/os/hal/
+├── boards
+│   ├── FREESCALE_FREEDOM_K20D50M
+│   │   ├── board.c
+│   │   ├── board.h
+│   │   └── board.mk
+│   ├── MCHCK_K20
+│   │   ├── board.c
+│   │   ├── board.h
+│   │   └── board.mk
+│   ├── PJRC_TEENSY_3
+│   │   ├── board.c
+│   │   ├── board.h
+│   │   └── board.mk
+│   ├── PJRC_TEENSY_3_1
+│   │   ├── board.c
+│   │   ├── board.h
+│   │   └── board.mk
+│   ├── PJRC_TEENSY_LC
+│   │   ├── board.c
+│   │   ├── board.h
+│   │   └── board.mk
+│   ├── readme.txt
+│   ├── simulator
+│   │   ├── board.c
+│   │   ├── board.h
+│   │   └── board.mk
+│   ├── ST_NUCLEO_F030R8
+│   │   ├── board.c
+│   │   ├── board.h
+│   │   ├── board.mk
+│   │   └── cfg
+│   │   └── board.chcfg
+├── hal.mk
+├── include
+│   ├── adc.h
+│   ├── can.h
+│   ├── dac.h
+│   ├── ext.h
+│   ├── gpt.h
+│   ├── hal_channels.h
+│   ├── hal_files.h
+│   ├── hal.h
+│   ├── hal_ioblock.h
+│   ├── hal_mmcsd.h
+│   ├── hal_queues.h
+│   ├── hal_streams.h
+│   ├── i2c.h
+│   ├── i2s.h
+│   ├── icu.h
+│   ├── mac.h
+│   ├── mii.h
+│   ├── mmc_spi.h
+│   ├── pal.h
+│   ├── pwm.h
+│   ├── rtc.h
+│   ├── sdc.h
+│   ├── serial.h
+│   ├── serial_usb.h
+│   ├── spi.h
+│   ├── st.h
+│   ├── uart.h
+│   └── usb.h
+├── lib
+│   └── streams
+│   ├── chprintf.c
+│   ├── chprintf.h
+│   ├── memstreams.c
+│   ├── memstreams.h
+│   ├── nullstreams.c
+│   └── nullstreams.h
+├── osal
+│   ├── nil
+│   │   ├── osal.c
+│   │   ├── osal.h
+│   │   └── osal.mk
+│   ├── os-less
+│   │   └── ARMCMx
+│   │   ├── osal.c
+│   │   ├── osal.h
+│   │   └── osal.mk
+│   └── rt
+│   ├── osal.c
+│   ├── osal.h
+│   └── osal.mk
+├── ports
+│   ├── AVR
+│   ├── common
+│   │   └── ARMCMx
+│   │   ├── mpu.h
+│   │   ├── nvic.c
+│   │   └── nvic.h
+│   ├── KINETIS
+│   │   ├── K20x
+│   │   │   ├── hal_lld.c
+│   │   │   ├── hal_lld.h
+│   │   │   ├── kinetis_registry.h
+│   │   │   ├── platform.dox
+│   │   │   ├── platform.mk
+│   │   │   ├── pwm_lld.c
+│   │   │   ├── pwm_lld.h
+│   │   │   ├── spi_lld.c
+│   │   │   └── spi_lld.h
+│   │   ├── KL2x
+│   │   │   ├── hal_lld.c
+│   │   │   ├── hal_lld.h
+│   │   │   ├── kinetis_registry.h
+│   │   │   ├── platform.mk
+│   │   │   ├── pwm_lld.c
+│   │   │   └── pwm_lld.h
+│   │   ├── LLD
+│   │   │   ├── adc_lld.c
+│   │   │   ├── adc_lld.h
+│   │   │   ├── ext_lld.c
+│   │   │   ├── ext_lld.h
+│   │   │   ├── gpt_lld.c
+│   │   │   ├── gpt_lld.h
+│   │   │   ├── i2c_lld.c
+│   │   │   ├── i2c_lld.h
+│   │   │   ├── pal_lld.c
+│   │   │   ├── pal_lld.h
+│   │   │   ├── serial_lld.c
+│   │   │   ├── serial_lld.h
+│   │   │   ├── st_lld.c
+│   │   │   ├── st_lld.h
+│   │   │   ├── usb_lld.c
+│   │   │   └── usb_lld.h
+│   │   └── README.md
+│   ├── LPC
+│   ├── simulator
+│   └── STM32
+├── src
+│   ├── adc.c
+│   ├── can.c
+│   ├── dac.c
+│   ├── ext.c
+│   ├── gpt.c
+│   ├── hal.c
+│   ├── hal_mmcsd.c
+│   ├── hal_queues.c
+│   ├── i2c.c
+│   ├── i2s.c
+│   ├── icu.c
+│   ├── mac.c
+│   ├── mmc_spi.c
+│   ├── pal.c
+│   ├── pwm.c
+│   ├── rtc.c
+│   ├── sdc.c
+│   ├── serial.c
+│   ├── serial_usb.c
+│   ├── spi.c
+│   ├── st.c
+│   ├── uart.c
+│   └── usb.c
+└── templates
+ ├── adc_lld.c
+ ├── adc_lld.h
+ ├── can_lld.c
+ ├── can_lld.h
+ ├── dac_lld.c
+ ├── dac_lld.h
+ ├── ext_lld.c
+ ├── ext_lld.h
+ ├── gpt_lld.c
+ ├── gpt_lld.h
+ ├── halconf.h
+ ├── hal_lld.c
+ ├── hal_lld.h
+ ├── i2c_lld.c
+ ├── i2c_lld.h
+ ├── i2s_lld.c
+ ├── i2s_lld.h
+ ├── icu_lld.c
+ ├── icu_lld.h
+ ├── mac_lld.c
+ ├── mac_lld.h
+ ├── mcuconf.h
+ ├── osal
+ │   ├── osal.c
+ │   ├── osal.h
+ │   └── osal.mk
+ ├── pal_lld.c
+ ├── pal_lld.h
+ ├── platform.mk
+ ├── pwm_lld.c
+ ├── pwm_lld.h
+ ├── rtc_lld.c
+ ├── rtc_lld.h
+ ├── sdc_lld.c
+ ├── sdc_lld.h
+ ├── serial_lld.c
+ ├── serial_lld.h
+ ├── spi_lld.c
+ ├── spi_lld.h
+ ├── st_lld.c
+ ├── st_lld.h
+ ├── uart_lld.c
+ ├── uart_lld.h
+ ├── usb_lld.c
+ └── usb_lld.h
diff --git a/keyboards/ergodox_infinity/Makefile b/keyboards/ergodox_infinity/Makefile
new file mode 100644
index 000000000..bd09e5885
--- /dev/null
+++ b/keyboards/ergodox_infinity/Makefile
@@ -0,0 +1,3 @@
+ifndef MAKEFILE_INCLUDED
+ include ../../../Makefile
+endif
diff --git a/keyboards/ergodox_infinity/board_is31fl3731c.h b/keyboards/ergodox_infinity/board_is31fl3731c.h
new file mode 100644
index 000000000..f248cc25b
--- /dev/null
+++ b/keyboards/ergodox_infinity/board_is31fl3731c.h
@@ -0,0 +1,110 @@
+/*
+Copyright 2016 Fred Sundvik <fsundvik@gmail.com>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _GDISP_LLD_BOARD_H
+#define _GDISP_LLD_BOARD_H
+
+static const I2CConfig i2ccfg = {
+ 400000 // clock speed (Hz); 400kHz max for IS31
+};
+
+static const uint8_t led_mask[] = {
+ 0xFF, 0x00, /* C1-1 -> C1-16 */
+ 0xFF, 0x00, /* C2-1 -> C2-16 */
+ 0xFF, 0x00, /* C3-1 -> C3-16 */
+ 0xFF, 0x00, /* C4-1 -> C4-16 */
+ 0x3F, 0x00, /* C5-1 -> C5-16 */
+ 0x00, 0x00, /* C6-1 -> C6-16 */
+ 0x00, 0x00, /* C7-1 -> C7-16 */
+ 0x00, 0x00, /* C8-1 -> C8-16 */
+ 0x00, 0x00, /* C9-1 -> C9-16 */
+};
+
+// The address of the LED
+#define LA(c, r) (c + r * 16 )
+// Need to be an address that is not mapped, but inside the range of the controller matrix
+#define NA LA(8, 8)
+
+// The numbers in the comments are the led numbers DXX on the PCB
+// The mapping is taken from the schematic of left hand side
+static const uint8_t led_mapping[GDISP_SCREEN_HEIGHT][GDISP_SCREEN_WIDTH] = {
+// 45 44 43 42 41 40 39
+ { LA(1, 1), LA(1, 0), LA(0, 4), LA(0, 3), LA(0, 2), LA(0, 1), LA(0, 0)},
+// 52 51 50 49 48 47 46
+ { LA(2, 3), LA(2, 2), LA(2, 1), LA(2, 0), LA(1, 4), LA(1, 3), LA(1, 2) },
+// 58 57 56 55 54 53 N/A
+ { LA(3, 4), LA(3, 3), LA(3, 2), LA(3, 1), LA(3, 0), LA(2, 4), NA },
+// 67 66 65 64 63 62 61
+ { LA(5, 3), LA(5, 2), LA(5, 1), LA(5, 0), LA(4, 4), LA(4, 3), LA(4, 2) },
+// 76 75 74 73 72 60 59
+ { LA(7, 3), LA(7, 2), LA(7, 1), LA(7, 0), LA(6, 3), LA(4, 1), LA(4, 0) },
+// N/A N/A N/A N/A N/A N/A 68
+ { NA, NA, NA, NA, NA, NA, LA(5, 4) },
+// N/A N/A N/A N/A 71 70 69
+ { NA, NA, NA, NA, LA(6, 2), LA(6, 1), LA(6, 0) },
+};
+
+
+#define IS31_ADDR_DEFAULT 0x74 // AD connected to GND
+#define IS31_TIMEOUT 5000
+
+static GFXINLINE void init_board(GDisplay *g) {
+ (void) g;
+ /* I2C pins */
+ palSetPadMode(GPIOB, 0, PAL_MODE_ALTERNATIVE_2); // PTB0/I2C0/SCL
+ palSetPadMode(GPIOB, 1, PAL_MODE_ALTERNATIVE_2); // PTB1/I2C0/SDA
+ palSetPadMode(GPIOB, 16, PAL_MODE_OUTPUT_PUSHPULL);
+ palClearPad(GPIOB, 16);
+ /* start I2C */
+ i2cStart(&I2CD1, &i2ccfg);
+ // try high drive (from kiibohd)
+ I2CD1.i2c->C2 |= I2Cx_C2_HDRS;
+ // try glitch fixing (from kiibohd)
+ I2CD1.i2c->FLT = 4;
+}
+
+static GFXINLINE void post_init_board(GDisplay *g) {
+ (void) g;
+}
+
+static GFXINLINE const uint8_t* get_led_mask(GDisplay* g) {
+ (void) g;
+ return led_mask;
+}
+
+static GFXINLINE uint8_t get_led_address(GDisplay* g, uint16_t x, uint16_t y)
+{
+ (void) g;
+ return led_mapping[y][x];
+}
+
+static GFXINLINE void set_hardware_shutdown(GDisplay* g, bool shutdown) {
+ (void) g;
+ if(!shutdown) {
+ palSetPad(GPIOB, 16);
+ }
+ else {
+ palClearPad(GPIOB, 16);
+ }
+}
+
+static GFXINLINE void write_data(GDisplay *g, uint8_t* data, uint16_t length) {
+ (void) g;
+ i2cMasterTransmitTimeout(&I2CD1, IS31_ADDR_DEFAULT, data, length, 0, 0, US2ST(IS31_TIMEOUT));
+}
+
+#endif /* _GDISP_LLD_BOARD_H */
diff --git a/keyboards/ergodox_infinity/board_st7565.h b/keyboards/ergodox_infinity/board_st7565.h
new file mode 100644
index 000000000..9ab636c95
--- /dev/null
+++ b/keyboards/ergodox_infinity/board_st7565.h
@@ -0,0 +1,113 @@
+/*
+ * This file is subject to the terms of the GFX License. If a copy of
+ * the license was not distributed with this file, you can obtain one at:
+ *
+ * http://ugfx.org/license.html
+ */
+
+#ifndef _GDISP_LLD_BOARD_H
+#define _GDISP_LLD_BOARD_H
+
+#define ST7565_LCD_BIAS ST7565_LCD_BIAS_9 // actually 6
+#define ST7565_ADC ST7565_ADC_NORMAL
+#define ST7565_COM_SCAN ST7565_COM_SCAN_DEC
+#define ST7565_PAGE_ORDER 0,1,2,3
+/*
+ * Custom page order for several LCD boards, e.g. HEM12864-99
+ * #define ST7565_PAGE_ORDER 4,5,6,7,0,1,2,3
+ */
+
+#define ST7565_GPIOPORT GPIOC
+#define ST7565_PORT PORTC
+#define ST7565_A0_PIN 7
+#define ST7565_RST_PIN 8
+#define ST7565_MOSI_PIN 6
+#define ST7565_SLCK_PIN 5
+#define ST7565_SS_PIN 4
+
+#define palSetPadModeRaw(portname, bits) \
+ ST7565_PORT->PCR[ST7565_##portname##_PIN] = bits
+
+#define palSetPadModeNamed(portname, portmode) \
+ palSetPadMode(ST7565_GPIOPORT, ST7565_##portname##_PIN, portmode)
+
+#define ST7565_SPI_MODE PORTx_PCRn_DSE | PORTx_PCRn_MUX(2)
+// DSPI Clock and Transfer Attributes
+// Frame Size: 8 bits
+// MSB First
+// CLK Low by default
+static const SPIConfig spi1config = {
+ // Operation complete callback or @p NULL.
+ .end_cb = NULL,
+ //The chip select line port - when not using pcs.
+ .ssport = ST7565_GPIOPORT,
+ // brief The chip select line pad number - when not using pcs.
+ .sspad=ST7565_SS_PIN,
+ // SPI initialization data.
+ .tar0 =
+ SPIx_CTARn_FMSZ(7) // Frame size = 8 bytes
+ | SPIx_CTARn_ASC(1) // After SCK Delay Scaler (min 50 ns) = 55.56ns
+ | SPIx_CTARn_DT(0) // Delay After Transfer Scaler (no minimum)= 27.78ns
+ | SPIx_CTARn_CSSCK(0) // PCS to SCK Delay Scaler (min 20 ns) = 27.78ns
+ | SPIx_CTARn_PBR(0) // Baud Rate Prescaler = 2
+ | SPIx_CTARn_BR(0) // Baud rate (min 50ns) = 55.56ns
+};
+
+static GFXINLINE void acquire_bus(GDisplay *g) {
+ (void) g;
+ // Only the LCD is using the SPI bus, so no need to acquire
+ // spiAcquireBus(&SPID1);
+ spiSelect(&SPID1);
+}
+
+static GFXINLINE void release_bus(GDisplay *g) {
+ (void) g;
+ // Only the LCD is using the SPI bus, so no need to release
+ //spiReleaseBus(&SPID1);
+ spiUnselect(&SPID1);
+}
+
+static GFXINLINE void init_board(GDisplay *g) {
+ (void) g;
+ palSetPadModeNamed(A0, PAL_MODE_OUTPUT_PUSHPULL);
+ palSetPad(ST7565_GPIOPORT, ST7565_A0_PIN);
+ palSetPadModeNamed(RST, PAL_MODE_OUTPUT_PUSHPULL);
+ palSetPad(ST7565_GPIOPORT, ST7565_RST_PIN);
+ palSetPadModeRaw(MOSI, ST7565_SPI_MODE);
+ palSetPadModeRaw(SLCK, ST7565_SPI_MODE);
+ palSetPadModeNamed(SS, PAL_MODE_OUTPUT_PUSHPULL);
+
+ spiInit();
+ spiStart(&SPID1, &spi1config);
+ release_bus(g);
+}
+
+static GFXINLINE void post_init_board(GDisplay *g) {
+ (void) g;
+}
+
+static GFXINLINE void setpin_reset(GDisplay *g, bool_t state) {
+ (void) g;
+ if (state) {
+ palClearPad(ST7565_GPIOPORT, ST7565_RST_PIN);
+ }
+ else {
+ palSetPad(ST7565_GPIOPORT, ST7565_RST_PIN);
+ }
+}
+
+static GFXINLINE void enter_data_mode(GDisplay *g) {
+ palSetPad(ST7565_GPIOPORT, ST7565_A0_PIN);
+}
+
+static GFXINLINE void enter_cmd_mode(GDisplay *g) {
+ palClearPad(ST7565_GPIOPORT, ST7565_A0_PIN);
+}
+
+
+static GFXINLINE void write_data(GDisplay *g, uint8_t* data, uint16_t length) {
+ (void) g;
+ spiSend(&SPID1, length, data);
+}
+
+#endif /* _GDISP_LLD_BOARD_H */
diff --git a/keyboards/ergodox_infinity/bootloader_defs.h b/keyboards/ergodox_infinity/bootloader_defs.h
new file mode 100644
index 000000000..c67153be6
--- /dev/null
+++ b/keyboards/ergodox_infinity/bootloader_defs.h
@@ -0,0 +1 @@
+#define KIIBOHD_BOOTLOADER
diff --git a/keyboards/ergodox_infinity/chconf.h b/keyboards/ergodox_infinity/chconf.h
new file mode 100644
index 000000000..d59c35eb6
--- /dev/null
+++ b/keyboards/ergodox_infinity/chconf.h
@@ -0,0 +1,524 @@
+/*
+ ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+/**
+ * @file templates/chconf.h
+ * @brief Configuration file template.
+ * @details A copy of this file must be placed in each project directory, it
+ * contains the application specific kernel settings.
+ *
+ * @addtogroup config
+ * @details Kernel related settings and hooks.
+ * @{
+ */
+
+#ifndef _CHCONF_H_
+#define _CHCONF_H_
+
+#define _CHIBIOS_RT_CONF_
+
+/*===========================================================================*/
+/**
+ * @name System timers settings
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief System time counter resolution.
+ * @note Allowed values are 16 or 32 bits.
+ */
+#define CH_CFG_ST_RESOLUTION 32
+
+/**
+ * @brief System tick frequency.
+ * @details Frequency of the system timer that drives the system ticks. This
+ * setting also defines the system tick time unit.
+ */
+#define CH_CFG_ST_FREQUENCY 100000
+
+/**
+ * @brief Time delta constant for the tick-less mode.
+ * @note If this value is zero then the system uses the classic
+ * periodic tick. This value represents the minimum number
+ * of ticks that is safe to specify in a timeout directive.
+ * The value one is not valid, timeouts are rounded up to
+ * this value.
+ */
+#define CH_CFG_ST_TIMEDELTA 0
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Kernel parameters and options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Round robin interval.
+ * @details This constant is the number of system ticks allowed for the
+ * threads before preemption occurs. Setting this value to zero
+ * disables the preemption for threads with equal priority and the
+ * round robin becomes cooperative. Note that higher priority
+ * threads can still preempt, the kernel is always preemptive.
+ * @note Disabling the round robin preemption makes the kernel more compact
+ * and generally faster.
+ * @note The round robin preemption is not supported in tickless mode and
+ * must be set to zero in that case.
+ */
+#define CH_CFG_TIME_QUANTUM 20
+
+/**
+ * @brief Managed RAM size.
+ * @details Size of the RAM area to be managed by the OS. If set to zero
+ * then the whole available RAM is used. The core memory is made
+ * available to the heap allocator and/or can be used directly through
+ * the simplified core memory allocator.
+ *
+ * @note In order to let the OS manage the whole RAM the linker script must
+ * provide the @p __heap_base__ and @p __heap_end__ symbols.
+ * @note Requires @p CH_CFG_USE_MEMCORE.
+ */
+#define CH_CFG_MEMCORE_SIZE 0
+
+/**
+ * @brief Idle thread automatic spawn suppression.
+ * @details When this option is activated the function @p chSysInit()
+ * does not spawn the idle thread. The application @p main()
+ * function becomes the idle thread and must implement an
+ * infinite loop.
+ */
+#define CH_CFG_NO_IDLE_THREAD FALSE
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Performance options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief OS optimization.
+ * @details If enabled then time efficient rather than space efficient code
+ * is used when two possible implementations exist.
+ *
+ * @note This is not related to the compiler optimization options.
+ * @note The default is @p TRUE.
+ */
+#define CH_CFG_OPTIMIZE_SPEED TRUE
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Subsystem options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Time Measurement APIs.
+ * @details If enabled then the time measurement APIs are included in
+ * the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#define CH_CFG_USE_TM FALSE
+
+/**
+ * @brief Threads registry APIs.
+ * @details If enabled then the registry APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#define CH_CFG_USE_REGISTRY TRUE
+
+/**
+ * @brief Threads synchronization APIs.
+ * @details If enabled then the @p chThdWait() function is included in
+ * the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#define CH_CFG_USE_WAITEXIT TRUE
+
+/**
+ * @brief Semaphores APIs.
+ * @details If enabled then the Semaphores APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#define CH_CFG_USE_SEMAPHORES TRUE
+
+/**
+ * @brief Semaphores queuing mode.
+ * @details If enabled then the threads are enqueued on semaphores by
+ * priority rather than in FIFO order.
+ *
+ * @note The default is @p FALSE. Enable this if you have special
+ * requirements.
+ * @note Requires @p CH_CFG_USE_SEMAPHORES.
+ */
+#define CH_CFG_USE_SEMAPHORES_PRIORITY FALSE
+
+/**
+ * @brief Mutexes APIs.
+ * @details If enabled then the mutexes APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#define CH_CFG_USE_MUTEXES TRUE
+
+/**
+ * @brief Enables recursive behavior on mutexes.
+ * @note Recursive mutexes are heavier and have an increased
+ * memory footprint.
+ *
+ * @note The default is @p FALSE.
+ * @note Requires @p CH_CFG_USE_MUTEXES.
+ */
+#define CH_CFG_USE_MUTEXES_RECURSIVE FALSE
+
+/**
+ * @brief Conditional Variables APIs.
+ * @details If enabled then the conditional variables APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_MUTEXES.
+ */
+#define CH_CFG_USE_CONDVARS TRUE
+
+/**
+ * @brief Conditional Variables APIs with timeout.
+ * @details If enabled then the conditional variables APIs with timeout
+ * specification are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_CONDVARS.
+ */
+#define CH_CFG_USE_CONDVARS_TIMEOUT TRUE
+
+/**
+ * @brief Events Flags APIs.
+ * @details If enabled then the event flags APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#define CH_CFG_USE_EVENTS TRUE
+
+/**
+ * @brief Events Flags APIs with timeout.
+ * @details If enabled then the events APIs with timeout specification
+ * are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_EVENTS.
+ */
+#define CH_CFG_USE_EVENTS_TIMEOUT TRUE
+
+/**
+ * @brief Synchronous Messages APIs.
+ * @details If enabled then the synchronous messages APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#define CH_CFG_USE_MESSAGES TRUE
+
+/**
+ * @brief Synchronous Messages queuing mode.
+ * @details If enabled then messages are served by priority rather than in
+ * FIFO order.
+ *
+ * @note The default is @p FALSE. Enable this if you have special
+ * requirements.
+ * @note Requires @p CH_CFG_USE_MESSAGES.
+ */
+#define CH_CFG_USE_MESSAGES_PRIORITY FALSE
+
+/**
+ * @brief Mailboxes APIs.
+ * @details If enabled then the asynchronous messages (mailboxes) APIs are
+ * included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_SEMAPHORES.
+ */
+#define CH_CFG_USE_MAILBOXES TRUE
+
+/**
+ * @brief I/O Queues APIs.
+ * @details If enabled then the I/O queues APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#define CH_CFG_USE_QUEUES TRUE
+
+/**
+ * @brief Core Memory Manager APIs.
+ * @details If enabled then the core memory manager APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#define CH_CFG_USE_MEMCORE TRUE
+
+/**
+ * @brief Heap Allocator APIs.
+ * @details If enabled then the memory heap allocator APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_MEMCORE and either @p CH_CFG_USE_MUTEXES or
+ * @p CH_CFG_USE_SEMAPHORES.
+ * @note Mutexes are recommended.
+ */
+#define CH_CFG_USE_HEAP TRUE
+
+/**
+ * @brief Memory Pools Allocator APIs.
+ * @details If enabled then the memory pools allocator APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#define CH_CFG_USE_MEMPOOLS TRUE
+
+/**
+ * @brief Dynamic Threads APIs.
+ * @details If enabled then the dynamic threads creation APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_WAITEXIT.
+ * @note Requires @p CH_CFG_USE_HEAP and/or @p CH_CFG_USE_MEMPOOLS.
+ */
+#define CH_CFG_USE_DYNAMIC TRUE
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Debug options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Debug option, kernel statistics.
+ *
+ * @note The default is @p FALSE.
+ */
+#define CH_DBG_STATISTICS FALSE
+
+/**
+ * @brief Debug option, system state check.
+ * @details If enabled the correct call protocol for system APIs is checked
+ * at runtime.
+ *
+ * @note The default is @p FALSE.
+ */
+#define CH_DBG_SYSTEM_STATE_CHECK FALSE
+
+/**
+ * @brief Debug option, parameters checks.
+ * @details If enabled then the checks on the API functions input
+ * parameters are activated.
+ *
+ * @note The default is @p FALSE.
+ */
+#define CH_DBG_ENABLE_CHECKS FALSE
+
+/**
+ * @brief Debug option, consistency checks.
+ * @details If enabled then all the assertions in the kernel code are
+ * activated. This includes consistency checks inside the kernel,
+ * runtime anomalies and port-defined checks.
+ *
+ * @note The default is @p FALSE.
+ */
+#define CH_DBG_ENABLE_ASSERTS FALSE
+
+/**
+ * @brief Debug option, trace buffer.
+ * @details If enabled then the context switch circular trace buffer is
+ * activated.
+ *
+ * @note The default is @p FALSE.
+ */
+#define CH_DBG_ENABLE_TRACE FALSE
+
+/**
+ * @brief Debug option, stack checks.
+ * @details If enabled then a runtime stack check is performed.
+ *
+ * @note The default is @p FALSE.
+ * @note The stack check is performed in a architecture/port dependent way.
+ * It may not be implemented or some ports.
+ * @note The default failure mode is to halt the system with the global
+ * @p panic_msg variable set to @p NULL.
+ */
+#define CH_DBG_ENABLE_STACK_CHECK FALSE
+
+/**
+ * @brief Debug option, stacks initialization.
+ * @details If enabled then the threads working area is filled with a byte
+ * value when a thread is created. This can be useful for the
+ * runtime measurement of the used stack.
+ *
+ * @note The default is @p FALSE.
+ */
+#define CH_DBG_FILL_THREADS FALSE
+
+/**
+ * @brief Debug option, threads profiling.
+ * @details If enabled then a field is added to the @p thread_t structure that
+ * counts the system ticks occurred while executing the thread.
+ *
+ * @note The default is @p FALSE.
+ * @note This debug option is not currently compatible with the
+ * tickless mode.
+ */
+#define CH_DBG_THREADS_PROFILING FALSE
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Kernel hooks
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Threads descriptor structure extension.
+ * @details User fields added to the end of the @p thread_t structure.
+ */
+#define CH_CFG_THREAD_EXTRA_FIELDS \
+ /* Add threads custom fields here.*/
+
+/**
+ * @brief Threads initialization hook.
+ * @details User initialization code added to the @p chThdInit() API.
+ *
+ * @note It is invoked from within @p chThdInit() and implicitly from all
+ * the threads creation APIs.
+ */
+#define CH_CFG_THREAD_INIT_HOOK(tp) { \
+ /* Add threads initialization code here.*/ \
+}
+
+/**
+ * @brief Threads finalization hook.
+ * @details User finalization code added to the @p chThdExit() API.
+ *
+ * @note It is inserted into lock zone.
+ * @note It is also invoked when the threads simply return in order to
+ * terminate.
+ */
+#define CH_CFG_THREAD_EXIT_HOOK(tp) { \
+ /* Add threads finalization code here.*/ \
+}
+
+/**
+ * @brief Context switch hook.
+ * @details This hook is invoked just before switching between threads.
+ */
+#define CH_CFG_CONTEXT_SWITCH_HOOK(ntp, otp) { \
+ /* Context switch code here.*/ \
+}
+
+/**
+ * @brief ISR enter hook.
+ */
+#define CH_CFG_IRQ_PROLOGUE_HOOK() { \
+ /* IRQ prologue code here.*/ \
+}
+
+/**
+ * @brief ISR exit hook.
+ */
+#define CH_CFG_IRQ_EPILOGUE_HOOK() { \
+ /* IRQ epilogue code here.*/ \
+}
+
+/**
+ * @brief Idle thread enter hook.
+ * @note This hook is invoked within a critical zone, no OS functions
+ * should be invoked from here.
+ * @note This macro can be used to activate a power saving mode.
+ */
+#define CH_CFG_IDLE_ENTER_HOOK() { \
+}
+
+/**
+ * @brief Idle thread leave hook.
+ * @note This hook is invoked within a critical zone, no OS functions
+ * should be invoked from here.
+ * @note This macro can be used to deactivate a power saving mode.
+ */
+#define CH_CFG_IDLE_LEAVE_HOOK() { \
+}
+
+/**
+ * @brief Idle Loop hook.
+ * @details This hook is continuously invoked by the idle thread loop.
+ */
+#define CH_CFG_IDLE_LOOP_HOOK() { \
+ /* Idle loop code here.*/ \
+}
+
+/**
+ * @brief System tick event hook.
+ * @details This hook is invoked in the system tick handler immediately
+ * after processing the virtual timers queue.
+ */
+#define CH_CFG_SYSTEM_TICK_HOOK() { \
+ /* System tick event code here.*/ \
+}
+
+/**
+ * @brief System halt hook.
+ * @details This hook is invoked in case to a system halting error before
+ * the system is halted.
+ */
+#define CH_CFG_SYSTEM_HALT_HOOK(reason) { \
+ /* System halt code here.*/ \
+}
+
+/**
+ * @brief Trace hook.
+ * @details This hook is invoked each time a new record is written in the
+ * trace buffer.
+ */
+#define CH_CFG_TRACE_HOOK(tep) { \
+ /* Trace code here.*/ \
+}
+
+/** @} */
+
+/*===========================================================================*/
+/* Port-specific settings (override port settings defaulted in chcore.h). */
+/*===========================================================================*/
+
+#endif /* _CHCONF_H_ */
+
+/** @} */
diff --git a/keyboards/ergodox_infinity/config.h b/keyboards/ergodox_infinity/config.h
new file mode 100644
index 000000000..094761e0b
--- /dev/null
+++ b/keyboards/ergodox_infinity/config.h
@@ -0,0 +1,98 @@
+/*
+Copyright 2015 Jun Wako <wakojun@gmail.com>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef INFINITY_ERGODOX_CONFIG_H
+#define INFINITY_ERGODOX_CONFIG_H
+
+
+/* USB Device descriptor parameter */
+#define VENDOR_ID 0xFEED
+#define PRODUCT_ID 0x6464
+#define DEVICE_VER 0x0001
+/* in python2: list(u"whatever".encode('utf-16-le')) */
+/* at most 32 characters or the ugly hack in usb_main.c borks */
+#define MANUFACTURER "TMK"
+#define USBSTR_MANUFACTURER 'T', '\x00', 'M', '\x00', 'K', '\x00', ' ', '\x00'
+#define PRODUCT "Infinity keyboard/TMK"
+#define USBSTR_PRODUCT 'I', '\x00', 'n', '\x00', 'f', '\x00', 'i', '\x00', 'n', '\x00', 'i', '\x00', 't', '\x00', 'y', '\x00', ' ', '\x00', 'k', '\x00', 'e', '\x00', 'y', '\x00', 'b', '\x00', 'o', '\x00', 'a', '\x00', 'r', '\x00', 'd', '\x00', '/', '\x00', 'T', '\x00', 'M', '\x00', 'K', '\x00'
+
+#define MOUSEKEY_INTERVAL 20
+#define MOUSEKEY_DELAY 0
+#define MOUSEKEY_TIME_TO_MAX 60
+#define MOUSEKEY_MAX_SPEED 7
+#define MOUSEKEY_WHEEL_DELAY 0
+
+#define TAPPING_TOGGLE 1
+
+/* define if matrix has ghost */
+//#define MATRIX_HAS_GHOST
+
+#define TAPPING_TERM 200
+#define IGNORE_MOD_TAP_INTERRUPT // this makes it possible to do rolling combos (zx) with keys that convert to other keys on hold (z becomes ctrl when you hold it, and when this option isn't enabled, z rapidly followed by x actually sends Ctrl-x. That's bad.)
+
+/* Mechanical locking support. Use KC_LCAP, KC_LNUM or KC_LSCR instead in keymap */
+#define LOCKING_SUPPORT_ENABLE
+/* Locking resynchronize hack */
+#define LOCKING_RESYNC_ENABLE
+
+/* key combination for command */
+#define IS_COMMAND() ( \
+ keyboard_report->mods == (MOD_BIT(KC_LCTL) | MOD_BIT(KC_RCTL)) || \
+ keyboard_report->mods == (MOD_BIT(KC_LSFT) | MOD_BIT(KC_RSFT)) \
+)
+
+/* key matrix size */
+#define MATRIX_ROWS 18
+#define MATRIX_COLS 5
+#define LOCAL_MATRIX_ROWS 9
+
+/* number of backlight levels */
+#define BACKLIGHT_LEVELS 3
+
+#define LED_BRIGHTNESS_LO 100
+#define LED_BRIGHTNESS_HI 255
+
+/* define if matrix has ghost */
+//#define MATRIX_HAS_GHOST
+
+/* Set 0 if debouncing isn't needed */
+#define DEBOUNCE 5
+
+#define SERIAL_LINK_BAUD 562500
+#define SERIAL_LINK_THREAD_PRIORITY (NORMALPRIO - 1)
+
+#define VISUALIZER_USER_DATA_SIZE 16
+
+/*
+ * Feature disable options
+ * These options are also useful to firmware size reduction.
+ */
+
+/* disable debug print */
+//#define NO_DEBUG
+
+/* disable print */
+//#define NO_PRINT
+
+/* disable action features */
+//#define NO_ACTION_LAYER
+//#define NO_ACTION_TAPPING
+//#define NO_ACTION_ONESHOT
+//#define NO_ACTION_MACRO
+//#define NO_ACTION_FUNCTION
+
+#endif
diff --git a/keyboards/ergodox_infinity/ergodox_infinity.c b/keyboards/ergodox_infinity/ergodox_infinity.c
new file mode 100644
index 000000000..05b2d7ce3
--- /dev/null
+++ b/keyboards/ergodox_infinity/ergodox_infinity.c
@@ -0,0 +1,195 @@
+#include QMK_KEYBOARD_H
+#include "ch.h"
+#include "hal.h"
+#include "serial_link/system/serial_link.h"
+#ifdef VISUALIZER_ENABLE
+#include "lcd_backlight.h"
+#endif
+
+void init_serial_link_hal(void) {
+ PORTA->PCR[1] = PORTx_PCRn_PE | PORTx_PCRn_PS | PORTx_PCRn_PFE | PORTx_PCRn_MUX(2);
+ PORTA->PCR[2] = PORTx_PCRn_DSE | PORTx_PCRn_SRE | PORTx_PCRn_MUX(2);
+ PORTE->PCR[0] = PORTx_PCRn_PE | PORTx_PCRn_PS | PORTx_PCRn_PFE | PORTx_PCRn_MUX(3);
+ PORTE->PCR[1] = PORTx_PCRn_DSE | PORTx_PCRn_SRE | PORTx_PCRn_MUX(3);
+}
+
+#define RED_PIN 1
+#define GREEN_PIN 2
+#define BLUE_PIN 3
+#define CHANNEL_RED FTM0->CHANNEL[0]
+#define CHANNEL_GREEN FTM0->CHANNEL[1]
+#define CHANNEL_BLUE FTM0->CHANNEL[2]
+
+#define RGB_PORT PORTC
+#define RGB_PORT_GPIO GPIOC
+
+// Base FTM clock selection (72 MHz system clock)
+// @ 0xFFFF period, 72 MHz / (0xFFFF * 2) = Actual period
+// Higher pre-scalar will use the most power (also look the best)
+// Pre-scalar calculations
+// 0 - 72 MHz -> 549 Hz
+// 1 - 36 MHz -> 275 Hz
+// 2 - 18 MHz -> 137 Hz
+// 3 - 9 MHz -> 69 Hz (Slightly visible flicker)
+// 4 - 4 500 kHz -> 34 Hz (Visible flickering)
+// 5 - 2 250 kHz -> 17 Hz
+// 6 - 1 125 kHz -> 9 Hz
+// 7 - 562 500 Hz -> 4 Hz
+// Using a higher pre-scalar without flicker is possible but FTM0_MOD will need to be reduced
+// Which will reduce the brightness range
+#define PRESCALAR_DEFINE 0
+void lcd_backlight_hal_init(void) {
+ // Setup Backlight
+ SIM->SCGC6 |= SIM_SCGC6_FTM0;
+ FTM0->CNT = 0; // Reset counter
+
+ // PWM Period
+ // 16-bit maximum
+ FTM0->MOD = 0xFFFF;
+
+ // Set FTM to PWM output - Edge Aligned, Low-true pulses
+#define CNSC_MODE FTM_SC_CPWMS | FTM_SC_PS(4) | FTM_SC_CLKS(0)
+ CHANNEL_RED.CnSC = CNSC_MODE;
+ CHANNEL_GREEN.CnSC = CNSC_MODE;
+ CHANNEL_BLUE.CnSC = CNSC_MODE;
+
+ // System clock, /w prescalar setting
+ FTM0->SC = FTM_SC_CLKS(1) | FTM_SC_PS(PRESCALAR_DEFINE);
+
+ CHANNEL_RED.CnV = 0;
+ CHANNEL_GREEN.CnV = 0;
+ CHANNEL_BLUE.CnV = 0;
+
+ RGB_PORT_GPIO->PDDR |= (1 << RED_PIN);
+ RGB_PORT_GPIO->PDDR |= (1 << GREEN_PIN);
+ RGB_PORT_GPIO->PDDR |= (1 << BLUE_PIN);
+
+#define RGB_MODE PORTx_PCRn_SRE | PORTx_PCRn_DSE | PORTx_PCRn_MUX(4)
+ RGB_PORT->PCR[RED_PIN] = RGB_MODE;
+ RGB_PORT->PCR[GREEN_PIN] = RGB_MODE;
+ RGB_PORT->PCR[BLUE_PIN] = RGB_MODE;
+}
+
+static uint16_t cie_lightness(uint16_t v) {
+ // The CIE 1931 formula for lightness
+ // Y = luminance (output) 0-1
+ // L = lightness input 0 - 100
+
+ // Y = (L* / 902.3) if L* <= 8
+ // Y = ((L* + 16) / 116)^3 if L* > 8
+
+ float l = 100.0f * (v / 65535.0f);
+ float y = 0.0f;
+ if (l <= 8.0f) {
+ y = l / 902.3;
+ }
+ else {
+ y = ((l + 16.0f) / 116.0f);
+ y = y * y * y;
+ if (y > 1.0f) {
+ y = 1.0f;
+ }
+ }
+ return y * 65535.0f;
+}
+
+void lcd_backlight_hal_color(uint16_t r, uint16_t g, uint16_t b) {
+ CHANNEL_RED.CnV = cie_lightness(r);
+ CHANNEL_GREEN.CnV = cie_lightness(g);
+ CHANNEL_BLUE.CnV = cie_lightness(b);
+}
+
+__attribute__ ((weak))
+void matrix_init_user(void) {
+}
+
+__attribute__ ((weak))
+void matrix_scan_user(void) {
+}
+
+
+void matrix_init_kb(void) {
+ // put your keyboard start-up code here
+ // runs once when the firmware starts up
+
+ matrix_init_user();
+ // The backlight always has to be initialized, otherwise it will stay lit
+#ifndef VISUALIZER_ENABLE
+ lcd_backlight_hal_init();
+#endif
+}
+
+void matrix_scan_kb(void) {
+ // put your looping keyboard code here
+ // runs every cycle (a lot)
+
+ matrix_scan_user();
+}
+
+__attribute__ ((weak))
+void ergodox_board_led_on(void){
+}
+
+__attribute__ ((weak))
+void ergodox_right_led_1_on(void){
+}
+
+__attribute__ ((weak))
+void ergodox_right_led_2_on(void){
+}
+
+__attribute__ ((weak))
+void ergodox_right_led_3_on(void){
+}
+
+__attribute__ ((weak))
+void ergodox_board_led_off(void){
+}
+
+__attribute__ ((weak))
+void ergodox_right_led_1_off(void){
+}
+
+__attribute__ ((weak))
+void ergodox_right_led_2_off(void){
+}
+
+__attribute__ ((weak))
+void ergodox_right_led_3_off(void){
+}
+
+__attribute__ ((weak))
+void ergodox_right_led_1_set(uint8_t n) {
+}
+
+__attribute__ ((weak))
+void ergodox_right_led_2_set(uint8_t n) {
+}
+
+__attribute__ ((weak))
+void ergodox_right_led_3_set(uint8_t n) {
+}
+
+#ifdef ONEHAND_ENABLE
+__attribute__ ((weak))
+const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = {
+ {{0, 9}, {1, 9}, {2, 9}, {3, 9}, {4, 9}},
+ {{0, 10}, {1, 10}, {2, 10}, {3, 10}, {4, 10}},
+ {{0, 11}, {1, 11}, {2, 11}, {3, 11}, {4, 11}},
+ {{0, 12}, {1, 12}, {2, 12}, {3, 12}, {4, 12}},
+ {{0, 13}, {1, 13}, {2, 13}, {3, 13}, {4, 13}},
+ {{0, 14}, {1, 14}, {2, 14}, {3, 14}, {4, 14}},
+ {{0, 15}, {1, 15}, {2, 15}, {3, 15}, {4, 15}},
+ {{0, 16}, {1, 16}, {2, 16}, {3, 16}, {4, 16}},
+ {{0, 17}, {1, 17}, {2, 17}, {3, 17}, {4, 17}},
+ {{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}},
+ {{0, 1}, {1, 1}, {2, 1}, {3, 1}, {4, 1}},
+ {{0, 2}, {1, 2}, {2, 2}, {3, 2}, {4, 2}},
+ {{0, 3}, {1, 3}, {2, 3}, {3, 3}, {4, 3}},
+ {{0, 4}, {1, 4}, {2, 4}, {3, 4}, {4, 4}},
+ {{0, 5}, {1, 5}, {2, 5}, {3, 5}, {4, 5}},
+ {{0, 6}, {1, 6}, {2, 6}, {3, 6}, {4, 6}},
+ {{0, 7}, {1, 7}, {2, 7}, {3, 7}, {4, 7}},
+ {{0, 8}, {1, 8}, {2, 8}, {3, 8}, {4, 8}},
+};
+#endif
diff --git a/keyboards/ergodox_infinity/ergodox_infinity.h b/keyboards/ergodox_infinity/ergodox_infinity.h
new file mode 100644
index 000000000..25249c714
--- /dev/null
+++ b/keyboards/ergodox_infinity/ergodox_infinity.h
@@ -0,0 +1,123 @@
+#ifndef KEYBOARDS_ERGODOX_INFINITY_INFINITY_H_
+#define KEYBOARDS_ERGODOX_INFINITY_INFINITY_H_
+
+#include "quantum.h"
+
+void ergodox_board_led_on(void);
+void ergodox_right_led_1_on(void);
+void ergodox_right_led_2_on(void);
+void ergodox_right_led_3_on(void);
+
+inline void ergodox_right_led_on(uint8_t led) {
+ switch (led) {
+ case 0:
+ ergodox_right_led_1_on();
+ break;
+ case 1:
+ ergodox_right_led_2_on();
+ break;
+ case 2:
+ ergodox_right_led_3_on();
+ break;
+ }
+}
+
+void ergodox_board_led_off(void);
+void ergodox_right_led_1_off(void);
+void ergodox_right_led_2_off(void);
+void ergodox_right_led_3_off(void);
+inline void ergodox_right_led_off(uint8_t led) {
+ switch (led) {
+ case 0:
+ ergodox_right_led_1_off();
+ break;
+ case 1:
+ ergodox_right_led_2_off();
+ break;
+ case 2:
+ ergodox_right_led_3_off();
+ break;
+ }
+}
+
+inline void ergodox_led_all_on(void)
+{
+ ergodox_board_led_on();
+ ergodox_right_led_1_on();
+ ergodox_right_led_2_on();
+ ergodox_right_led_3_on();
+}
+
+inline void ergodox_led_all_off(void)
+{
+ ergodox_board_led_off();
+ ergodox_right_led_1_off();
+ ergodox_right_led_2_off();
+ ergodox_right_led_3_off();
+}
+
+void ergodox_right_led_1_set(uint8_t n);
+void ergodox_right_led_2_set(uint8_t n);
+void ergodox_right_led_3_set(uint8_t n);
+
+inline void ergodox_right_led_set(uint8_t led, uint8_t n){
+ switch (led) {
+ case 0:
+ ergodox_right_led_1_set(n);
+ break;
+ case 1:
+ ergodox_right_led_2_set(n);
+ break;
+ case 2:
+ ergodox_right_led_3_set(n);
+ break;
+ }
+}
+
+inline void ergodox_led_all_set(uint8_t n) {
+ ergodox_right_led_1_set(n);
+ ergodox_right_led_2_set(n);
+ ergodox_right_led_3_set(n);
+}
+
+#define KEYMAP( \
+ A80, A70, A60, A50, A40, A30, A20, \
+ A81, A71, A61, A51, A41, A31, A21, \
+ A82, A72, A62, A52, A42, A32, \
+ A83, A73, A63, A53, A43, A33, A23, \
+ A84, A74, A64, A54, A44, \
+ A13, A03, \
+ A04, \
+ A34, A24, A14, \
+ B20, B30, B40, B50, B60, B70, B80, \
+ B21, B31, B41, B51, B61, B71, B81, \
+ B32, B42, B52, B62, B72, B82, \
+ B23, B33, B43, B53, B63, B73, B83, \
+ B44, B54, B64, B74, B84, \
+ B03, B13, \
+ B04, \
+ B14, B24, B34 \
+) { \
+ { KC_NO, KC_NO, KC_NO, A03, A04 }, \
+ { KC_NO, KC_NO, KC_NO, A13, A14 }, \
+ { A20, A21, KC_NO, A23, A24 }, \
+ { A30, A31, A32, A33, A34 }, \
+ { A40, A41, A42, A43, A44 }, \
+ { A50, A51, A52, A53, A54 }, \
+ { A60, A61, A62, A63, A64 }, \
+ { A70, A71, A72, A73, A74 }, \
+ { A80, A81, A82, A83, A84 }, \
+ { KC_NO, KC_NO, KC_NO, B03, B04 }, \
+ { KC_NO, KC_NO, KC_NO, B13, B14 }, \
+ { B20, B21, KC_NO, B23, B24 }, \
+ { B30, B31, B32, B33, B34 }, \
+ { B40, B41, B42, B43, B44 }, \
+ { B50, B51, B52, B53, B54 }, \
+ { B60, B61, B62, B63, B64 }, \
+ { B70, B71, B72, B73, B74 }, \
+ { B80, B81, B82, B83, B84 } \
+}
+
+#define LAYOUT_ergodox KEYMAP
+
+#endif /* KEYBOARDS_ERGODOX_INFINITY_INFINITY_H_ */
diff --git a/keyboards/ergodox_infinity/gfxconf.h b/keyboards/ergodox_infinity/gfxconf.h
new file mode 100644
index 000000000..ca338399d
--- /dev/null
+++ b/keyboards/ergodox_infinity/gfxconf.h
@@ -0,0 +1,27 @@
+/**
+ * This file has a different license to the rest of the uGFX system.
+ * You can copy, modify and distribute this file as you see fit.
+ * You do not need to publish your source modifications to this file.
+ * The only thing you are not permitted to do is to relicense it
+ * under a different license.
+ */
+
+/**
+ * Copy this file into your project directory and rename it as gfxconf.h
+ * Edit your copy to turn on the uGFX features you want to use.
+ * The values below are the defaults.
+ *
+ * Only remove the comments from lines where you want to change the
+ * default value. This allows definitions to be included from
+ * driver makefiles when required and provides the best future
+ * compatibility for your project.
+ *
+ * Please use spaces instead of tabs in this file.
+ */
+
+#ifndef _GFXCONF_H
+#define _GFXCONF_H
+
+#include "common_gfxconf.h"
+
+#endif /* _GFXCONF_H */
diff --git a/keyboards/ergodox_infinity/halconf.h b/keyboards/ergodox_infinity/halconf.h
new file mode 100644
index 000000000..55dd5e88d
--- /dev/null
+++ b/keyboards/ergodox_infinity/halconf.h
@@ -0,0 +1,353 @@
+/*
+ ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+/**
+ * @file templates/halconf.h
+ * @brief HAL configuration header.
+ * @details HAL configuration file, this file allows to enable or disable the
+ * various device drivers from your application. You may also use
+ * this file in order to override the device drivers default settings.
+ *
+ * @addtogroup HAL_CONF
+ * @{
+ */
+
+#ifndef _HALCONF_H_
+#define _HALCONF_H_
+
+#include "mcuconf.h"
+
+/**
+ * @brief Enables the PAL subsystem.
+ */
+#if !defined(HAL_USE_PAL) || defined(__DOXYGEN__)
+#define HAL_USE_PAL TRUE
+#endif
+
+/**
+ * @brief Enables the ADC subsystem.
+ */
+#if !defined(HAL_USE_ADC) || defined(__DOXYGEN__)
+#define HAL_USE_ADC FALSE
+#endif
+
+/**
+ * @brief Enables the CAN subsystem.
+ */
+#if !defined(HAL_USE_CAN) || defined(__DOXYGEN__)
+#define HAL_USE_CAN FALSE
+#endif
+
+/**
+ * @brief Enables the DAC subsystem.
+ */
+#if !defined(HAL_USE_DAC) || defined(__DOXYGEN__)
+#define HAL_USE_DAC FALSE
+#endif
+
+/**
+ * @brief Enables the EXT subsystem.
+ */
+#if !defined(HAL_USE_EXT) || defined(__DOXYGEN__)
+#define HAL_USE_EXT FALSE
+#endif
+
+/**
+ * @brief Enables the GPT subsystem.
+ */
+#if !defined(HAL_USE_GPT) || defined(__DOXYGEN__)
+#define HAL_USE_GPT FALSE
+#endif
+
+/**
+ * @brief Enables the I2C subsystem.
+ */
+#if !defined(HAL_USE_I2C) || defined(__DOXYGEN__)
+#define HAL_USE_I2C TRUE
+#endif
+
+/**
+ * @brief Enables the I2S subsystem.
+ */
+#if !defined(HAL_USE_I2S) || defined(__DOXYGEN__)
+#define HAL_USE_I2S FALSE
+#endif
+
+/**
+ * @brief Enables the ICU subsystem.
+ */
+#if !defined(HAL_USE_ICU) || defined(__DOXYGEN__)
+#define HAL_USE_ICU FALSE
+#endif
+
+/**
+ * @brief Enables the MAC subsystem.
+ */
+#if !defined(HAL_USE_MAC) || defined(__DOXYGEN__)
+#define HAL_USE_MAC FALSE
+#endif
+
+/**
+ * @brief Enables the MMC_SPI subsystem.
+ */
+#if !defined(HAL_USE_MMC_SPI) || defined(__DOXYGEN__)
+#define HAL_USE_MMC_SPI FALSE
+#endif
+
+/**
+ * @brief Enables the PWM subsystem.
+ */
+#if !defined(HAL_USE_PWM) || defined(__DOXYGEN__)
+#define HAL_USE_PWM FALSE
+#endif
+
+/**
+ * @brief Enables the RTC subsystem.
+ */
+#if !defined(HAL_USE_RTC) || defined(__DOXYGEN__)
+#define HAL_USE_RTC FALSE
+#endif
+
+/**
+ * @brief Enables the SDC subsystem.
+ */
+#if !defined(HAL_USE_SDC) || defined(__DOXYGEN__)
+#define HAL_USE_SDC FALSE
+#endif
+
+/**
+ * @brief Enables the SERIAL subsystem.
+ */
+#if !defined(HAL_USE_SERIAL) || defined(__DOXYGEN__)
+#define HAL_USE_SERIAL TRUE
+#endif
+
+/**
+ * @brief Enables the SERIAL over USB subsystem.
+ */
+#if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__)
+#define HAL_USE_SERIAL_USB TRUE
+#endif
+
+/**
+ * @brief Enables the SPI subsystem.
+ */
+#if !defined(HAL_USE_SPI) || defined(__DOXYGEN__)
+#define HAL_USE_SPI TRUE
+#endif
+
+/**
+ * @brief Enables the UART subsystem.
+ */
+#if !defined(HAL_USE_UART) || defined(__DOXYGEN__)
+#define HAL_USE_UART FALSE
+#endif
+
+/**
+ * @brief Enables the USB subsystem.
+ */
+#if !defined(HAL_USE_USB) || defined(__DOXYGEN__)
+#define HAL_USE_USB TRUE
+#endif
+
+/**
+ * @brief Enables the WDG subsystem.
+ */
+#if !defined(HAL_USE_WDG) || defined(__DOXYGEN__)
+#define HAL_USE_WDG FALSE
+#endif
+
+/*===========================================================================*/
+/* ADC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(ADC_USE_WAIT) || defined(__DOXYGEN__)
+#define ADC_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p adcAcquireBus() and @p adcReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(ADC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define ADC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* CAN driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Sleep mode related APIs inclusion switch.
+ */
+#if !defined(CAN_USE_SLEEP_MODE) || defined(__DOXYGEN__)
+#define CAN_USE_SLEEP_MODE TRUE
+#endif
+
+/*===========================================================================*/
+/* I2C driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables the mutual exclusion APIs on the I2C bus.
+ */
+#if !defined(I2C_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define I2C_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* MAC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables an event sources for incoming packets.
+ */
+#if !defined(MAC_USE_ZERO_COPY) || defined(__DOXYGEN__)
+#define MAC_USE_ZERO_COPY FALSE
+#endif
+
+/**
+ * @brief Enables an event sources for incoming packets.
+ */
+#if !defined(MAC_USE_EVENTS) || defined(__DOXYGEN__)
+#define MAC_USE_EVENTS TRUE
+#endif
+
+/*===========================================================================*/
+/* MMC_SPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Delays insertions.
+ * @details If enabled this options inserts delays into the MMC waiting
+ * routines releasing some extra CPU time for the threads with
+ * lower priority, this may slow down the driver a bit however.
+ * This option is recommended also if the SPI driver does not
+ * use a DMA channel and heavily loads the CPU.
+ */
+#if !defined(MMC_NICE_WAITING) || defined(__DOXYGEN__)
+#define MMC_NICE_WAITING TRUE
+#endif
+
+/*===========================================================================*/
+/* SDC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Number of initialization attempts before rejecting the card.
+ * @note Attempts are performed at 10mS intervals.
+ */
+#if !defined(SDC_INIT_RETRY) || defined(__DOXYGEN__)
+#define SDC_INIT_RETRY 100
+#endif
+
+/**
+ * @brief Include support for MMC cards.
+ * @note MMC support is not yet implemented so this option must be kept
+ * at @p FALSE.
+ */
+#if !defined(SDC_MMC_SUPPORT) || defined(__DOXYGEN__)
+#define SDC_MMC_SUPPORT FALSE
+#endif
+
+/**
+ * @brief Delays insertions.
+ * @details If enabled this options inserts delays into the MMC waiting
+ * routines releasing some extra CPU time for the threads with
+ * lower priority, this may slow down the driver a bit however.
+ */
+#if !defined(SDC_NICE_WAITING) || defined(__DOXYGEN__)
+#define SDC_NICE_WAITING TRUE
+#endif
+
+/*===========================================================================*/
+/* SERIAL driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Default bit rate.
+ * @details Configuration parameter, this is the baud rate selected for the
+ * default configuration.
+ */
+#if !defined(SERIAL_DEFAULT_BITRATE) || defined(__DOXYGEN__)
+#define SERIAL_DEFAULT_BITRATE 38400
+#endif
+
+/**
+ * @brief Serial buffers size.
+ * @details Configuration parameter, you can change the depth of the queue
+ * buffers depending on the requirements of your application.
+ * @note The default is 64 bytes for both the transmission and receive
+ * buffers.
+ */
+#if !defined(SERIAL_BUFFERS_SIZE) || defined(__DOXYGEN__)
+#define SERIAL_BUFFERS_SIZE 128
+#endif
+
+/*===========================================================================*/
+/* SERIAL_USB driver related setting. */
+/*===========================================================================*/
+
+/**
+ * @brief Serial over USB buffers size.
+ * @details Configuration parameter, the buffer size must be a multiple of
+ * the USB data endpoint maximum packet size.
+ * @note The default is 64 bytes for both the transmission and receive
+ * buffers.
+ */
+#if !defined(SERIAL_USB_BUFFERS_SIZE) || defined(__DOXYGEN__)
+#define SERIAL_USB_BUFFERS_SIZE 256
+#endif
+
+/*===========================================================================*/
+/* SPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(SPI_USE_WAIT) || defined(__DOXYGEN__)
+#define SPI_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p spiAcquireBus() and @p spiReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(SPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define SPI_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* USB driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(USB_USE_WAIT) || defined(__DOXYGEN__)
+#define USB_USE_WAIT TRUE
+#endif
+
+#endif /* _HALCONF_H_ */
+
+/** @} */
diff --git a/keyboards/ergodox_infinity/led.c b/keyboards/ergodox_infinity/led.c
new file mode 100644
index 000000000..8175c1c5c
--- /dev/null
+++ b/keyboards/ergodox_infinity/led.c
@@ -0,0 +1,26 @@
+/*
+Copyright 2012 Jun Wako <wakojun@gmail.com>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "hal.h"
+
+#include "led.h"
+
+
+void led_set(uint8_t usb_led) {
+ //TODO: Add led emulation if there's no customized visualization
+ (void)usb_led;
+}
diff --git a/keyboards/ergodox_infinity/matrix.c b/keyboards/ergodox_infinity/matrix.c
new file mode 100644
index 000000000..3364f8c90
--- /dev/null
+++ b/keyboards/ergodox_infinity/matrix.c
@@ -0,0 +1,173 @@
+/*
+Copyright 2016 Fred Sundvik <fsundvik@gmail.com>
+Jun Wako <wakojun@gmail.com>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include "hal.h"
+#include "timer.h"
+#include "wait.h"
+#include "print.h"
+#include "debug.h"
+#include "matrix.h"
+#include "serial_link/system/serial_link.h"
+
+
+/*
+ * Infinity ErgoDox Pinusage:
+ * Column pins are input with internal pull-down. Row pins are output and strobe with high.
+ * Key is high or 1 when it turns on.
+ *
+ * col: { PTD1, PTD4, PTD5, PTD6, PTD7 }
+ * row: { PTB2, PTB3, PTB18, PTB19, PTC0, PTC9, PTC10, PTC11, PTD0 }
+ */
+/* matrix state(1:on, 0:off) */
+static matrix_row_t matrix[MATRIX_ROWS];
+static matrix_row_t matrix_debouncing[LOCAL_MATRIX_ROWS];
+static bool debouncing = false;
+static uint16_t debouncing_time = 0;
+
+
+void matrix_init(void)
+{
+ /* Column(sense) */
+ palSetPadMode(GPIOD, 1, PAL_MODE_INPUT_PULLDOWN);
+ palSetPadMode(GPIOD, 4, PAL_MODE_INPUT_PULLDOWN);
+ palSetPadMode(GPIOD, 5, PAL_MODE_INPUT_PULLDOWN);
+ palSetPadMode(GPIOD, 6, PAL_MODE_INPUT_PULLDOWN);
+ palSetPadMode(GPIOD, 7, PAL_MODE_INPUT_PULLDOWN);
+
+ /* Row(strobe) */
+ palSetPadMode(GPIOB, 2, PAL_MODE_OUTPUT_PUSHPULL);
+ palSetPadMode(GPIOB, 3, PAL_MODE_OUTPUT_PUSHPULL);
+ palSetPadMode(GPIOB, 18, PAL_MODE_OUTPUT_PUSHPULL);
+ palSetPadMode(GPIOB, 19, PAL_MODE_OUTPUT_PUSHPULL);
+ palSetPadMode(GPIOC, 0, PAL_MODE_OUTPUT_PUSHPULL);
+ palSetPadMode(GPIOC, 9, PAL_MODE_OUTPUT_PUSHPULL);
+ palSetPadMode(GPIOC, 10, PAL_MODE_OUTPUT_PUSHPULL);
+ palSetPadMode(GPIOC, 11, PAL_MODE_OUTPUT_PUSHPULL);
+ palSetPadMode(GPIOD, 0, PAL_MODE_OUTPUT_PUSHPULL);
+
+ memset(matrix, 0, MATRIX_ROWS);
+ memset(matrix_debouncing, 0, LOCAL_MATRIX_ROWS);
+
+ matrix_init_quantum();
+}
+
+uint8_t matrix_scan(void)
+{
+ for (int row = 0; row < LOCAL_MATRIX_ROWS; row++) {
+ matrix_row_t data = 0;
+
+ // strobe row
+ switch (row) {
+ case 0: palSetPad(GPIOB, 2); break;
+ case 1: palSetPad(GPIOB, 3); break;
+ case 2: palSetPad(GPIOB, 18); break;
+ case 3: palSetPad(GPIOB, 19); break;
+ case 4: palSetPad(GPIOC, 0); break;
+ case 5: palSetPad(GPIOC, 9); break;
+ case 6: palSetPad(GPIOC, 10); break;
+ case 7: palSetPad(GPIOC, 11); break;
+ case 8: palSetPad(GPIOD, 0); break;
+ }
+
+ // need wait to settle pin state
+ // if you wait too short, or have a too high update rate
+ // the keyboard might freeze, or there might not be enough
+ // processing power to update the LCD screen properly.
+ // 20us, or two ticks at 100000Hz seems to be OK
+ wait_us(20);
+
+ // read col data: { PTD1, PTD4, PTD5, PTD6, PTD7 }
+ data = ((palReadPort(GPIOD) & 0xF0) >> 3) |
+ ((palReadPort(GPIOD) & 0x02) >> 1);
+
+ // un-strobe row
+ switch (row) {
+ case 0: palClearPad(GPIOB, 2); break;
+ case 1: palClearPad(GPIOB, 3); break;
+ case 2: palClearPad(GPIOB, 18); break;
+ case 3: palClearPad(GPIOB, 19); break;
+ case 4: palClearPad(GPIOC, 0); break;
+ case 5: palClearPad(GPIOC, 9); break;
+ case 6: palClearPad(GPIOC, 10); break;
+ case 7: palClearPad(GPIOC, 11); break;
+ case 8: palClearPad(GPIOD, 0); break;
+ }
+
+ if (matrix_debouncing[row] != data) {
+ matrix_debouncing[row] = data;
+ debouncing = true;
+ debouncing_time = timer_read();
+ }
+ }
+
+ uint8_t offset = 0;
+#ifdef MASTER_IS_ON_RIGHT
+ if (is_serial_link_master()) {
+ offset = MATRIX_ROWS - LOCAL_MATRIX_ROWS;
+ }
+#endif
+
+ if (debouncing && timer_elapsed(debouncing_time) > DEBOUNCE) {
+ for (int row = 0; row < LOCAL_MATRIX_ROWS; row++) {
+ matrix[offset + row] = matrix_debouncing[row];
+ }
+ debouncing = false;
+ }
+ matrix_scan_quantum();
+ return 1;
+}
+
+bool matrix_is_on(uint8_t row, uint8_t col)
+{
+ return (matrix[row] & (1<<col));
+}
+
+matrix_row_t matrix_get_row(uint8_t row)
+{
+ return matrix[row];
+}
+
+void matrix_print(void)
+{
+ xprintf("\nr/c 01234567\n");
+ for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
+ xprintf("%X0: ", row);
+ matrix_row_t data = matrix_get_row(row);
+ for (int col = 0; col < MATRIX_COLS; col++) {
+ if (data & (1<<col))
+ xprintf("1");
+ else
+ xprintf("0");
+ }
+ xprintf("\n");
+ }
+}
+
+void matrix_set_remote(matrix_row_t* rows, uint8_t index) {
+ uint8_t offset = 0;
+#ifdef MASTER_IS_ON_RIGHT
+ offset = MATRIX_ROWS - LOCAL_MATRIX_ROWS * (index + 2);
+#else
+ offset = LOCAL_MATRIX_ROWS * (index + 1);
+#endif
+ for (int row = 0; row < LOCAL_MATRIX_ROWS; row++) {
+ matrix[offset + row] = rows[row];
+ }
+}
diff --git a/keyboards/ergodox_infinity/mcuconf.h b/keyboards/ergodox_infinity/mcuconf.h
new file mode 100644
index 000000000..f6730b99c
--- /dev/null
+++ b/keyboards/ergodox_infinity/mcuconf.h
@@ -0,0 +1,74 @@
+/*
+ ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#ifndef _MCUCONF_H_
+#define _MCUCONF_H_
+
+#define K20x_MCUCONF
+
+/*
+ * HAL driver system settings.
+ */
+
+#define K20x7
+
+/* Select the MCU clocking mode below by enabling the appropriate block. */
+
+#define KINETIS_NO_INIT FALSE
+
+/* PEE mode - 48MHz system clock driven by external crystal. */
+#define KINETIS_MCG_MODE KINETIS_MCG_MODE_PEE
+#define KINETIS_PLLCLK_FREQUENCY 72000000UL
+#define KINETIS_SYSCLK_FREQUENCY 72000000UL
+#define KINETIS_BUSCLK_FREQUENCY 36000000UL
+#define KINETIS_FLASHCLK_FREQUENCY 24000000UL
+
+#if 0
+/* FEI mode - 48 MHz with internal 32.768 kHz crystal */
+#define KINETIS_MCG_MODE KINETIS_MCG_MODE_FEI
+#define KINETIS_MCG_FLL_DMX32 1 /* Fine-tune for 32.768 kHz */
+#define KINETIS_MCG_FLL_DRS 1 /* 1464x FLL factor */
+#define KINETIS_SYSCLK_FREQUENCY 47972352UL /* 32.768 kHz * 1464 (~48 MHz) */
+#define KINETIS_CLKDIV1_OUTDIV1 1
+#define KINETIS_CLKDIV1_OUTDIV2 1
+#define KINETIS_CLKDIV1_OUTDIV4 2
+#define KINETIS_BUSCLK_FREQUENCY KINETIS_SYSCLK_FREQUENCY
+#define KINETIS_FLASHCLK_FREQUENCY KINETIS_SYSCLK_FREQUENCY/2
+#endif
+
+/*
+ * SERIAL driver system settings.
+ */
+#define KINETIS_SERIAL_USE_UART0 TRUE
+#define KINETIS_SERIAL_USE_UART1 TRUE
+
+/*
+ * USB driver settings
+ */
+#define KINETIS_USB_USE_USB0 TRUE
+/* Need to redefine this, since the default is for K20x */
+/* This is for Teensy LC; you should comment it out (or change to 5)
+ * for Teensy 3.x */
+#define KINETIS_USB_USB0_IRQ_PRIORITY 2
+
+/*
+ * SPI driver system settings.
+ */
+#define KINETIS_SPI_USE_SPI0 TRUE
+
+#define KINETIS_I2C_USE_I2C0 TRUE
+
+#endif /* _MCUCONF_H_ */
diff --git a/keyboards/ergodox_infinity/readme.md b/keyboards/ergodox_infinity/readme.md
new file mode 100644
index 000000000..2d037d1c9
--- /dev/null
+++ b/keyboards/ergodox_infinity/readme.md
@@ -0,0 +1,45 @@
+# ErgoDox Infinity
+
+The Infinity is two completely independent keyboards, and needs to be flashed
+for the left and right halves seperately. To flash them:
+
+ - Build the firmware with `make infinity-keymapname`
+
+ - Plug in the left hand keyboard only.
+
+ - Press the program button (back of keyboard, above thumb pad).
+
+ - Install the firmware with `sudo make infinity-keymapname-dfu-util`
+
+ - Build right hand firmware with `make infinity-keymapname MASTER=right`
+
+ - Plug in the right hand keyboard only.
+
+ - Press the program button (back of keyboard, above thumb pad).
+
+ - Install the firmware with `sudo make infinity-keymapname-dfu-util MASTER=right`
+
+More information on the Infinity firmware is available in the [TMK/chibios for
+Input Club Infinity Ergodox](https://github.com/fredizzimo/infinity_ergodox/blob/master/README.md)
+
+## Infinity Master/Two Halves
+
+The Infinity is two completely independent keyboards, that can connect together.
+You have a few options in how you flash the firmware:
+
+- Flash the left half, rebuild the firmware with "MASTER=right" and then flash
+ the right half. This allows you to plug in either half directly to the
+ computer and is what the above instructions do.
+
+- Flash the left half, then flash the same firmware on the right. This only
+ works when the left half is plugged directly to the computer and the keymap
+ is mirrored. It saves the small extra step of rebuilding with
+ "MASTER=right".
+
+- The same as the previous one but with "MASTER=right" when you build the
+ firmware, then flash the same firmware to both halves. You just have to
+ directly connect the right half to the computer.
+
+- For minor changes such as changing only the keymap without having updated
+ any part of the firmware code itself, you can program only the MASTER half.
+ It is safest to program both halves though. \ No newline at end of file
diff --git a/keyboards/ergodox_infinity/rules.mk b/keyboards/ergodox_infinity/rules.mk
new file mode 100644
index 000000000..cb1115255
--- /dev/null
+++ b/keyboards/ergodox_infinity/rules.mk
@@ -0,0 +1,89 @@
+# project specific files
+SRC = matrix.c \
+ led.c
+
+## chip/board settings
+# - the next two should match the directories in
+# <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
+# - For Teensies, FAMILY = KINETIS and SERIES is either
+# KL2x (LC) or K20x (3.0,3.1,3.2).
+# - For Infinity KB, SERIES = K20x
+MCU_FAMILY = KINETIS
+MCU_SERIES = K20x
+
+# Linker script to use
+# - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
+# or <this_dir>/ld/
+# - NOTE: a custom ld script is needed for EEPROM on Teensy LC
+# - LDSCRIPT =
+# - MKL26Z64 for Teensy LC
+# - MK20DX128 for Teensy 3.0
+# - MK20DX256 for Teensy 3.1 and 3.2
+# - MK20DX128BLDR4 for Infinity 60% with Kiibohd bootloader
+# - MK20DX256BLDR8 for Infinity ErgoDox with Kiibohd bootloader
+MCU_LDSCRIPT = MK20DX256BLDR8
+
+# Startup code to use
+# - it should exist in <chibios>/os/common/ports/ARMCMx/compilers/GCC/mk/
+# - STARTUP =
+# - kl2x for Teensy LC
+# - k20x5 for Teensy 3.0 and Infinity 60%
+# - k20x7 for Teensy 3.1, 3.2 and Infinity ErgoDox
+MCU_STARTUP = k20x7
+
+# Board: it should exist either in <chibios>/os/hal/boards/
+# or <this_dir>/boards
+# - BOARD =
+# - PJRC_TEENSY_LC for Teensy LC
+# - PJRC_TEENSY_3 for Teensy 3.0
+# - PJRC_TEENSY_3_1 for Teensy 3.1 or 3.2
+# - MCHCK_K20 for Infinity KB
+#BOARD = MCHCK_K20
+BOARD = PJRC_TEENSY_3_1
+
+# Cortex version
+# Teensy LC is cortex-m0; Teensy 3.x are cortex-m4
+MCU = cortex-m4
+
+# ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
+# I.e. 6 for Teensy LC; 7 for Teensy 3.x
+ARMV = 7
+
+# Vector table for application
+# 0x00000000-0x00001000 area is occupied by bootlaoder.*/
+# The CORTEX_VTOR... is needed only for MCHCK/Infinity KB
+OPT_DEFS += -DCORTEX_VTOR_INIT=0x00002000
+
+# Build Options
+# comment out to disable the options.
+#
+
+BOOTMAGIC_ENABLE = no # Virtual DIP switch configuration(+1000)
+MOUSEKEY_ENABLE = yes # Mouse keys(+4700)
+EXTRAKEY_ENABLE = yes # Audio control and System control(+450)
+CONSOLE_ENABLE = no # Console for debug(+400)
+COMMAND_ENABLE = yes # Commands for debug and configuration
+CUSTOM_MATRIX = yes # Custom matrix file for the ErgoDox EZ
+SLEEP_LED_ENABLE = yes # Breathing sleep LED during USB suspend
+NKRO_ENABLE = yes # USB Nkey Rollover - if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work
+UNICODE_ENABLE = yes # Unicode
+ONEHAND_ENABLE = yes # Allow swapping hands of keyboard
+
+CUSTOM_MATRIX = yes # Custom matrix file
+SERIAL_LINK_ENABLE = yes
+VISUALIZER_ENABLE = yes
+LCD_ENABLE = yes
+BACKLIGHT_ENABLE = yes
+LCD_BACKLIGHT_ENABLE = yes
+MIDI_ENABLE = no
+RGBLIGHT_ENABLE = no
+
+LCD_DRIVER = st7565
+LCD_WIDTH = 128
+LCD_HEIGHT = 32
+
+LED_DRIVER = is31fl3731c
+LED_WIDTH = 7
+LED_HEIGHT = 7
+
+LAYOUTS = ergodox ergodox_80 \ No newline at end of file
diff --git a/keyboards/ergodox_infinity/simple_visualizer.h b/keyboards/ergodox_infinity/simple_visualizer.h
new file mode 100644
index 000000000..73d0e0887
--- /dev/null
+++ b/keyboards/ergodox_infinity/simple_visualizer.h
@@ -0,0 +1,123 @@
+/* Copyright 2017 Fred Sundvik
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef KEYBOARDS_ERGODOX_INFINITY_SIMPLE_VISUALIZER_H_
+#define KEYBOARDS_ERGODOX_INFINITY_SIMPLE_VISUALIZER_H_
+
+// Currently we are assuming that both the backlight and LCD are enabled
+// But it's entirely possible to write a custom visualizer that use only
+// one of them
+#ifndef LCD_BACKLIGHT_ENABLE
+#error This visualizer needs that LCD backlight is enabled
+#endif
+
+#ifndef LCD_ENABLE
+#error This visualizer needs that LCD is enabled
+#endif
+
+#include "visualizer.h"
+#include "visualizer_keyframes.h"
+#include "lcd_keyframes.h"
+#include "lcd_backlight_keyframes.h"
+#include "system/serial_link.h"
+#include "led.h"
+#include "default_animations.h"
+
+static const uint32_t logo_background_color = LCD_COLOR(0x00, 0x00, 0xFF);
+static const uint32_t initial_color = LCD_COLOR(0, 0, 0);
+
+static bool initial_update = true;
+
+// Feel free to modify the animations below, or even add new ones if needed
+
+static keyframe_animation_t lcd_layer_display = {
+ .num_frames = 1,
+ .loop = false,
+ .frame_lengths = {gfxMillisecondsToTicks(0)},
+ .frame_functions = {lcd_keyframe_display_layer_and_led_states}
+};
+
+// The color animation animates the LCD color when you change layers
+static keyframe_animation_t color_animation = {
+ .num_frames = 2,
+ .loop = false,
+ // Note that there's a 200 ms no-operation frame,
+ // this prevents the color from changing when activating the layer
+ // momentarily
+ .frame_lengths = {gfxMillisecondsToTicks(200), gfxMillisecondsToTicks(500)},
+ .frame_functions = {keyframe_no_operation, lcd_backlight_keyframe_animate_color},
+};
+
+void initialize_user_visualizer(visualizer_state_t* state) {
+ // The brightness will be dynamically adjustable in the future
+ // But for now, change it here.
+ lcd_backlight_brightness(130);
+ state->current_lcd_color = initial_color;
+ state->target_lcd_color = logo_background_color;
+ initial_update = true;
+ start_keyframe_animation(&default_startup_animation);
+}
+
+
+// This function should be implemented by the keymap visualizer
+// Don't change anything else than state->target_lcd_color and state->layer_text as that's the only thing
+// that the simple_visualizer assumes that you are updating
+// Also make sure that the buffer passed to state->layer_text remains valid until the previous animation is
+// stopped. This can be done by either double buffering it or by using constant strings
+static void get_visualizer_layer_and_color(visualizer_state_t* state);
+
+void update_user_visualizer_state(visualizer_state_t* state, visualizer_keyboard_status_t* prev_status) {
+ // Add more tests, change the colors and layer texts here
+ // Usually you want to check the high bits (higher layers first)
+ // because that's the order layers are processed for keypresses
+ // You can for check for example:
+ // state->status.layer
+ // state->status.default_layer
+ // state->status.leds (see led.h for available statuses)
+
+ uint32_t prev_color = state->target_lcd_color;
+ const char* prev_layer_text = state->layer_text;
+
+ get_visualizer_layer_and_color(state);
+
+ if (initial_update || prev_color != state->target_lcd_color) {
+ start_keyframe_animation(&color_animation);
+ }
+
+ if (initial_update || prev_layer_text != state->layer_text) {
+ start_keyframe_animation(&lcd_layer_display);
+ }
+ // You can also stop existing animations, and start your custom ones here
+ // remember that you should normally have only one animation for the LCD
+ // and one for the background. But you can also combine them if you want.
+}
+
+void user_visualizer_suspend(visualizer_state_t* state) {
+ state->layer_text = "Suspending...";
+ uint8_t hue = LCD_HUE(state->current_lcd_color);
+ uint8_t sat = LCD_SAT(state->current_lcd_color);
+ state->target_lcd_color = LCD_COLOR(hue, sat, 0);
+ start_keyframe_animation(&default_suspend_animation);
+}
+
+void user_visualizer_resume(visualizer_state_t* state) {
+ state->current_lcd_color = initial_color;
+ state->target_lcd_color = logo_background_color;
+ initial_update = true;
+ start_keyframe_animation(&default_startup_animation);
+}
+
+#endif /* KEYBOARDS_ERGODOX_INFINITY_SIMPLE_VISUALIZER_H_ */
diff --git a/keyboards/ergodox_infinity/visualizer.c b/keyboards/ergodox_infinity/visualizer.c
new file mode 100644
index 000000000..4b16021ab
--- /dev/null
+++ b/keyboards/ergodox_infinity/visualizer.c
@@ -0,0 +1,329 @@
+/*
+Copyright 2016 Fred Sundvik <fsundvik@gmail.com>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+// Currently we are assuming that both the backlight and LCD are enabled
+// But it's entirely possible to write a custom visualizer that use only
+// one of them
+#ifndef LCD_BACKLIGHT_ENABLE
+#error This visualizer needs that LCD backlight is enabled
+#endif
+
+#ifndef LCD_ENABLE
+#error This visualizer needs that LCD is enabled
+#endif
+
+#include "visualizer.h"
+#include "visualizer_keyframes.h"
+#include "lcd_keyframes.h"
+#include "lcd_backlight_keyframes.h"
+#include "system/serial_link.h"
+#include "default_animations.h"
+
+static const uint32_t logo_background_color = LCD_COLOR(0x00, 0x00, 0xFF);
+static const uint32_t initial_color = LCD_COLOR(0, 0, 0);
+
+static const uint32_t led_emulation_colors[4] = {
+ LCD_COLOR(0, 0, 0),
+ LCD_COLOR(255, 255, 255),
+ LCD_COLOR(84, 255, 255),
+ LCD_COLOR(168, 255, 255),
+};
+
+static uint32_t next_led_target_color = 0;
+
+typedef enum {
+ LCD_STATE_INITIAL,
+ LCD_STATE_LAYER_BITMAP,
+ LCD_STATE_BITMAP_AND_LEDS,
+} lcd_state_t;
+
+static lcd_state_t lcd_state = LCD_STATE_INITIAL;
+
+typedef struct {
+ uint8_t led_on;
+ uint8_t led1;
+ uint8_t led2;
+ uint8_t led3;
+} visualizer_user_data_t;
+
+// Don't access from visualization function, use the visualizer state instead
+static visualizer_user_data_t user_data_keyboard = {
+ .led_on = 0,
+ .led1 = LED_BRIGHTNESS_HI,
+ .led2 = LED_BRIGHTNESS_HI,
+ .led3 = LED_BRIGHTNESS_HI,
+};
+
+_Static_assert(sizeof(visualizer_user_data_t) <= VISUALIZER_USER_DATA_SIZE,
+ "Please increase the VISUALIZER_USER_DATA_SIZE");
+
+// Feel free to modify the animations below, or even add new ones if needed
+
+
+// The color animation animates the LCD color when you change layers
+static keyframe_animation_t one_led_color = {
+ .num_frames = 1,
+ .loop = false,
+ .frame_lengths = {gfxMillisecondsToTicks(0)},
+ .frame_functions = {lcd_backlight_keyframe_set_color},
+};
+
+bool swap_led_target_color(keyframe_animation_t* animation, visualizer_state_t* state) {
+ uint32_t temp = next_led_target_color;
+ next_led_target_color = state->target_lcd_color;
+ state->target_lcd_color = temp;
+ return false;
+}
+
+// The color animation animates the LCD color when you change layers
+static keyframe_animation_t two_led_colors = {
+ .num_frames = 2,
+ .loop = true,
+ .frame_lengths = {gfxMillisecondsToTicks(1000), gfxMillisecondsToTicks(0)},
+ .frame_functions = {lcd_backlight_keyframe_set_color, swap_led_target_color},
+};
+
+// The LCD animation alternates between the layer name display and a
+// bitmap that displays all active layers
+static keyframe_animation_t lcd_bitmap_animation = {
+ .num_frames = 1,
+ .loop = false,
+ .frame_lengths = {gfxMillisecondsToTicks(0)},
+ .frame_functions = {lcd_keyframe_display_layer_bitmap},
+};
+
+static keyframe_animation_t lcd_bitmap_leds_animation = {
+ .num_frames = 2,
+ .loop = true,
+ .frame_lengths = {gfxMillisecondsToTicks(2000), gfxMillisecondsToTicks(2000)},
+ .frame_functions = {lcd_keyframe_display_layer_bitmap, lcd_keyframe_display_led_states},
+};
+
+void initialize_user_visualizer(visualizer_state_t* state) {
+ // The brightness will be dynamically adjustable in the future
+ // But for now, change it here.
+ lcd_backlight_brightness(130);
+ state->current_lcd_color = initial_color;
+ state->target_lcd_color = logo_background_color;
+ lcd_state = LCD_STATE_INITIAL;
+ start_keyframe_animation(&default_startup_animation);
+}
+
+static inline bool is_led_on(visualizer_user_data_t* user_data, uint8_t num) {
+ return user_data->led_on & (1u << num);
+}
+
+static uint8_t get_led_index_master(visualizer_user_data_t* user_data) {
+ for (int i=0; i < 3; i++) {
+ if (is_led_on(user_data, i)) {
+ return i + 1;
+ }
+ }
+ return 0;
+}
+
+static uint8_t get_led_index_slave(visualizer_user_data_t* user_data) {
+ uint8_t master_index = get_led_index_master(user_data);
+ if (master_index!=0) {
+ for (int i=master_index; i < 3; i++) {
+ if (is_led_on(user_data, i)) {
+ return i + 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static uint8_t get_secondary_led_index(visualizer_user_data_t* user_data) {
+ if (is_led_on(user_data, 0) &&
+ is_led_on(user_data, 1) &&
+ is_led_on(user_data, 2)) {
+ return 3;
+ }
+ return 0;
+}
+
+static uint8_t get_brightness(visualizer_user_data_t* user_data, uint8_t index) {
+ switch (index) {
+ case 1:
+ return user_data->led1;
+ case 2:
+ return user_data->led2;
+ case 3:
+ return user_data->led3;
+ }
+ return 0;
+}
+
+static void update_emulated_leds(visualizer_state_t* state, visualizer_keyboard_status_t* prev_status) {
+ visualizer_user_data_t* user_data_new = (visualizer_user_data_t*)state->status.user_data;
+ visualizer_user_data_t* user_data_old = (visualizer_user_data_t*)prev_status->user_data;
+
+ uint8_t new_index;
+ uint8_t old_index;
+
+ if (is_serial_link_master()) {
+ new_index = get_led_index_master(user_data_new);
+ old_index = get_led_index_master(user_data_old);
+ }
+ else {
+ new_index = get_led_index_slave(user_data_new);
+ old_index = get_led_index_slave(user_data_old);
+ }
+ uint8_t new_secondary_index = get_secondary_led_index(user_data_new);
+ uint8_t old_secondary_index = get_secondary_led_index(user_data_old);
+
+ uint8_t old_brightness = get_brightness(user_data_old, old_index);
+ uint8_t new_brightness = get_brightness(user_data_new, new_index);
+
+ uint8_t old_secondary_brightness = get_brightness(user_data_old, old_secondary_index);
+ uint8_t new_secondary_brightness = get_brightness(user_data_new, new_secondary_index);
+
+ if (lcd_state == LCD_STATE_INITIAL ||
+ new_index != old_index ||
+ new_secondary_index != old_secondary_index ||
+ new_brightness != old_brightness ||
+ new_secondary_brightness != old_secondary_brightness) {
+
+ if (new_secondary_index != 0) {
+ state->target_lcd_color = change_lcd_color_intensity(
+ led_emulation_colors[new_index], new_brightness);
+ next_led_target_color = change_lcd_color_intensity(
+ led_emulation_colors[new_secondary_index], new_secondary_brightness);
+
+ stop_keyframe_animation(&one_led_color);
+ start_keyframe_animation(&two_led_colors);
+ } else {
+ state->target_lcd_color = change_lcd_color_intensity(
+ led_emulation_colors[new_index], new_brightness);
+ stop_keyframe_animation(&two_led_colors);
+ start_keyframe_animation(&one_led_color);
+ }
+ }
+}
+
+static void update_lcd_text(visualizer_state_t* state, visualizer_keyboard_status_t* prev_status) {
+ if (state->status.leds) {
+ if (lcd_state != LCD_STATE_BITMAP_AND_LEDS ||
+ state->status.leds != prev_status->leds ||
+ state->status.layer != prev_status->layer ||
+ state->status.default_layer != prev_status->default_layer) {
+
+ // NOTE: that it doesn't matter if the animation isn't playing, stop will do nothing in that case
+ stop_keyframe_animation(&lcd_bitmap_animation);
+
+ lcd_state = LCD_STATE_BITMAP_AND_LEDS;
+ // For information:
+ // The logic in this function makes sure that this doesn't happen, but if you call start on an
+ // animation that is already playing it will be restarted.
+ start_keyframe_animation(&lcd_bitmap_leds_animation);
+ }
+ } else {
+ if (lcd_state != LCD_STATE_LAYER_BITMAP ||
+ state->status.layer != prev_status->layer ||
+ state->status.default_layer != prev_status->default_layer) {
+
+ stop_keyframe_animation(&lcd_bitmap_leds_animation);
+
+ lcd_state = LCD_STATE_LAYER_BITMAP;
+ start_keyframe_animation(&lcd_bitmap_animation);
+ }
+ }
+}
+
+void update_user_visualizer_state(visualizer_state_t* state, visualizer_keyboard_status_t* prev_status) {
+ // Check the status here to start and stop animations
+ // You might have to save some state, like the current animation here so that you can start the right
+ // This function is called every time the status changes
+
+ // NOTE that this is called from the visualizer thread, so don't access anything else outside the status
+ // This is also important because the slave won't have access to the active layer for example outside the
+ // status.
+
+ update_emulated_leds(state, prev_status);
+ update_lcd_text(state, prev_status);
+
+}
+
+void user_visualizer_suspend(visualizer_state_t* state) {
+ state->layer_text = "Suspending...";
+ uint8_t hue = LCD_HUE(state->current_lcd_color);
+ uint8_t sat = LCD_SAT(state->current_lcd_color);
+ state->target_lcd_color = LCD_COLOR(hue, sat, 0);
+ start_keyframe_animation(&default_suspend_animation);
+}
+
+void user_visualizer_resume(visualizer_state_t* state) {
+ state->current_lcd_color = initial_color;
+ state->target_lcd_color = logo_background_color;
+ lcd_state = LCD_STATE_INITIAL;
+ start_keyframe_animation(&default_startup_animation);
+}
+
+void ergodox_board_led_on(void){
+ // No board led support
+}
+
+void ergodox_right_led_1_on(void){
+ user_data_keyboard.led_on |= (1u << 0);
+ visualizer_set_user_data(&user_data_keyboard);
+}
+
+void ergodox_right_led_2_on(void){
+ user_data_keyboard.led_on |= (1u << 1);
+ visualizer_set_user_data(&user_data_keyboard);
+}
+
+void ergodox_right_led_3_on(void){
+ user_data_keyboard.led_on |= (1u << 2);
+ visualizer_set_user_data(&user_data_keyboard);
+}
+
+void ergodox_board_led_off(void){
+ // No board led support
+}
+
+void ergodox_right_led_1_off(void){
+ user_data_keyboard.led_on &= ~(1u << 0);
+ visualizer_set_user_data(&user_data_keyboard);
+}
+
+void ergodox_right_led_2_off(void){
+ user_data_keyboard.led_on &= ~(1u << 1);
+ visualizer_set_user_data(&user_data_keyboard);
+}
+
+void ergodox_right_led_3_off(void){
+ user_data_keyboard.led_on &= ~(1u << 2);
+ visualizer_set_user_data(&user_data_keyboard);
+}
+
+void ergodox_right_led_1_set(uint8_t n) {
+ user_data_keyboard.led1 = n;
+ visualizer_set_user_data(&user_data_keyboard);
+}
+
+void ergodox_right_led_2_set(uint8_t n) {
+ user_data_keyboard.led2 = n;
+ visualizer_set_user_data(&user_data_keyboard);
+}
+
+void ergodox_right_led_3_set(uint8_t n) {
+ user_data_keyboard.led3 = n;
+ visualizer_set_user_data(&user_data_keyboard);
+}