Adding 80 Key Ergodox Support
parent
e28d151a8a
commit
a485e13afe
|
@ -0,0 +1,5 @@
|
||||||
|
SUBPROJECT_DEFAULT = ergodox80
|
||||||
|
|
||||||
|
ifndef MAKEFILE_INCLUDED
|
||||||
|
include ../../Makefile
|
||||||
|
endif
|
|
@ -0,0 +1,31 @@
|
||||||
|
#ifndef KEYBOARDS_ERGODOX_CONFIG_H_
|
||||||
|
#define KEYBOARDS_ERGODOX_CONFIG_H_
|
||||||
|
|
||||||
|
#define MOUSEKEY_DELAY 100
|
||||||
|
#define MOUSEKEY_INTERVAL 20
|
||||||
|
#define MOUSEKEY_MAX_SPEED 3
|
||||||
|
#define MOUSEKEY_TIME_TO_MAX 10
|
||||||
|
|
||||||
|
#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)) \
|
||||||
|
)
|
||||||
|
|
||||||
|
#include "ergodox80/config.h"
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* KEYBOARDS_ERGODOX_CONFIG_H_ */
|
|
@ -0,0 +1,5 @@
|
||||||
|
#ifndef KEYBOARDS_ERGODOX_ERGODOX_H_
|
||||||
|
#define KEYBOARDS_ERGODOX_ERGODOX_H_
|
||||||
|
#include "ergodox80.h"
|
||||||
|
|
||||||
|
#endif /* KEYBOARDS_ERGODOX_ERGODOX_H_ */
|
|
@ -0,0 +1,19 @@
|
||||||
|
#!/bin/bash
|
||||||
|
#a tool to fix broken keymaps as a result of pull request #190
|
||||||
|
#changing the declaration of matrix_scan_user() and matrix_init_user()
|
||||||
|
#
|
||||||
|
#This script will save a copy of the specified keymap as keymap.c.bak
|
||||||
|
#and then create a new keymap.c with the definion corrected.
|
||||||
|
#this script must be run from the ergodox_ez directory
|
||||||
|
if [ $# -ne 1 ]; then
|
||||||
|
echo $0: usage: ./190hotfix keymap_name
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo Saving backup as ./keymaps/$1/keymap.c.bak ...
|
||||||
|
mv ./keymaps/$1/keymap.c ./keymaps/$1/keymap.c.bak
|
||||||
|
|
||||||
|
echo Modifying ./keymaps/$1/keymap.c ...
|
||||||
|
cat ./keymaps/$1/keymap.c.bak | sed -r 's/^void \* matrix_/void matrix_/'>./keymaps/$1/keymap.c
|
||||||
|
|
||||||
|
echo Complete!
|
|
@ -0,0 +1,3 @@
|
||||||
|
ifndef MAKEFILE_INCLUDED
|
||||||
|
include ../../../Makefile
|
||||||
|
endif
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
Copyright 2012 Jun Wako <wakojun@gmail.com>
|
||||||
|
Copyright 2013 Oleg Kostyuk <cub.uanic@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 ERGODOX_EZ_CONFIG_H
|
||||||
|
#define ERGODOX_EZ_CONFIG_H
|
||||||
|
|
||||||
|
#include "../config.h"
|
||||||
|
|
||||||
|
/* USB Device descriptor parameter */
|
||||||
|
#define VENDOR_ID 0xFEED
|
||||||
|
#define PRODUCT_ID 0x1307
|
||||||
|
#define DEVICE_VER 0x0001
|
||||||
|
#define MANUFACTURER ErgoDox EZ
|
||||||
|
#define PRODUCT ErgoDox EZ
|
||||||
|
#define DESCRIPTION QMK keyboard firmware for Ergodox EZ
|
||||||
|
|
||||||
|
/* key matrix size */
|
||||||
|
#define MATRIX_ROWS 14
|
||||||
|
#define MATRIX_COLS 6
|
||||||
|
|
||||||
|
/* number of backlight levels */
|
||||||
|
#define BACKLIGHT_LEVELS 3
|
||||||
|
|
||||||
|
#define LED_BRIGHTNESS_LO 15
|
||||||
|
#define LED_BRIGHTNESS_HI 255
|
||||||
|
|
||||||
|
|
||||||
|
/* Set 0 if debouncing isn't needed */
|
||||||
|
#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
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,85 @@
|
||||||
|
#include "ergodox80.h"
|
||||||
|
#include "i2cmaster.h"
|
||||||
|
|
||||||
|
bool i2c_initialized = 0;
|
||||||
|
uint8_t mcp23018_status = 0x20;
|
||||||
|
|
||||||
|
void matrix_init_kb(void) {
|
||||||
|
// keyboard LEDs (see "PWM on ports OC1(A|B|C)" in "teensy-2-0.md")
|
||||||
|
TCCR1A = 0b10101001; // set and configure fast PWM
|
||||||
|
TCCR1B = 0b00001001; // set and configure fast PWM
|
||||||
|
|
||||||
|
// (tied to Vcc for hardware convenience)
|
||||||
|
DDRB &= ~(1<<4); // set B(4) as input
|
||||||
|
PORTB &= ~(1<<4); // set B(4) internal pull-up disabled
|
||||||
|
|
||||||
|
// unused pins - C7, D4, D5, D7, E6
|
||||||
|
// set as input with internal pull-ip enabled
|
||||||
|
DDRC &= ~(1<<7);
|
||||||
|
DDRD &= ~(1<<7 | 1<<5 | 1<<4);
|
||||||
|
DDRE &= ~(1<<6);
|
||||||
|
PORTC |= (1<<7);
|
||||||
|
PORTD |= (1<<7 | 1<<5 | 1<<4);
|
||||||
|
PORTE |= (1<<6);
|
||||||
|
|
||||||
|
ergodox_blink_all_leds();
|
||||||
|
|
||||||
|
matrix_init_user();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ergodox_blink_all_leds(void)
|
||||||
|
{
|
||||||
|
ergodox_led_all_off();
|
||||||
|
ergodox_led_all_set(LED_BRIGHTNESS_HI);
|
||||||
|
ergodox_right_led_1_on();
|
||||||
|
_delay_ms(50);
|
||||||
|
ergodox_right_led_2_on();
|
||||||
|
_delay_ms(50);
|
||||||
|
ergodox_right_led_3_on();
|
||||||
|
_delay_ms(50);
|
||||||
|
ergodox_right_led_1_off();
|
||||||
|
_delay_ms(50);
|
||||||
|
ergodox_right_led_2_off();
|
||||||
|
_delay_ms(50);
|
||||||
|
ergodox_right_led_3_off();
|
||||||
|
//ergodox_led_all_on();
|
||||||
|
//_delay_ms(333);
|
||||||
|
ergodox_led_all_off();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t init_mcp23018(void) {
|
||||||
|
mcp23018_status = 0x20;
|
||||||
|
|
||||||
|
// I2C subsystem
|
||||||
|
if (i2c_initialized == 0) {
|
||||||
|
i2c_init(); // on pins D(1,0)
|
||||||
|
i2c_initialized++;
|
||||||
|
_delay_ms(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set pin direction
|
||||||
|
// - unused : input : 1
|
||||||
|
// - input : input : 1
|
||||||
|
// - driving : output : 0
|
||||||
|
mcp23018_status = i2c_start(I2C_ADDR_WRITE); if (mcp23018_status) goto out;
|
||||||
|
mcp23018_status = i2c_write(IODIRA); if (mcp23018_status) goto out;
|
||||||
|
mcp23018_status = i2c_write(0b00000000); if (mcp23018_status) goto out;
|
||||||
|
mcp23018_status = i2c_write(0b00111111); if (mcp23018_status) goto out;
|
||||||
|
i2c_stop();
|
||||||
|
|
||||||
|
// set pull-up
|
||||||
|
// - unused : on : 1
|
||||||
|
// - input : on : 1
|
||||||
|
// - driving : off : 0
|
||||||
|
mcp23018_status = i2c_start(I2C_ADDR_WRITE); if (mcp23018_status) goto out;
|
||||||
|
mcp23018_status = i2c_write(GPPUA); if (mcp23018_status) goto out;
|
||||||
|
mcp23018_status = i2c_write(0b00000000); if (mcp23018_status) goto out;
|
||||||
|
mcp23018_status = i2c_write(0b00111111); if (mcp23018_status) goto out;
|
||||||
|
|
||||||
|
out:
|
||||||
|
i2c_stop();
|
||||||
|
|
||||||
|
return mcp23018_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,122 @@
|
||||||
|
#ifndef ERGODOX_EZ_H
|
||||||
|
#define ERGODOX_EZ_H
|
||||||
|
|
||||||
|
#include "quantum.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "i2cmaster.h"
|
||||||
|
#include <util/delay.h>
|
||||||
|
|
||||||
|
#define CPU_PRESCALE(n) (CLKPR = 0x80, CLKPR = (n))
|
||||||
|
#define CPU_16MHz 0x00
|
||||||
|
|
||||||
|
// I2C aliases and register addresses (see "mcp23018.md")
|
||||||
|
#define I2C_ADDR 0b0100000
|
||||||
|
#define I2C_ADDR_WRITE ( (I2C_ADDR<<1) | I2C_WRITE )
|
||||||
|
#define I2C_ADDR_READ ( (I2C_ADDR<<1) | I2C_READ )
|
||||||
|
#define IODIRA 0x00 // i/o direction register
|
||||||
|
#define IODIRB 0x01
|
||||||
|
#define GPPUA 0x0C // GPIO pull-up resistor register
|
||||||
|
#define GPPUB 0x0D
|
||||||
|
#define GPIOA 0x12 // general purpose i/o port register (write modifies OLAT)
|
||||||
|
#define GPIOB 0x13
|
||||||
|
#define OLATA 0x14 // output latch register
|
||||||
|
#define OLATB 0x15
|
||||||
|
|
||||||
|
extern uint8_t mcp23018_status;
|
||||||
|
|
||||||
|
void init_ergodox(void);
|
||||||
|
void ergodox_blink_all_leds(void);
|
||||||
|
uint8_t init_mcp23018(void);
|
||||||
|
uint8_t ergodox_left_leds_update(void);
|
||||||
|
|
||||||
|
#define LED_BRIGHTNESS_LO 15
|
||||||
|
#define LED_BRIGHTNESS_HI 255
|
||||||
|
|
||||||
|
|
||||||
|
inline void ergodox_board_led_on(void) { DDRD |= (1<<6); PORTD |= (1<<6); }
|
||||||
|
inline void ergodox_right_led_1_on(void) { DDRB |= (1<<5); PORTB |= (1<<5); }
|
||||||
|
inline void ergodox_right_led_2_on(void) { DDRB |= (1<<6); PORTB |= (1<<6); }
|
||||||
|
inline void ergodox_right_led_3_on(void) { DDRB |= (1<<7); PORTB |= (1<<7); }
|
||||||
|
inline void ergodox_right_led_on(uint8_t led) { DDRB |= (1<<(led+4)); PORTB |= (1<<(led+4)); }
|
||||||
|
|
||||||
|
inline void ergodox_board_led_off(void) { DDRD &= ~(1<<6); PORTD &= ~(1<<6); }
|
||||||
|
inline void ergodox_right_led_1_off(void) { DDRB &= ~(1<<5); PORTB &= ~(1<<5); }
|
||||||
|
inline void ergodox_right_led_2_off(void) { DDRB &= ~(1<<6); PORTB &= ~(1<<6); }
|
||||||
|
inline void ergodox_right_led_3_off(void) { DDRB &= ~(1<<7); PORTB &= ~(1<<7); }
|
||||||
|
inline void ergodox_right_led_off(uint8_t led) { DDRB &= ~(1<<(led+4)); PORTB &= ~(1<<(led+4)); }
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void ergodox_right_led_1_set(uint8_t n) { OCR1A = n; }
|
||||||
|
inline void ergodox_right_led_2_set(uint8_t n) { OCR1B = n; }
|
||||||
|
inline void ergodox_right_led_3_set(uint8_t n) { OCR1C = n; }
|
||||||
|
inline void ergodox_right_led_set(uint8_t led, uint8_t n) {
|
||||||
|
(led == 1) ? (OCR1A = n) :
|
||||||
|
(led == 2) ? (OCR1B = n) :
|
||||||
|
(OCR1C = n);
|
||||||
|
}
|
||||||
|
|
||||||
|
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( \
|
||||||
|
\
|
||||||
|
/* left hand, spatial positions */ \
|
||||||
|
k00,k01,k02,k03,k04,k05,k06, \
|
||||||
|
k10,k11,k12,k13,k14,k15,k16, \
|
||||||
|
k20,k21,k22,k23,k24,k25, \
|
||||||
|
k30,k31,k32,k33,k34,k35,k36, \
|
||||||
|
k40,k41,k42,k43,k44, \
|
||||||
|
k55,k56, \
|
||||||
|
k45,k46,k54, \
|
||||||
|
k53,k52,k51, \
|
||||||
|
\
|
||||||
|
/* right hand, spatial positions */ \
|
||||||
|
k07,k08,k09,k0A,k0B,k0C,k0D, \
|
||||||
|
k17,k18,k19,k1A,k1B,k1C,k1D, \
|
||||||
|
k28,k29,k2A,k2B,k2C,k2D, \
|
||||||
|
k37,k38,k39,k3A,k3B,k3C,k3D, \
|
||||||
|
k49,k4A,k4B,k4C,k4D, \
|
||||||
|
k57,k58, \
|
||||||
|
k59,k47,k48, \
|
||||||
|
k5C,k5B,k5A ) \
|
||||||
|
\
|
||||||
|
/* matrix positions */ \
|
||||||
|
{ \
|
||||||
|
{ k00, k10, k20, k30, k40, KC_NO }, \
|
||||||
|
{ k01, k11, k21, k31, k41, k51 }, \
|
||||||
|
{ k02, k12, k22, k32, k42, k52 }, \
|
||||||
|
{ k03, k13, k23, k33, k43, k53 }, \
|
||||||
|
{ k04, k14, k24, k34, k44, k54 }, \
|
||||||
|
{ k05, k15, k25, k35, k45, k55 }, \
|
||||||
|
{ k06, k16, KC_NO, k36, k46, k56 }, \
|
||||||
|
\
|
||||||
|
{ k07, k17, KC_NO, k37, k47, k57 }, \
|
||||||
|
{ k08, k18, k28, k38, k48, k58 }, \
|
||||||
|
{ k09, k19, k29, k39, k49, k59 }, \
|
||||||
|
{ k0A, k1A, k2A, k3A, k4A, k5A }, \
|
||||||
|
{ k0B, k1B, k2B, k3B, k4B, k5B }, \
|
||||||
|
{ k0C, k1C, k2C, k3C, k4C, k5C }, \
|
||||||
|
{ k0D, k1D, k2D, k3D, k4D, KC_NO } \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,178 @@
|
||||||
|
#ifndef _I2CMASTER_H
|
||||||
|
#define _I2CMASTER_H 1
|
||||||
|
/*************************************************************************
|
||||||
|
* Title: C include file for the I2C master interface
|
||||||
|
* (i2cmaster.S or twimaster.c)
|
||||||
|
* Author: Peter Fleury <pfleury@gmx.ch> http://jump.to/fleury
|
||||||
|
* File: $Id: i2cmaster.h,v 1.10 2005/03/06 22:39:57 Peter Exp $
|
||||||
|
* Software: AVR-GCC 3.4.3 / avr-libc 1.2.3
|
||||||
|
* Target: any AVR device
|
||||||
|
* Usage: see Doxygen manual
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
#ifdef DOXYGEN
|
||||||
|
/**
|
||||||
|
@defgroup pfleury_ic2master I2C Master library
|
||||||
|
@code #include <i2cmaster.h> @endcode
|
||||||
|
|
||||||
|
@brief I2C (TWI) Master Software Library
|
||||||
|
|
||||||
|
Basic routines for communicating with I2C slave devices. This single master
|
||||||
|
implementation is limited to one bus master on the I2C bus.
|
||||||
|
|
||||||
|
This I2c library is implemented as a compact assembler software implementation of the I2C protocol
|
||||||
|
which runs on any AVR (i2cmaster.S) and as a TWI hardware interface for all AVR with built-in TWI hardware (twimaster.c).
|
||||||
|
Since the API for these two implementations is exactly the same, an application can be linked either against the
|
||||||
|
software I2C implementation or the hardware I2C implementation.
|
||||||
|
|
||||||
|
Use 4.7k pull-up resistor on the SDA and SCL pin.
|
||||||
|
|
||||||
|
Adapt the SCL and SDA port and pin definitions and eventually the delay routine in the module
|
||||||
|
i2cmaster.S to your target when using the software I2C implementation !
|
||||||
|
|
||||||
|
Adjust the CPU clock frequence F_CPU in twimaster.c or in the Makfile when using the TWI hardware implementaion.
|
||||||
|
|
||||||
|
@note
|
||||||
|
The module i2cmaster.S is based on the Atmel Application Note AVR300, corrected and adapted
|
||||||
|
to GNU assembler and AVR-GCC C call interface.
|
||||||
|
Replaced the incorrect quarter period delays found in AVR300 with
|
||||||
|
half period delays.
|
||||||
|
|
||||||
|
@author Peter Fleury pfleury@gmx.ch http://jump.to/fleury
|
||||||
|
|
||||||
|
@par API Usage Example
|
||||||
|
The following code shows typical usage of this library, see example test_i2cmaster.c
|
||||||
|
|
||||||
|
@code
|
||||||
|
|
||||||
|
#include <i2cmaster.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define Dev24C02 0xA2 // device address of EEPROM 24C02, see datasheet
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
unsigned char ret;
|
||||||
|
|
||||||
|
i2c_init(); // initialize I2C library
|
||||||
|
|
||||||
|
// write 0x75 to EEPROM address 5 (Byte Write)
|
||||||
|
i2c_start_wait(Dev24C02+I2C_WRITE); // set device address and write mode
|
||||||
|
i2c_write(0x05); // write address = 5
|
||||||
|
i2c_write(0x75); // write value 0x75 to EEPROM
|
||||||
|
i2c_stop(); // set stop conditon = release bus
|
||||||
|
|
||||||
|
|
||||||
|
// read previously written value back from EEPROM address 5
|
||||||
|
i2c_start_wait(Dev24C02+I2C_WRITE); // set device address and write mode
|
||||||
|
|
||||||
|
i2c_write(0x05); // write address = 5
|
||||||
|
i2c_rep_start(Dev24C02+I2C_READ); // set device address and read mode
|
||||||
|
|
||||||
|
ret = i2c_readNak(); // read one byte from EEPROM
|
||||||
|
i2c_stop();
|
||||||
|
|
||||||
|
for(;;);
|
||||||
|
}
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
*/
|
||||||
|
#endif /* DOXYGEN */
|
||||||
|
|
||||||
|
/**@{*/
|
||||||
|
|
||||||
|
#if (__GNUC__ * 100 + __GNUC_MINOR__) < 304
|
||||||
|
#error "This library requires AVR-GCC 3.4 or later, update to newer AVR-GCC compiler !"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <avr/io.h>
|
||||||
|
|
||||||
|
/** defines the data direction (reading from I2C device) in i2c_start(),i2c_rep_start() */
|
||||||
|
#define I2C_READ 1
|
||||||
|
|
||||||
|
/** defines the data direction (writing to I2C device) in i2c_start(),i2c_rep_start() */
|
||||||
|
#define I2C_WRITE 0
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief initialize the I2C master interace. Need to be called only once
|
||||||
|
@param void
|
||||||
|
@return none
|
||||||
|
*/
|
||||||
|
extern void i2c_init(void);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Terminates the data transfer and releases the I2C bus
|
||||||
|
@param void
|
||||||
|
@return none
|
||||||
|
*/
|
||||||
|
extern void i2c_stop(void);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Issues a start condition and sends address and transfer direction
|
||||||
|
|
||||||
|
@param addr address and transfer direction of I2C device
|
||||||
|
@retval 0 device accessible
|
||||||
|
@retval 1 failed to access device
|
||||||
|
*/
|
||||||
|
extern unsigned char i2c_start(unsigned char addr);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Issues a repeated start condition and sends address and transfer direction
|
||||||
|
|
||||||
|
@param addr address and transfer direction of I2C device
|
||||||
|
@retval 0 device accessible
|
||||||
|
@retval 1 failed to access device
|
||||||
|
*/
|
||||||
|
extern unsigned char i2c_rep_start(unsigned char addr);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Issues a start condition and sends address and transfer direction
|
||||||
|
|
||||||
|
If device is busy, use ack polling to wait until device ready
|
||||||
|
@param addr address and transfer direction of I2C device
|
||||||
|
@return none
|
||||||
|
*/
|
||||||
|
extern void i2c_start_wait(unsigned char addr);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Send one byte to I2C device
|
||||||
|
@param data byte to be transfered
|
||||||
|
@retval 0 write successful
|
||||||
|
@retval 1 write failed
|
||||||
|
*/
|
||||||
|
extern unsigned char i2c_write(unsigned char data);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief read one byte from the I2C device, request more data from device
|
||||||
|
@return byte read from I2C device
|
||||||
|
*/
|
||||||
|
extern unsigned char i2c_readAck(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief read one byte from the I2C device, read is followed by a stop condition
|
||||||
|
@return byte read from I2C device
|
||||||
|
*/
|
||||||
|
extern unsigned char i2c_readNak(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief read one byte from the I2C device
|
||||||
|
|
||||||
|
Implemented as a macro, which calls either i2c_readAck or i2c_readNak
|
||||||
|
|
||||||
|
@param ack 1 send ack, request more data from device<br>
|
||||||
|
0 send nak, read is followed by a stop condition
|
||||||
|
@return byte read from I2C device
|
||||||
|
*/
|
||||||
|
extern unsigned char i2c_read(unsigned char ack);
|
||||||
|
#define i2c_read(ack) (ack) ? i2c_readAck() : i2c_readNak();
|
||||||
|
|
||||||
|
|
||||||
|
/**@}*/
|
||||||
|
#endif
|
|
@ -0,0 +1,382 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
Note for ErgoDox EZ customizers: Here be dragons!
|
||||||
|
This is not a file you want to be messing with.
|
||||||
|
All of the interesting stuff for you is under keymaps/ :)
|
||||||
|
Love, Erez
|
||||||
|
|
||||||
|
Copyright 2013 Oleg Kostyuk <cub.uanic@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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* scan matrix
|
||||||
|
*/
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <avr/io.h>
|
||||||
|
#include "wait.h"
|
||||||
|
#include "action_layer.h"
|
||||||
|
#include "print.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "matrix.h"
|
||||||
|
#include "ergodox80.h"
|
||||||
|
#include "i2cmaster.h"
|
||||||
|
#ifdef DEBUG_MATRIX_SCAN_RATE
|
||||||
|
#include "timer.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This constant define not debouncing time in msecs, but amount of matrix
|
||||||
|
* scan loops which should be made to get stable debounced results.
|
||||||
|
*
|
||||||
|
* On Ergodox matrix scan rate is relatively low, because of slow I2C.
|
||||||
|
* Now it's only 317 scans/second, or about 3.15 msec/scan.
|
||||||
|
* According to Cherry specs, debouncing time is 5 msec.
|
||||||
|
*
|
||||||
|
* And so, there is no sense to have DEBOUNCE higher than 2.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DEBOUNCE
|
||||||
|
# define DEBOUNCE 5
|
||||||
|
#endif
|
||||||
|
static uint8_t debouncing = DEBOUNCE;
|
||||||
|
|
||||||
|
/* matrix state(1:on, 0:off) */
|
||||||
|
static matrix_row_t matrix[MATRIX_ROWS];
|
||||||
|
static matrix_row_t matrix_debouncing[MATRIX_ROWS];
|
||||||
|
|
||||||
|
static matrix_row_t read_cols(uint8_t row);
|
||||||
|
static void init_cols(void);
|
||||||
|
static void unselect_rows(void);
|
||||||
|
static void select_row(uint8_t row);
|
||||||
|
|
||||||
|
static uint8_t mcp23018_reset_loop;
|
||||||
|
|
||||||
|
#ifdef DEBUG_MATRIX_SCAN_RATE
|
||||||
|
uint32_t matrix_timer;
|
||||||
|
uint32_t matrix_scan_count;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__ ((weak))
|
||||||
|
void matrix_init_user(void) {}
|
||||||
|
|
||||||
|
__attribute__ ((weak))
|
||||||
|
void matrix_scan_user(void) {}
|
||||||
|
|
||||||
|
__attribute__ ((weak))
|
||||||
|
void matrix_init_kb(void) {
|
||||||
|
matrix_init_user();
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__ ((weak))
|
||||||
|
void matrix_scan_kb(void) {
|
||||||
|
matrix_scan_user();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
uint8_t matrix_rows(void)
|
||||||
|
{
|
||||||
|
return MATRIX_ROWS;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
uint8_t matrix_cols(void)
|
||||||
|
{
|
||||||
|
return MATRIX_COLS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void matrix_init(void)
|
||||||
|
{
|
||||||
|
// initialize row and col
|
||||||
|
|
||||||
|
mcp23018_status = init_mcp23018();
|
||||||
|
|
||||||
|
|
||||||
|
unselect_rows();
|
||||||
|
init_cols();
|
||||||
|
|
||||||
|
// initialize matrix state: all keys off
|
||||||
|
for (uint8_t i=0; i < MATRIX_ROWS; i++) {
|
||||||
|
matrix[i] = 0;
|
||||||
|
matrix_debouncing[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_MATRIX_SCAN_RATE
|
||||||
|
matrix_timer = timer_read32();
|
||||||
|
matrix_scan_count = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
matrix_init_kb();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void matrix_power_up(void) {
|
||||||
|
mcp23018_status = init_mcp23018();
|
||||||
|
|
||||||
|
unselect_rows();
|
||||||
|
init_cols();
|
||||||
|
|
||||||
|
// initialize matrix state: all keys off
|
||||||
|
for (uint8_t i=0; i < MATRIX_ROWS; i++) {
|
||||||
|
matrix[i] = 0;
|
||||||
|
matrix_debouncing[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_MATRIX_SCAN_RATE
|
||||||
|
matrix_timer = timer_read32();
|
||||||
|
matrix_scan_count = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t matrix_scan(void)
|
||||||
|
{
|
||||||
|
if (mcp23018_status) { // if there was an error
|
||||||
|
if (++mcp23018_reset_loop == 0) {
|
||||||
|
// since mcp23018_reset_loop is 8 bit - we'll try to reset once in 255 matrix scans
|
||||||
|
// this will be approx bit more frequent than once per second
|
||||||
|
print("trying to reset mcp23018\n");
|
||||||
|
mcp23018_status = init_mcp23018();
|
||||||
|
if (mcp23018_status) {
|
||||||
|
print("left side not responding\n");
|
||||||
|
} else {
|
||||||
|
print("left side attached\n");
|
||||||
|
ergodox_blink_all_leds();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_MATRIX_SCAN_RATE
|
||||||
|
matrix_scan_count++;
|
||||||
|
|
||||||
|
uint32_t timer_now = timer_read32();
|
||||||
|
if (TIMER_DIFF_32(timer_now, matrix_timer)>1000) {
|
||||||
|
print("matrix scan frequency: ");
|
||||||
|
pdec(matrix_scan_count);
|
||||||
|
print("\n");
|
||||||
|
|
||||||
|
matrix_timer = timer_now;
|
||||||
|
matrix_scan_count = 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
|
||||||
|
select_row(i);
|
||||||
|
wait_us(30); // without this wait read unstable value.
|
||||||
|
matrix_row_t cols = read_cols(i);
|
||||||
|
if (matrix_debouncing[i] != cols) {
|
||||||
|
matrix_debouncing[i] = cols;
|
||||||
|
if (debouncing) {
|
||||||
|
debug("bounce!: "); debug_hex(debouncing); debug("\n");
|
||||||
|
}
|
||||||
|
debouncing = DEBOUNCE;
|
||||||
|
}
|
||||||
|
unselect_rows();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debouncing) {
|
||||||
|
if (--debouncing) {
|
||||||
|
wait_us(1);
|
||||||
|
// this should be wait_ms(1) but has been left as-is at EZ's request
|
||||||
|
} else {
|
||||||
|
for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
|
||||||
|
matrix[i] = matrix_debouncing[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
matrix_scan_quantum();
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Column pin configuration
|
||||||
|
*
|
||||||
|
* Teensy
|
||||||
|
* col: 0 1 2 3 4 5
|
||||||
|
* pin: F0 F1 F4 F5 F6 F7
|
||||||
|
*
|
||||||
|
* MCP23018
|
||||||
|
* col: 0 1 2 3 4 5
|
||||||
|
* pin: B5 B4 B3 B2 B1 B0
|
||||||
|
*/
|
||||||
|
static void init_cols(void)
|
||||||
|
{
|
||||||
|
// init on mcp23018
|
||||||
|
// not needed, already done as part of init_mcp23018()
|
||||||
|
|
||||||
|
// init on teensy
|
||||||
|
// Input with pull-up(DDR:0, PORT:1)
|
||||||
|
DDRF &= ~(1<<7 | 1<<6 | 1<<5 | 1<<4 | 1<<1 | 1<<0);
|
||||||
|
PORTF |= (1<<7 | 1<<6 | 1<<5 | 1<<4 | 1<<1 | 1<<0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static matrix_row_t read_cols(uint8_t row)
|
||||||
|
{
|
||||||
|
if (row < 7) {
|
||||||
|
if (mcp23018_status) { // if there was an error
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
uint8_t data = 0;
|
||||||
|
mcp23018_status = i2c_start(I2C_ADDR_WRITE); if (mcp23018_status) goto out;
|
||||||
|
mcp23018_status = i2c_write(GPIOB); if (mcp23018_status) goto out;
|
||||||
|
mcp23018_status = i2c_start(I2C_ADDR_READ); if (mcp23018_status) goto out;
|
||||||
|
data = i2c_readNak();
|
||||||
|
data = ~data;
|
||||||
|
out:
|
||||||
|
i2c_stop();
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// read from teensy
|
||||||
|
return
|
||||||
|
(PINF&(1<<0) ? 0 : (1<<0)) |
|
||||||
|
(PINF&(1<<1) ? 0 : (1<<1)) |
|
||||||
|
(PINF&(1<<4) ? 0 : (1<<2)) |
|
||||||
|
(PINF&(1<<5) ? 0 : (1<<3)) |
|
||||||
|
(PINF&(1<<6) ? 0 : (1<<4)) |
|
||||||
|
(PINF&(1<<7) ? 0 : (1<<5)) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Row pin configuration
|
||||||
|
*
|
||||||
|
* Teensy
|
||||||
|
* row: 7 8 9 10 11 12 13
|
||||||
|
* pin: B0 B1 B2 B3 D2 D3 C6
|
||||||
|
*
|
||||||
|
* MCP23018
|
||||||
|
* row: 0 1 2 3 4 5 6
|
||||||
|
* pin: A0 A1 A2 A3 A4 A5 A6
|
||||||
|
*/
|
||||||
|
static void unselect_rows(void)
|
||||||
|
{
|
||||||
|
// unselect on mcp23018
|
||||||
|
if (mcp23018_status) { // if there was an error
|
||||||
|
// do nothing
|
||||||
|
} else {
|
||||||
|
// set all rows hi-Z : 1
|
||||||
|
mcp23018_status = i2c_start(I2C_ADDR_WRITE); if (mcp23018_status) goto out;
|
||||||
|
mcp23018_status = i2c_write(GPIOA); if (mcp23018_status) goto out;
|
||||||
|
mcp23018_status = i2c_write( 0xFF
|
||||||
|
& ~(0<<7)
|
||||||
|
); if (mcp23018_status) goto out;
|
||||||
|
out:
|
||||||
|
i2c_stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// unselect on teensy
|
||||||
|
// Hi-Z(DDR:0, PORT:0) to unselect
|
||||||
|
DDRB &= ~(1<<0 | 1<<1 | 1<<2 | 1<<3);
|
||||||
|
PORTB &= ~(1<<0 | 1<<1 | 1<<2 | 1<<3);
|
||||||
|
DDRD &= ~(1<<2 | 1<<3);
|
||||||
|
PORTD &= ~(1<<2 | 1<<3);
|
||||||
|
DDRC &= ~(1<<6);
|
||||||
|
PORTC &= ~(1<<6);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void select_row(uint8_t row)
|
||||||
|
{
|
||||||
|
if (row < 7) {
|
||||||
|
// select on mcp23018
|
||||||
|
if (mcp23018_status) { // if there was an error
|
||||||
|
// do nothing
|
||||||
|
} else {
|
||||||
|
// set active row low : 0
|
||||||
|
// set other rows hi-Z : 1
|
||||||
|
mcp23018_status = i2c_start(I2C_ADDR_WRITE); if (mcp23018_status) goto out;
|
||||||
|
mcp23018_status = i2c_write(GPIOA); if (mcp23018_status) goto out;
|
||||||
|
mcp23018_status = i2c_write( 0xFF & ~(1<<row)
|
||||||
|
& ~(0<<7)
|
||||||
|
); if (mcp23018_status) goto out;
|
||||||
|
out:
|
||||||
|
i2c_stop();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// select on teensy
|
||||||
|
// Output low(DDR:1, PORT:0) to select
|
||||||
|
switch (row) {
|
||||||
|
case 7:
|
||||||
|
DDRB |= (1<<0);
|
||||||
|
PORTB &= ~(1<<0);
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
DDRB |= (1<<1);
|
||||||
|
PORTB &= ~(1<<1);
|
||||||
|
break;
|
||||||
|
case 9:
|
||||||
|
DDRB |= (1<<2);
|
||||||
|
PORTB &= ~(1<<2);
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
DDRB |= (1<<3);
|
||||||
|
PORTB &= ~(1<<3);
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
DDRD |= (1<<2);
|
||||||
|
PORTD &= ~(1<<3);
|
||||||
|
break;
|
||||||
|
case 12:
|
||||||
|
DDRD |= (1<<3);
|
||||||
|
PORTD &= ~(1<<3);
|
||||||
|
break;
|
||||||
|
case 13:
|
||||||
|
DDRC |= (1<<6);
|
||||||
|
PORTC &= ~(1<<6);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
#----------------------------------------------------------------------------
|
||||||
|
# On command line:
|
||||||
|
#
|
||||||
|
# make = Make software.
|
||||||
|
#
|
||||||
|
# make clean = Clean out built project files.
|
||||||
|
#
|
||||||
|
# That's pretty much all you need. To compile, always go make clean,
|
||||||
|
# followed by make.
|
||||||
|
#
|
||||||
|
# For advanced users only:
|
||||||
|
# make teensy = Download the hex file to the device, using teensy_loader_cli.
|
||||||
|
# (must have teensy_loader_cli installed).
|
||||||
|
#
|
||||||
|
#----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# # project specific files
|
||||||
|
SRC = twimaster.c \
|
||||||
|
matrix.c
|
||||||
|
|
||||||
|
# MCU name
|
||||||
|
MCU = atmega32u4
|
||||||
|
|
||||||
|
# Processor frequency.
|
||||||
|
# This will define a symbol, F_CPU, in all source code files equal to the
|
||||||
|
# processor frequency in Hz. You can then use this symbol in your source code to
|
||||||
|
# calculate timings. Do NOT tack on a 'UL' at the end, this will be done
|
||||||
|
# automatically to create a 32-bit value in your source code.
|
||||||
|
#
|
||||||
|
# This will be an integer division of F_USB below, as it is sourced by
|
||||||
|
# F_USB after it has run through any CPU prescalers. Note that this value
|
||||||
|
# does not *change* the processor frequency - it should merely be updated to
|
||||||
|
# reflect the processor speed set externally so that the code can use accurate
|
||||||
|
# software delays.
|
||||||
|
F_CPU = 16000000
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# LUFA specific
|
||||||
|
#
|
||||||
|
# Target architecture (see library "Board Types" documentation).
|
||||||
|
ARCH = AVR8
|
||||||
|
|
||||||
|
# Input clock frequency.
|
||||||
|
# This will define a symbol, F_USB, in all source code files equal to the
|
||||||
|
# input clock frequency (before any prescaling is performed) in Hz. This value may
|
||||||
|
# differ from F_CPU if prescaling is used on the latter, and is required as the
|
||||||
|
# raw input clock is fed directly to the PLL sections of the AVR for high speed
|
||||||
|
# clock generation for the USB and other AVR subsections. Do NOT tack on a 'UL'
|
||||||
|
# at the end, this will be done automatically to create a 32-bit value in your
|
||||||
|
# source code.
|
||||||
|
#
|
||||||
|
# If no clock division is performed on the input clock inside the AVR (via the
|
||||||
|
# CPU clock adjust registers or the clock division fuses), this will be equal to F_CPU.
|
||||||
|
F_USB = $(F_CPU)
|
||||||
|
|
||||||
|
# Interrupt driven control endpoint task(+60)
|
||||||
|
OPT_DEFS += -DINTERRUPT_CONTROL_ENDPOINT
|
||||||
|
|
||||||
|
|
||||||
|
# Boot Section Size in *bytes*
|
||||||
|
# Teensy halfKay 512
|
||||||
|
# Teensy++ halfKay 1024
|
||||||
|
# Atmel DFU loader 4096
|
||||||
|
# LUFA bootloader 4096
|
||||||
|
# USBaspLoader 2048
|
||||||
|
OPT_DEFS += -DBOOTLOADER_SIZE=512
|
||||||
|
|
||||||
|
|
||||||
|
# Build Options
|
||||||
|
# comment out to disable the options.
|
||||||
|
#
|
||||||
|
|
||||||
|
ifndef QUANTUM_DIR
|
||||||
|
include ../../../Makefile
|
||||||
|
endif
|
|
@ -0,0 +1,208 @@
|
||||||
|
/*************************************************************************
|
||||||
|
* Title: I2C master library using hardware TWI interface
|
||||||
|
* Author: Peter Fleury <pfleury@gmx.ch> http://jump.to/fleury
|
||||||
|
* File: $Id: twimaster.c,v 1.3 2005/07/02 11:14:21 Peter Exp $
|
||||||
|
* Software: AVR-GCC 3.4.3 / avr-libc 1.2.3
|
||||||
|
* Target: any AVR device with hardware TWI
|
||||||
|
* Usage: API compatible with I2C Software Library i2cmaster.h
|
||||||
|
**************************************************************************/
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <compat/twi.h>
|
||||||
|
|
||||||
|
#include <i2cmaster.h>
|
||||||
|
|
||||||
|
|
||||||
|
/* define CPU frequency in Mhz here if not defined in Makefile */
|
||||||
|
#ifndef F_CPU
|
||||||
|
#define F_CPU 16000000UL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* I2C clock in Hz */
|
||||||
|
#define SCL_CLOCK 400000L
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
Initialization of the I2C bus interface. Need to be called only once
|
||||||
|
*************************************************************************/
|
||||||
|
void i2c_init(void)
|
||||||
|
{
|
||||||
|
/* initialize TWI clock
|
||||||
|
* minimal values in Bit Rate Register (TWBR) and minimal Prescaler
|
||||||
|
* bits in the TWI Status Register should give us maximal possible
|
||||||
|
* I2C bus speed - about 444 kHz
|
||||||
|
*
|
||||||
|
* for more details, see 20.5.2 in ATmega16/32 secification
|
||||||
|
*/
|
||||||
|
|
||||||
|
TWSR = 0; /* no prescaler */
|
||||||
|
TWBR = 10; /* must be >= 10 for stable operation */
|
||||||
|
|
||||||
|
}/* i2c_init */
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
Issues a start condition and sends address and transfer direction.
|
||||||
|
return 0 = device accessible, 1= failed to access device
|
||||||
|
*************************************************************************/
|
||||||
|
unsigned char i2c_start(unsigned char address)
|
||||||
|
{
|
||||||
|
uint8_t twst;
|
||||||
|
|
||||||
|
// send START condition
|
||||||
|
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
|
||||||
|
|
||||||
|
// wait until transmission completed
|
||||||
|
while(!(TWCR & (1<<TWINT)));
|
||||||
|
|
||||||
|
// check value of TWI Status Register. Mask prescaler bits.
|
||||||
|
twst = TW_STATUS & 0xF8;
|
||||||
|
if ( (twst != TW_START) && (twst != TW_REP_START)) return 1;
|
||||||
|
|
||||||
|
// send device address
|
||||||
|
TWDR = address;
|
||||||
|
TWCR = (1<<TWINT) | (1<<TWEN);
|
||||||
|
|
||||||
|
// wail until transmission completed and ACK/NACK has been received
|
||||||
|
while(!(TWCR & (1<<TWINT)));
|
||||||
|
|
||||||
|
// check value of TWI Status Register. Mask prescaler bits.
|
||||||
|
twst = TW_STATUS & 0xF8;
|
||||||
|
if ( (twst != TW_MT_SLA_ACK) && (twst != TW_MR_SLA_ACK) ) return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}/* i2c_start */
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
Issues a start condition and sends address and transfer direction.
|
||||||
|
If device is busy, use ack polling to wait until device is ready
|
||||||
|
|
||||||
|
Input: address and transfer direction of I2C device
|
||||||
|
*************************************************************************/
|
||||||
|
void i2c_start_wait(unsigned char address)
|
||||||
|
{
|
||||||
|
uint8_t twst;
|
||||||
|
|
||||||
|
|
||||||
|
while ( 1 )
|
||||||
|
{
|
||||||
|
// send START condition
|
||||||
|
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
|
||||||
|
|
||||||
|
// wait until transmission completed
|
||||||
|
while(!(TWCR & (1<<TWINT)));
|
||||||
|
|
||||||
|
// check value of TWI Status Register. Mask prescaler bits.
|
||||||
|
twst = TW_STATUS & 0xF8;
|
||||||
|
if ( (twst != TW_START) && (twst != TW_REP_START)) continue;
|
||||||
|
|
||||||
|
// send device address
|
||||||
|
TWDR = address;
|
||||||
|
TWCR = (1<<TWINT) | (1<<TWEN);
|
||||||
|
|
||||||
|
// wail until transmission completed
|
||||||
|
while(!(TWCR & (1<<TWINT)));
|
||||||
|
|
||||||
|
// check value of TWI Status Register. Mask prescaler bits.
|
||||||
|
twst = TW_STATUS & 0xF8;
|
||||||
|
if ( (twst == TW_MT_SLA_NACK )||(twst ==TW_MR_DATA_NACK) )
|
||||||
|
{
|
||||||
|
/* device busy, send stop condition to terminate write operation */
|
||||||
|
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
|
||||||
|
|
||||||
|
// wait until stop condition is executed and bus released
|
||||||
|
while(TWCR & (1<<TWSTO));
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
//if( twst != TW_MT_SLA_ACK) return 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}/* i2c_start_wait */
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
Issues a repeated start condition and sends address and transfer direction
|
||||||
|
|
||||||
|
Input: address and transfer direction of I2C device
|
||||||
|
|
||||||
|
Return: 0 device accessible
|
||||||
|
1 failed to access device
|
||||||
|
*************************************************************************/
|
||||||
|
unsigned char i2c_rep_start(unsigned char address)
|
||||||
|
{
|
||||||
|
return i2c_start( address );
|
||||||
|
|
||||||
|
}/* i2c_rep_start */
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
Terminates the data transfer and releases the I2C bus
|
||||||
|
*************************************************************************/
|
||||||
|
void i2c_stop(void)
|
||||||
|
{
|
||||||
|
/* send stop condition */
|
||||||
|
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
|
||||||
|
|
||||||
|
// wait until stop condition is executed and bus released
|
||||||
|
while(TWCR & (1<<TWSTO));
|
||||||
|
|
||||||
|
}/* i2c_stop */
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
Send one byte to I2C device
|
||||||
|
|
||||||
|
Input: byte to be transfered
|
||||||
|
Return: 0 write successful
|
||||||
|
1 write failed
|
||||||
|
*************************************************************************/
|
||||||
|
unsigned char i2c_write( unsigned char data )
|
||||||
|
{
|
||||||
|
uint8_t twst;
|
||||||
|
|
||||||
|
// send data to the previously addressed device
|
||||||
|
TWDR = data;
|
||||||
|
TWCR = (1<<TWINT) | (1<<TWEN);
|
||||||
|
|
||||||
|
// wait until transmission completed
|
||||||
|
while(!(TWCR & (1<<TWINT)));
|
||||||
|
|
||||||
|
// check value of TWI Status Register. Mask prescaler bits
|
||||||
|
twst = TW_STATUS & 0xF8;
|
||||||
|
if( twst != TW_MT_DATA_ACK) return 1;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}/* i2c_write */
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
Read one byte from the I2C device, request more data from device
|
||||||
|
|
||||||
|
Return: byte read from I2C device
|
||||||
|
*************************************************************************/
|
||||||
|
unsigned char i2c_readAck(void)
|
||||||
|
{
|
||||||
|
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA);
|
||||||
|
while(!(TWCR & (1<<TWINT)));
|
||||||
|
|
||||||
|
return TWDR;
|
||||||
|
|
||||||
|
}/* i2c_readAck */
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
Read one byte from the I2C device, read is followed by a stop condition
|
||||||
|
|
||||||
|
Return: byte read from I2C device
|
||||||
|
*************************************************************************/
|
||||||
|
unsigned char i2c_readNak(void)
|
||||||
|
{
|
||||||
|
TWCR = (1<<TWINT) | (1<<TWEN);
|
||||||
|
while(!(TWCR & (1<<TWINT)));
|
||||||
|
|
||||||
|
return TWDR;
|
||||||
|
|
||||||
|
}/* i2c_readNak */
|
|
@ -0,0 +1,710 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""Compiler for keymap.c files
|
||||||
|
|
||||||
|
This scrip will generate a keymap.c file from a simple
|
||||||
|
markdown file with a specific layout.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python compile_keymap.py INPUT_PATH [OUTPUT_PATH]
|
||||||
|
"""
|
||||||
|
from __future__ import division
|
||||||
|
from __future__ import print_function
|
||||||
|
from __future__ import absolute_import
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import os
|
||||||
|
import io
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
import unicodedata
|
||||||
|
import collections
|
||||||
|
import itertools as it
|
||||||
|
|
||||||
|
PY2 = sys.version_info.major == 2
|
||||||
|
|
||||||
|
if PY2:
|
||||||
|
chr = unichr
|
||||||
|
|
||||||
|
|
||||||
|
KEYBOARD_LAYOUTS = {
|
||||||
|
# These map positions in the parsed layout to
|
||||||
|
# positions in the KEYMAP MATRIX
|
||||||
|
'ergodox_ez': [
|
||||||
|
[ 0, 1, 2, 3, 4, 5, 6], [38, 39, 40, 41, 42, 43, 44],
|
||||||
|
[ 7, 8, 9, 10, 11, 12, 13], [45, 46, 47, 48, 49, 50, 51],
|
||||||
|
[14, 15, 16, 17, 18, 19 ], [ 52, 53, 54, 55, 56, 57],
|
||||||
|
[20, 21, 22, 23, 24, 25, 26], [58, 59, 60, 61, 62, 63, 64],
|
||||||
|
[27, 28, 29, 30, 31 ], [ 65, 66, 67, 68, 69],
|
||||||
|
[ 32, 33], [70, 71 ],
|
||||||
|
[ 34], [72 ],
|
||||||
|
[ 35, 36, 37], [73, 74, 75 ],
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
ROW_INDENTS = {
|
||||||
|
'ergodox_ez': [0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 5, 0, 6, 0, 4, 0]
|
||||||
|
}
|
||||||
|
|
||||||
|
BLANK_LAYOUTS = [
|
||||||
|
# Compact Layout
|
||||||
|
"""
|
||||||
|
.------------------------------------.------------------------------------.
|
||||||
|
| | | | | | | | | | | | | | |
|
||||||
|
!-----+----+----+----+----+----------!-----+----+----+----+----+----+-----!
|
||||||
|
| | | | | | | | | | | | | | |
|
||||||
|
!-----+----+----+----x----x----! ! !----x----x----+----+----+-----!
|
||||||
|
| | | | | | |-----!-----! | | | | | |
|
||||||
|
!-----+----+----+----x----x----! ! !----x----x----+----+----+-----!
|
||||||
|
| | | | | | | | | | | | | | |
|
||||||
|
'-----+----+----+----+----+----------'----------+----+----+----+----+-----'
|
||||||
|
| | | | | | ! | | | | |
|
||||||
|
'------------------------' '------------------------'
|
||||||
|
.-----------. .-----------.
|
||||||
|
| | | ! | |
|
||||||
|
.-----+-----+-----! !-----+-----+-----.
|
||||||
|
! ! | | ! | ! !
|
||||||
|
! ! !-----! !-----! ! !
|
||||||
|
| | | | ! | | |
|
||||||
|
'-----------------' '-----------------'
|
||||||
|
""",
|
||||||
|
|
||||||
|
# Wide Layout
|
||||||
|
"""
|
||||||
|
.---------------------------------------------. .---------------------------------------------.
|
||||||
|
| | | | | | | | ! | | | | | | |
|
||||||
|
!-------+-----+-----+-----+-----+-------------! !-------+-----+-----+-----+-----+-----+-------!
|
||||||
|
| | | | | | | | ! | | | | | | |
|
||||||
|
!-------+-----+-----+-----x-----x-----! ! ! !-----x-----x-----+-----+-----+-------!
|
||||||
|
| | | | | | |-------! !-------! | | | | | |
|
||||||
|
!-------+-----+-----+-----x-----x-----! ! ! !-----x-----x-----+-----+-----+-------!
|
||||||
|
| | | | | | | | ! | | | | | | |
|
||||||
|
'-------+-----+-----+-----+-----+-------------' '-------------+-----+-----+-----+-----+-------'
|
||||||
|
| | | | | | ! | | | | |
|
||||||
|
'------------------------------' '------------------------------'
|
||||||
|
.---------------. .---------------.
|
||||||
|
| | | ! | |
|
||||||
|
.-------+-------+-------! !-------+-------+-------.
|
||||||
|
! ! | | ! | ! !
|
||||||
|
! ! !-------! !-------! ! !
|
||||||
|
| | | | ! | | |
|
||||||
|
'-----------------------' '-----------------------'
|
||||||
|
""",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULT_CONFIG = {
|
||||||
|
"keymaps_includes": [
|
||||||
|
"keymap_common.h",
|
||||||
|
],
|
||||||
|
'filler': "-+.'!:x",
|
||||||
|
'separator': "|",
|
||||||
|
'default_key_prefix': ["KC_"],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SECTIONS = [
|
||||||
|
'layout_config',
|
||||||
|
'layers',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# Markdown Parsing
|
||||||
|
|
||||||
|
ONELINE_COMMENT_RE = re.compile(r"""
|
||||||
|
^ # comment must be at the start of the line
|
||||||
|
\s* # arbitrary whitespace
|
||||||
|
// # start of the comment
|
||||||
|
(.*) # the comment
|
||||||
|
$ # until the end of line
|
||||||
|
""", re.MULTILINE | re.VERBOSE
|
||||||
|
)
|
||||||
|
|
||||||
|
INLINE_COMMENT_RE = re.compile(r"""
|
||||||
|
([\,\"\[\]\{\}\d]) # anythig that might end a expression
|
||||||
|
\s+ # comment must be preceded by whitespace
|
||||||
|
// # start of the comment
|
||||||
|
\s # and succeded by whitespace
|
||||||
|
(?:[^\"\]\}\{\[]*) # the comment (except things which might be json)
|
||||||
|
$ # until the end of line
|
||||||
|
""", re.MULTILINE | re.VERBOSE)
|
||||||
|
|
||||||
|
TRAILING_COMMA_RE = re.compile(r"""
|
||||||
|
, # the comma
|
||||||
|
(?:\s*) # arbitrary whitespace
|
||||||
|
$ # only works if the trailing comma is followed by newline
|
||||||
|
(\s*) # arbitrary whitespace
|
||||||
|
([\]\}]) # end of an array or object
|
||||||
|
""", re.MULTILINE | re.VERBOSE)
|
||||||
|
|
||||||
|
|
||||||
|
def loads(raw_data):
|
||||||
|
if isinstance(raw_data, bytes):
|
||||||
|
raw_data = raw_data.decode('utf-8')
|
||||||
|
|
||||||
|
raw_data = ONELINE_COMMENT_RE.sub(r"", raw_data)
|
||||||
|
raw_data = INLINE_COMMENT_RE.sub(r"\1", raw_data)
|
||||||
|
raw_data = TRAILING_COMMA_RE.sub(r"\1\2", raw_data)
|
||||||
|
return json.loads(raw_data)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_config(path):
|
||||||
|
def reset_section():
|
||||||
|
section.update({
|
||||||
|
'name': section.get('name', ""),
|
||||||
|
'sub_name': "",
|
||||||
|
'start_line': -1,
|
||||||
|
'end_line': -1,
|
||||||
|
'code_lines': [],
|
||||||
|
})
|
||||||
|
|
||||||
|
def start_section(line_index, line):
|
||||||
|
end_section()
|
||||||
|
if line.startswith("# "):
|
||||||
|
name = line[2:]
|
||||||
|
elif line.startswith("## "):
|
||||||
|
name = line[3:]
|
||||||
|
else:
|
||||||
|
name = ""
|
||||||
|
|
||||||
|
name = name.strip().replace(" ", "_").lower()
|
||||||
|
if name in SECTIONS:
|
||||||
|
section['name'] = name
|
||||||
|
else:
|
||||||
|
section['sub_name'] = name
|
||||||
|
section['start_line'] = line_index
|
||||||
|
|
||||||
|
def end_section():
|
||||||
|
if section['start_line'] >= 0:
|
||||||
|
if section['name'] == 'layout_config':
|
||||||
|
config.update(loads("\n".join(
|
||||||
|
section['code_lines']
|
||||||
|
)))
|
||||||
|
elif section['sub_name'].startswith('layer'):
|
||||||
|
layer_name = section['sub_name']
|
||||||
|
config['layer_lines'][layer_name] = section['code_lines']
|
||||||
|
|
||||||
|
reset_section()
|
||||||
|
|
||||||
|
def amend_section(line_index, line):
|
||||||
|
section['end_line'] = line_index
|
||||||
|
section['code_lines'].append(line)
|
||||||
|
|
||||||
|
config = DEFAULT_CONFIG.copy()
|
||||||
|
config.update({
|
||||||
|
'layer_lines': collections.OrderedDict(),
|
||||||
|
'macro_ids': {'UM'},
|
||||||
|
'unicode_macros': {},
|
||||||
|
})
|
||||||
|
|
||||||
|
section = {}
|
||||||
|
reset_section()
|
||||||
|
|
||||||
|
with io.open(path, encoding="utf-8") as fh:
|
||||||
|
for i, line in enumerate(fh):
|
||||||
|
if line.startswith("#"):
|
||||||
|
start_section(i, line)
|
||||||
|
elif line.startswith(" "):
|
||||||
|
amend_section(i, line[4:])
|
||||||
|
else:
|
||||||
|
# TODO: maybe parse description
|
||||||
|
pass
|
||||||
|
|
||||||
|
end_section()
|
||||||
|
assert 'layout' in config
|
||||||
|
return config
|
||||||
|
|
||||||
|
# header file parsing
|
||||||
|
|
||||||
|
IF0_RE = re.compile(r"""
|
||||||
|
^
|
||||||
|
#if 0
|
||||||
|
$.*?
|
||||||
|
#endif
|
||||||
|
""", re.MULTILINE | re.DOTALL | re.VERBOSE)
|
||||||
|
|
||||||
|
|
||||||
|
COMMENT_RE = re.compile(r"""
|
||||||
|
/\*
|
||||||
|
.*?
|
||||||
|
\*/"
|
||||||
|
""", re.MULTILINE | re.DOTALL | re.VERBOSE)
|
||||||
|
|
||||||
|
|
||||||
|
def read_header_file(path):
|
||||||
|
with io.open(path, encoding="utf-8") as fh:
|
||||||
|
data = fh.read()
|
||||||
|
data, _ = COMMENT_RE.subn("", data)
|
||||||
|
data, _ = IF0_RE.subn("", data)
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def regex_partial(re_str_fmt, flags):
|
||||||
|
def partial(*args, **kwargs):
|
||||||
|
re_str = re_str_fmt.format(*args, **kwargs)
|
||||||
|
return re.compile(re_str, flags)
|
||||||
|
return partial
|
||||||
|
|
||||||
|
|
||||||
|
KEYDEF_REP = regex_partial(r"""
|
||||||
|
#define
|
||||||
|
\s
|
||||||
|
(
|
||||||
|
(?:{}) # the prefixes
|
||||||
|
(?:\w+) # the key name
|
||||||
|
) # capture group end
|
||||||
|
""", re.MULTILINE | re.DOTALL | re.VERBOSE)
|
||||||
|
|
||||||
|
|
||||||
|
ENUM_RE = re.compile(r"""
|
||||||
|
(
|
||||||
|
enum
|
||||||
|
\s\w+\s
|
||||||
|
\{
|
||||||
|
.*? # the enum content
|
||||||
|
\}
|
||||||
|
;
|
||||||
|
) # capture group end
|
||||||
|
""", re.MULTILINE | re.DOTALL | re.VERBOSE)
|
||||||
|
|
||||||
|
|
||||||
|
ENUM_KEY_REP = regex_partial(r"""
|
||||||
|
(
|
||||||
|
{} # the prefixes
|
||||||
|
\w+ # the key name
|
||||||
|
) # capture group end
|
||||||
|
""", re.MULTILINE | re.DOTALL | re.VERBOSE)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_keydefs(config, data):
|
||||||
|
prefix_options = "|".join(config['key_prefixes'])
|
||||||
|
keydef_re = KEYDEF_REP(prefix_options)
|
||||||
|
enum_key_re = ENUM_KEY_REP(prefix_options)
|
||||||
|
for match in keydef_re.finditer(data):
|
||||||
|
yield match.groups()[0]
|
||||||
|
|
||||||
|
for enum_match in ENUM_RE.finditer(data):
|
||||||
|
enum = enum_match.groups()[0]
|
||||||
|
for key_match in enum_key_re.finditer(enum):
|
||||||
|
yield key_match.groups()[0]
|
||||||
|
|
||||||
|
|
||||||
|
def parse_valid_keys(config, out_path):
|
||||||
|
basepath = os.path.abspath(os.path.join(os.path.dirname(out_path)))
|
||||||
|
dirpaths = []
|
||||||
|
subpaths = []
|
||||||
|
while len(subpaths) < 6:
|
||||||
|
path = os.path.join(basepath, *subpaths)
|
||||||
|
dirpaths.append(path)
|
||||||
|
dirpaths.append(os.path.join(path, "tmk_core", "common"))
|
||||||
|
dirpaths.append(os.path.join(path, "quantum"))
|
||||||
|
subpaths.append('..')
|
||||||
|
|
||||||
|
includes = set(config['keymaps_includes'])
|
||||||
|
includes.add("keycode.h")
|
||||||
|
|
||||||
|
valid_keycodes = set()
|
||||||
|
for dirpath, include in it.product(dirpaths, includes):
|
||||||
|
include_path = os.path.join(dirpath, include)
|
||||||
|
if os.path.exists(include_path):
|
||||||
|
header_data = read_header_file(include_path)
|
||||||
|
valid_keycodes.update(
|
||||||
|
parse_keydefs(config, header_data)
|
||||||
|
)
|
||||||
|
return valid_keycodes
|
||||||
|
|
||||||
|
|
||||||
|
# Keymap Parsing
|
||||||
|
|
||||||
|
def iter_raw_codes(layer_lines, filler, separator):
|
||||||
|
filler_re = re.compile("[" + filler + " ]")
|
||||||
|
for line in layer_lines:
|
||||||
|
line, _ = filler_re.subn("", line.strip())
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
codes = line.split(separator)
|
||||||
|
for code in codes[1:-1]:
|
||||||
|
yield code
|
||||||
|
|
||||||
|
|
||||||
|
def iter_indexed_codes(raw_codes, key_indexes):
|
||||||
|
key_rows = {}
|
||||||
|
key_indexes_flat = []
|
||||||
|
|
||||||
|
for row_index, key_indexes in enumerate(key_indexes):
|
||||||
|
for key_index in key_indexes:
|
||||||
|
key_rows[key_index] = row_index
|
||||||
|
key_indexes_flat.extend(key_indexes)
|
||||||
|
assert len(raw_codes) == len(key_indexes_flat)
|
||||||
|
for raw_code, key_index in zip(raw_codes, key_indexes_flat):
|
||||||
|
# we keep track of the row mostly for layout purposes
|
||||||
|
yield raw_code, key_index, key_rows[key_index]
|
||||||
|
|
||||||
|
|
||||||
|
LAYER_CHANGE_RE = re.compile(r"""
|
||||||
|
(DF|TG|MO)\(\d+\)
|
||||||
|
""", re.VERBOSE)
|
||||||
|
|
||||||
|
|
||||||
|
MACRO_RE = re.compile(r"""
|
||||||
|
M\(\w+\)
|
||||||
|
""", re.VERBOSE)
|
||||||
|
|
||||||
|
|
||||||
|
UNICODE_RE = re.compile(r"""
|
||||||
|
U[0-9A-F]{4}
|
||||||
|
""", re.VERBOSE)
|
||||||
|
|
||||||
|
|
||||||
|
NON_CODE = re.compile(r"""
|
||||||
|
^[^A-Z0-9_]$
|
||||||
|
""", re.VERBOSE)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_uni_code(raw_code):
|
||||||
|
macro_id = "UC_" + (
|
||||||
|
unicodedata.name(raw_code)
|
||||||
|
.replace(" ", "_")
|
||||||
|
.replace("-", "_")
|
||||||
|
)
|
||||||
|
code = "M({})".format(macro_id)
|
||||||
|
uc_hex = "{:04X}".format(ord(raw_code))
|
||||||
|
return code, macro_id, uc_hex
|
||||||
|
|
||||||
|
|
||||||
|
def parse_key_code(raw_code, key_prefixes, valid_keycodes):
|
||||||
|
if raw_code in valid_keycodes:
|
||||||
|
return raw_code
|
||||||
|
|
||||||
|
for prefix in key_prefixes:
|
||||||
|
code = prefix + raw_code
|
||||||
|
if code in valid_keycodes:
|
||||||
|
return code
|
||||||
|
|
||||||
|
|
||||||
|
def parse_code(raw_code, key_prefixes, valid_keycodes):
|
||||||
|
if not raw_code:
|
||||||
|
return 'KC_TRNS', None, None
|
||||||
|
|
||||||
|
if LAYER_CHANGE_RE.match(raw_code):
|
||||||
|
return raw_code, None, None
|
||||||
|
|
||||||
|
if MACRO_RE.match(raw_code):
|
||||||
|
macro_id = raw_code[2:-1]
|
||||||
|
return raw_code, macro_id, None
|
||||||
|
|
||||||
|
if UNICODE_RE.match(raw_code):
|
||||||
|
hex_code = raw_code[1:]
|
||||||
|
return parse_uni_code(chr(int(hex_code, 16)))
|
||||||
|
|
||||||
|
if NON_CODE.match(raw_code):
|
||||||
|
return parse_uni_code(raw_code)
|
||||||
|
|
||||||
|
code = parse_key_code(raw_code, key_prefixes, valid_keycodes)
|
||||||
|
return code, None, None
|
||||||
|
|
||||||
|
|
||||||
|
def parse_keymap(config, key_indexes, layer_lines, valid_keycodes):
|
||||||
|
keymap = {}
|
||||||
|
raw_codes = list(iter_raw_codes(
|
||||||
|
layer_lines, config['filler'], config['separator']
|
||||||
|
))
|
||||||
|
indexed_codes = iter_indexed_codes(raw_codes, key_indexes)
|
||||||
|
key_prefixes = config['key_prefixes']
|
||||||
|
for raw_code, key_index, row_index in indexed_codes:
|
||||||
|
code, macro_id, uc_hex = parse_code(
|
||||||
|
raw_code, key_prefixes, valid_keycodes
|
||||||
|
)
|
||||||
|
# TODO: line numbers for invalid codes
|
||||||
|
err_msg = "Could not parse key '{}' on row {}".format(
|
||||||
|
raw_code, row_index
|
||||||
|
)
|
||||||
|
assert code is not None, err_msg
|
||||||
|
# print(repr(raw_code), repr(code), macro_id, uc_hex)
|
||||||
|
if macro_id:
|
||||||
|
config['macro_ids'].add(macro_id)
|
||||||
|
if uc_hex:
|
||||||
|
config['unicode_macros'][macro_id] = uc_hex
|
||||||
|
keymap[key_index] = (code, row_index)
|
||||||
|
return keymap
|
||||||
|
|
||||||
|
|
||||||
|
def parse_keymaps(config, valid_keycodes):
|
||||||
|
keymaps = collections.OrderedDict()
|
||||||
|
key_indexes = config.get(
|
||||||
|
'key_indexes', KEYBOARD_LAYOUTS[config['layout']]
|
||||||
|
)
|
||||||
|
# TODO: maybe validate key_indexes
|
||||||
|
|
||||||
|
for layer_name, layer_lines, in config['layer_lines'].items():
|
||||||
|
keymaps[layer_name] = parse_keymap(
|
||||||
|
config, key_indexes, layer_lines, valid_keycodes
|
||||||
|
)
|
||||||
|
return keymaps
|
||||||
|
|
||||||
|
# keymap.c output
|
||||||
|
|
||||||
|
USERCODE = """
|
||||||
|
// Runs just one time when the keyboard initializes.
|
||||||
|
void matrix_init_user(void) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// Runs constantly in the background, in a loop.
|
||||||
|
void matrix_scan_user(void) {
|
||||||
|
uint8_t layer = biton32(layer_state);
|
||||||
|
|
||||||
|
ergodox_board_led_off();
|
||||||
|
ergodox_right_led_1_off();
|
||||||
|
ergodox_right_led_2_off();
|
||||||
|
ergodox_right_led_3_off();
|
||||||
|
switch (layer) {
|
||||||
|
case L1:
|
||||||
|
ergodox_right_led_1_on();
|
||||||
|
break;
|
||||||
|
case L2:
|
||||||
|
ergodox_right_led_2_on();
|
||||||
|
break;
|
||||||
|
case L3:
|
||||||
|
ergodox_right_led_3_on();
|
||||||
|
break;
|
||||||
|
case L4:
|
||||||
|
ergodox_right_led_1_on();
|
||||||
|
ergodox_right_led_2_on();
|
||||||
|
break;
|
||||||
|
case L5:
|
||||||
|
ergodox_right_led_1_on();
|
||||||
|
ergodox_right_led_3_on();
|
||||||
|
break;
|
||||||
|
// case L6:
|
||||||
|
// ergodox_right_led_2_on();
|
||||||
|
// ergodox_right_led_3_on();
|
||||||
|
// break;
|
||||||
|
// case L7:
|
||||||
|
// ergodox_right_led_1_on();
|
||||||
|
// ergodox_right_led_2_on();
|
||||||
|
// ergodox_right_led_3_on();
|
||||||
|
// break;
|
||||||
|
default:
|
||||||
|
ergodox_board_led_off();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
"""
|
||||||
|
|
||||||
|
MACROCODE = """
|
||||||
|
#define UC_MODE_WIN 0
|
||||||
|
#define UC_MODE_LINUX 1
|
||||||
|
#define UC_MODE_OSX 2
|
||||||
|
|
||||||
|
// TODO: allow default mode to be configured
|
||||||
|
static uint16_t unicode_mode = UC_MODE_WIN;
|
||||||
|
|
||||||
|
uint16_t hextokeycode(uint8_t hex) {{
|
||||||
|
if (hex == 0x0) {{
|
||||||
|
return KC_P0;
|
||||||
|
}}
|
||||||
|
if (hex < 0xA) {{
|
||||||
|
return KC_P1 + (hex - 0x1);
|
||||||
|
}}
|
||||||
|
return KC_A + (hex - 0xA);
|
||||||
|
}}
|
||||||
|
|
||||||
|
void unicode_action_function(uint16_t hi, uint16_t lo) {{
|
||||||
|
switch (unicode_mode) {{
|
||||||
|
case UC_MODE_WIN:
|
||||||
|
register_code(KC_LALT);
|
||||||
|
|
||||||
|
register_code(KC_PPLS);
|
||||||
|
unregister_code(KC_PPLS);
|
||||||
|
|
||||||
|
register_code(hextokeycode((hi & 0xF0) >> 4));
|
||||||
|
unregister_code(hextokeycode((hi & 0xF0) >> 4));
|
||||||
|
register_code(hextokeycode((hi & 0x0F)));
|
||||||
|
unregister_code(hextokeycode((hi & 0x0F)));
|
||||||
|
register_code(hextokeycode((lo & 0xF0) >> 4));
|
||||||
|
unregister_code(hextokeycode((lo & 0xF0) >> 4));
|
||||||
|
register_code(hextokeycode((lo & 0x0F)));
|
||||||
|
unregister_code(hextokeycode((lo & 0x0F)));
|
||||||
|
|
||||||
|
unregister_code(KC_LALT);
|
||||||
|
break;
|
||||||
|
case UC_MODE_LINUX:
|
||||||
|
register_code(KC_LCTL);
|
||||||
|
register_code(KC_LSFT);
|
||||||
|
|
||||||
|
register_code(KC_U);
|
||||||
|
unregister_code(KC_U);
|
||||||
|
|
||||||
|
register_code(hextokeycode((hi & 0xF0) >> 4));
|
||||||
|
unregister_code(hextokeycode((hi & 0xF0) >> 4));
|
||||||
|
register_code(hextokeycode((hi & 0x0F)));
|
||||||
|
unregister_code(hextokeycode((hi & 0x0F)));
|
||||||
|
register_code(hextokeycode((lo & 0xF0) >> 4));
|
||||||
|
unregister_code(hextokeycode((lo & 0xF0) >> 4));
|
||||||
|
register_code(hextokeycode((lo & 0x0F)));
|
||||||
|
unregister_code(hextokeycode((lo & 0x0F)));
|
||||||
|
|
||||||
|
unregister_code(KC_LCTL);
|
||||||
|
unregister_code(KC_LSFT);
|
||||||
|
break;
|
||||||
|
case UC_MODE_OSX:
|
||||||
|
break;
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
|
||||||
|
const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt) {{
|
||||||
|
if (!record->event.pressed) {{
|
||||||
|
return MACRO_NONE;
|
||||||
|
}}
|
||||||
|
// MACRODOWN only works in this function
|
||||||
|
switch(id) {{
|
||||||
|
case UM:
|
||||||
|
unicode_mode = (unicode_mode + 1) % 2;
|
||||||
|
break;
|
||||||
|
{macro_cases}
|
||||||
|
{unicode_macro_cases}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}}
|
||||||
|
return MACRO_NONE;
|
||||||
|
}};
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
UNICODE_MACRO_TEMPLATE = """
|
||||||
|
case {macro_id}:
|
||||||
|
unicode_action_function(0x{hi:02x}, 0x{lo:02x});
|
||||||
|
break;
|
||||||
|
""".strip()
|
||||||
|
|
||||||
|
|
||||||
|
def unicode_macro_cases(config):
|
||||||
|
for macro_id, uc_hex in config['unicode_macros'].items():
|
||||||
|
hi = int(uc_hex, 16) >> 8
|
||||||
|
lo = int(uc_hex, 16) & 0xFF
|
||||||
|
unimacro_keys = ", ".join(
|
||||||
|
"T({})".format(
|
||||||
|
"KP_" + digit if digit.isdigit() else digit
|
||||||
|
) for digit in uc_hex
|
||||||
|
)
|
||||||
|
yield UNICODE_MACRO_TEMPLATE.format(
|
||||||
|
macro_id=macro_id, hi=hi, lo=lo
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def iter_keymap_lines(keymap, row_indents=None):
|
||||||
|
col_widths = {}
|
||||||
|
col = 0
|
||||||
|
# first pass, figure out the column widths
|
||||||
|
prev_row_index = None
|
||||||
|
for code, row_index in keymap.values():
|
||||||
|
if row_index != prev_row_index:
|
||||||
|
col = 0
|
||||||
|
if row_indents:
|
||||||
|
col = row_indents[row_index]
|
||||||
|
col_widths[col] = max(len(code), col_widths.get(col, 0))
|
||||||
|
prev_row_index = row_index
|
||||||
|
col += 1
|
||||||
|
|
||||||
|
# second pass, yield the cell values
|
||||||
|
col = 0
|
||||||
|
prev_row_index = None
|
||||||
|
for key_index in sorted(keymap):
|
||||||
|
code, row_index = keymap[key_index]
|
||||||
|
if row_index != prev_row_index:
|
||||||
|
col = 0
|
||||||
|
yield "\n"
|
||||||
|
if row_indents:
|
||||||
|
for indent_col in range(row_indents[row_index]):
|
||||||
|
pad = " " * (col_widths[indent_col] - 4)
|
||||||
|
yield (" /*-*/" + pad)
|
||||||
|
col = row_indents[row_index]
|
||||||
|
else:
|
||||||
|
yield pad
|
||||||
|
yield " {}".format(code)
|
||||||
|
if key_index < len(keymap) - 1:
|
||||||
|
yield ","
|
||||||
|
# This will be yielded on the next iteration when
|
||||||
|
# we know that we're not at the end of a line.
|
||||||
|
pad = " " * (col_widths[col] - len(code))
|
||||||
|
prev_row_index = row_index
|
||||||
|
col += 1
|
||||||
|
|
||||||
|
|
||||||
|
def iter_keymap_parts(config, keymaps):
|
||||||
|
# includes
|
||||||
|
for include_path in config['keymaps_includes']:
|
||||||
|
yield '#include "{}"\n'.format(include_path)
|
||||||
|
|
||||||
|
yield "\n"
|
||||||
|
|
||||||
|
# definitions
|
||||||
|
for i, macro_id in enumerate(sorted(config['macro_ids'])):
|
||||||
|
yield "#define {} {}\n".format(macro_id, i)
|
||||||
|
|
||||||
|
yield "\n"
|
||||||
|
|
||||||
|
for i, layer_name in enumerate(config['layer_lines']):
|
||||||
|
yield '#define L{0:<3} {0:<5} // {1}\n'.format(i, layer_name)
|
||||||
|
|
||||||
|
yield "\n"
|
||||||
|
|
||||||
|
# keymaps
|
||||||
|
yield "const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n"
|
||||||
|
|
||||||
|
for i, layer_name in enumerate(config['layer_lines']):
|
||||||
|
# comment
|
||||||
|
layer_lines = config['layer_lines'][layer_name]
|
||||||
|
prefixed_lines = " * " + " * ".join(layer_lines)
|
||||||
|
yield "/*\n{} */\n".format(prefixed_lines)
|
||||||
|
|
||||||
|
# keymap codes
|
||||||
|
keymap = keymaps[layer_name]
|
||||||
|
row_indents = ROW_INDENTS.get(config['layout'])
|
||||||
|
keymap_lines = "".join(iter_keymap_lines(keymap, row_indents))
|
||||||
|
yield "[L{0}] = KEYMAP({1}\n),\n".format(i, keymap_lines)
|
||||||
|
|
||||||
|
yield "};\n\n"
|
||||||
|
|
||||||
|
# no idea what this is for
|
||||||
|
yield "const uint16_t PROGMEM fn_actions[] = {};\n"
|
||||||
|
|
||||||
|
# macros
|
||||||
|
yield MACROCODE.format(
|
||||||
|
macro_cases="",
|
||||||
|
unicode_macro_cases="\n".join(unicode_macro_cases(config)),
|
||||||
|
)
|
||||||
|
|
||||||
|
# TODO: dynamically create blinking lights
|
||||||
|
yield USERCODE
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv=sys.argv[1:]):
|
||||||
|
if not argv or '-h' in argv or '--help' in argv:
|
||||||
|
print(__doc__)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
in_path = os.path.abspath(argv[0])
|
||||||
|
if not os.path.exists(in_path):
|
||||||
|
print("No such file '{}'".format(in_path))
|
||||||
|
return 1
|
||||||
|
|
||||||
|
if len(argv) > 1:
|
||||||
|
out_path = os.path.abspath(argv[1])
|
||||||
|
else:
|
||||||
|
dirname = os.path.dirname(in_path)
|
||||||
|
out_path = os.path.join(dirname, "keymap.c")
|
||||||
|
|
||||||
|
config = parse_config(in_path)
|
||||||
|
valid_keys = parse_valid_keys(config, out_path)
|
||||||
|
keymaps = parse_keymaps(config, valid_keys)
|
||||||
|
|
||||||
|
with io.open(out_path, mode="w", encoding="utf-8") as fh:
|
||||||
|
for part in iter_keymap_parts(config, keymaps):
|
||||||
|
fh.write(part)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
|
@ -0,0 +1,3 @@
|
||||||
|
# ErgoDox EZ Utilities
|
||||||
|
|
||||||
|
The Python script in this directory, by [mbarkhau](https://github.com/mbarkhau) allows you to write out a basic ErgoDox EZ keymap using Markdown notation, and then transpile it to C, which you can then compile. It's experimental, but if you're not comfortable using C, it's a nice option.
|
Binary file not shown.
After Width: | Height: | Size: 189 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.4 MiB |
|
@ -0,0 +1,183 @@
|
||||||
|
#include "ergodox80.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "action_layer.h"
|
||||||
|
#include "version.h"
|
||||||
|
|
||||||
|
#define BASE 0 // default layer
|
||||||
|
#define SYMB 1 // symbols
|
||||||
|
#define MDIA 2 // media keys
|
||||||
|
|
||||||
|
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
|
||||||
|
/* Keymap 0: Basic layer
|
||||||
|
*
|
||||||
|
* ,--------------------------------------------------. ,--------------------------------------------------.
|
||||||
|
* | = | 1 | 2 | 3 | 4 | 5 | LEFT | | RIGHT| 6 | 7 | 8 | 9 | 0 | - |
|
||||||
|
* |--------+------+------+------+------+-------------| |------+------+------+------+------+------+--------|
|
||||||
|
* | Del | Q | W | E | R | T | L1 | | L1 | Y | U | I | O | P | \ |
|
||||||
|
* |--------+------+------+------+------+------| | | |------+------+------+------+------+--------|
|
||||||
|
* | BkSp | A | S | D | F | G |------| |------| H | J | K | L |; / L2|' / Cmd |
|
||||||
|
* |--------+------+------+------+------+------| Hyper| | Meh |------+------+------+------+------+--------|
|
||||||
|
* | LShift |Z/Ctrl| X | C | V | B | | | | N | M | , | . |//Ctrl| RShift |
|
||||||
|
* `--------+------+------+------+------+-------------' `-------------+------+------+------+------+--------'
|
||||||
|
* |Grv/L1| '" |AltShf| Left | Right| | Up | Down | [ | ] | ~L1 |
|
||||||
|
* `----------------------------------' `----------------------------------'
|
||||||
|
* ,-------------. ,-------------.
|
||||||
|
* | App | LGui | | Alt |Ctrl/Esc|
|
||||||
|
* ,------|------|------| |------+--------+------.
|
||||||
|
* | 1 | 2 | Home | | PgUp | 3 | 4 |
|
||||||
|
* |------|------|------| |------|--------|------|
|
||||||
|
* | Space| BkSp | End | | PgDn | Tab |Enter |
|
||||||
|
* `--------------------' `----------------------'
|
||||||
|
*/
|
||||||
|
// If it accepts an argument (i.e, is a function), it doesn't need KC_.
|
||||||
|
// Otherwise, it needs KC_*
|
||||||
|
[BASE] = KEYMAP( // layer 0 : default
|
||||||
|
// left hand
|
||||||
|
KC_EQL, KC_1, KC_2, KC_3, KC_4, KC_5, KC_LEFT,
|
||||||
|
KC_DELT, KC_Q, KC_W, KC_E, KC_R, KC_T, TG(SYMB),
|
||||||
|
KC_BSPC, KC_A, KC_S, KC_D, KC_F, KC_G,
|
||||||
|
KC_LSFT, CTL_T(KC_Z), KC_X, KC_C, KC_V, KC_B, ALL_T(KC_NO),
|
||||||
|
LT(SYMB,KC_GRV),KC_QUOT, LALT(KC_LSFT), KC_LEFT,KC_RGHT,
|
||||||
|
ALT_T(KC_APP), KC_LGUI,
|
||||||
|
KC_1, KC_2, KC_HOME,
|
||||||
|
KC_SPC,KC_BSPC,KC_END,
|
||||||
|
// right hand
|
||||||
|
KC_RGHT, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS,
|
||||||
|
TG(SYMB), KC_Y, KC_U, KC_I, KC_O, KC_P, KC_BSLS,
|
||||||
|
KC_H, KC_J, KC_K, KC_L, LT(MDIA, KC_SCLN),GUI_T(KC_QUOT),
|
||||||
|
MEH_T(KC_NO),KC_N, KC_M, KC_COMM,KC_DOT, CTL_T(KC_SLSH), KC_RSFT,
|
||||||
|
KC_UP, KC_DOWN,KC_LBRC,KC_RBRC, KC_FN1,
|
||||||
|
KC_LALT, CTL_T(KC_ESC),
|
||||||
|
KC_PGUP,KC_3, KC_4,
|
||||||
|
KC_PGDN,KC_TAB, KC_ENT
|
||||||
|
),
|
||||||
|
/* Keymap 1: Symbol Layer
|
||||||
|
*
|
||||||
|
* ,--------------------------------------------------. ,--------------------------------------------------.
|
||||||
|
* |Version | F1 | F2 | F3 | F4 | F5 | | | | F6 | F7 | F8 | F9 | F10 | F11 |
|
||||||
|
* |--------+------+------+------+------+-------------| |------+------+------+------+------+------+--------|
|
||||||
|
* | | ! | @ | { | } | | | | | | Up | 7 | 8 | 9 | * | F12 |
|
||||||
|
* |--------+------+------+------+------+------| | | |------+------+------+------+------+--------|
|
||||||
|
* | | # | $ | ( | ) | ` |------| |------| Down | 4 | 5 | 6 | + | |
|
||||||
|
* |--------+------+------+------+------+------| | | |------+------+------+------+------+--------|
|
||||||
|
* | | % | ^ | [ | ] | ~ | | | | & | 1 | 2 | 3 | \ | |
|
||||||
|
* `--------+------+------+------+------+-------------' `-------------+------+------+------+------+--------'
|
||||||
|
* | | | | | | | | . | 0 | = | |
|
||||||
|
* `----------------------------------' `----------------------------------'
|
||||||
|
* ,-------------. ,-------------.
|
||||||
|
* | | | | | |
|
||||||
|
* ,------|------|------| |------+------+------.
|
||||||
|
* | | | | | | | |
|
||||||
|
* |------|------|------| |------|------|------|
|
||||||
|
* | | | | | | | |
|
||||||
|
* `--------------------' `--------------------'
|
||||||
|
*/
|
||||||
|
// SYMBOLS
|
||||||
|
[SYMB] = KEYMAP(
|
||||||
|
// left hand
|
||||||
|
M(0), KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_TRNS,
|
||||||
|
KC_TRNS,KC_EXLM,KC_AT, KC_LCBR,KC_RCBR,KC_PIPE,KC_TRNS,
|
||||||
|
KC_TRNS,KC_HASH,KC_DLR, KC_LPRN,KC_RPRN,KC_GRV,
|
||||||
|
KC_TRNS,KC_PERC,KC_CIRC,KC_LBRC,KC_RBRC,KC_TILD,KC_TRNS,
|
||||||
|
KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,
|
||||||
|
KC_TRNS,KC_TRNS,
|
||||||
|
KC_TRNS,KC_TRNS,KC_TRNS,
|
||||||
|
KC_TRNS,KC_TRNS,KC_TRNS,
|
||||||
|
// right hand
|
||||||
|
KC_TRNS, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11,
|
||||||
|
KC_TRNS, KC_UP, KC_7, KC_8, KC_9, KC_ASTR, KC_F12,
|
||||||
|
KC_DOWN, KC_4, KC_5, KC_6, KC_PLUS, KC_TRNS,
|
||||||
|
KC_TRNS, KC_AMPR, KC_1, KC_2, KC_3, KC_BSLS, KC_TRNS,
|
||||||
|
KC_TRNS,KC_DOT, KC_0, KC_EQL, KC_TRNS,
|
||||||
|
KC_TRNS, KC_TRNS,
|
||||||
|
KC_TRNS, KC_TRNS, KC_TRNS,
|
||||||
|
KC_TRNS, KC_TRNS, KC_TRNS
|
||||||
|
),
|
||||||
|
/* Keymap 2: Media and mouse keys
|
||||||
|
*
|
||||||
|
* ,--------------------------------------------------. ,--------------------------------------------------.
|
||||||
|
* | | | | | | | | | | | | | | | |
|
||||||
|
* |--------+------+------+------+------+-------------| |------+------+------+------+------+------+--------|
|
||||||
|
* | | | | MsUp | | | | | | | | | | | |
|
||||||
|
* |--------+------+------+------+------+------| | | |------+------+------+------+------+--------|
|
||||||
|
* | | |MsLeft|MsDown|MsRght| |------| |------| | | | | | Play |
|
||||||
|
* |--------+------+------+------+------+------| | | |------+------+------+------+------+--------|
|
||||||
|
* | | | | | | | | | | | | Prev | Next | | |
|
||||||
|
* `--------+------+------+------+------+-------------' `-------------+------+------+------+------+--------'
|
||||||
|
* | | | | Lclk | Rclk | |VolUp |VolDn | Mute | | |
|
||||||
|
* `----------------------------------' `----------------------------------'
|
||||||
|
* ,-------------. ,-------------.
|
||||||
|
* | | | | | |
|
||||||
|
* ,------|------|------| |------+------+----------.
|
||||||
|
* | | | | | | | |
|
||||||
|
* |------|------|------| |------|------|----------|
|
||||||
|
* | | | | | | |BrwserBack|
|
||||||
|
* `--------------------' `------------------------'
|
||||||
|
*/
|
||||||
|
// MEDIA AND MOUSE
|
||||||
|
[MDIA] = KEYMAP(
|
||||||
|
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
|
||||||
|
KC_TRNS, KC_TRNS, KC_TRNS, KC_MS_U, KC_TRNS, KC_TRNS, KC_TRNS,
|
||||||
|
KC_TRNS, KC_TRNS, KC_MS_L, KC_MS_D, KC_MS_R, KC_TRNS,
|
||||||
|
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
|
||||||
|
KC_TRNS, KC_TRNS, KC_TRNS, KC_BTN1, KC_BTN2,
|
||||||
|
KC_TRNS, KC_TRNS,
|
||||||
|
KC_TRNS, KC_TRNS, KC_TRNS,
|
||||||
|
KC_TRNS, KC_TRNS, KC_TRNS,
|
||||||
|
// right hand
|
||||||
|
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
|
||||||
|
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
|
||||||
|
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_MPLY,
|
||||||
|
KC_TRNS, KC_TRNS, KC_TRNS, KC_MPRV, KC_MNXT, KC_TRNS, KC_TRNS,
|
||||||
|
KC_VOLU, KC_VOLD, KC_MUTE, KC_TRNS, KC_TRNS,
|
||||||
|
KC_TRNS, KC_TRNS,
|
||||||
|
KC_TRNS, KC_TRNS, KC_TRNS,
|
||||||
|
KC_TRNS, KC_TRNS, KC_WBAK
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint16_t PROGMEM fn_actions[] = {
|
||||||
|
[1] = ACTION_LAYER_TAP_TOGGLE(SYMB) // FN1 - Momentary Layer 1 (Symbols)
|
||||||
|
};
|
||||||
|
|
||||||
|
const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt)
|
||||||
|
{
|
||||||
|
// MACRODOWN only works in this function
|
||||||
|
switch(id) {
|
||||||
|
case 0:
|
||||||
|
if (record->event.pressed) {
|
||||||
|
SEND_STRING (QMK_KEYBOARD "/" QMK_KEYMAP " @ " QMK_VERSION);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return MACRO_NONE;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Runs just one time when the keyboard initializes.
|
||||||
|
void matrix_init_user(void) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// Runs constantly in the background, in a loop.
|
||||||
|
void matrix_scan_user(void) {
|
||||||
|
|
||||||
|
uint8_t layer = biton32(layer_state);
|
||||||
|
|
||||||
|
ergodox_board_led_off();
|
||||||
|
ergodox_right_led_1_off();
|
||||||
|
ergodox_right_led_2_off();
|
||||||
|
ergodox_right_led_3_off();
|
||||||
|
switch (layer) {
|
||||||
|
// TODO: Make this relevant to the ErgoDox EZ.
|
||||||
|
case 1:
|
||||||
|
ergodox_right_led_1_on();
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
ergodox_right_led_2_on();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// none
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
|
@ -0,0 +1,10 @@
|
||||||
|
# ErgoDox EZ Default Configuration
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
|
||||||
|
* Feb 2, 2016 (V1.1):
|
||||||
|
* Made the right-hand quote key double as Cmd/Win on hold. So you get ' when you tap it, " when you tap it with Shift, and Cmd or Win when you hold it. You can then use it as a modifier, or just press and hold it for a moment (and then let go) to send a single Cmd or Win keystroke (handy for opening the Start menu on Windows).
|
||||||
|
|
||||||
|
This is what we ship with out of the factory. :) The image says it all:
|
||||||
|
|
||||||
|
![Default](default_highres.png)
|
|
@ -0,0 +1,165 @@
|
||||||
|
# The Easy Way
|
||||||
|
|
||||||
|
If you can find firmware someone else has made that does what you want, that
|
||||||
|
is the easiest way to customize your ErgoDox. It requires no programming
|
||||||
|
experience or the setup of a build environment.
|
||||||
|
|
||||||
|
Quickstart:
|
||||||
|
|
||||||
|
- Find and download an existing firmware
|
||||||
|
[from Other Firmware Options](#other-firmware-options)
|
||||||
|
|
||||||
|
- Then flash the firmware to your [ErgoDox Ez](#ergodox-ez)
|
||||||
|
or [ErgoDox Infinity](#ergodox-infinity)
|
||||||
|
|
||||||
|
# Customizing Keymaps
|
||||||
|
|
||||||
|
There are many existing keymaps in the "keymaps" directory. If you just want
|
||||||
|
to use one of them, you don't need to modify keymaps and can just build and
|
||||||
|
flash the firmware as described below. These directories each have a
|
||||||
|
"readme.md" file which describe them.
|
||||||
|
|
||||||
|
If none of the existing keymaps suit you, you can create your own custom
|
||||||
|
keymap. This will require some experience with coding. Follow these steps
|
||||||
|
to customize a keymap:
|
||||||
|
|
||||||
|
- Read the [qmk firmware README](https://github.com/jackhumbert/qmk_firmware) from top to bottom. Then come back here. :)
|
||||||
|
|
||||||
|
- Clone the qmk_firmware repository
|
||||||
|
|
||||||
|
- Set up your build environment (see below).
|
||||||
|
|
||||||
|
- Make a new directory under "keymaps" to hold your customizations.
|
||||||
|
|
||||||
|
- Copy an existing keymap that is close to what you want, such as
|
||||||
|
"keymaps/default/keymap.c".
|
||||||
|
|
||||||
|
- Use an editor to modify the new "keymap.c". See "Finding the keycodes you
|
||||||
|
need" below). Try to edit the comments as well, so the "text graphics"
|
||||||
|
represent your layout correctly.
|
||||||
|
|
||||||
|
- Compile your new firmware (see below)
|
||||||
|
|
||||||
|
- Flash your firmware (see below)
|
||||||
|
|
||||||
|
- Test the changes.
|
||||||
|
|
||||||
|
- Submit your keymap as a pull request to the qmk_firmware repository so
|
||||||
|
others can use it. You will want to add a "readme.md" that describes the
|
||||||
|
keymap.
|
||||||
|
|
||||||
|
# Build Dependencies
|
||||||
|
|
||||||
|
Before you can build, you will need the build dependencies. There is a script
|
||||||
|
to try to do this for Linux:
|
||||||
|
|
||||||
|
- Run the `util/install_dependencies.sh` script as root.
|
||||||
|
|
||||||
|
For the Infinity, you need the chibios submodules to be checked out or you
|
||||||
|
will receive errors about the build process being unable to find the chibios
|
||||||
|
files. Check them out with:
|
||||||
|
|
||||||
|
- Go to the top level repo directory and run: `git submodule update --init --recursive`
|
||||||
|
|
||||||
|
# Flashing Firmware
|
||||||
|
|
||||||
|
## ErgoDox Ez
|
||||||
|
|
||||||
|
The Ez uses the [Teensy Loader](https://www.pjrc.com/teensy/loader.html).
|
||||||
|
|
||||||
|
Linux users need to modify udev rules as described on the Teensy Linux page.
|
||||||
|
Some distributions provide a binary, maybe called `teensy-loader-cli`).
|
||||||
|
|
||||||
|
To flash the firmware:
|
||||||
|
|
||||||
|
- Build the firmware with `make keymapname`, for example `make default`
|
||||||
|
- This will result in a hex file called `ergodox_ez_keymapname.hex`, e.g.
|
||||||
|
`ergodox_ez_default.hex`
|
||||||
|
|
||||||
|
- Start the teensy loader.
|
||||||
|
|
||||||
|
- Load the .hex file into it.
|
||||||
|
|
||||||
|
- Press the Reset button by inserting a paperclip gently into the reset hole
|
||||||
|
in the top right corder.
|
||||||
|
|
||||||
|
- Click the button in the Teensy app to download the firmware.
|
||||||
|
|
||||||
|
## 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 left 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.
|
||||||
|
|
||||||
|
# Contributing your keymap
|
||||||
|
|
||||||
|
The QMK firmware is open-source, so it would be wonderful to have your contribution! Within a very short time after launching we already amassed dozens of user-contributed keymaps, with all sorts of creative improvements and tweaks. This is very valuable for people who aren't comfortable coding, but do want to customize their ErgoDox. To make it easy for these people to use your layout, I recommend submitting your PR in the following format.
|
||||||
|
|
||||||
|
1. All work goes inside your keymap subdirectory (`keymaps/german` in this example).
|
||||||
|
2. `keymap.c` - this is your actual keymap file; please update the ASCII comments in the file so they correspond with what you did.
|
||||||
|
3. `readme.md` - a readme file, which GitHub would display by default when people go to your directory. Explain what's different about your keymap, what you tweaked or how it works. No specific format to follow, just communicate what you did. :)
|
||||||
|
4. Any graphics you wish to add. This is absolutely not a must. If you feel like it, you can use [Keyboard Layout Editor](http://keyboard-layout-editor.com) to make something and grab a screenshot, but it's really not a must. If you do have graphics, your readme can just embed the graphic as a link, just like I did with the default layout.
|
||||||
|
|
||||||
|
# Finding the keycodes you need
|
||||||
|
|
||||||
|
Let's say you want a certain key in your layout to send a colon; to figure out what keycode to use to make it do that, you're going to need `quantum/keymap_common.h`.
|
||||||
|
|
||||||
|
That file contains a big list of all of the special, fancy keys (like, being able to send % on its own and whatnot).
|
||||||
|
|
||||||
|
If you want to send a plain vanilla key, you can look up its code under `doc/keycode.txt`. That's where all the boring keys hang out.
|
||||||
|
|
||||||
|
# Other Firmware Options
|
||||||
|
|
||||||
|
There are external tools for customizing the layout, but those do not use
|
||||||
|
the featurs of this qmk firmware. These sites include:
|
||||||
|
|
||||||
|
- [Massdrop configurator](https://keyboard-configurator.massdrop.com/ext/ergodox) for Ez
|
||||||
|
- [Input Club configurator](https://input.club/configurator-ergodox) for Infinity, provides left and right files
|
||||||
|
|
||||||
|
You can also find an existing firmware that you like, for example from:
|
||||||
|
|
||||||
|
- [Dozens of community-contributed keymaps](http://qmk.fm/keyboards/ergodox/)
|
||||||
|
|
||||||
|
This qmk firmware also provides the ability to customize keymaps, but requires
|
||||||
|
a toolchain to build the firmware. See below for instructions on building
|
||||||
|
firmware and customizing keymaps.
|
|
@ -0,0 +1,28 @@
|
||||||
|
#----------------------------------------------------------------------------
|
||||||
|
# On command line:
|
||||||
|
#
|
||||||
|
# make = Make software.
|
||||||
|
#
|
||||||
|
# make clean = Clean out built project files.
|
||||||
|
#
|
||||||
|
# That's pretty much all you need. To compile, always go make clean,
|
||||||
|
# followed by make.
|
||||||
|
#
|
||||||
|
# For advanced users only:
|
||||||
|
# make teensy = Download the hex file to the device, using teensy_loader_cli.
|
||||||
|
# (must have teensy_loader_cli installed).
|
||||||
|
#
|
||||||
|
#----------------------------------------------------------------------------
|
||||||
|
# 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
|
||||||
|
MIDI_ENABLE ?= no # MIDI controls
|
||||||
|
UNICODE_ENABLE ?= yes # Unicode
|
Loading…
Reference in New Issue