~ruther/qmk_firmware

42eeb315a5424fc576239b7e57061affc2ffa8ab — Michael Schwingen 5 years ago ac3dfa7
[Keyboard] add support for ModelM USB board (#9846)

* add support for ModelM USB board

* EMI improvement: remove unnecessary toggling of MOSI pin

* address review comments

* Update keyboards/mschwingen/modelm/rules.mk

Co-authored-by: James Young <18669334+noroadsleft@users.noreply.github.com>

* Update keyboards/mschwingen/modelm/rules.mk

Co-authored-by: James Young <18669334+noroadsleft@users.noreply.github.com>

* Update keyboards/mschwingen/modelm/config.h

Co-authored-by: James Young <18669334+noroadsleft@users.noreply.github.com>

* Update keyboards/mschwingen/modelm/config.h

Co-authored-by: James Young <18669334+noroadsleft@users.noreply.github.com>

* Update keyboards/mschwingen/modelm/rules.mk

Co-authored-by: Drashna Jaelre <drashna@live.com>

* Update keyboards/mschwingen/modelm/keymaps/default/keymap.c

Co-authored-by: Drashna Jaelre <drashna@live.com>

* update printf usage

* add comment

* EMI improvement: remove unnecessary toggling of MOSI signal

* remove trailing space

* use shorter macros as suggested in review by noroadsleft, re-format table to line up columns

* Update keyboards/mschwingen/modelm/config.h

Co-authored-by: Ryan <fauxpark@gmail.com>

* Update keyboards/mschwingen/modelm/rules.mk

Co-authored-by: Ryan <fauxpark@gmail.com>

* Update keyboards/mschwingen/modelm/rules.mk

Co-authored-by: Ryan <fauxpark@gmail.com>

* Update keyboards/mschwingen/modelm/rules.mk

Co-authored-by: Ryan <fauxpark@gmail.com>

* Update keyboards/mschwingen/modelm/README.md

Co-authored-by: Ryan <fauxpark@gmail.com>

* Update keyboards/mschwingen/modelm/README.md

Co-authored-by: Ryan <fauxpark@gmail.com>

* Apply suggestions from code review

use spi_read from core insteads of our own copy

Co-authored-by: Ryan <fauxpark@gmail.com>

* include spi_master.c to use spi_read()

* Update keyboards/mschwingen/modelm/README.md

Co-authored-by: James Young <18669334+noroadsleft@users.noreply.github.com>

* Apply suggestions from code review: correct indenting in keymap

Co-authored-by: James Young <18669334+noroadsleft@users.noreply.github.com>

* Apply suggestions from code review

use automatic variant defines from makefile instead of defining our own

Co-authored-by: Drashna Jaelre <drashna@live.com>

* Update keyboards/mschwingen/modelm/rules.mk: use QUANTUM_LIB_SRC for uart.c

Co-authored-by: Drashna Jaelre <drashna@live.com>

Co-authored-by: Michael Schwingen <michael@schwingen.org>
Co-authored-by: James Young <18669334+noroadsleft@users.noreply.github.com>
Co-authored-by: Drashna Jaelre <drashna@live.com>
Co-authored-by: Ryan <fauxpark@gmail.com>
A keyboards/mschwingen/modelm/README.md => keyboards/mschwingen/modelm/README.md +25 -0
@@ 0,0 1,25 @@
# atmega32U4 board for IBM Model M

![modelm](https://raw.githubusercontent.com/mschwingen/hardware/master/modelm-usb/images/PCB.jpg)

This is a configuration of QMK intended to be used with the [Model M USB PCB](https://github.com/mschwingen/hardware/tree/master/modelm-usb).

* Keyboard Maintainer: [Michael Schwingen](https://github.com/mschwingen/)
* Hardware Supported: [Model M USB PCB](https://github.com/mschwingen/hardware/tree/master/modelm-usb)
* Hardware Availability: need to build your own.

Make example for this keyboard (after setting up your build environment), run one of:

    make mschwingen/modelm/led_wired:default
    make mschwingen/modelm/led_ffc:default
    make mschwingen/modelm/led_ws2812:default

flash:

    make mschwingen/modelm/led_wired:default:flash
    make mschwingen/modelm/led_ffc:default:flash
    make mschwingen/modelm/led_ws2812:default:flash

Bootloader: do not use the QMK bootloader, use the bootloader from [here](https://github.com/mschwingen/modelm-lufa-bootloader)

See the [build environment setup](https://docs.qmk.fm/#/getting_started_build_tools) and the [make instructions](https://docs.qmk.fm/#/getting_started_make_guide) for more information. Brand new to QMK? Start with our [Complete Newbs Guide](https://docs.qmk.fm/#/newbs).

A keyboards/mschwingen/modelm/config.h => keyboards/mschwingen/modelm/config.h +91 -0
@@ 0,0 1,91 @@
/*
 * Copyright 2020 Michael Schwingen

 * 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/>.
 */

#pragma once

#include "config_common.h"

/* USB Device descriptor parameter */
#define VENDOR_ID       0xFEED
#define PRODUCT_ID      0x558E
#define DEVICE_VER      0x0001
#define MANUFACTURER    mschwingen
#define PRODUCT         IBM Model M 101/102

/* key matrix size */
#define MATRIX_ROWS 16
#define MATRIX_COLS 8

/* pins for external shift registers */
#define SR_LOAD_PIN B0
#define SR_CLK_PIN B1
#define SR_DIN_PIN B3
#define SR_DOUT_PIN B2

/* Debounce reduces chatter (unintended double-presses) - set 0 if debouncing is not needed (5 is default) */
#define DEBOUNCE 5

/*
 * Feature disable options
 *  These options are also useful to firmware size reduction.
 */

/* disable debug print */
//#define NO_DEBUG

/* disable print */
//#define NO_PRINT
#define NORMAL_PRINT
//#define USER_PRINT


/* disable action features */
//#define NO_ACTION_LAYER
//#define NO_ACTION_TAPPING
//#define NO_ACTION_ONESHOT
//#define NO_ACTION_MACRO
//#define NO_ACTION_FUNCTION

//#define DEBUG_MATRIX_SCAN_RATE
#define DYNAMIC_MACRO_NO_NESTING

#define QMK_LED E6

#define MODELM_LED1 B5
#define MODELM_LED2 B6
#define MODELM_LED3 D0

#if defined(KEYBOARD_mschwingen_modelm_led_wired)
# define MODELM_LED_CAPSLOCK  MODELM_LED1
# define MODELM_LED_SCROLLOCK MODELM_LED2
# define MODELM_LED_NUMLOCK   MODELM_LED3
#elif defined(KEYBOARD_mschwingen_modelm_led_ffc)
# define MODELM_LED_CAPSLOCK  MODELM_LED2
# define MODELM_LED_SCROLLOCK MODELM_LED3
# define MODELM_LED_NUMLOCK   MODELM_LED1
#elif defined(KEYBOARD_mschwingen_modelm_led_ws2812)
#else
# error one of MODELM_LEDS_FFC, MODELM_LEDS_WIRED or MODELM_LEDS_WS2812 must be set!
#endif

// 3* WS2812 LEDs instead of singlecolor GPIO LEDs
#define RGB_DI_PIN B6
#define RGBLED_NUM 3

// disabled, needs PCB patch.
//#define C6_AUDIO
//#define NO_MUSIC_MODE

A keyboards/mschwingen/modelm/keymaps/default/keymap.c => keyboards/mschwingen/modelm/keymaps/default/keymap.c +86 -0
@@ 0,0 1,86 @@
/* Copyright 2019 ashpil
 *
 * 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 QMK_KEYBOARD_H

enum layers {
    _BL0,
    _BL1,
    _FL,
    _MS
};

const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
  [_BL0] = LAYOUT( /* Base layer - Windows key instead of CapsLock, hold ESC for special functions */
    LT(_FL,KC_ESC),   KC_F1  , KC_F2  , KC_F3  , KC_F4  , KC_F5  , KC_F6  , KC_F7  , KC_F8  , KC_F9  , KC_F10,  KC_F11 , KC_F12 , KC_PSCR, KC_SLCK, KC_PAUS,
    KC_GRV , KC_1,    KC_2   , KC_3   , KC_4   , KC_5   , KC_6   , KC_7   , KC_8   , KC_9   , KC_0   , KC_MINS, KC_EQL , KC_BSPC, KC_INS , KC_HOME, KC_PGUP, KC_NLCK, KC_PSLS, KC_PAST, KC_PMNS,
    KC_TAB , KC_Q,    KC_W   , KC_E   , KC_R   , KC_T   , KC_Y   , KC_U   , KC_I   , KC_O   , KC_P   , KC_LBRC, KC_RBRC, KC_BSLS, KC_DEL , KC_END , KC_PGDN, KC_P7,   KC_P8  , KC_P9  , KC_PPLS,
    KC_LWIN, KC_A,    KC_S   , KC_D   , KC_F   , KC_G   , KC_H   , KC_J   , KC_K   , KC_L   , KC_SCLN, KC_QUOT, KC_NUHS, KC_ENT ,                            KC_P4,   KC_P5  , KC_P6  ,
    KC_LSFT, KC_NUBS, KC_Z   , KC_X   , KC_C   , KC_V   , KC_B   , KC_N   , KC_M   , KC_COMM, KC_DOT , KC_SLSH,          KC_RSFT,          KC_UP  ,          KC_P1,   KC_P2  , KC_P3  , KC_PENT,
    KC_LCTL,          KC_LALT,                            KC_SPC ,                                     KC_RALT,          KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT, KC_P0,            KC_PDOT
  ),

  [_BL1] = LAYOUT( /* Base layer - standard layout without any special functions */
    KC_ESC ,          KC_F1  , KC_F2  , KC_F3  , KC_F4  , KC_F5  , KC_F6  , KC_F7  , KC_F8  , KC_F9  , KC_F10 , KC_F11 , KC_F12 , KC_PSCR, KC_SLCK, KC_PAUS,
    KC_GRV , KC_1   , KC_2   , KC_3   , KC_4   , KC_5   , KC_6   , KC_7   , KC_8   , KC_9   , KC_0   , KC_MINS, KC_EQL , KC_BSPC, KC_INS , KC_HOME, KC_PGUP, KC_NLCK, KC_PSLS, KC_PAST, KC_PMNS,
    KC_TAB , KC_Q   , KC_W   , KC_E   , KC_R   , KC_T   , KC_Y   , KC_U   , KC_I   , KC_O   , KC_P   , KC_LBRC, KC_RBRC, KC_BSLS, KC_DEL , KC_END , KC_PGDN, KC_P7,   KC_P8  , KC_P9  , KC_PPLS,
    KC_CAPS, KC_A   , KC_S   , KC_D   , KC_F   , KC_G   , KC_H   , KC_J   , KC_K   , KC_L   , KC_SCLN, KC_QUOT, KC_NUHS, KC_ENT ,                            KC_P4,   KC_P5  , KC_P6  ,
    KC_LSFT, KC_NUBS, KC_Z   , KC_X   , KC_C   , KC_V   , KC_B   , KC_N   , KC_M   , KC_COMM, KC_DOT , KC_SLSH,          KC_RSFT,          KC_UP  ,          KC_P1,   KC_P2  , KC_P3  , KC_PENT,
    KC_LCTL,          KC_LALT,                            KC_SPC ,                                     KC_RALT,          KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT, KC_P0,            KC_PDOT
  ),

  [_FL] = LAYOUT( /* special functions layer */
    // F1: dyn. macro 1 play
    // F2: dyn. macro 2 play
    // F3: dyn. macro 1 record
    // F4: dyn. macro 2 record
    // F5: dyn. macro record stop
    // Capslock: CapsLock (really!)
    // ~: Key Lock
    // Cursor: Media Pref / Next / Volume Up / Volume Down
    // Space:  Media Play / Pause
    // m: enter mouse layer
    _______,          DM_PLY1, DM_PLY2, DM_REC1, DM_REC2, DM_RSTP, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
    KC_LOCK, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
    _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
    KC_CAPS, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,                            _______, _______, _______,
    _______, _______, _______, _______, _______, _______, _______, _______, TG(_MS), _______, _______, _______,          _______,          KC_VOLU,          _______, _______, _______, _______,
    _______,          _______,                            KC_MPLY,                                     _______,          _______, KC_MPRV, KC_VOLD, KC_MNXT, _______,          _______
  ),

  [_MS] = LAYOUT( /* mouse key layer */
  // Cursor: mouse, INS/HOME/PgUp: Mouse Accel, Del, End, PageDn: mouse buttons
    TG(_MS),          _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
    _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, KC_ACL0, KC_ACL1, KC_ACL2, _______, _______, _______, _______,
    _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, KC_BTN1, KC_BTN3, KC_BTN2, _______, _______, _______, _______,
    _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,                            _______, _______, _______,
    _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,          _______,          KC_MS_U,          _______, _______, _______, _______,
    _______,          _______,                            _______,                                     _______,          _______, KC_MS_L, KC_MS_D, KC_MS_R, _______,          _______
  )
};

void keyboard_post_init_user(void) {
  // Customise these values to desired behaviour
  //debug_enable=true;
  //debug_matrix=true;
  //debug_keyboard=true;
  //debug_mouse=true;
}

bool process_record_user(uint16_t keycode, keyrecord_t *record) {
  // If console is enabled, it will print the matrix position and status of each key pressed
    dprintf("KL: kc: %u, col: %u, row: %u, pressed: %u\n", keycode, record->event.key.col, record->event.key.row, record->event.pressed);
    return true;
}

A keyboards/mschwingen/modelm/led_ffc/rules.mk => keyboards/mschwingen/modelm/led_ffc/rules.mk +1 -0
@@ 0,0 1,1 @@


A keyboards/mschwingen/modelm/led_wired/rules.mk => keyboards/mschwingen/modelm/led_wired/rules.mk +1 -0
@@ 0,0 1,1 @@


A keyboards/mschwingen/modelm/led_ws2812/rules.mk => keyboards/mschwingen/modelm/led_ws2812/rules.mk +2 -0
@@ 0,0 1,2 @@
# variant for WS2812 LEDs
SRC += ws2812.c

A keyboards/mschwingen/modelm/matrix.c => keyboards/mschwingen/modelm/matrix.c +117 -0
@@ 0,0 1,117 @@
/*
 * Copyright 2020 Michael Schwingen

 * 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 "util.h"
#include "matrix.h"
#include "debounce.h"
#include "quantum.h"
#include "spi_master.h"
#include "print.h"
#include "modelm.h"

#define DEBUG 0

#define SPI_TIMEOUT 100

/* Keyboard Matrix Assignments */
static uint16_t row_bits[MATRIX_ROWS] = {
    0x4000, 0x8000, 0x2000, 0x1000, 0x0800, 0x0400, 0x0100, 0x0200,
    0x0040, 0x0080, 0x0020, 0x0010, 0x0008, 0x0004, 0x0001, 0x0002};

static const pin_t col_pins[MATRIX_COLS] = {D1, D4, D7, B4, F7, F6, F5, F4};

static void select_col(uint8_t col) {
    setPinOutput(col_pins[col]);
    writePinLow(col_pins[col]);
}

static void unselect_col(uint8_t col) { setPinInputHigh(col_pins[col]); }

static void unselect_cols(void) {
    for (uint8_t x = 0; x < MATRIX_COLS; x++) {
        setPinInputHigh(col_pins[x]);
    }
}

static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) {
    uint16_t row_data;
    bool     matrix_changed = false;

    // Select col and wait for col selecton to stabilize
    select_col(current_col);
    matrix_io_delay();

    writePinLow(SR_LOAD_PIN);
    writePinHigh(SR_LOAD_PIN);

    row_data = spi_read() << 8;
    row_data |= spi_read();

#if DEBUG
    phex(~row_data);
    uprint(" ");
#endif
    // For each row...
    for (uint8_t row_index = 0; row_index < MATRIX_ROWS; row_index++) {
        // Store last value of row prior to reading
        matrix_row_t last_row_value    = current_matrix[row_index];
        matrix_row_t current_row_value = last_row_value;

        // Check row pin state
        if ((row_data & row_bits[row_index]) == 0) {
            // Pin LO, set col bit
            current_row_value |= (MATRIX_ROW_SHIFTER << current_col);
        } else {
            // Pin HI, clear col bit
            current_row_value &= ~(MATRIX_ROW_SHIFTER << current_col);
        }

        // Determine if the matrix changed state
        if ((last_row_value != current_row_value)) {
            matrix_changed            = true;
            current_matrix[row_index] = current_row_value;
        }
    }

    // Unselect col
    unselect_col(current_col);

    return matrix_changed;
}

void matrix_init_custom(void) {
    unselect_cols();
    
    // set 4MHz SPI clock
    SPSR = 0;
    SPCR = _BV(SPE) | _BV(MSTR) | _BV(CPOL);
}

bool matrix_scan_custom(matrix_row_t current_matrix[]) {
    bool changed = false;

#if DEBUG
    uprint("\r\nScan: ");
#endif
    // Set col, read rows
    for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++) {
        changed |= read_rows_on_col(current_matrix, current_col);
    }
    update_layer_leds();
    return changed;
}

A keyboards/mschwingen/modelm/modelm.c => keyboards/mschwingen/modelm/modelm.c +214 -0
@@ 0,0 1,214 @@
/*
 * Copyright 2020 Michael Schwingen

 * 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 <util/delay.h>
#include "modelm.h"
#include "uart.h"
#include "print.h"
#include "sendchar.h"
#include "ws2812.h"
#include "modelm.h"
#include "sleep_led.h"

#ifdef UART_DEBUG
#    undef sendchar
static int8_t capture_sendchar(uint8_t c) {
    //  sendchar(c);
    uart_putchar(c);
    return 0;
}
#endif

static uint16_t blink_cycle_timer;
static bool     blink_state = false;
static uint8_t  isRecording = 0;

#ifdef KEYBOARD_mschwingen_modelm_led_ws2812
#    if RGBLED_NUM < 3
#        error we need at least 3 RGB LEDs!
#    endif
static cRGB led[RGBLED_NUM] = {{255, 255, 255}, {255, 255, 255}, {255, 255, 255}};

#    define BRIGHT 32
#    define DIM 6

static const cRGB black = {.r = 0, .g = 0, .b = 0};

static const cRGB green  = {.r = 0, .g = BRIGHT, .b = 0};
static const cRGB lgreen = {.r = 0, .g = DIM, .b = 0};

static const cRGB red  = {.r = BRIGHT, .g = 0, .b = 0};
static const cRGB lred = {.r = DIM, .g = 0, .b = 0};

static const cRGB blue  = {.r = 0, .g = 0, .b = BRIGHT};
static const cRGB lblue = {.r = 0, .g = 0, .b = DIM};

static const cRGB turq  = {.r = 0, .g = BRIGHT, .b = BRIGHT};
static const cRGB lturq = {.r = 0, .g = DIM, .b = DIM};

static const cRGB white = {.r = BRIGHT, .g = BRIGHT, .b = BRIGHT};

static led_t   led_state;
static uint8_t layer;
static uint8_t default_layer;
#endif

// we need our own sleep_led_* implementation to get callbacks on USB
// suspend/resume in order to completely turn off WS2812 LEDs
static bool suspend_active = false;
void sleep_led_init(void) {}

void sleep_led_toggle(void) {}

void sleep_led_disable(void) {
    suspend_active = false;
    writePinHigh(QMK_LED);
}

void sleep_led_enable(void) {
    suspend_active = true;
    writePinLow(QMK_LED);
#ifdef KEYBOARD_mschwingen_modelm_led_ws2812
    led[0] = black;
    led[1] = black;
    led[2] = black;
    ws2812_setleds(led, RGBLED_NUM);
#endif
}

void keyboard_pre_init_kb(void) {
#ifdef KEYBOARD_mschwingen_modelm_led_ws2812
    ws2812_setleds(led, RGBLED_NUM);
#else
    /* Set status LEDs pins to output and Low (on) */
    setPinOutput(MODELM_LED_CAPSLOCK);
    setPinOutput(MODELM_LED_SCROLLOCK);
    setPinOutput(MODELM_LED_NUMLOCK);
    writePinLow(MODELM_LED_CAPSLOCK);
    writePinLow(MODELM_LED_SCROLLOCK);
    writePinLow(MODELM_LED_NUMLOCK);
#endif
    setPinOutput(QMK_LED);
    writePinHigh(QMK_LED);
    _delay_ms(50);
#ifdef UART_DEBUG
    uart_init(115200);
    print_set_sendchar(capture_sendchar);
    uprintf("\r\nHello world!\r\n");
#endif

    setPinOutput(SR_LOAD_PIN);
    setPinOutput(SR_CLK_PIN);
    setPinOutput(SR_DOUT_PIN);  // MOSI - unused
    writePinLow(SR_CLK_PIN);
}

#ifdef KEYBOARD_mschwingen_modelm_led_ws2812
static void led_update_rgb(void) {
    if (isRecording && blink_state) {
        led[0] = white;
    } else {
        switch (default_layer) {
            case 0:
                led[0] = led_state.num_lock ? blue : lblue;
                break;
            case 1:
                led[0] = led_state.num_lock ? green : black;
                break;
        }
    }

    led[1] = led_state.caps_lock ? green : black;

    switch (layer) {
        case 0:
        case 1:
        default:
            led[2] = led_state.scroll_lock ? green : black;
            break;
        case 2:
            led[2] = led_state.scroll_lock ? red : lred;
            break;
        case 3:
            led[2] = led_state.scroll_lock ? turq : lturq;
            break;
    }
    if (!suspend_active) {
	ws2812_setleds(led, RGBLED_NUM);
    }
}

bool led_update_kb(led_t state) {
    dprintf("LED Update: %d %d %d", led_state.num_lock, led_state.caps_lock, led_state.scroll_lock);
    led_state = state;
    led_update_rgb();

    return true;
}

void update_layer_leds(void) {
    static uint8_t old_layer         = 255;
    static uint8_t old_default_layer = 255;

    layer         = biton32(layer_state);
    default_layer = biton32(default_layer_state);

    if (isRecording && timer_elapsed(blink_cycle_timer) > 150) {
        blink_state       = !blink_state;
        blink_cycle_timer = timer_read();
        old_layer         = 255;  // fallthrough next check
    }

    if (layer == old_layer && default_layer == old_default_layer) {
	return;
    }
    old_layer         = layer;
    old_default_layer = default_layer;
    dprintf("Layer change: %d %d", default_layer, layer);
    led_update_rgb();
}

/*****************************************************************************/
#else  // classic LEDs on GPIO
bool led_update_kb(led_t led_state) {
    dprintf("LED Update: %d %d %d", led_state.num_lock, led_state.caps_lock, led_state.scroll_lock);

    if (led_update_user(led_state)) {
        if (!isRecording) writePin(MODELM_LED_NUMLOCK, !led_state.num_lock);
        writePin(MODELM_LED_CAPSLOCK, !led_state.caps_lock);
        writePin(MODELM_LED_SCROLLOCK, !led_state.scroll_lock);
    }
    return true;
}

void update_layer_leds(void) {
    if (isRecording && timer_elapsed(blink_cycle_timer) > 150) {
        blink_state = !blink_state;
        blink_cycle_timer = timer_read();
        writePin(MODELM_LED_NUMLOCK, blink_state);
    }
}

#endif

void dynamic_macro_record_start_user(void) {
    isRecording++;
    blink_cycle_timer = timer_read();
}

void dynamic_macro_record_end_user(int8_t direction) {
    if (isRecording) isRecording--;
}

A keyboards/mschwingen/modelm/modelm.h => keyboards/mschwingen/modelm/modelm.h +58 -0
@@ 0,0 1,58 @@
/*
 * Copyright 2020 Michael Schwingen

 * 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/>.
 */

#pragma once

extern void update_layer_leds(void);

#include "quantum.h"

/* This a shortcut to help you visually see your layout.
 * The first section contains "names" for physical keys of the keyboard
 * and defines their position on the board.
 * The second section defines position of the keys on the switch matrix
 * (where COLUMNS and ROWS crosses). */

/* universla layout for both 101 and 102-key versions */
#define LAYOUT( \
    K5A,      K5B, K5C, K5D, K5E, K5F, K5G, K5H, K5I, K5J, K5K, K5L, K5M,   K5N, K5O, K5P, \
    \
    K4A, K4B, K4C, K4D, K4E, K4F, K4G, K4H, K4I, K4J, K4K, K4L, K4M, K4N,   K4O, K4P, K4Q,   K4R, K4S, K4T, K4U, \
    K3A, K3B, K3C, K3D, K3E, K3F, K3G, K3H, K3I, K3J, K3K, K3L, K3M, K3N,   K3O, K3P, K3Q,   K3R, K3S, K3T, K3U, \
    K2A, K2B, K2C, K2D, K2E, K2F, K2G, K2H, K2I, K2J, K2K, K2L, K2M, K2N,                    K2O, K2P, K2Q, \
    K1A, K1B, K1C, K1D, K1E, K1F, K1G, K1H, K1I, K1J, K1K, K1L,      K1M,        K1N,        K1O, K1P, K1Q, K1R, \
    K0A,      K0B,                K0C,                     K0D,      K0E,   K0F, K0G, K0H,   K0I,      K0J       \
) \
    {/* COL0    ............                           COL7 */		\
    { K0D  , KC_NO, KC_NO, K5O  , K5N  , KC_NO, KC_NO, K0B   }, /* ROW0 */ \
    { K0F  , K5P  , K1R  , K3U  , K3P  , K4P  , KC_NO, K1N   }, \
    { K4U  , K4T  , K1Q  , K3T  , K3Q  , K4Q  , K2Q  , K0J   }, \
    { K0H  , K4S  , K1P  , K3S  , K5M  , K4O  , K2P  , K0I   }, \
    { K0G  , K4R  , K1O  , K3R  , K5L  , K3O  , K2O  , KC_NO }, \
    { K1L  , K2M  , K2K  , K3K  , K4K  , K4L  , K3L  , K2L   }, \
    { KC_NO, K1K  , K2J  , K3J  , K4J  , K5I  , K5H  , KC_NO }, \
    { KC_NO, K1J  , K2I  , K3I  , K4I  , K4M  , K3M  , K5G   }, \
    { K1H  , K1I  , K2H  , K3H  , K4H  , K4G  , K3G  , K2G   }, \
    { K0C  , K2N  , K3N  , KC_NO, K5K  , K5J  , K4N  , K5F   }, \
    { K1G  , K1F  , K2E  , K3E  , K4E  , K4F  , K3F  , K2F   }, \
    { KC_NO, K1E  , K2D  , K3D  , K4D  , K5C  , K5D  , K5E   }, \
    { KC_NO, K1D  , K2C  , K3C  , K4C  , K5B  , K2A  , K1B   }, \
    { KC_NO, K1C  , K2B  , K3B  , K4B  , K4A  , K3A  , K5A   }, \
    { KC_NO, K1M  , KC_NO, KC_NO, KC_NO, KC_NO, K1A  , KC_NO }, \
    { KC_NO, K0E  , KC_NO, KC_NO, KC_NO, K0A  , KC_NO, KC_NO }, /* ROW15 */ \
}


A keyboards/mschwingen/modelm/rules.mk => keyboards/mschwingen/modelm/rules.mk +43 -0
@@ 0,0 1,43 @@
# MCU name
MCU = atmega32u4

# Bootloader selection
BOOTLOADER = lufa-dfu

# Build Options
#   change yes to no to disable
#
BOOTMAGIC_ENABLE = no       # Virtual DIP switch configuration
MOUSEKEY_ENABLE = yes       # Mouse keys
EXTRAKEY_ENABLE = yes       # Audio control and System control
CONSOLE_ENABLE = yes        # Console for debug
COMMAND_ENABLE = yes        # Commands for debug and configuration
# Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE
SLEEP_LED_ENABLE = no       # Breathing sleep LED during USB suspend
# if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work
NKRO_ENABLE = no            # USB Nkey Rollover
BACKLIGHT_ENABLE = no       # Enable keyboard backlight functionality
RGBLIGHT_ENABLE = no        # Enable keyboard RGB underglow
BLUETOOTH_ENABLE = no       # Enable Bluetooth
AUDIO_ENABLE = no           # Audio output

CUSTOM_MATRIX = lite
KEY_LOCK_ENABLE = yes

DYNAMIC_MACRO_ENABLE = yes

UART_DEBUG = no

SRC += matrix.c
QUANTUM_LIB_SRC += $(COMMON_DIR)/uart.c \
                   spi_master.c

OPT_DEFS += -DSLEEP_LED_ENABLE # we need our own sleep callbacks to turn of WS2812 LEDs

LTO_ENABLE = yes

ifeq ($(strip $(UART_DEBUG)), yes)
    OPT_DEFS += -DUART_DEBUG
endif

DEFAULT_FOLDER = mschwingen/modelm/led_wired