~ruther/qmk_firmware

b368db9e027015ba393e87cee416d8d5b2b56a8c — Joel Challis 5 years ago 76189d9
Refactor fortitude60 to use split_common (#8113)

15 files changed, 38 insertions(+), 1290 deletions(-)

M keyboards/fortitude60/config.h
M keyboards/fortitude60/fortitude60.h
M keyboards/fortitude60/keymaps/default/keymap.c
D keyboards/fortitude60/matrix.c
M keyboards/fortitude60/readme.md
M keyboards/fortitude60/rev1/config.h
M keyboards/fortitude60/rev1/rev1.c
M keyboards/fortitude60/rev1/rev1.h
M keyboards/fortitude60/rev1/rules.mk
M keyboards/fortitude60/rules.mk
D keyboards/fortitude60/serial.c
D keyboards/fortitude60/serial.h
D keyboards/fortitude60/serial_config.h
D keyboards/fortitude60/split_util.c
D keyboards/fortitude60/split_util.h
M keyboards/fortitude60/config.h => keyboards/fortitude60/config.h +1 -7
@@ 15,10 15,4 @@ 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 CONFIG_H
#define CONFIG_H

#include "config_common.h"
#include <serial_config.h>

#endif  // CONFIG_H
#pragma once

M keyboards/fortitude60/fortitude60.h => keyboards/fortitude60/fortitude60.h +1 -4
@@ 1,5 1,4 @@
#ifndef FORTITUDE60_H
#define FORTITUDE60_H
#pragma once

#ifdef KEYBOARD_fortitude60_rev1
    #include "rev1.h"


@@ 22,5 21,3 @@
        KC_##L30, KC_##L31, KC_##L32, KC_##L33, KC_##L34, KC_##L35, KC_##LT5, KC_##RT5, KC_##R30, KC_##R31, KC_##R32, KC_##R33, KC_##R34, KC_##R35, \
                            KC_##LT0, KC_##LT1, KC_##LT2, KC_##LT3, KC_##LT4, KC_##RT4, KC_##RT3, KC_##RT2, KC_##RT1, KC_##RT0 \
    )

#endif

M keyboards/fortitude60/keymaps/default/keymap.c => keyboards/fortitude60/keymaps/default/keymap.c +8 -9
@@ 5,12 5,14 @@
// The underscores don't mean anything - you can have a layer called STUFF or any other name.
// Layer names don't all need to be of the same length, obviously, and you can also skip them
// entirely and just use numbers.
#define _QWERTY 0
#define _COLEMAK 1
#define _DVORAK 2
#define _LOWER 3
#define _RAISE 4
#define _ADJUST 16
enum my_layers {
  _QWERTY,
  _COLEMAK,
  _DVORAK,
  _LOWER,
  _RAISE,
  _ADJUST
};

enum custom_keycodes {
  QWERTY = SAFE_RANGE,


@@ 166,19 168,16 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
        set_single_persistent_default_layer(_QWERTY);
      }
      return false;
      break;
    case COLEMAK:
      if (record->event.pressed) {
        set_single_persistent_default_layer(_COLEMAK);
      }
      return false;
      break;
    case DVORAK:
      if (record->event.pressed) {
        set_single_persistent_default_layer(_DVORAK);
      }
      return false;
      break;
  }
  return true;
}

D keyboards/fortitude60/matrix.c => keyboards/fortitude60/matrix.c +0 -423
@@ 1,423 0,0 @@
/*
Copyright 2017 Danny Nguyen <danny@keeb.io>

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

/*
 * scan matrix
 */
#include <stdint.h>
#include <stdbool.h>
#include <avr/io.h>
#include "wait.h"
#include "print.h"
#include "debug.h"
#include "util.h"
#include "matrix.h"
#include "split_util.h"
#include "pro_micro.h"
#include "config.h"
#include "timer.h"

#ifdef BACKLIGHT_ENABLE
#include "backlight.h"
extern backlight_config_t backlight_config;
#endif

#include "serial.h"

#ifndef DEBOUNCE
#   define DEBOUNCE 5
#endif

#if (DEBOUNCE > 0)
    static uint16_t debouncing_time;
    static bool debouncing = false;
#endif

#if (MATRIX_COLS <= 8)
#    define print_matrix_header()  print("\nr/c 01234567\n")
#    define print_matrix_row(row)  print_bin_reverse8(matrix_get_row(row))
#    define matrix_bitpop(i)       bitpop(matrix[i])
#    define ROW_SHIFTER ((uint8_t)1)
#else
#    error "Currently only supports 8 COLS"
#endif
static matrix_row_t matrix_debouncing[MATRIX_ROWS];

#define ERROR_DISCONNECT_COUNT 5

#define SERIAL_LED_ADDR 0x00

#define ROWS_PER_HAND (MATRIX_ROWS/2)

static uint8_t error_count = 0;

static const uint8_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS;
static const uint8_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS;

/* matrix state(1:on, 0:off) */
static matrix_row_t matrix[MATRIX_ROWS];
static matrix_row_t matrix_debouncing[MATRIX_ROWS];

#if (DIODE_DIRECTION == COL2ROW)
    static void init_cols(void);
    static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row);
    static void unselect_rows(void);
    static void select_row(uint8_t row);
    static void unselect_row(uint8_t row);
#elif (DIODE_DIRECTION == ROW2COL)
    static void init_rows(void);
    static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col);
    static void unselect_cols(void);
    static void unselect_col(uint8_t col);
    static void select_col(uint8_t col);
#endif


__attribute__ ((weak))
void matrix_init_kb(void) {
    matrix_init_user();
}

__attribute__ ((weak))
void matrix_scan_kb(void) {
    matrix_scan_user();
}

__attribute__ ((weak))
void matrix_init_user(void) {
}

__attribute__ ((weak))
void matrix_scan_user(void) {
}

inline
uint8_t matrix_rows(void)
{
    return MATRIX_ROWS;
}

inline
uint8_t matrix_cols(void)
{
    return MATRIX_COLS;
}

void matrix_init(void)
{
    debug_enable = true;
    debug_matrix = true;
    debug_mouse = true;
    // initialize row and col
    unselect_rows();
    init_cols();

    TX_RX_LED_INIT;

    // initialize matrix state: all keys off
    for (uint8_t i=0; i < MATRIX_ROWS; i++) {
        matrix[i] = 0;
        matrix_debouncing[i] = 0;
    }

    matrix_init_quantum();

}

uint8_t _matrix_scan(void)
{
    int offset = isLeftHand ? 0 : (ROWS_PER_HAND);
#if (DIODE_DIRECTION == COL2ROW)
    // Set row, read cols
    for (uint8_t current_row = 0; current_row < ROWS_PER_HAND; current_row++) {
#       if (DEBOUNCE > 0)
            bool matrix_changed = read_cols_on_row(matrix_debouncing+offset, current_row);

            if (matrix_changed) {
                debouncing = true;
                debouncing_time = timer_read();
            }

#       else
            read_cols_on_row(matrix+offset, current_row);
#       endif

    }

#elif (DIODE_DIRECTION == ROW2COL)
    // Set col, read rows
    for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++) {
#       if (DEBOUNCE > 0)
            bool matrix_changed = read_rows_on_col(matrix_debouncing+offset, current_col);
            if (matrix_changed) {
                debouncing = true;
                debouncing_time = timer_read();
            }
#       else
             read_rows_on_col(matrix+offset, current_col);
#       endif

    }
#endif

#   if (DEBOUNCE > 0)
        if (debouncing && (timer_elapsed(debouncing_time) > DEBOUNCE)) {
            for (uint8_t i = 0; i < ROWS_PER_HAND; i++) {
                matrix[i+offset] = matrix_debouncing[i+offset];
            }
            debouncing = false;
        }
#   endif

    return 1;
}

int serial_transaction(void) {
    int slaveOffset = (isLeftHand) ? (ROWS_PER_HAND) : 0;

    if (serial_update_buffers()) {
        return 1;
    }

    for (int i = 0; i < ROWS_PER_HAND; ++i) {
        matrix[slaveOffset+i] = serial_slave_buffer[i];
    }

#ifdef BACKLIGHT_ENABLE
    // Write backlight level for slave to read
    serial_master_buffer[SERIAL_LED_ADDR] = backlight_config.enable ? backlight_config.level : 0;
#endif
    return 0;
}


uint8_t matrix_scan(void)
{
    uint8_t ret = _matrix_scan();

    if( serial_transaction() ) {
        // turn on the indicator led when halves are disconnected
        TXLED1;

        error_count++;

        if (error_count > ERROR_DISCONNECT_COUNT) {
            // reset other half if disconnected
            int slaveOffset = (isLeftHand) ? (ROWS_PER_HAND) : 0;
            for (int i = 0; i < ROWS_PER_HAND; ++i) {
                matrix[slaveOffset+i] = 0;
            }
        }
    } else {
        // turn off the indicator led on no error
        TXLED0;
        error_count = 0;
    }
    matrix_scan_quantum();
    return ret;
}

void matrix_slave_scan(void) {
    _matrix_scan();

    int offset = (isLeftHand) ? 0 : ROWS_PER_HAND;

    for (int i = 0; i < ROWS_PER_HAND; ++i) {
        serial_slave_buffer[i] = matrix[offset+i];
    }

#ifdef BACKLIGHT_ENABLE
    // Read backlight level sent from master and update level on slave
    backlight_set(serial_master_buffer[SERIAL_LED_ADDR]);
#endif
}

bool matrix_is_modified(void)
{
    if (debouncing) return false;
    return true;
}

inline
bool matrix_is_on(uint8_t row, uint8_t col)
{
    return (matrix[row] & ((matrix_row_t)1<<col));
}

inline
matrix_row_t matrix_get_row(uint8_t row)
{
    return matrix[row];
}

void matrix_print(void)
{
    print("\nr/c 0123456789ABCDEF\n");
    for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
        phex(row); print(": ");
        pbin_reverse16(matrix_get_row(row));
        print("\n");
    }
}

uint8_t matrix_key_count(void)
{
    uint8_t count = 0;
    for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
        count += bitpop16(matrix[i]);
    }
    return count;
}

#if (DIODE_DIRECTION == COL2ROW)

static void init_cols(void)
{
    for(uint8_t x = 0; x < MATRIX_COLS; x++) {
        uint8_t pin = col_pins[x];
        _SFR_IO8((pin >> 4) + 1) &= ~_BV(pin & 0xF); // IN
        _SFR_IO8((pin >> 4) + 2) |=  _BV(pin & 0xF); // HI
    }
}

static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row)
{
    // Store last value of row prior to reading
    matrix_row_t last_row_value = current_matrix[current_row];

    // Clear data in matrix row
    current_matrix[current_row] = 0;

    // Select row and wait for row selecton to stabilize
    select_row(current_row);
    wait_us(30);

    // For each col...
    for(uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++) {

        // Select the col pin to read (active low)
        uint8_t pin = col_pins[col_index];
        uint8_t pin_state = (_SFR_IO8(pin >> 4) & _BV(pin & 0xF));

        // Populate the matrix row with the state of the col pin
        current_matrix[current_row] |=  pin_state ? 0 : (ROW_SHIFTER << col_index);
    }

    // Unselect row
    unselect_row(current_row);

    return (last_row_value != current_matrix[current_row]);
}

static void select_row(uint8_t row)
{
    uint8_t pin = row_pins[row];
    _SFR_IO8((pin >> 4) + 1) |=  _BV(pin & 0xF); // OUT
    _SFR_IO8((pin >> 4) + 2) &= ~_BV(pin & 0xF); // LOW
}

static void unselect_row(uint8_t row)
{
    uint8_t pin = row_pins[row];
    _SFR_IO8((pin >> 4) + 1) &= ~_BV(pin & 0xF); // IN
    _SFR_IO8((pin >> 4) + 2) |=  _BV(pin & 0xF); // HI
}

static void unselect_rows(void)
{
    for(uint8_t x = 0; x < ROWS_PER_HAND; x++) {
        uint8_t pin = row_pins[x];
        _SFR_IO8((pin >> 4) + 1) &= ~_BV(pin & 0xF); // IN
        _SFR_IO8((pin >> 4) + 2) |=  _BV(pin & 0xF); // HI
    }
}

#elif (DIODE_DIRECTION == ROW2COL)

static void init_rows(void)
{
    for(uint8_t x = 0; x < ROWS_PER_HAND; x++) {
        uint8_t pin = row_pins[x];
        _SFR_IO8((pin >> 4) + 1) &= ~_BV(pin & 0xF); // IN
        _SFR_IO8((pin >> 4) + 2) |=  _BV(pin & 0xF); // HI
    }
}

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

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

    // For each row...
    for(uint8_t row_index = 0; row_index < ROWS_PER_HAND; row_index++)
    {

        // Store last value of row prior to reading
        matrix_row_t last_row_value = current_matrix[row_index];

        // Check row pin state
        if ((_SFR_IO8(row_pins[row_index] >> 4) & _BV(row_pins[row_index] & 0xF)) == 0)
        {
            // Pin LO, set col bit
            current_matrix[row_index] |= (ROW_SHIFTER << current_col);
        }
        else
        {
            // Pin HI, clear col bit
            current_matrix[row_index] &= ~(ROW_SHIFTER << current_col);
        }

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

    // Unselect col
    unselect_col(current_col);

    return matrix_changed;
}

static void select_col(uint8_t col)
{
    uint8_t pin = col_pins[col];
    _SFR_IO8((pin >> 4) + 1) |=  _BV(pin & 0xF); // OUT
    _SFR_IO8((pin >> 4) + 2) &= ~_BV(pin & 0xF); // LOW
}

static void unselect_col(uint8_t col)
{
    uint8_t pin = col_pins[col];
    _SFR_IO8((pin >> 4) + 1) &= ~_BV(pin & 0xF); // IN
    _SFR_IO8((pin >> 4) + 2) |=  _BV(pin & 0xF); // HI
}

static void unselect_cols(void)
{
    for(uint8_t x = 0; x < MATRIX_COLS; x++) {
        uint8_t pin = col_pins[x];
        _SFR_IO8((pin >> 4) + 1) &= ~_BV(pin & 0xF); // IN
        _SFR_IO8((pin >> 4) + 2) |=  _BV(pin & 0xF); // HI
    }
}

#endif

M keyboards/fortitude60/readme.md => keyboards/fortitude60/readme.md +5 -5
@@ 4,12 4,12 @@

👊A 60% (12x5) split keyboard with staggerd column layout.👊

Keyboard Maintainer: [Pekaso](https://github.com/Pekaso) [@Pekaso](https://twitter.com/Pekaso)  
Hardware Supported: Fortitude60 PCB, Beetle 32u4  
Hardware Availability: [plustk2s.com](http://plustk2s.com)
* Keyboard Maintainer: [Pekaso](https://github.com/Pekaso) [@Pekaso](https://twitter.com/Pekaso)
* Hardware Supported: Fortitude60 PCB, Beetle 32u4
* Hardware Availability: [plustk2s.com](http://plustk2s.com)

Make example for this keyboard (after setting up your build environment):

    make fortitude60/rev1:default:avrdude
    make fortitude60/rev1:default:flash

See [build environment setup](https://docs.qmk.fm/#/getting_started_build_tools) then the [make instructions](https://docs.qmk.fm/#/getting_started_make_guide) for more information.
\ No newline at end of file
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).
\ No newline at end of file

M keyboards/fortitude60/rev1/config.h => keyboards/fortitude60/rev1/config.h +8 -4
@@ 15,8 15,9 @@ 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 REV1_CONFIG_H
#define REV1_CONFIG_H
#pragma once

#include "config_common.h"

/* USB Device descriptor parameter */
#define VENDOR_ID       0xCB10


@@ 38,6 39,11 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
/* COL2ROW or ROW2COL */
#define DIODE_DIRECTION COL2ROW

/*
 * Split Keyboard specific options, make sure you have 'SPLIT_KEYBOARD = yes' in your rules.mk, and define SOFT_SERIAL_PIN.
 */
#define SOFT_SERIAL_PIN D2

/* define if matrix has ghost */
//#define MATRIX_HAS_GHOST



@@ 79,5 85,3 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
//#define NO_ACTION_ONESHOT
//#define NO_ACTION_MACRO
//#define NO_ACTION_FUNCTION

#endif

M keyboards/fortitude60/rev1/rev1.c => keyboards/fortitude60/rev1/rev1.c +0 -21
@@ 1,22 1,1 @@
#include "rev1.h"

#ifdef SSD1306OLED
void led_set_kb(uint8_t usb_led) {
    // put your keyboard LED indicator (ex: Caps Lock LED) toggling code here
    led_set_user(usb_led);
}
#endif

void matrix_init_kb(void) {

    // // green led on
    // DDRD |= (1<<5);
    // PORTD &= ~(1<<5);

    // // orange led on
    // DDRB |= (1<<0);
    // PORTB &= ~(1<<0);

	matrix_init_user();
};


M keyboards/fortitude60/rev1/rev1.h => keyboards/fortitude60/rev1/rev1.h +1 -12
@@ 1,18 1,9 @@
#ifndef REV1_H
#define REV1_H
#pragma once

#include "fortitude60.h"

#include "quantum.h"

#ifdef USE_I2C
#include <stddef.h>
#ifdef __AVR__
  #include <avr/io.h>
  #include <avr/interrupt.h>
#endif
#endif

// Standard Keymap
// (TRRS jack on the left half is to the right, TRRS jack on the right half is to the left)
#define LAYOUT( \


@@ 34,5 25,3 @@
    { R35, R34, R33, R32, R31, R30 }, \
    { RT0, RT1, RT2, RT3, RT4, RT5 } \
  }

#endif

M keyboards/fortitude60/rev1/rules.mk => keyboards/fortitude60/rev1/rules.mk +3 -0
@@ 0,0 1,3 @@
# Revision Specific Build Options
#   change yes to no to disable
#

M keyboards/fortitude60/rules.mk => keyboards/fortitude60/rules.mk +11 -14
@@ 12,28 12,25 @@ MCU = atmega32u4
BOOTLOADER = caterina

# Build Options
#   change to "no" to disable the options, or define them in the Makefile in
#   the appropriate keymap folder that will get included automatically
#   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 = no         # Console for debug
COMMAND_ENABLE = yes        # Commands for debug and configuration
NKRO_ENABLE = no            # Nkey Rollover - if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work
# Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE
SLEEP_LED_ENABLE = yes      # 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
MIDI_ENABLE = no            # MIDI controls
AUDIO_ENABLE = no           # Audio output on port C6
UNICODE_ENABLE = no         # Unicode
RGBLIGHT_ENABLE = no        # Enable keyboard RGB underglow
MIDI_ENABLE = no            # MIDI support
BLUETOOTH_ENABLE = no       # Enable Bluetooth with the Adafruit EZ-Key HID
RGBLIGHT_ENABLE = no       # Enable WS2812 RGB underlight. 
USE_SERIAL = yes	# Serial support only on fortitude60
# Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE
SLEEP_LED_ENABLE = yes    # Breathing sleep LED during USB suspend
AUDIO_ENABLE = no           # Audio output on port C6
FAUXCLICKY_ENABLE = no      # Use buzzer to emulate clicky switches
HD44780_ENABLE = no         # Enable support for HD44780 based LCDs

CUSTOM_MATRIX = yes
SRC += matrix.c \
       split_util.c \
       serial.c
SPLIT_KEYBOARD = yes

DEFAULT_FOLDER = fortitude60/rev1

D keyboards/fortitude60/serial.c => keyboards/fortitude60/serial.c +0 -589
@@ 1,589 0,0 @@
/*
 * WARNING: be careful changing this code, it is very timing dependent
 *
 * 2018-10-28 checked
 *  avr-gcc 4.9.2
 *  avr-gcc 5.4.0
 *  avr-gcc 7.3.0
 */

#ifndef F_CPU
#define F_CPU 16000000
#endif

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stddef.h>
#include <stdbool.h>
#include "serial.h"

#ifdef SOFT_SERIAL_PIN

#ifdef __AVR_ATmega32U4__
  // if using ATmega32U4 I2C, can not use PD0 and PD1 in soft serial.
  #ifdef USE_I2C
    #if SOFT_SERIAL_PIN == D0 || SOFT_SERIAL_PIN == D1
      #error Using ATmega32U4 I2C, so can not use PD0, PD1
    #endif
  #endif

  #if SOFT_SERIAL_PIN >= D0 && SOFT_SERIAL_PIN <= D3
    #define SERIAL_PIN_DDR   DDRD
    #define SERIAL_PIN_PORT  PORTD
    #define SERIAL_PIN_INPUT PIND
    #if SOFT_SERIAL_PIN == D0
      #define SERIAL_PIN_MASK _BV(PD0)
      #define EIMSK_BIT       _BV(INT0)
      #define EICRx_BIT       (~(_BV(ISC00) | _BV(ISC01)))
      #define SERIAL_PIN_INTERRUPT INT0_vect
    #elif  SOFT_SERIAL_PIN == D1
      #define SERIAL_PIN_MASK _BV(PD1)
      #define EIMSK_BIT       _BV(INT1)
      #define EICRx_BIT       (~(_BV(ISC10) | _BV(ISC11)))
      #define SERIAL_PIN_INTERRUPT INT1_vect
    #elif  SOFT_SERIAL_PIN == D2
      #define SERIAL_PIN_MASK _BV(PD2)
      #define EIMSK_BIT       _BV(INT2)
      #define EICRx_BIT       (~(_BV(ISC20) | _BV(ISC21)))
      #define SERIAL_PIN_INTERRUPT INT2_vect
    #elif  SOFT_SERIAL_PIN == D3
      #define SERIAL_PIN_MASK _BV(PD3)
      #define EIMSK_BIT       _BV(INT3)
      #define EICRx_BIT       (~(_BV(ISC30) | _BV(ISC31)))
      #define SERIAL_PIN_INTERRUPT INT3_vect
    #endif
  #elif  SOFT_SERIAL_PIN == E6
    #define SERIAL_PIN_DDR   DDRE
    #define SERIAL_PIN_PORT  PORTE
    #define SERIAL_PIN_INPUT PINE
    #define SERIAL_PIN_MASK  _BV(PE6)
    #define EIMSK_BIT        _BV(INT6)
    #define EICRx_BIT        (~(_BV(ISC60) | _BV(ISC61)))
    #define SERIAL_PIN_INTERRUPT INT6_vect
  #else
  #error invalid SOFT_SERIAL_PIN value
  #endif

#else
 #error serial.c now support ATmega32U4 only
#endif

//////////////// for backward compatibility ////////////////////////////////
#if !defined(SERIAL_USE_SINGLE_TRANSACTION) && !defined(SERIAL_USE_MULTI_TRANSACTION)
/* --- USE OLD API (compatible with let's split serial.c) */
  #if SERIAL_SLAVE_BUFFER_LENGTH > 0
  uint8_t volatile serial_slave_buffer[SERIAL_SLAVE_BUFFER_LENGTH] = {0};
  #endif
  #if SERIAL_MASTER_BUFFER_LENGTH > 0
  uint8_t volatile serial_master_buffer[SERIAL_MASTER_BUFFER_LENGTH] = {0};
  #endif
  uint8_t volatile status0 = 0;

SSTD_t transactions[] = {
    { (uint8_t *)&status0,
  #if SERIAL_MASTER_BUFFER_LENGTH > 0
      sizeof(serial_master_buffer), (uint8_t *)serial_master_buffer,
  #else
      0, (uint8_t *)NULL,
  #endif
  #if SERIAL_SLAVE_BUFFER_LENGTH > 0
      sizeof(serial_slave_buffer), (uint8_t *)serial_slave_buffer
  #else
      0, (uint8_t *)NULL,
  #endif
  }
};

void serial_master_init(void)
{ soft_serial_initiator_init(transactions, TID_LIMIT(transactions)); }

void serial_slave_init(void)
{ soft_serial_target_init(transactions, TID_LIMIT(transactions)); }

// 0 => no error
// 1 => slave did not respond
// 2 => checksum error
int serial_update_buffers()
{
    int result;
    result = soft_serial_transaction();
    return result;
}

#endif // end of OLD API (compatible with let's split serial.c)
////////////////////////////////////////////////////////////////////////////

#define ALWAYS_INLINE __attribute__((always_inline))
#define NO_INLINE __attribute__((noinline))
#define _delay_sub_us(x)    __builtin_avr_delay_cycles(x)

// parity check
#define ODD_PARITY 1
#define EVEN_PARITY 0
#define PARITY EVEN_PARITY

#ifdef SERIAL_DELAY
  // custom setup in config.h
  // #define TID_SEND_ADJUST 2
  // #define SERIAL_DELAY 6             // micro sec
  // #define READ_WRITE_START_ADJUST 30 // cycles
  // #define READ_WRITE_WIDTH_ADJUST 8 // cycles
#else
// ============ Standard setups ============

#ifndef SELECT_SOFT_SERIAL_SPEED
#define SELECT_SOFT_SERIAL_SPEED 1
//  0: about 189kbps
//  1: about 137kbps (default)
//  2: about 75kbps
//  3: about 39kbps
//  4: about 26kbps
//  5: about 20kbps
#endif

#if __GNUC__ < 6
  #define TID_SEND_ADJUST 14
#else
  #define TID_SEND_ADJUST 2
#endif

#if SELECT_SOFT_SERIAL_SPEED == 0
  // Very High speed
  #define SERIAL_DELAY 4             // micro sec
  #if __GNUC__ < 6
    #define READ_WRITE_START_ADJUST 33 // cycles
    #define READ_WRITE_WIDTH_ADJUST 3 // cycles
  #else
    #define READ_WRITE_START_ADJUST 34 // cycles
    #define READ_WRITE_WIDTH_ADJUST 7 // cycles
  #endif
#elif SELECT_SOFT_SERIAL_SPEED == 1
  // High speed
  #define SERIAL_DELAY 6             // micro sec
  #if __GNUC__ < 6
    #define READ_WRITE_START_ADJUST 30 // cycles
    #define READ_WRITE_WIDTH_ADJUST 3 // cycles
  #else
    #define READ_WRITE_START_ADJUST 33 // cycles
    #define READ_WRITE_WIDTH_ADJUST 7 // cycles
  #endif
#elif SELECT_SOFT_SERIAL_SPEED == 2
  // Middle speed
  #define SERIAL_DELAY 12            // micro sec
  #define READ_WRITE_START_ADJUST 30 // cycles
  #if __GNUC__ < 6
    #define READ_WRITE_WIDTH_ADJUST 3 // cycles
  #else
    #define READ_WRITE_WIDTH_ADJUST 7 // cycles
  #endif
#elif SELECT_SOFT_SERIAL_SPEED == 3
  // Low speed
  #define SERIAL_DELAY 24            // micro sec
  #define READ_WRITE_START_ADJUST 30 // cycles
  #if __GNUC__ < 6
    #define READ_WRITE_WIDTH_ADJUST 3 // cycles
  #else
    #define READ_WRITE_WIDTH_ADJUST 7 // cycles
  #endif
#elif SELECT_SOFT_SERIAL_SPEED == 4
  // Very Low speed
  #define SERIAL_DELAY 36            // micro sec
  #define READ_WRITE_START_ADJUST 30 // cycles
  #if __GNUC__ < 6
    #define READ_WRITE_WIDTH_ADJUST 3 // cycles
  #else
    #define READ_WRITE_WIDTH_ADJUST 7 // cycles
  #endif
#elif SELECT_SOFT_SERIAL_SPEED == 5
  // Ultra Low speed
  #define SERIAL_DELAY 48            // micro sec
  #define READ_WRITE_START_ADJUST 30 // cycles
  #if __GNUC__ < 6
    #define READ_WRITE_WIDTH_ADJUST 3 // cycles
  #else
    #define READ_WRITE_WIDTH_ADJUST 7 // cycles
  #endif
#else
#error invalid SELECT_SOFT_SERIAL_SPEED value
#endif /* SELECT_SOFT_SERIAL_SPEED */
#endif /* SERIAL_DELAY */

#define SERIAL_DELAY_HALF1 (SERIAL_DELAY/2)
#define SERIAL_DELAY_HALF2 (SERIAL_DELAY - SERIAL_DELAY/2)

#define SLAVE_INT_WIDTH_US 1
#ifndef SERIAL_USE_MULTI_TRANSACTION
  #define SLAVE_INT_RESPONSE_TIME SERIAL_DELAY
#else
  #define SLAVE_INT_ACK_WIDTH_UNIT 2
  #define SLAVE_INT_ACK_WIDTH 4
#endif

static SSTD_t *Transaction_table = NULL;
static uint8_t Transaction_table_size = 0;

inline static void serial_delay(void) ALWAYS_INLINE;
inline static
void serial_delay(void) {
  _delay_us(SERIAL_DELAY);
}

inline static void serial_delay_half1(void) ALWAYS_INLINE;
inline static
void serial_delay_half1(void) {
  _delay_us(SERIAL_DELAY_HALF1);
}

inline static void serial_delay_half2(void) ALWAYS_INLINE;
inline static
void serial_delay_half2(void) {
  _delay_us(SERIAL_DELAY_HALF2);
}

inline static void serial_output(void) ALWAYS_INLINE;
inline static
void serial_output(void) {
  SERIAL_PIN_DDR |= SERIAL_PIN_MASK;
}

// make the serial pin an input with pull-up resistor
inline static void serial_input_with_pullup(void) ALWAYS_INLINE;
inline static
void serial_input_with_pullup(void) {
  SERIAL_PIN_DDR  &= ~SERIAL_PIN_MASK;
  SERIAL_PIN_PORT |= SERIAL_PIN_MASK;
}

inline static uint8_t serial_read_pin(void) ALWAYS_INLINE;
inline static
uint8_t serial_read_pin(void) {
  return !!(SERIAL_PIN_INPUT & SERIAL_PIN_MASK);
}

inline static void serial_low(void) ALWAYS_INLINE;
inline static
void serial_low(void) {
  SERIAL_PIN_PORT &= ~SERIAL_PIN_MASK;
}

inline static void serial_high(void) ALWAYS_INLINE;
inline static
void serial_high(void) {
  SERIAL_PIN_PORT |= SERIAL_PIN_MASK;
}

void soft_serial_initiator_init(SSTD_t *sstd_table, int sstd_table_size)
{
    Transaction_table = sstd_table;
    Transaction_table_size = (uint8_t)sstd_table_size;
    serial_output();
    serial_high();
}

void soft_serial_target_init(SSTD_t *sstd_table, int sstd_table_size)
{
    Transaction_table = sstd_table;
    Transaction_table_size = (uint8_t)sstd_table_size;
    serial_input_with_pullup();

    // Enable INT0-INT3,INT6
    EIMSK |= EIMSK_BIT;
#if SERIAL_PIN_MASK == _BV(PE6)
    // Trigger on falling edge of INT6
    EICRB &= EICRx_BIT;
#else
    // Trigger on falling edge of INT0-INT3
    EICRA &= EICRx_BIT;
#endif
}

// Used by the sender to synchronize timing with the reciver.
static void sync_recv(void) NO_INLINE;
static
void sync_recv(void) {
  for (uint8_t i = 0; i < SERIAL_DELAY*5 && serial_read_pin(); i++ ) {
  }
  // This shouldn't hang if the target disconnects because the
  // serial line will float to high if the target does disconnect.
  while (!serial_read_pin());
}

// Used by the reciver to send a synchronization signal to the sender.
static void sync_send(void) NO_INLINE;
static
void sync_send(void) {
  serial_low();
  serial_delay();
  serial_high();
}

// Reads a byte from the serial line
static uint8_t serial_read_chunk(uint8_t *pterrcount, uint8_t bit) NO_INLINE;
static uint8_t serial_read_chunk(uint8_t *pterrcount, uint8_t bit) {
    uint8_t byte, i, p, pb;

  _delay_sub_us(READ_WRITE_START_ADJUST);
  for( i = 0, byte = 0, p = PARITY; i < bit; i++ ) {
      serial_delay_half1();   // read the middle of pulses
      if( serial_read_pin() ) {
          byte = (byte << 1) | 1; p ^= 1;
      } else {
          byte = (byte << 1) | 0; p ^= 0;
      }
      _delay_sub_us(READ_WRITE_WIDTH_ADJUST);
      serial_delay_half2();
  }
  /* recive parity bit */
  serial_delay_half1();   // read the middle of pulses
  pb = serial_read_pin();
  _delay_sub_us(READ_WRITE_WIDTH_ADJUST);
  serial_delay_half2();

  *pterrcount += (p != pb)? 1 : 0;

  return byte;
}

// Sends a byte with MSB ordering
void serial_write_chunk(uint8_t data, uint8_t bit) NO_INLINE;
void serial_write_chunk(uint8_t data, uint8_t bit) {
    uint8_t b, p;
    for( p = PARITY, b = 1<<(bit-1); b ; b >>= 1) {
        if(data & b) {
            serial_high(); p ^= 1;
        } else {
            serial_low();  p ^= 0;
        }
        serial_delay();
    }
    /* send parity bit */
    if(p & 1) { serial_high(); }
    else      { serial_low(); }
    serial_delay();

    serial_low(); // sync_send() / senc_recv() need raise edge
}

static void serial_send_packet(uint8_t *buffer, uint8_t size) NO_INLINE;
static
void serial_send_packet(uint8_t *buffer, uint8_t size) {
  for (uint8_t i = 0; i < size; ++i) {
    uint8_t data;
    data = buffer[i];
    sync_send();
    serial_write_chunk(data,8);
  }
}

static uint8_t serial_recive_packet(uint8_t *buffer, uint8_t size) NO_INLINE;
static
uint8_t serial_recive_packet(uint8_t *buffer, uint8_t size) {
  uint8_t pecount = 0;
  for (uint8_t i = 0; i < size; ++i) {
    uint8_t data;
    sync_recv();
    data = serial_read_chunk(&pecount, 8);
    buffer[i] = data;
  }
  return pecount == 0;
}

inline static
void change_sender2reciver(void) {
    sync_send();          //0
    serial_delay_half1(); //1
    serial_low();         //2
    serial_input_with_pullup(); //2
    serial_delay_half1(); //3
}

inline static
void change_reciver2sender(void) {
    sync_recv();     //0
    serial_delay();  //1
    serial_low();    //3
    serial_output(); //3
    serial_delay_half1(); //4
}

static inline uint8_t nibble_bits_count(uint8_t bits)
{
    bits = (bits & 0x5) + (bits >> 1 & 0x5);
    bits = (bits & 0x3) + (bits >> 2 & 0x3);
    return bits;
}

// interrupt handle to be used by the target device
ISR(SERIAL_PIN_INTERRUPT) {

#ifndef SERIAL_USE_MULTI_TRANSACTION
  serial_low();
  serial_output();
  SSTD_t *trans = Transaction_table;
#else
  // recive transaction table index
  uint8_t tid, bits;
  uint8_t pecount = 0;
  sync_recv();
  bits = serial_read_chunk(&pecount,7);
  tid = bits>>3;
  bits = (bits&7) != nibble_bits_count(tid);
  if( bits || pecount> 0 || tid > Transaction_table_size ) {
      return;
  }
  serial_delay_half1();

  serial_high(); // response step1 low->high
  serial_output();
  _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT*SLAVE_INT_ACK_WIDTH);
  SSTD_t *trans = &Transaction_table[tid];
  serial_low(); // response step2 ack high->low
#endif

  // target send phase
  if( trans->target2initiator_buffer_size > 0 )
      serial_send_packet((uint8_t *)trans->target2initiator_buffer,
                         trans->target2initiator_buffer_size);
  // target switch to input
  change_sender2reciver();

  // target recive phase
  if( trans->initiator2target_buffer_size > 0 ) {
      if (serial_recive_packet((uint8_t *)trans->initiator2target_buffer,
                               trans->initiator2target_buffer_size) ) {
          *trans->status = TRANSACTION_ACCEPTED;
      } else {
          *trans->status = TRANSACTION_DATA_ERROR;
      }
  } else {
      *trans->status = TRANSACTION_ACCEPTED;
  }

  sync_recv(); //weit initiator output to high
}

/////////
//  start transaction by initiator
//
// int  soft_serial_transaction(int sstd_index)
//
// Returns:
//    TRANSACTION_END
//    TRANSACTION_NO_RESPONSE
//    TRANSACTION_DATA_ERROR
// this code is very time dependent, so we need to disable interrupts
#ifndef SERIAL_USE_MULTI_TRANSACTION
int  soft_serial_transaction(void) {
  SSTD_t *trans = Transaction_table;
#else
int  soft_serial_transaction(int sstd_index) {
  if( sstd_index > Transaction_table_size )
      return TRANSACTION_TYPE_ERROR;
  SSTD_t *trans = &Transaction_table[sstd_index];
#endif
  cli();

  // signal to the target that we want to start a transaction
  serial_output();
  serial_low();
  _delay_us(SLAVE_INT_WIDTH_US);

#ifndef SERIAL_USE_MULTI_TRANSACTION
  // wait for the target response
  serial_input_with_pullup();
  _delay_us(SLAVE_INT_RESPONSE_TIME);

  // check if the target is present
  if (serial_read_pin()) {
    // target failed to pull the line low, assume not present
    serial_output();
    serial_high();
    *trans->status = TRANSACTION_NO_RESPONSE;
    sei();
    return TRANSACTION_NO_RESPONSE;
  }

#else
  // send transaction table index
  int tid = (sstd_index<<3) | (7 & nibble_bits_count(sstd_index));
  sync_send();
  _delay_sub_us(TID_SEND_ADJUST);
  serial_write_chunk(tid, 7);
  serial_delay_half1();

  // wait for the target response (step1 low->high)
  serial_input_with_pullup();
  while( !serial_read_pin() ) {
      _delay_sub_us(2);
  }

  // check if the target is present (step2 high->low)
  for( int i = 0; serial_read_pin(); i++ ) {
      if (i > SLAVE_INT_ACK_WIDTH + 1) {
          // slave failed to pull the line low, assume not present
          serial_output();
          serial_high();
          *trans->status = TRANSACTION_NO_RESPONSE;
          sei();
          return TRANSACTION_NO_RESPONSE;
      }
      _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT);
  }
#endif

  // initiator recive phase
  // if the target is present syncronize with it
  if( trans->target2initiator_buffer_size > 0 ) {
      if (!serial_recive_packet((uint8_t *)trans->target2initiator_buffer,
                                trans->target2initiator_buffer_size) ) {
          serial_output();
          serial_high();
          *trans->status = TRANSACTION_DATA_ERROR;
          sei();
          return TRANSACTION_DATA_ERROR;
      }
   }

  // initiator switch to output
  change_reciver2sender();

  // initiator send phase
  if( trans->initiator2target_buffer_size > 0 ) {
      serial_send_packet((uint8_t *)trans->initiator2target_buffer,
                         trans->initiator2target_buffer_size);
  }

  // always, release the line when not in use
  sync_send();

  *trans->status = TRANSACTION_END;
  sei();
  return TRANSACTION_END;
}

#ifdef SERIAL_USE_MULTI_TRANSACTION
int soft_serial_get_and_clean_status(int sstd_index) {
    SSTD_t *trans = &Transaction_table[sstd_index];
    cli();
    int retval = *trans->status;
    *trans->status = 0;;
    sei();
    return retval;
}
#endif

#endif

// Helix serial.c history
//   2018-1-29 fork from let's split and add PD2, modify sync_recv() (#2308, bceffdefc)
//   2018-6-28 bug fix master to slave comm and speed up (#3255, 1038bbef4)
//             (adjusted with avr-gcc 4.9.2)
//   2018-7-13 remove USE_SERIAL_PD2 macro (#3374, f30d6dd78)
//             (adjusted with avr-gcc 4.9.2)
//   2018-8-11 add support multi-type transaction (#3608, feb5e4aae)
//             (adjusted with avr-gcc 4.9.2)
//   2018-10-21 fix serial and RGB animation conflict (#4191, 4665e4fff)
//             (adjusted with avr-gcc 7.3.0)
//   2018-10-28 re-adjust compiler depend value of delay (#4269, 8517f8a66)
//             (adjusted with avr-gcc 5.4.0, 7.3.0)

D keyboards/fortitude60/serial.h => keyboards/fortitude60/serial.h +0 -89
@@ 1,89 0,0 @@
#ifndef SOFT_SERIAL_H
#define SOFT_SERIAL_H

#include <stdbool.h>

// /////////////////////////////////////////////////////////////////
// Need Soft Serial defines in config.h
// /////////////////////////////////////////////////////////////////
// ex.
//  #define SOFT_SERIAL_PIN ??   // ?? = D0,D1,D2,D3,E6
//  OPTIONAL: #define SELECT_SOFT_SERIAL_SPEED ? // ? = 1,2,3,4,5
//                                               //  1: about 137kbps (default)
//                                               //  2: about 75kbps
//                                               //  3: about 39kbps
//                                               //  4: about 26kbps
//                                               //  5: about 20kbps
//
// //// USE OLD API (compatible with let's split serial.c)
// ex.
//  #define SERIAL_SLAVE_BUFFER_LENGTH MATRIX_ROWS/2
//  #define SERIAL_MASTER_BUFFER_LENGTH 1
//
// //// USE NEW API
//    //// USE simple API (using signle-type transaction function)
//      #define SERIAL_USE_SINGLE_TRANSACTION
//    //// USE flexible API (using multi-type transaction function)
//      #define SERIAL_USE_MULTI_TRANSACTION
//
// /////////////////////////////////////////////////////////////////


//////////////// for backward compatibility ////////////////////////////////
#if !defined(SERIAL_USE_SINGLE_TRANSACTION) && !defined(SERIAL_USE_MULTI_TRANSACTION)
/* --- USE OLD API (compatible with let's split serial.c) */
 #if SERIAL_SLAVE_BUFFER_LENGTH > 0
 extern volatile uint8_t serial_slave_buffer[SERIAL_SLAVE_BUFFER_LENGTH];
 #endif
 #if SERIAL_MASTER_BUFFER_LENGTH > 0
 extern volatile uint8_t serial_master_buffer[SERIAL_MASTER_BUFFER_LENGTH];
 #endif

 void serial_master_init(void);
 void serial_slave_init(void);
 int serial_update_buffers(void);

#endif // end of USE OLD API
////////////////////////////////////////////////////////////////////////////

// Soft Serial Transaction Descriptor
typedef struct _SSTD_t  {
    uint8_t *status;
    uint8_t initiator2target_buffer_size;
    uint8_t *initiator2target_buffer;
    uint8_t target2initiator_buffer_size;
    uint8_t *target2initiator_buffer;
} SSTD_t;
#define TID_LIMIT( table ) (sizeof(table) / sizeof(SSTD_t))

// initiator is transaction start side
void soft_serial_initiator_init(SSTD_t *sstd_table, int sstd_table_size);
// target is interrupt accept side
void soft_serial_target_init(SSTD_t *sstd_table, int sstd_table_size);

// initiator resullt
#define TRANSACTION_END 0
#define TRANSACTION_NO_RESPONSE 0x1
#define TRANSACTION_DATA_ERROR  0x2
#define TRANSACTION_TYPE_ERROR  0x4
#ifndef SERIAL_USE_MULTI_TRANSACTION
int  soft_serial_transaction(void);
#else
int  soft_serial_transaction(int sstd_index);
#endif

// target status
// *SSTD_t.status has
//   initiator:
//       TRANSACTION_END
//    or TRANSACTION_NO_RESPONSE
//    or TRANSACTION_DATA_ERROR
//   target:
//       TRANSACTION_DATA_ERROR
//    or TRANSACTION_ACCEPTED
#define TRANSACTION_ACCEPTED 0x8
#ifdef SERIAL_USE_MULTI_TRANSACTION
int  soft_serial_get_and_clean_status(int sstd_index);
#endif

#endif /* SOFT_SERIAL_H */

D keyboards/fortitude60/serial_config.h => keyboards/fortitude60/serial_config.h +0 -10
@@ 1,10 0,0 @@
#ifndef SOFT_SERIAL_CONFIG_H
#define SOFT_SERIAL_CONFIG_H

/* Soft Serial defines */
#define SOFT_SERIAL_PIN D2

#define SERIAL_SLAVE_BUFFER_LENGTH MATRIX_ROWS/2
#define SERIAL_MASTER_BUFFER_LENGTH 1

#endif /* SOFT_SERIAL_CONFIG_H */

D keyboards/fortitude60/split_util.c => keyboards/fortitude60/split_util.c +0 -85
@@ 1,85 0,0 @@
#include <avr/io.h>
#include <avr/wdt.h>
#include <avr/power.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/eeprom.h>
#include "split_util.h"
#include "matrix.h"
#include "keyboard.h"
#include "config.h"
#include "timer.h"

#include "serial.h"

volatile bool isLeftHand = true;

static void setup_handedness(void) {
  #ifdef EE_HANDS
    isLeftHand = eeprom_read_byte(EECONFIG_HANDEDNESS);
  #else
    // I2C_MASTER_RIGHT is deprecated, use MASTER_RIGHT instead, since this works for both serial and i2c
    #if defined(I2C_MASTER_RIGHT) || defined(MASTER_RIGHT)
      isLeftHand = !has_usb();
    #else
      isLeftHand = has_usb();
    #endif
  #endif
}

static void keyboard_master_setup(void) {
#ifdef USE_I2C
    i2c_master_init();
#ifdef SSD1306OLED
    matrix_master_OLED_init ();
#endif
#else
    serial_master_init();
#endif
}

static void keyboard_slave_setup(void) {
  timer_init();
#ifdef USE_I2C
    i2c_slave_init(SLAVE_I2C_ADDRESS);
#else
    serial_slave_init();
#endif
}

bool has_usb(void) {
  /* return (UDADDR & _BV(ADDEN)); */
   USBCON |= (1 << OTGPADE); //enables VBUS pad
   _delay_us(5);
   return (USBSTA & (1<<VBUS));  //checks state of VBUS
}

void split_keyboard_setup(void) {
   setup_handedness();

   if (isLeftHand) {
   /* if (has_usb()) { */
      keyboard_master_setup();
   } else {
      keyboard_slave_setup();
   }
   sei();
}

void keyboard_slave_loop(void) {
   matrix_init();

   while (1) {
      matrix_slave_scan();
   }
}

// this code runs before the usb and keyboard is initialized
void matrix_setup(void) {
    split_keyboard_setup();

    if (!isLeftHand) {
    /* if (!has_usb()) { */
        keyboard_slave_loop();
    }
}

D keyboards/fortitude60/split_util.h => keyboards/fortitude60/split_util.h +0 -18
@@ 1,18 0,0 @@
#ifndef SPLIT_KEYBOARD_UTIL_H
#define SPLIT_KEYBOARD_UTIL_H

#include <stdbool.h>
#include "eeconfig.h"

extern volatile bool isLeftHand;

// slave version of matix scan, defined in matrix.c
void matrix_slave_scan(void);

void split_keyboard_setup(void);
bool has_usb(void);
void keyboard_slave_loop(void);

void matrix_master_OLED_init (void);

#endif