diff options
Diffstat (limited to 'quantum')
138 files changed, 17470 insertions, 0 deletions
diff --git a/quantum/analog.c b/quantum/analog.c new file mode 100644 index 0000000000..1ec38df75d --- /dev/null +++ b/quantum/analog.c @@ -0,0 +1,69 @@ +/* Copyright 2015 Jack Humbert + * + * 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/>. + */ + +// Simple analog to digitial conversion + +#include <avr/io.h> +#include <avr/pgmspace.h> +#include <stdint.h> +#include "analog.h" + + +static uint8_t aref = (1<<REFS0); // default to AREF = Vcc + + +void analogReference(uint8_t mode) +{ +	aref = mode & 0xC0; +} + + +// Arduino compatible pin input +int16_t analogRead(uint8_t pin) +{ +#if defined(__AVR_ATmega32U4__) +	static const uint8_t PROGMEM pin_to_mux[] = { +		0x00, 0x01, 0x04, 0x05, 0x06, 0x07, +		0x25, 0x24, 0x23, 0x22, 0x21, 0x20}; +	if (pin >= 12) return 0; +	return adc_read(pgm_read_byte(pin_to_mux + pin)); +#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__) +	if (pin >= 8) return 0; +	return adc_read(pin); +#else +	return 0; +#endif +} + +// Mux input +int16_t adc_read(uint8_t mux) +{ +#if defined(__AVR_AT90USB162__) +	return 0; +#else +	uint8_t low; + +	ADCSRA = (1<<ADEN) | ADC_PRESCALER;		// enable ADC +	ADCSRB = (1<<ADHSM) | (mux & 0x20);		// high speed mode +	ADMUX = aref | (mux & 0x1F);			// configure mux input +	ADCSRA = (1<<ADEN) | ADC_PRESCALER | (1<<ADSC);	// start the conversion +	while (ADCSRA & (1<<ADSC)) ;			// wait for result +	low = ADCL;					// must read LSB first +	return (ADCH << 8) | low;			// must read MSB only once! +#endif +} + + diff --git a/quantum/analog.h b/quantum/analog.h new file mode 100644 index 0000000000..8d93de7dc2 --- /dev/null +++ b/quantum/analog.h @@ -0,0 +1,52 @@ +/* Copyright 2015 Jack Humbert + * + * 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 _analog_h_included__ +#define _analog_h_included__ + +#include <stdint.h> + +void analogReference(uint8_t mode); +int16_t analogRead(uint8_t pin); +int16_t adc_read(uint8_t mux); + +#define ADC_REF_POWER     (1<<REFS0) +#define ADC_REF_INTERNAL  ((1<<REFS1) | (1<<REFS0)) +#define ADC_REF_EXTERNAL  (0) + +// These prescaler values are for high speed mode, ADHSM = 1 +#if F_CPU == 16000000L +#define ADC_PRESCALER ((1<<ADPS2) | (1<<ADPS1)) +#elif F_CPU == 8000000L +#define ADC_PRESCALER ((1<<ADPS2) | (1<<ADPS0)) +#elif F_CPU == 4000000L +#define ADC_PRESCALER ((1<<ADPS2)) +#elif F_CPU == 2000000L +#define ADC_PRESCALER ((1<<ADPS1) | (1<<ADPS0)) +#elif F_CPU == 1000000L +#define ADC_PRESCALER ((1<<ADPS1)) +#else +#define ADC_PRESCALER ((1<<ADPS0)) +#endif + +// some avr-libc versions do not properly define ADHSM +#if defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__) +#if !defined(ADHSM) +#define ADHSM (7) +#endif +#endif + +#endif diff --git a/quantum/api.c b/quantum/api.c new file mode 100644 index 0000000000..52dfe23e17 --- /dev/null +++ b/quantum/api.c @@ -0,0 +1,195 @@ +/* Copyright 2016 Jack Humbert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include "api.h" +#include "quantum.h" + +void dword_to_bytes(uint32_t dword, uint8_t * bytes) { +    bytes[0] = (dword >> 24) & 0xFF; +    bytes[1] = (dword >> 16) & 0xFF;  +    bytes[2] = (dword >> 8) & 0xFF;  +    bytes[3] = (dword >> 0) & 0xFF;  +} + +uint32_t bytes_to_dword(uint8_t * bytes, uint8_t index) { +    return ((uint32_t)bytes[index + 0] << 24) | ((uint32_t)bytes[index + 1] << 16) | ((uint32_t)bytes[index + 2] << 8) | (uint32_t)bytes[index + 3]; +} + +__attribute__ ((weak)) +bool process_api_quantum(uint8_t length, uint8_t * data) { +    return process_api_keyboard(length, data); +} + +__attribute__ ((weak)) +bool process_api_keyboard(uint8_t length, uint8_t * data) { +    return process_api_user(length, data); +} + +__attribute__ ((weak)) +bool process_api_user(uint8_t length, uint8_t * data) { +    return true; +} + +void process_api(uint16_t length, uint8_t * data) { +    // SEND_STRING("\nRX: "); +    // for (uint8_t i = 0; i < length; i++) { +    //     send_byte(data[i]); +    //     SEND_STRING(" "); +    // } +    if (!process_api_quantum(length, data)) +        return; + +    switch (data[0]) { +        case MT_SET_DATA: +            switch (data[1]) { +                case DT_DEFAULT_LAYER: { +                    eeconfig_update_default_layer(data[2]); +                    default_layer_set((uint32_t)(data[2])); +                    break; +                } +                case DT_KEYMAP_OPTIONS: { +                    eeconfig_update_keymap(data[2]); +                    break; +                } +                case DT_RGBLIGHT: { +                    #ifdef RGBLIGHT_ENABLE +                        uint32_t rgblight = bytes_to_dword(data, 2); +                        rgblight_update_dword(rgblight); +                    #endif +                    break; +                } +            } +        case MT_GET_DATA: +            switch (data[1]) { +                case DT_HANDSHAKE: { +                    MT_GET_DATA_ACK(DT_HANDSHAKE, NULL, 0); +                    break; +                } +                case DT_DEBUG: { +                    uint8_t debug_bytes[1] = { eeprom_read_byte(EECONFIG_DEBUG) }; +                    MT_GET_DATA_ACK(DT_DEBUG, debug_bytes, 1); +                    break; +                } +                case DT_DEFAULT_LAYER: { +                    uint8_t default_bytes[1] = { eeprom_read_byte(EECONFIG_DEFAULT_LAYER) }; +                    MT_GET_DATA_ACK(DT_DEFAULT_LAYER, default_bytes, 1); +                    break; +                } +                case DT_CURRENT_LAYER: { +                    uint8_t layer_state_bytes[4]; +                    dword_to_bytes(layer_state, layer_state_bytes); +                    MT_GET_DATA_ACK(DT_CURRENT_LAYER, layer_state_bytes, 4); +                    break; +                } +                case DT_AUDIO: { +                    #ifdef AUDIO_ENABLE +                        uint8_t audio_bytes[1] = { eeprom_read_byte(EECONFIG_AUDIO) }; +                        MT_GET_DATA_ACK(DT_AUDIO, audio_bytes, 1); +                    #else +                        MT_GET_DATA_ACK(DT_AUDIO, NULL, 0); +                    #endif +                    break; +                } +                case DT_BACKLIGHT: { +                    #ifdef BACKLIGHT_ENABLE +                        uint8_t backlight_bytes[1] = { eeprom_read_byte(EECONFIG_BACKLIGHT) }; +                        MT_GET_DATA_ACK(DT_BACKLIGHT, backlight_bytes, 1); +                    #else +                        MT_GET_DATA_ACK(DT_BACKLIGHT, NULL, 0); +                    #endif +                    break; +                } +                case DT_RGBLIGHT: { +                    #ifdef RGBLIGHT_ENABLE +                        uint8_t rgblight_bytes[4]; +                        dword_to_bytes(eeconfig_read_rgblight(), rgblight_bytes); +                        MT_GET_DATA_ACK(DT_RGBLIGHT, rgblight_bytes, 4); +                    #else +                        MT_GET_DATA_ACK(DT_RGBLIGHT, NULL, 0); +                    #endif +                    break; +                } +                case DT_KEYMAP_OPTIONS: { +                    uint8_t keymap_bytes[1] = { eeconfig_read_keymap() }; +                    MT_GET_DATA_ACK(DT_KEYMAP_OPTIONS, keymap_bytes, 1); +                    break; +                } +                case DT_KEYMAP_SIZE: { +                    uint8_t keymap_size[2] = {MATRIX_ROWS, MATRIX_COLS}; +                    MT_GET_DATA_ACK(DT_KEYMAP_SIZE, keymap_size, 2); +                    break; +                } +                // This may be too much +                // case DT_KEYMAP: { +                //     uint8_t keymap_data[MATRIX_ROWS * MATRIX_COLS * 4 + 3]; +                //     keymap_data[0] = data[2]; +                //     keymap_data[1] = MATRIX_ROWS; +                //     keymap_data[2] = MATRIX_COLS; +                //     for (int i = 0; i < MATRIX_ROWS; i++) { +                //         for (int j = 0; j < MATRIX_COLS; j++) { +                //             keymap_data[3 + (i*MATRIX_COLS*2) + (j*2)] = pgm_read_word(&keymaps[data[2]][i][j]) >> 8; +                //             keymap_data[3 + (i*MATRIX_COLS*2) + (j*2) + 1] = pgm_read_word(&keymaps[data[2]][i][j]) & 0xFF; +                //         } +                //     } +                //     MT_GET_DATA_ACK(DT_KEYMAP, keymap_data, MATRIX_ROWS * MATRIX_COLS * 4 + 3); +                //     // uint8_t keymap_data[5]; +                //     // keymap_data[0] = data[2]; +                //     // keymap_data[1] = data[3]; +                //     // keymap_data[2] = data[4]; +                //     // keymap_data[3] = pgm_read_word(&keymaps[data[2]][data[3]][data[4]]) >> 8; +                //     // keymap_data[4] = pgm_read_word(&keymaps[data[2]][data[3]][data[4]]) & 0xFF; + +                //     // MT_GET_DATA_ACK(DT_KEYMAP, keymap_data, 5); +                //     break; +                // } +                default: +                    break; +            } +            break; +        case MT_SET_DATA_ACK: +        case MT_GET_DATA_ACK: +            break; +        case MT_SEND_DATA: +            break; +        case MT_SEND_DATA_ACK: +            break; +        case MT_EXE_ACTION: +            break; +        case MT_EXE_ACTION_ACK: +            break; +        case MT_TYPE_ERROR: +            break; +        default: ; // command not recognised +            SEND_BYTES(MT_TYPE_ERROR, DT_NONE, data, length); +            break; + +        // #ifdef RGBLIGHT_ENABLE +        // case 0x27: ; // RGB LED functions +        //     switch (*data++) { +        //         case 0x00: ; // Update HSV +        //             rgblight_sethsv((data[0] << 8 | data[1]) % 360, data[2], data[3]); +        //             break; +        //         case 0x01: ; // Update RGB +        //             break; +        //         case 0x02: ; // Update mode +        //             rgblight_mode(data[0]); +        //             break; +        //     } +        //     break; +        // #endif +    } + +} diff --git a/quantum/api.h b/quantum/api.h new file mode 100644 index 0000000000..efc0ddca12 --- /dev/null +++ b/quantum/api.h @@ -0,0 +1,75 @@ +/* Copyright 2016 Jack Humbert + * + * 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 _API_H_ +#define _API_H_ + +#include "lufa.h" + +enum MESSAGE_TYPE { +    MT_GET_DATA =      0x10, // Get data from keyboard +    MT_GET_DATA_ACK =  0x11, // returned data to process (ACK) +    MT_SET_DATA =      0x20, // Set data on keyboard +    MT_SET_DATA_ACK =  0x21, // returned data to confirm (ACK) +    MT_SEND_DATA =     0x30, // Sending data/action from keyboard +    MT_SEND_DATA_ACK = 0x31, // returned data/action confirmation (ACK) +    MT_EXE_ACTION =    0x40, // executing actions on keyboard +    MT_EXE_ACTION_ACK =0x41, // return confirmation/value (ACK) +    MT_TYPE_ERROR =    0x80 // type not recofgnised (ACK) +}; + +enum DATA_TYPE { +    DT_NONE = 0x00, +    DT_HANDSHAKE, +    DT_DEFAULT_LAYER, +    DT_CURRENT_LAYER, +    DT_KEYMAP_OPTIONS, +    DT_BACKLIGHT, +    DT_RGBLIGHT, +    DT_UNICODE, +    DT_DEBUG, +    DT_AUDIO, +    DT_QUANTUM_ACTION, +    DT_KEYBOARD_ACTION, +    DT_USER_ACTION, +    DT_KEYMAP_SIZE, +    DT_KEYMAP +}; + +void dword_to_bytes(uint32_t dword, uint8_t * bytes); +uint32_t bytes_to_dword(uint8_t * bytes, uint8_t index); + +#define MT_GET_DATA(data_type, data, length) SEND_BYTES(MT_GET_DATA, data_type, data, length) +#define MT_GET_DATA_ACK(data_type, data, length) SEND_BYTES(MT_GET_DATA_ACK, data_type, data, length) +#define MT_SET_DATA(data_type, data, length) SEND_BYTES(MT_SET_DATA, data_type, data, length) +#define MT_SET_DATA_ACK(data_type, data, length) SEND_BYTES(MT_SET_DATA_ACK, data_type, data, length) +#define MT_SEND_DATA(data_type, data, length) SEND_BYTES(MT_SEND_DATA, data_type, data, length) +#define MT_SEND_DATA_ACK(data_type, data, length) SEND_BYTES(MT_SEND_DATA_ACK, data_type, data, length) +#define MT_EXE_ACTION(data_type, data, length) SEND_BYTES(MT_EXE_ACTION, data_type, data, length) +#define MT_EXE_ACTION_ACK(data_type, data, length) SEND_BYTES(MT_EXE_ACTION_ACK, data_type, data, length) + +void process_api(uint16_t length, uint8_t * data); + +__attribute__ ((weak)) +bool process_api_quantum(uint8_t length, uint8_t * data); + +__attribute__ ((weak)) +bool process_api_keyboard(uint8_t length, uint8_t * data); + +__attribute__ ((weak)) +bool process_api_user(uint8_t length, uint8_t * data); + +#endif diff --git a/quantum/api/api_sysex.c b/quantum/api/api_sysex.c new file mode 100644 index 0000000000..6a2ee90124 --- /dev/null +++ b/quantum/api/api_sysex.c @@ -0,0 +1,72 @@ +/* Copyright 2016 Jack Humbert, Fred Sundvik + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ +#include "api_sysex.h" +#include "sysex_tools.h" +#include "print.h" + +void send_bytes_sysex(uint8_t message_type, uint8_t data_type, uint8_t * bytes, uint16_t length) { +    // SEND_STRING("\nTX: "); +    // for (uint8_t i = 0; i < length; i++) { +    //     send_byte(bytes[i]); +    //     SEND_STRING(" "); +    // } +    if (length > API_SYSEX_MAX_SIZE) { +        xprintf("Sysex msg too big %d %d %d", message_type, data_type, length); +        return; +    } + + +    // The buffer size required is calculated as the following +    // API_SYSEX_MAX_SIZE is the maximum length +    // In addition to that we have a two byte message header consisting of the message_type and data_type +    // This has to be encoded with an additional overhead of one byte for every starting 7 bytes +    // We just add one extra byte in case it's not divisible by 7 +    // Then we have an unencoded header consisting of 4 bytes +    // Plus a one byte terminator +    const unsigned message_header = 2; +    const unsigned unencoded_message = API_SYSEX_MAX_SIZE + message_header; +    const unsigned encoding_overhead = unencoded_message / 7 + 1; +    const unsigned encoded_size = unencoded_message + encoding_overhead; +    const unsigned unencoded_header = 4; +    const unsigned terminator = 1; +    const unsigned buffer_size = encoded_size + unencoded_header + terminator; +    uint8_t buffer[encoded_size + unencoded_header + terminator]; +    // The unencoded header +    buffer[0] = 0xF0; +    buffer[1] = 0x00; +    buffer[2] = 0x00; +    buffer[3] = 0x00; + +    // We copy the message to the end of the array, this way we can do an inplace encoding, using the same +    // buffer for both input and output +    const unsigned message_size = length + message_header; +    uint8_t* unencoded_start = buffer + buffer_size - message_size; +    uint8_t* ptr = unencoded_start; +    *(ptr++) = message_type; +    *(ptr++) = data_type; +    memcpy(ptr, bytes, length); + +    unsigned encoded_length = sysex_encode(buffer + unencoded_header, unencoded_start, message_size); +    unsigned final_size = unencoded_header + encoded_length + terminator; +    buffer[final_size - 1] = 0xF7; +    midi_send_array(&midi_device, final_size, buffer); + +    // SEND_STRING("\nTD: "); +    // for (uint8_t i = 0; i < encoded_length + 5; i++) { +    //     send_byte(buffer[i]); +    //     SEND_STRING(" "); +    // } +} diff --git a/quantum/api/api_sysex.h b/quantum/api/api_sysex.h new file mode 100644 index 0000000000..a23f00f572 --- /dev/null +++ b/quantum/api/api_sysex.h @@ -0,0 +1,26 @@ +/* Copyright 2016 Jack Humbert + * + * 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 _API_SYSEX_H_ +#define _API_SYSEX_H_ + +#include "api.h" + +void send_bytes_sysex(uint8_t message_type, uint8_t data_type, uint8_t * bytes, uint16_t length); + +#define SEND_BYTES(mt, dt, b, l) send_bytes_sysex(mt, dt, b, l) + +#endif diff --git a/quantum/audio/audio.c b/quantum/audio/audio.c new file mode 100644 index 0000000000..c924f2bd58 --- /dev/null +++ b/quantum/audio/audio.c @@ -0,0 +1,780 @@ +/* Copyright 2016 Jack Humbert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ +#include <stdio.h> +#include <string.h> +//#include <math.h> +#include <avr/pgmspace.h> +#include <avr/interrupt.h> +#include <avr/io.h> +#include "print.h" +#include "audio.h" +#include "keymap.h" + +#include "eeconfig.h" + +#define CPU_PRESCALER 8 + +// ----------------------------------------------------------------------------- +// Timer Abstractions +// ----------------------------------------------------------------------------- + +// TIMSK3 - Timer/Counter #3 Interrupt Mask Register +// Turn on/off 3A interputs, stopping/enabling the ISR calls +#ifdef C6_AUDIO +    #define ENABLE_AUDIO_COUNTER_3_ISR TIMSK3 |= _BV(OCIE3A) +    #define DISABLE_AUDIO_COUNTER_3_ISR TIMSK3 &= ~_BV(OCIE3A) +#endif + +#ifdef B5_AUDIO +    #define ENABLE_AUDIO_COUNTER_1_ISR TIMSK1 |= _BV(OCIE1A) +    #define DISABLE_AUDIO_COUNTER_1_ISR TIMSK1 &= ~_BV(OCIE1A) +#endif + +// TCCR3A: Timer/Counter #3 Control Register +// Compare Output Mode (COM3An) = 0b00 = Normal port operation, OC3A disconnected from PC6 + +#ifdef C6_AUDIO +    #define ENABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A |= _BV(COM3A1); +    #define DISABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A &= ~(_BV(COM3A1) | _BV(COM3A0)); +#endif + +#ifdef B5_AUDIO +    #define ENABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A |= _BV(COM1A1); +    #define DISABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A &= ~(_BV(COM1A1) | _BV(COM1A0)); +#endif + +// Fast PWM Mode Controls + +#ifdef C6_AUDIO +    #define TIMER_3_PERIOD     ICR3 +    #define TIMER_3_DUTY_CYCLE OCR3A +#endif + +#ifdef B5_AUDIO +    #define TIMER_1_PERIOD     ICR1 +    #define TIMER_1_DUTY_CYCLE OCR1A +#endif + + +// ----------------------------------------------------------------------------- + + +int voices = 0; +int voice_place = 0; +float frequency = 0; +float frequency_alt = 0; +int volume = 0; +long position = 0; + +float frequencies[8] = {0, 0, 0, 0, 0, 0, 0, 0}; +int volumes[8] = {0, 0, 0, 0, 0, 0, 0, 0}; +bool sliding = false; + +float place = 0; + +uint8_t * sample; +uint16_t sample_length = 0; + +bool     playing_notes = false; +bool     playing_note = false; +float    note_frequency = 0; +float    note_length = 0; +uint8_t  note_tempo = TEMPO_DEFAULT; +float    note_timbre = TIMBRE_DEFAULT; +uint16_t note_position = 0; +float (* notes_pointer)[][2]; +uint16_t notes_count; +bool     notes_repeat; +float    notes_rest; +bool     note_resting = false; + +uint8_t current_note = 0; +uint8_t rest_counter = 0; + +#ifdef VIBRATO_ENABLE +float vibrato_counter = 0; +float vibrato_strength = .5; +float vibrato_rate = 0.125; +#endif + +float polyphony_rate = 0; + +static bool audio_initialized = false; + +audio_config_t audio_config; + +uint16_t envelope_index = 0; +bool glissando = true; + +void audio_init() +{ + +    // Check EEPROM +    if (!eeconfig_is_enabled()) +    { +        eeconfig_init(); +    } +    audio_config.raw = eeconfig_read_audio(); + +    // Set port PC6 (OC3A and /OC4A) as output + +    #ifdef C6_AUDIO +        DDRC |= _BV(PORTC6); +    #else +        DDRC |= _BV(PORTC6); +        PORTC &= ~_BV(PORTC6); +    #endif + +    #ifdef B5_AUDIO +        DDRB |= _BV(PORTB5); +    #else +        DDRB |= _BV(PORTB5); +        PORTB &= ~_BV(PORTB5); +    #endif + +    #ifdef C6_AUDIO +        DISABLE_AUDIO_COUNTER_3_ISR; +    #endif +     +    #ifdef B5_AUDIO +        DISABLE_AUDIO_COUNTER_1_ISR; +    #endif + +    // TCCR3A / TCCR3B: Timer/Counter #3 Control Registers +    // Compare Output Mode (COM3An) = 0b00 = Normal port operation, OC3A disconnected from PC6 +    // Waveform Generation Mode (WGM3n) = 0b1110 = Fast PWM Mode 14 (Period = ICR3, Duty Cycle = OCR3A) +    // Clock Select (CS3n) = 0b010 = Clock / 8 + +    #ifdef C6_AUDIO +        TCCR3A = (0 << COM3A1) | (0 << COM3A0) | (1 << WGM31) | (0 << WGM30); +        TCCR3B = (1 << WGM33)  | (1 << WGM32)  | (0 << CS32)  | (1 << CS31) | (0 << CS30); +    #endif + +    #ifdef B5_AUDIO +        TCCR1A = (0 << COM1A1) | (0 << COM1A0) | (1 << WGM11) | (0 << WGM10); +        TCCR1B = (1 << WGM13)  | (1 << WGM12)  | (0 << CS12)  | (1 << CS11) | (0 << CS10); +    #endif + +    audio_initialized = true; +} + +void stop_all_notes() +{ +    dprintf("audio stop all notes"); + +    if (!audio_initialized) { +        audio_init(); +    } +    voices = 0; + + +    #ifdef C6_AUDIO +        DISABLE_AUDIO_COUNTER_3_ISR; +        DISABLE_AUDIO_COUNTER_3_OUTPUT; +    #endif + +    #ifdef B5_AUDIO +        DISABLE_AUDIO_COUNTER_1_ISR; +        DISABLE_AUDIO_COUNTER_1_OUTPUT; +    #endif + +    playing_notes = false; +    playing_note = false; +    frequency = 0; +    frequency_alt = 0; +    volume = 0; + +    for (uint8_t i = 0; i < 8; i++) +    { +        frequencies[i] = 0; +        volumes[i] = 0; +    } +} + +void stop_note(float freq) +{ +    dprintf("audio stop note freq=%d", (int)freq); + +    if (playing_note) { +        if (!audio_initialized) { +            audio_init(); +        } +        for (int i = 7; i >= 0; i--) { +            if (frequencies[i] == freq) { +                frequencies[i] = 0; +                volumes[i] = 0; +                for (int j = i; (j < 7); j++) { +                    frequencies[j] = frequencies[j+1]; +                    frequencies[j+1] = 0; +                    volumes[j] = volumes[j+1]; +                    volumes[j+1] = 0; +                } +                break; +            } +        } +        voices--; +        if (voices < 0) +            voices = 0; +        if (voice_place >= voices) { +            voice_place = 0; +        } +        if (voices == 0) { +            #ifdef C6_AUDIO +                DISABLE_AUDIO_COUNTER_3_ISR; +                DISABLE_AUDIO_COUNTER_3_OUTPUT; +            #endif +            #ifdef B5_AUDIO +                DISABLE_AUDIO_COUNTER_1_ISR; +                DISABLE_AUDIO_COUNTER_1_OUTPUT; +            #endif +            frequency = 0; +            frequency_alt = 0; +            volume = 0; +            playing_note = false; +        } +    } +} + +#ifdef VIBRATO_ENABLE + +float mod(float a, int b) +{ +    float r = fmod(a, b); +    return r < 0 ? r + b : r; +} + +float vibrato(float average_freq) { +    #ifdef VIBRATO_STRENGTH_ENABLE +        float vibrated_freq = average_freq * pow(vibrato_lut[(int)vibrato_counter], vibrato_strength); +    #else +        float vibrated_freq = average_freq * vibrato_lut[(int)vibrato_counter]; +    #endif +    vibrato_counter = mod((vibrato_counter + vibrato_rate * (1.0 + 440.0/average_freq)), VIBRATO_LUT_LENGTH); +    return vibrated_freq; +} + +#endif + +#ifdef C6_AUDIO +ISR(TIMER3_COMPA_vect) +{ +    float freq; + +    if (playing_note) { +        if (voices > 0) { + +            #ifdef B5_AUDIO +            float freq_alt = 0; +                if (voices > 1) { +                    if (polyphony_rate == 0) { +                        if (glissando) { +                            if (frequency_alt != 0 && frequency_alt < frequencies[voices - 2] && frequency_alt < frequencies[voices - 2] * pow(2, -440/frequencies[voices - 2]/12/2)) { +                                frequency_alt = frequency_alt * pow(2, 440/frequency_alt/12/2); +                            } else if (frequency_alt != 0 && frequency_alt > frequencies[voices - 2] && frequency_alt > frequencies[voices - 2] * pow(2, 440/frequencies[voices - 2]/12/2)) { +                                frequency_alt = frequency_alt * pow(2, -440/frequency_alt/12/2); +                            } else { +                                frequency_alt = frequencies[voices - 2]; +                            } +                        } else { +                            frequency_alt = frequencies[voices - 2]; +                        } + +                        #ifdef VIBRATO_ENABLE +                            if (vibrato_strength > 0) { +                                freq_alt = vibrato(frequency_alt); +                            } else { +                                freq_alt = frequency_alt; +                            } +                        #else +                            freq_alt = frequency_alt; +                        #endif +                    } + +                    if (envelope_index < 65535) { +                        envelope_index++; +                    } + +                    freq_alt = voice_envelope(freq_alt); + +                    if (freq_alt < 30.517578125) { +                        freq_alt = 30.52; +                    } + +                    TIMER_1_PERIOD = (uint16_t)(((float)F_CPU) / (freq_alt * CPU_PRESCALER)); +                    TIMER_1_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (freq_alt * CPU_PRESCALER)) * note_timbre); +                } +            #endif + +            if (polyphony_rate > 0) { +                if (voices > 1) { +                    voice_place %= voices; +                    if (place++ > (frequencies[voice_place] / polyphony_rate / CPU_PRESCALER)) { +                        voice_place = (voice_place + 1) % voices; +                        place = 0.0; +                    } +                } + +                #ifdef VIBRATO_ENABLE +                    if (vibrato_strength > 0) { +                        freq = vibrato(frequencies[voice_place]); +                    } else { +                        freq = frequencies[voice_place]; +                    } +                #else +                    freq = frequencies[voice_place]; +                #endif +            } else { +                if (glissando) { +                    if (frequency != 0 && frequency < frequencies[voices - 1] && frequency < frequencies[voices - 1] * pow(2, -440/frequencies[voices - 1]/12/2)) { +                        frequency = frequency * pow(2, 440/frequency/12/2); +                    } else if (frequency != 0 && frequency > frequencies[voices - 1] && frequency > frequencies[voices - 1] * pow(2, 440/frequencies[voices - 1]/12/2)) { +                        frequency = frequency * pow(2, -440/frequency/12/2); +                    } else { +                        frequency = frequencies[voices - 1]; +                    } +                } else { +                    frequency = frequencies[voices - 1]; +                } + +                #ifdef VIBRATO_ENABLE +                    if (vibrato_strength > 0) { +                        freq = vibrato(frequency); +                    } else { +                        freq = frequency; +                    } +                #else +                    freq = frequency; +                #endif +            } + +            if (envelope_index < 65535) { +                envelope_index++; +            } + +            freq = voice_envelope(freq); + +            if (freq < 30.517578125) { +                freq = 30.52; +            } + +            TIMER_3_PERIOD = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER)); +            TIMER_3_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre); +        } +    } + +    if (playing_notes) { +        if (note_frequency > 0) { +            #ifdef VIBRATO_ENABLE +                if (vibrato_strength > 0) { +                    freq = vibrato(note_frequency); +                } else { +                    freq = note_frequency; +                } +            #else +                    freq = note_frequency; +            #endif + +            if (envelope_index < 65535) { +                envelope_index++; +            } +            freq = voice_envelope(freq); + +            TIMER_3_PERIOD = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER)); +            TIMER_3_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre); +        } else { +            TIMER_3_PERIOD = 0; +            TIMER_3_DUTY_CYCLE = 0; +        } + +        note_position++; +        bool end_of_note = false; +        if (TIMER_3_PERIOD > 0) { +            end_of_note = (note_position >= (note_length / TIMER_3_PERIOD * 0xFFFF)); +        } else { +            end_of_note = (note_position >= (note_length * 0x7FF)); +        } + +        if (end_of_note) { +            current_note++; +            if (current_note >= notes_count) { +                if (notes_repeat) { +                    current_note = 0; +                } else { +                    DISABLE_AUDIO_COUNTER_3_ISR; +                    DISABLE_AUDIO_COUNTER_3_OUTPUT; +                    playing_notes = false; +                    return; +                } +            } +            if (!note_resting && (notes_rest > 0)) { +                note_resting = true; +                note_frequency = 0; +                note_length = notes_rest; +                current_note--; +            } else { +                note_resting = false; +                envelope_index = 0; +                note_frequency = (*notes_pointer)[current_note][0]; +                note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100); +            } + +            note_position = 0; +        } +    } + +    if (!audio_config.enable) { +        playing_notes = false; +        playing_note = false; +    } +} +#endif + +#ifdef B5_AUDIO +ISR(TIMER1_COMPA_vect) +{ +    #if defined(B5_AUDIO) && !defined(C6_AUDIO) +    float freq = 0; + +    if (playing_note) { +        if (voices > 0) { +            if (polyphony_rate > 0) { +                if (voices > 1) { +                    voice_place %= voices; +                    if (place++ > (frequencies[voice_place] / polyphony_rate / CPU_PRESCALER)) { +                        voice_place = (voice_place + 1) % voices; +                        place = 0.0; +                    } +                } + +                #ifdef VIBRATO_ENABLE +                    if (vibrato_strength > 0) { +                        freq = vibrato(frequencies[voice_place]); +                    } else { +                        freq = frequencies[voice_place]; +                    } +                #else +                    freq = frequencies[voice_place]; +                #endif +            } else { +                if (glissando) { +                    if (frequency != 0 && frequency < frequencies[voices - 1] && frequency < frequencies[voices - 1] * pow(2, -440/frequencies[voices - 1]/12/2)) { +                        frequency = frequency * pow(2, 440/frequency/12/2); +                    } else if (frequency != 0 && frequency > frequencies[voices - 1] && frequency > frequencies[voices - 1] * pow(2, 440/frequencies[voices - 1]/12/2)) { +                        frequency = frequency * pow(2, -440/frequency/12/2); +                    } else { +                        frequency = frequencies[voices - 1]; +                    } +                } else { +                    frequency = frequencies[voices - 1]; +                } + +                #ifdef VIBRATO_ENABLE +                    if (vibrato_strength > 0) { +                        freq = vibrato(frequency); +                    } else { +                        freq = frequency; +                    } +                #else +                    freq = frequency; +                #endif +            } + +            if (envelope_index < 65535) { +                envelope_index++; +            } + +            freq = voice_envelope(freq); + +            if (freq < 30.517578125) { +                freq = 30.52; +            } + +            TIMER_1_PERIOD = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER)); +            TIMER_1_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre); +        } +    } + +    if (playing_notes) { +        if (note_frequency > 0) { +            #ifdef VIBRATO_ENABLE +                if (vibrato_strength > 0) { +                    freq = vibrato(note_frequency); +                } else { +                    freq = note_frequency; +                } +            #else +                    freq = note_frequency; +            #endif + +            if (envelope_index < 65535) { +                envelope_index++; +            } +            freq = voice_envelope(freq); + +            TIMER_1_PERIOD = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER)); +            TIMER_1_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre); +        } else { +            TIMER_1_PERIOD = 0; +            TIMER_1_DUTY_CYCLE = 0; +        } + +        note_position++; +        bool end_of_note = false; +        if (TIMER_1_PERIOD > 0) { +            end_of_note = (note_position >= (note_length / TIMER_1_PERIOD * 0xFFFF)); +        } else { +            end_of_note = (note_position >= (note_length * 0x7FF)); +        } + +        if (end_of_note) { +            current_note++; +            if (current_note >= notes_count) { +                if (notes_repeat) { +                    current_note = 0; +                } else { +                    DISABLE_AUDIO_COUNTER_1_ISR; +                    DISABLE_AUDIO_COUNTER_1_OUTPUT; +                    playing_notes = false; +                    return; +                } +            } +            if (!note_resting && (notes_rest > 0)) { +                note_resting = true; +                note_frequency = 0; +                note_length = notes_rest; +                current_note--; +            } else { +                note_resting = false; +                envelope_index = 0; +                note_frequency = (*notes_pointer)[current_note][0]; +                note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100); +            } + +            note_position = 0; +        } +    } + +    if (!audio_config.enable) { +        playing_notes = false; +        playing_note = false; +    } +#endif +} +#endif + +void play_note(float freq, int vol) { + +    dprintf("audio play note freq=%d vol=%d", (int)freq, vol); + +    if (!audio_initialized) { +        audio_init(); +    } + +    if (audio_config.enable && voices < 8) { +        #ifdef C6_AUDIO +            DISABLE_AUDIO_COUNTER_3_ISR; +        #endif +        #ifdef B5_AUDIO +            DISABLE_AUDIO_COUNTER_1_ISR; +        #endif + +        // Cancel notes if notes are playing +        if (playing_notes) +            stop_all_notes(); + +        playing_note = true; + +        envelope_index = 0; + +        if (freq > 0) { +            frequencies[voices] = freq; +            volumes[voices] = vol; +            voices++; +        } + +        #ifdef C6_AUDIO +            ENABLE_AUDIO_COUNTER_3_ISR; +            ENABLE_AUDIO_COUNTER_3_OUTPUT; +        #endif +        #ifdef B5_AUDIO +            #ifdef C6_AUDIO +            if (voices > 1) { +                ENABLE_AUDIO_COUNTER_1_ISR; +                ENABLE_AUDIO_COUNTER_1_OUTPUT; +            } +            #else +            ENABLE_AUDIO_COUNTER_1_ISR; +            ENABLE_AUDIO_COUNTER_1_OUTPUT; +            #endif +        #endif +    } + +} + +void play_notes(float (*np)[][2], uint16_t n_count, bool n_repeat, float n_rest) +{ + +    if (!audio_initialized) { +        audio_init(); +    } + +    if (audio_config.enable) { + +        #ifdef C6_AUDIO +            DISABLE_AUDIO_COUNTER_3_ISR; +        #endif +        #ifdef B5_AUDIO +            DISABLE_AUDIO_COUNTER_1_ISR; +        #endif + +        // Cancel note if a note is playing +        if (playing_note) +            stop_all_notes(); + +        playing_notes = true; + +        notes_pointer = np; +        notes_count = n_count; +        notes_repeat = n_repeat; +        notes_rest = n_rest; + +        place = 0; +        current_note = 0; + +        note_frequency = (*notes_pointer)[current_note][0]; +        note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100); +        note_position = 0; + + +        #ifdef C6_AUDIO +            ENABLE_AUDIO_COUNTER_3_ISR; +            ENABLE_AUDIO_COUNTER_3_OUTPUT; +        #endif +        #ifdef B5_AUDIO +            #ifndef C6_AUDIO +            ENABLE_AUDIO_COUNTER_1_ISR; +            ENABLE_AUDIO_COUNTER_1_OUTPUT; +            #endif +        #endif +    } + +} + +bool is_playing_notes(void) { +    return playing_notes; +} + +bool is_audio_on(void) { +    return (audio_config.enable != 0); +} + +void audio_toggle(void) { +    audio_config.enable ^= 1; +    eeconfig_update_audio(audio_config.raw); +    if (audio_config.enable) +        audio_on_user(); +} + +void audio_on(void) { +    audio_config.enable = 1; +    eeconfig_update_audio(audio_config.raw); +    audio_on_user(); +} + +void audio_off(void) { +    audio_config.enable = 0; +    eeconfig_update_audio(audio_config.raw); +} + +#ifdef VIBRATO_ENABLE + +// Vibrato rate functions + +void set_vibrato_rate(float rate) { +    vibrato_rate = rate; +} + +void increase_vibrato_rate(float change) { +    vibrato_rate *= change; +} + +void decrease_vibrato_rate(float change) { +    vibrato_rate /= change; +} + +#ifdef VIBRATO_STRENGTH_ENABLE + +void set_vibrato_strength(float strength) { +    vibrato_strength = strength; +} + +void increase_vibrato_strength(float change) { +    vibrato_strength *= change; +} + +void decrease_vibrato_strength(float change) { +    vibrato_strength /= change; +} + +#endif  /* VIBRATO_STRENGTH_ENABLE */ + +#endif /* VIBRATO_ENABLE */ + +// Polyphony functions + +void set_polyphony_rate(float rate) { +    polyphony_rate = rate; +} + +void enable_polyphony() { +    polyphony_rate = 5; +} + +void disable_polyphony() { +    polyphony_rate = 0; +} + +void increase_polyphony_rate(float change) { +    polyphony_rate *= change; +} + +void decrease_polyphony_rate(float change) { +    polyphony_rate /= change; +} + +// Timbre function + +void set_timbre(float timbre) { +    note_timbre = timbre; +} + +// Tempo functions + +void set_tempo(uint8_t tempo) { +    note_tempo = tempo; +} + +void decrease_tempo(uint8_t tempo_change) { +    note_tempo += tempo_change; +} + +void increase_tempo(uint8_t tempo_change) { +    if (note_tempo - tempo_change < 10) { +        note_tempo = 10; +    } else { +        note_tempo -= tempo_change; +    } +} diff --git a/quantum/audio/audio.h b/quantum/audio/audio.h new file mode 100644 index 0000000000..27fdc2ab63 --- /dev/null +++ b/quantum/audio/audio.h @@ -0,0 +1,106 @@ +/* Copyright 2016 Jack Humbert + * + * 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 AUDIO_H +#define AUDIO_H + +#include <stdint.h> +#include <stdbool.h> +#include <avr/io.h> +#include <util/delay.h> +#include "musical_notes.h" +#include "song_list.h" +#include "voices.h" +#include "quantum.h" + +// Largely untested PWM audio mode (doesn't sound as good) +// #define PWM_AUDIO + +// #define VIBRATO_ENABLE + +// Enable vibrato strength/amplitude - slows down ISR too much +// #define VIBRATO_STRENGTH_ENABLE + +typedef union { +    uint8_t raw; +    struct { +        bool    enable :1; +        uint8_t level  :7; +    }; +} audio_config_t; + +bool is_audio_on(void); +void audio_toggle(void); +void audio_on(void); +void audio_off(void); + +// Vibrato rate functions + +#ifdef VIBRATO_ENABLE + +void set_vibrato_rate(float rate); +void increase_vibrato_rate(float change); +void decrease_vibrato_rate(float change); + +#ifdef VIBRATO_STRENGTH_ENABLE + +void set_vibrato_strength(float strength); +void increase_vibrato_strength(float change); +void decrease_vibrato_strength(float change); + +#endif + +#endif + +// Polyphony functions + +void set_polyphony_rate(float rate); +void enable_polyphony(void); +void disable_polyphony(void); +void increase_polyphony_rate(float change); +void decrease_polyphony_rate(float change); + +void set_timbre(float timbre); +void set_tempo(uint8_t tempo); + +void increase_tempo(uint8_t tempo_change); +void decrease_tempo(uint8_t tempo_change); + +void audio_init(void); + +#ifdef PWM_AUDIO +void play_sample(uint8_t * s, uint16_t l, bool r); +#endif +void play_note(float freq, int vol); +void stop_note(float freq); +void stop_all_notes(void); +void play_notes(float (*np)[][2], uint16_t n_count, bool n_repeat, float n_rest); + +#define SCALE (int8_t []){ 0 + (12*0), 2 + (12*0), 4 + (12*0), 5 + (12*0), 7 + (12*0), 9 + (12*0), 11 + (12*0), \ +                           0 + (12*1), 2 + (12*1), 4 + (12*1), 5 + (12*1), 7 + (12*1), 9 + (12*1), 11 + (12*1), \ +                           0 + (12*2), 2 + (12*2), 4 + (12*2), 5 + (12*2), 7 + (12*2), 9 + (12*2), 11 + (12*2), \ +                           0 + (12*3), 2 + (12*3), 4 + (12*3), 5 + (12*3), 7 + (12*3), 9 + (12*3), 11 + (12*3), \ +                           0 + (12*4), 2 + (12*4), 4 + (12*4), 5 + (12*4), 7 + (12*4), 9 + (12*4), 11 + (12*4), } + +// These macros are used to allow play_notes to play an array of indeterminate +// length. This works around the limitation of C's sizeof operation on pointers. +// The global float array for the song must be used here. +#define NOTE_ARRAY_SIZE(x) ((int16_t)(sizeof(x) / (sizeof(x[0])))) +#define PLAY_NOTE_ARRAY(note_array, note_repeat, note_rest_style) play_notes(¬e_array, NOTE_ARRAY_SIZE((note_array)), (note_repeat), (note_rest_style)); + + +bool is_playing_notes(void); + +#endif diff --git a/quantum/audio/audio_pwm.c b/quantum/audio/audio_pwm.c new file mode 100644 index 0000000000..ded86edeea --- /dev/null +++ b/quantum/audio/audio_pwm.c @@ -0,0 +1,658 @@ +/* Copyright 2016 Jack Humbert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ +#include <stdio.h> +#include <string.h> +//#include <math.h> +#include <avr/pgmspace.h> +#include <avr/interrupt.h> +#include <avr/io.h> +#include "print.h" +#include "audio.h" +#include "keymap.h" + +#include "eeconfig.h" + +#define PI 3.14159265 + +#define CPU_PRESCALER 8 + + +// Timer Abstractions + +// TIMSK3 - Timer/Counter #3 Interrupt Mask Register +// Turn on/off 3A interputs, stopping/enabling the ISR calls +#define ENABLE_AUDIO_COUNTER_3_ISR TIMSK3 |= _BV(OCIE3A) +#define DISABLE_AUDIO_COUNTER_3_ISR TIMSK3 &= ~_BV(OCIE3A) + + +// TCCR3A: Timer/Counter #3 Control Register +// Compare Output Mode (COM3An) = 0b00 = Normal port operation, OC3A disconnected from PC6 +#define ENABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A |= _BV(COM3A1); +#define DISABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A &= ~(_BV(COM3A1) | _BV(COM3A0)); + + +#define NOTE_PERIOD ICR3 +#define NOTE_DUTY_CYCLE OCR3A + + +#ifdef PWM_AUDIO +    #include "wave.h" +    #define SAMPLE_DIVIDER 39 +    #define SAMPLE_RATE (2000000.0/SAMPLE_DIVIDER/2048) +    // Resistor value of 1/ (2 * PI * 10nF * (2000000 hertz / SAMPLE_DIVIDER / 10)) for 10nF cap + +    float places[8] = {0, 0, 0, 0, 0, 0, 0, 0}; +    uint16_t place_int = 0; +    bool repeat = true; +#endif + +void delay_us(int count) { +  while(count--) { +    _delay_us(1); +  } +} + +int voices = 0; +int voice_place = 0; +float frequency = 0; +int volume = 0; +long position = 0; + +float frequencies[8] = {0, 0, 0, 0, 0, 0, 0, 0}; +int volumes[8] = {0, 0, 0, 0, 0, 0, 0, 0}; +bool sliding = false; + +float place = 0; + +uint8_t * sample; +uint16_t sample_length = 0; +// float freq = 0; + +bool     playing_notes = false; +bool     playing_note = false; +float    note_frequency = 0; +float    note_length = 0; +uint8_t  note_tempo = TEMPO_DEFAULT; +float    note_timbre = TIMBRE_DEFAULT; +uint16_t note_position = 0; +float (* notes_pointer)[][2]; +uint16_t notes_count; +bool     notes_repeat; +float    notes_rest; +bool     note_resting = false; + +uint8_t current_note = 0; +uint8_t rest_counter = 0; + +#ifdef VIBRATO_ENABLE +float vibrato_counter = 0; +float vibrato_strength = .5; +float vibrato_rate = 0.125; +#endif + +float polyphony_rate = 0; + +static bool audio_initialized = false; + +audio_config_t audio_config; + +uint16_t envelope_index = 0; + +void audio_init() { + +    // Check EEPROM +    if (!eeconfig_is_enabled()) +    { +        eeconfig_init(); +    } +    audio_config.raw = eeconfig_read_audio(); + +    #ifdef PWM_AUDIO + +        PLLFRQ = _BV(PDIV2); +        PLLCSR = _BV(PLLE); +        while(!(PLLCSR & _BV(PLOCK))); +        PLLFRQ |= _BV(PLLTM0); /* PCK 48MHz */ + +        /* Init a fast PWM on Timer4 */ +        TCCR4A = _BV(COM4A0) | _BV(PWM4A); /* Clear OC4A on Compare Match */ +        TCCR4B = _BV(CS40); /* No prescaling => f = PCK/256 = 187500Hz */ +        OCR4A = 0; + +        /* Enable the OC4A output */ +        DDRC |= _BV(PORTC6); + +        DISABLE_AUDIO_COUNTER_3_ISR; // Turn off 3A interputs + +        TCCR3A = 0x0; // Options not needed +        TCCR3B = _BV(CS31) | _BV(CS30) | _BV(WGM32); // 64th prescaling and CTC +        OCR3A = SAMPLE_DIVIDER - 1; // Correct count/compare, related to sample playback + +    #else + +    	// Set port PC6 (OC3A and /OC4A) as output +        DDRC |= _BV(PORTC6); + +        DISABLE_AUDIO_COUNTER_3_ISR; + +		// TCCR3A / TCCR3B: Timer/Counter #3 Control Registers +		// Compare Output Mode (COM3An) = 0b00 = Normal port operation, OC3A disconnected from PC6 +		// Waveform Generation Mode (WGM3n) = 0b1110 = Fast PWM Mode 14 (Period = ICR3, Duty Cycle = OCR3A) +		// Clock Select (CS3n) = 0b010 = Clock / 8 +        TCCR3A = (0 << COM3A1) | (0 << COM3A0) | (1 << WGM31) | (0 << WGM30); +        TCCR3B = (1 << WGM33)  | (1 << WGM32)  | (0 << CS32)  | (1 << CS31) | (0 << CS30); + +    #endif + +    audio_initialized = true; +} + +void stop_all_notes() { +    if (!audio_initialized) { +        audio_init(); +    } +    voices = 0; +    #ifdef PWM_AUDIO +	    DISABLE_AUDIO_COUNTER_3_ISR; +    #else +        DISABLE_AUDIO_COUNTER_3_ISR; +        DISABLE_AUDIO_COUNTER_3_OUTPUT; +    #endif + +    playing_notes = false; +    playing_note = false; +    frequency = 0; +    volume = 0; + +    for (uint8_t i = 0; i < 8; i++) +    { +        frequencies[i] = 0; +        volumes[i] = 0; +    } +} + +void stop_note(float freq) +{ +    if (playing_note) { +        if (!audio_initialized) { +            audio_init(); +        } +        #ifdef PWM_AUDIO +            freq = freq / SAMPLE_RATE; +        #endif +        for (int i = 7; i >= 0; i--) { +            if (frequencies[i] == freq) { +                frequencies[i] = 0; +                volumes[i] = 0; +                for (int j = i; (j < 7); j++) { +                    frequencies[j] = frequencies[j+1]; +                    frequencies[j+1] = 0; +                    volumes[j] = volumes[j+1]; +                    volumes[j+1] = 0; +                } +                break; +            } +        } +        voices--; +        if (voices < 0) +            voices = 0; +        if (voice_place >= voices) { +            voice_place = 0; +        } +        if (voices == 0) { +            #ifdef PWM_AUDIO +                DISABLE_AUDIO_COUNTER_3_ISR; +            #else +                DISABLE_AUDIO_COUNTER_3_ISR; +                DISABLE_AUDIO_COUNTER_3_OUTPUT; +            #endif +            frequency = 0; +            volume = 0; +            playing_note = false; +        } +    } +} + +#ifdef VIBRATO_ENABLE + +float mod(float a, int b) +{ +    float r = fmod(a, b); +    return r < 0 ? r + b : r; +} + +float vibrato(float average_freq) { +    #ifdef VIBRATO_STRENGTH_ENABLE +        float vibrated_freq = average_freq * pow(vibrato_lut[(int)vibrato_counter], vibrato_strength); +    #else +        float vibrated_freq = average_freq * vibrato_lut[(int)vibrato_counter]; +    #endif +    vibrato_counter = mod((vibrato_counter + vibrato_rate * (1.0 + 440.0/average_freq)), VIBRATO_LUT_LENGTH); +    return vibrated_freq; +} + +#endif + +ISR(TIMER3_COMPA_vect) +{ +    if (playing_note) { +        #ifdef PWM_AUDIO +            if (voices == 1) { +                // SINE +                OCR4A = pgm_read_byte(&sinewave[(uint16_t)place]) >> 2; + +                // SQUARE +                // if (((int)place) >= 1024){ +                //     OCR4A = 0xFF >> 2; +                // } else { +                //     OCR4A = 0x00; +                // } + +                // SAWTOOTH +                // OCR4A = (int)place / 4; + +                // TRIANGLE +                // if (((int)place) >= 1024) { +                //     OCR4A = (int)place / 2; +                // } else { +                //     OCR4A = 2048 - (int)place / 2; +                // } + +                place += frequency; + +                if (place >= SINE_LENGTH) +                    place -= SINE_LENGTH; + +            } else { +                int sum = 0; +                for (int i = 0; i < voices; i++) { +                    // SINE +                    sum += pgm_read_byte(&sinewave[(uint16_t)places[i]]) >> 2; + +                    // SQUARE +                    // if (((int)places[i]) >= 1024){ +                    //     sum += 0xFF >> 2; +                    // } else { +                    //     sum += 0x00; +                    // } + +                    places[i] += frequencies[i]; + +                    if (places[i] >= SINE_LENGTH) +                        places[i] -= SINE_LENGTH; +                } +                OCR4A = sum; +            } +        #else +            if (voices > 0) { +                float freq; +                if (polyphony_rate > 0) { +                    if (voices > 1) { +                        voice_place %= voices; +                        if (place++ > (frequencies[voice_place] / polyphony_rate / CPU_PRESCALER)) { +                            voice_place = (voice_place + 1) % voices; +                            place = 0.0; +                        } +                    } +                    #ifdef VIBRATO_ENABLE +                    if (vibrato_strength > 0) { +                        freq = vibrato(frequencies[voice_place]); +                    } else { +                    #else +                    { +                    #endif +                        freq = frequencies[voice_place]; +                    } +                } else { +                    if (frequency != 0 && frequency < frequencies[voices - 1] && frequency < frequencies[voices - 1] * pow(2, -440/frequencies[voices - 1]/12/2)) { +                        frequency = frequency * pow(2, 440/frequency/12/2); +                    } else if (frequency != 0 && frequency > frequencies[voices - 1] && frequency > frequencies[voices - 1] * pow(2, 440/frequencies[voices - 1]/12/2)) { +                        frequency = frequency * pow(2, -440/frequency/12/2); +                    } else { +                        frequency = frequencies[voices - 1]; +                    } + + +                    #ifdef VIBRATO_ENABLE +                    if (vibrato_strength > 0) { +                        freq = vibrato(frequency); +                    } else { +                    #else +                    { +                    #endif +                        freq = frequency; +                    } +                } + +                if (envelope_index < 65535) { +                    envelope_index++; +                } +                freq = voice_envelope(freq); + +                if (freq < 30.517578125) +                    freq = 30.52; +                NOTE_PERIOD = (int)(((double)F_CPU) / (freq * CPU_PRESCALER)); // Set max to the period +                NOTE_DUTY_CYCLE = (int)((((double)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre); // Set compare to half the period +            } +        #endif +    } + +    // SAMPLE +    // OCR4A = pgm_read_byte(&sample[(uint16_t)place_int]); + +    // place_int++; + +    // if (place_int >= sample_length) +    //     if (repeat) +    //         place_int -= sample_length; +    //     else +    //         DISABLE_AUDIO_COUNTER_3_ISR; + + +    if (playing_notes) { +        #ifdef PWM_AUDIO +            OCR4A = pgm_read_byte(&sinewave[(uint16_t)place]) >> 0; + +            place += note_frequency; +            if (place >= SINE_LENGTH) +                place -= SINE_LENGTH; +        #else +            if (note_frequency > 0) { +                float freq; + +                #ifdef VIBRATO_ENABLE +                if (vibrato_strength > 0) { +                    freq = vibrato(note_frequency); +                } else { +                #else +                { +                #endif +                    freq = note_frequency; +                } + +                if (envelope_index < 65535) { +                    envelope_index++; +                } +                freq = voice_envelope(freq); + +                NOTE_PERIOD = (int)(((double)F_CPU) / (freq * CPU_PRESCALER)); // Set max to the period +                NOTE_DUTY_CYCLE = (int)((((double)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre); // Set compare to half the period +            } else { +                NOTE_PERIOD = 0; +                NOTE_DUTY_CYCLE = 0; +            } +        #endif + + +        note_position++; +        bool end_of_note = false; +        if (NOTE_PERIOD > 0) +            end_of_note = (note_position >= (note_length / NOTE_PERIOD * 0xFFFF)); +        else +            end_of_note = (note_position >= (note_length * 0x7FF)); +        if (end_of_note) { +            current_note++; +            if (current_note >= notes_count) { +                if (notes_repeat) { +                    current_note = 0; +                } else { +                    #ifdef PWM_AUDIO +                        DISABLE_AUDIO_COUNTER_3_ISR; +                    #else +                        DISABLE_AUDIO_COUNTER_3_ISR; +                        DISABLE_AUDIO_COUNTER_3_OUTPUT; +                    #endif +                    playing_notes = false; +                    return; +                } +            } +            if (!note_resting && (notes_rest > 0)) { +                note_resting = true; +                note_frequency = 0; +                note_length = notes_rest; +                current_note--; +            } else { +                note_resting = false; +                #ifdef PWM_AUDIO +                    note_frequency = (*notes_pointer)[current_note][0] / SAMPLE_RATE; +                    note_length = (*notes_pointer)[current_note][1] * (((float)note_tempo) / 100); +                #else +                    envelope_index = 0; +                    note_frequency = (*notes_pointer)[current_note][0]; +                    note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100); +                #endif +            } +            note_position = 0; +        } + +    } + +    if (!audio_config.enable) { +        playing_notes = false; +        playing_note = false; +    } +} + +void play_note(float freq, int vol) { + +    if (!audio_initialized) { +        audio_init(); +    } + +	if (audio_config.enable && voices < 8) { +	    DISABLE_AUDIO_COUNTER_3_ISR; + +	    // Cancel notes if notes are playing +	    if (playing_notes) +	        stop_all_notes(); + +	    playing_note = true; + +	    envelope_index = 0; + +	    #ifdef PWM_AUDIO +	        freq = freq / SAMPLE_RATE; +	    #endif +	    if (freq > 0) { +	        frequencies[voices] = freq; +	        volumes[voices] = vol; +	        voices++; +	    } + +	    #ifdef PWM_AUDIO +	        ENABLE_AUDIO_COUNTER_3_ISR; +	    #else +	        ENABLE_AUDIO_COUNTER_3_ISR; +	        ENABLE_AUDIO_COUNTER_3_OUTPUT; +	    #endif +	} + +} + +void play_notes(float (*np)[][2], uint16_t n_count, bool n_repeat, float n_rest) +{ + +    if (!audio_initialized) { +        audio_init(); +    } + +	if (audio_config.enable) { + +	    DISABLE_AUDIO_COUNTER_3_ISR; + +		// Cancel note if a note is playing +	    if (playing_note) +	        stop_all_notes(); + +	    playing_notes = true; + +	    notes_pointer = np; +	    notes_count = n_count; +	    notes_repeat = n_repeat; +	    notes_rest = n_rest; + +	    place = 0; +	    current_note = 0; + +	    #ifdef PWM_AUDIO +	        note_frequency = (*notes_pointer)[current_note][0] / SAMPLE_RATE; +	        note_length = (*notes_pointer)[current_note][1] * (((float)note_tempo) / 100); +	    #else +	        note_frequency = (*notes_pointer)[current_note][0]; +	        note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100); +	    #endif +	    note_position = 0; + + +	    #ifdef PWM_AUDIO +	        ENABLE_AUDIO_COUNTER_3_ISR; +	    #else +	        ENABLE_AUDIO_COUNTER_3_ISR; +	        ENABLE_AUDIO_COUNTER_3_OUTPUT; +	    #endif +	} + +} + +#ifdef PWM_AUDIO +void play_sample(uint8_t * s, uint16_t l, bool r) { +    if (!audio_initialized) { +        audio_init(); +    } + +    if (audio_config.enable) { +        DISABLE_AUDIO_COUNTER_3_ISR; +        stop_all_notes(); +        place_int = 0; +        sample = s; +        sample_length = l; +        repeat = r; + +        ENABLE_AUDIO_COUNTER_3_ISR; +    } +} +#endif + + +void audio_toggle(void) { +    audio_config.enable ^= 1; +    eeconfig_update_audio(audio_config.raw); +} + +void audio_on(void) { +    audio_config.enable = 1; +    eeconfig_update_audio(audio_config.raw); +} + +void audio_off(void) { +    audio_config.enable = 0; +    eeconfig_update_audio(audio_config.raw); +} + +#ifdef VIBRATO_ENABLE + +// Vibrato rate functions + +void set_vibrato_rate(float rate) { +    vibrato_rate = rate; +} + +void increase_vibrato_rate(float change) { +    vibrato_rate *= change; +} + +void decrease_vibrato_rate(float change) { +    vibrato_rate /= change; +} + +#ifdef VIBRATO_STRENGTH_ENABLE + +void set_vibrato_strength(float strength) { +    vibrato_strength = strength; +} + +void increase_vibrato_strength(float change) { +    vibrato_strength *= change; +} + +void decrease_vibrato_strength(float change) { +    vibrato_strength /= change; +} + +#endif  /* VIBRATO_STRENGTH_ENABLE */ + +#endif /* VIBRATO_ENABLE */ + +// Polyphony functions + +void set_polyphony_rate(float rate) { +    polyphony_rate = rate; +} + +void enable_polyphony() { +    polyphony_rate = 5; +} + +void disable_polyphony() { +    polyphony_rate = 0; +} + +void increase_polyphony_rate(float change) { +    polyphony_rate *= change; +} + +void decrease_polyphony_rate(float change) { +    polyphony_rate /= change; +} + +// Timbre function + +void set_timbre(float timbre) { +    note_timbre = timbre; +} + +// Tempo functions + +void set_tempo(uint8_t tempo) { +    note_tempo = tempo; +} + +void decrease_tempo(uint8_t tempo_change) { +    note_tempo += tempo_change; +} + +void increase_tempo(uint8_t tempo_change) { +    if (note_tempo - tempo_change < 10) { +        note_tempo = 10; +    } else { +        note_tempo -= tempo_change; +    } +} + + +//------------------------------------------------------------------------------ +// Override these functions in your keymap file to play different tunes on +// startup and bootloader jump +__attribute__ ((weak)) +void play_startup_tone() +{ +} + +__attribute__ ((weak)) +void play_goodbye_tone() +{ +} +//------------------------------------------------------------------------------ diff --git a/quantum/audio/luts.c b/quantum/audio/luts.c new file mode 100644 index 0000000000..57f2d5924c --- /dev/null +++ b/quantum/audio/luts.c @@ -0,0 +1,398 @@ +/* Copyright 2016 IBNobody + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include <avr/io.h> +#include <avr/interrupt.h> +#include <avr/pgmspace.h> +#include "luts.h" + +const float vibrato_lut[VIBRATO_LUT_LENGTH] = +{ +	1.0022336811487, +	1.0042529943610, +	1.0058584256028, +	1.0068905285205, +	1.0072464122237, +	1.0068905285205, +	1.0058584256028, +	1.0042529943610, +	1.0022336811487, +	1.0000000000000, +	0.9977712970630, +	0.9957650169978, +	0.9941756956510, +	0.9931566259436, +	0.9928057204913, +	0.9931566259436, +	0.9941756956510, +	0.9957650169978, +	0.9977712970630, +	1.0000000000000, +}; + +const uint16_t frequency_lut[FREQUENCY_LUT_LENGTH] = +{ +	0x8E0B, +	0x8C02, +	0x8A00, +	0x8805, +	0x8612, +	0x8426, +	0x8241, +	0x8063, +	0x7E8C, +	0x7CBB, +	0x7AF2, +	0x792E, +	0x7772, +	0x75BB, +	0x740B, +	0x7261, +	0x70BD, +	0x6F20, +	0x6D88, +	0x6BF6, +	0x6A69, +	0x68E3, +	0x6762, +	0x65E6, +	0x6470, +	0x6300, +	0x6194, +	0x602E, +	0x5ECD, +	0x5D71, +	0x5C1A, +	0x5AC8, +	0x597B, +	0x5833, +	0x56EF, +	0x55B0, +	0x5475, +	0x533F, +	0x520E, +	0x50E1, +	0x4FB8, +	0x4E93, +	0x4D73, +	0x4C57, +	0x4B3E, +	0x4A2A, +	0x491A, +	0x480E, +	0x4705, +	0x4601, +	0x4500, +	0x4402, +	0x4309, +	0x4213, +	0x4120, +	0x4031, +	0x3F46, +	0x3E5D, +	0x3D79, +	0x3C97, +	0x3BB9, +	0x3ADD, +	0x3A05, +	0x3930, +	0x385E, +	0x3790, +	0x36C4, +	0x35FB, +	0x3534, +	0x3471, +	0x33B1, +	0x32F3, +	0x3238, +	0x3180, +	0x30CA, +	0x3017, +	0x2F66, +	0x2EB8, +	0x2E0D, +	0x2D64, +	0x2CBD, +	0x2C19, +	0x2B77, +	0x2AD8, +	0x2A3A, +	0x299F, +	0x2907, +	0x2870, +	0x27DC, +	0x2749, +	0x26B9, +	0x262B, +	0x259F, +	0x2515, +	0x248D, +	0x2407, +	0x2382, +	0x2300, +	0x2280, +	0x2201, +	0x2184, +	0x2109, +	0x2090, +	0x2018, +	0x1FA3, +	0x1F2E, +	0x1EBC, +	0x1E4B, +	0x1DDC, +	0x1D6E, +	0x1D02, +	0x1C98, +	0x1C2F, +	0x1BC8, +	0x1B62, +	0x1AFD, +	0x1A9A, +	0x1A38, +	0x19D8, +	0x1979, +	0x191C, +	0x18C0, +	0x1865, +	0x180B, +	0x17B3, +	0x175C, +	0x1706, +	0x16B2, +	0x165E, +	0x160C, +	0x15BB, +	0x156C, +	0x151D, +	0x14CF, +	0x1483, +	0x1438, +	0x13EE, +	0x13A4, +	0x135C, +	0x1315, +	0x12CF, +	0x128A, +	0x1246, +	0x1203, +	0x11C1, +	0x1180, +	0x1140, +	0x1100, +	0x10C2, +	0x1084, +	0x1048, +	0x100C, +	0xFD1, +	0xF97, +	0xF5E, +	0xF25, +	0xEEE, +	0xEB7, +	0xE81, +	0xE4C, +	0xE17, +	0xDE4, +	0xDB1, +	0xD7E, +	0xD4D, +	0xD1C, +	0xCEC, +	0xCBC, +	0xC8E, +	0xC60, +	0xC32, +	0xC05, +	0xBD9, +	0xBAE, +	0xB83, +	0xB59, +	0xB2F, +	0xB06, +	0xADD, +	0xAB6, +	0xA8E, +	0xA67, +	0xA41, +	0xA1C, +	0x9F7, +	0x9D2, +	0x9AE, +	0x98A, +	0x967, +	0x945, +	0x923, +	0x901, +	0x8E0, +	0x8C0, +	0x8A0, +	0x880, +	0x861, +	0x842, +	0x824, +	0x806, +	0x7E8, +	0x7CB, +	0x7AF, +	0x792, +	0x777, +	0x75B, +	0x740, +	0x726, +	0x70B, +	0x6F2, +	0x6D8, +	0x6BF, +	0x6A6, +	0x68E, +	0x676, +	0x65E, +	0x647, +	0x630, +	0x619, +	0x602, +	0x5EC, +	0x5D7, +	0x5C1, +	0x5AC, +	0x597, +	0x583, +	0x56E, +	0x55B, +	0x547, +	0x533, +	0x520, +	0x50E, +	0x4FB, +	0x4E9, +	0x4D7, +	0x4C5, +	0x4B3, +	0x4A2, +	0x491, +	0x480, +	0x470, +	0x460, +	0x450, +	0x440, +	0x430, +	0x421, +	0x412, +	0x403, +	0x3F4, +	0x3E5, +	0x3D7, +	0x3C9, +	0x3BB, +	0x3AD, +	0x3A0, +	0x393, +	0x385, +	0x379, +	0x36C, +	0x35F, +	0x353, +	0x347, +	0x33B, +	0x32F, +	0x323, +	0x318, +	0x30C, +	0x301, +	0x2F6, +	0x2EB, +	0x2E0, +	0x2D6, +	0x2CB, +	0x2C1, +	0x2B7, +	0x2AD, +	0x2A3, +	0x299, +	0x290, +	0x287, +	0x27D, +	0x274, +	0x26B, +	0x262, +	0x259, +	0x251, +	0x248, +	0x240, +	0x238, +	0x230, +	0x228, +	0x220, +	0x218, +	0x210, +	0x209, +	0x201, +	0x1FA, +	0x1F2, +	0x1EB, +	0x1E4, +	0x1DD, +	0x1D6, +	0x1D0, +	0x1C9, +	0x1C2, +	0x1BC, +	0x1B6, +	0x1AF, +	0x1A9, +	0x1A3, +	0x19D, +	0x197, +	0x191, +	0x18C, +	0x186, +	0x180, +	0x17B, +	0x175, +	0x170, +	0x16B, +	0x165, +	0x160, +	0x15B, +	0x156, +	0x151, +	0x14C, +	0x148, +	0x143, +	0x13E, +	0x13A, +	0x135, +	0x131, +	0x12C, +	0x128, +	0x124, +	0x120, +	0x11C, +	0x118, +	0x114, +	0x110, +	0x10C, +	0x108, +	0x104, +	0x100, +	0xFD, +	0xF9, +	0xF5, +	0xF2, +	0xEE, +}; + diff --git a/quantum/audio/luts.h b/quantum/audio/luts.h new file mode 100644 index 0000000000..155e34e88d --- /dev/null +++ b/quantum/audio/luts.h @@ -0,0 +1,31 @@ +/* Copyright 2016 IBNobody + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include <avr/io.h> +#include <avr/interrupt.h> +#include <avr/pgmspace.h> + +#ifndef LUTS_H +#define LUTS_H + +#define VIBRATO_LUT_LENGTH 20 + +#define FREQUENCY_LUT_LENGTH 349 + +extern const float vibrato_lut[VIBRATO_LUT_LENGTH]; +extern const uint16_t frequency_lut[FREQUENCY_LUT_LENGTH]; + +#endif /* LUTS_H */ diff --git a/quantum/audio/musical_notes.h b/quantum/audio/musical_notes.h new file mode 100644 index 0000000000..a3aaa2f199 --- /dev/null +++ b/quantum/audio/musical_notes.h @@ -0,0 +1,233 @@ +/* Copyright 2016 Jack Humbert + * + * 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 MUSICAL_NOTES_H +#define MUSICAL_NOTES_H + +// Tempo Placeholder +#define TEMPO_DEFAULT 100 + + +#define SONG(notes...) { notes } + + +// Note Types +#define MUSICAL_NOTE(note, duration)   {(NOTE##note), duration} +#define WHOLE_NOTE(note)               MUSICAL_NOTE(note, 64) +#define HALF_NOTE(note)                MUSICAL_NOTE(note, 32) +#define QUARTER_NOTE(note)             MUSICAL_NOTE(note, 16) +#define EIGHTH_NOTE(note)              MUSICAL_NOTE(note,  8) +#define SIXTEENTH_NOTE(note)           MUSICAL_NOTE(note,  4) + +#define WHOLE_DOT_NOTE(note)           MUSICAL_NOTE(note, 64+32) +#define HALF_DOT_NOTE(note)            MUSICAL_NOTE(note, 32+16) +#define QUARTER_DOT_NOTE(note)         MUSICAL_NOTE(note, 16+8) +#define EIGHTH_DOT_NOTE(note)          MUSICAL_NOTE(note,  8+4) +#define SIXTEENTH_DOT_NOTE(note)       MUSICAL_NOTE(note,  4+2) + +// Note Type Shortcuts +#define M__NOTE(note, duration)        MUSICAL_NOTE(note, duration) +#define W__NOTE(n)                     WHOLE_NOTE(n) +#define H__NOTE(n)                     HALF_NOTE(n) +#define Q__NOTE(n)                     QUARTER_NOTE(n) +#define E__NOTE(n)                     EIGHTH_NOTE(n) +#define S__NOTE(n)                     SIXTEENTH_NOTE(n) +#define WD_NOTE(n)                     WHOLE_DOT_NOTE(n) +#define HD_NOTE(n)                     HALF_DOT_NOTE(n) +#define QD_NOTE(n)                     QUARTER_DOT_NOTE(n) +#define ED_NOTE(n)                     EIGHTH_DOT_NOTE(n) +#define SD_NOTE(n)                     SIXTEENTH_DOT_NOTE(n) + +// Note Styles +// Staccato makes sure there is a rest between each note. Think: TA TA TA +// Legato makes notes flow together. Think: TAAA +#define STACCATO 0.01 +#define LEGATO   0 + +// Note Timbre +// Changes how the notes sound +#define TIMBRE_12       0.125 +#define TIMBRE_25       0.250 +#define TIMBRE_50       0.500 +#define TIMBRE_75       0.750 +#define TIMBRE_DEFAULT  TIMBRE_50 + + +// Notes - # = Octave + +#define NOTE_REST         0.00 + +/* These notes are currently bugged +#define NOTE_C0          16.35 +#define NOTE_CS0         17.32 +#define NOTE_D0          18.35 +#define NOTE_DS0         19.45 +#define NOTE_E0          20.60 +#define NOTE_F0          21.83 +#define NOTE_FS0         23.12 +#define NOTE_G0          24.50 +#define NOTE_GS0         25.96 +#define NOTE_A0          27.50 +#define NOTE_AS0         29.14 +#define NOTE_B0          30.87 +#define NOTE_C1          32.70 +#define NOTE_CS1         34.65 +#define NOTE_D1          36.71 +#define NOTE_DS1         38.89 +#define NOTE_E1          41.20 +#define NOTE_F1          43.65 +#define NOTE_FS1         46.25 +#define NOTE_G1          49.00 +#define NOTE_GS1         51.91 +#define NOTE_A1          55.00 +#define NOTE_AS1         58.27 +*/ + +#define NOTE_B1          61.74 +#define NOTE_C2          65.41 +#define NOTE_CS2         69.30 +#define NOTE_D2          73.42 +#define NOTE_DS2         77.78 +#define NOTE_E2          82.41 +#define NOTE_F2          87.31 +#define NOTE_FS2         92.50 +#define NOTE_G2          98.00 +#define NOTE_GS2        103.83 +#define NOTE_A2         110.00 +#define NOTE_AS2        116.54 +#define NOTE_B2         123.47 +#define NOTE_C3         130.81 +#define NOTE_CS3        138.59 +#define NOTE_D3         146.83 +#define NOTE_DS3        155.56 +#define NOTE_E3         164.81 +#define NOTE_F3         174.61 +#define NOTE_FS3        185.00 +#define NOTE_G3         196.00 +#define NOTE_GS3        207.65 +#define NOTE_A3         220.00 +#define NOTE_AS3        233.08 +#define NOTE_B3         246.94 +#define NOTE_C4         261.63 +#define NOTE_CS4        277.18 +#define NOTE_D4         293.66 +#define NOTE_DS4        311.13 +#define NOTE_E4         329.63 +#define NOTE_F4         349.23 +#define NOTE_FS4        369.99 +#define NOTE_G4         392.00 +#define NOTE_GS4        415.30 +#define NOTE_A4         440.00 +#define NOTE_AS4        466.16 +#define NOTE_B4         493.88 +#define NOTE_C5         523.25 +#define NOTE_CS5        554.37 +#define NOTE_D5         587.33 +#define NOTE_DS5        622.25 +#define NOTE_E5         659.26 +#define NOTE_F5         698.46 +#define NOTE_FS5        739.99 +#define NOTE_G5         783.99 +#define NOTE_GS5        830.61 +#define NOTE_A5         880.00 +#define NOTE_AS5        932.33 +#define NOTE_B5         987.77 +#define NOTE_C6        1046.50 +#define NOTE_CS6       1108.73 +#define NOTE_D6        1174.66 +#define NOTE_DS6       1244.51 +#define NOTE_E6        1318.51 +#define NOTE_F6        1396.91 +#define NOTE_FS6       1479.98 +#define NOTE_G6        1567.98 +#define NOTE_GS6       1661.22 +#define NOTE_A6        1760.00 +#define NOTE_AS6       1864.66 +#define NOTE_B6        1975.53 +#define NOTE_C7        2093.00 +#define NOTE_CS7       2217.46 +#define NOTE_D7        2349.32 +#define NOTE_DS7       2489.02 +#define NOTE_E7        2637.02 +#define NOTE_F7        2793.83 +#define NOTE_FS7       2959.96 +#define NOTE_G7        3135.96 +#define NOTE_GS7       3322.44 +#define NOTE_A7        3520.00 +#define NOTE_AS7       3729.31 +#define NOTE_B7        3951.07 +#define NOTE_C8        4186.01 +#define NOTE_CS8       4434.92 +#define NOTE_D8        4698.64 +#define NOTE_DS8       4978.03 +#define NOTE_E8        5274.04 +#define NOTE_F8        5587.65 +#define NOTE_FS8       5919.91 +#define NOTE_G8        6271.93 +#define NOTE_GS8       6644.88 +#define NOTE_A8        7040.00 +#define NOTE_AS8       7458.62 +#define NOTE_B8        7902.13 + +// Flat Aliases +#define NOTE_DF0 NOTE_CS0 +#define NOTE_EF0 NOTE_DS0 +#define NOTE_GF0 NOTE_FS0 +#define NOTE_AF0 NOTE_GS0 +#define NOTE_BF0 NOTE_AS0 +#define NOTE_DF1 NOTE_CS1 +#define NOTE_EF1 NOTE_DS1 +#define NOTE_GF1 NOTE_FS1 +#define NOTE_AF1 NOTE_GS1 +#define NOTE_BF1 NOTE_AS1 +#define NOTE_DF2 NOTE_CS2 +#define NOTE_EF2 NOTE_DS2 +#define NOTE_GF2 NOTE_FS2 +#define NOTE_AF2 NOTE_GS2 +#define NOTE_BF2 NOTE_AS2 +#define NOTE_DF3 NOTE_CS3 +#define NOTE_EF3 NOTE_DS3 +#define NOTE_GF3 NOTE_FS3 +#define NOTE_AF3 NOTE_GS3 +#define NOTE_BF3 NOTE_AS3 +#define NOTE_DF4 NOTE_CS4 +#define NOTE_EF4 NOTE_DS4 +#define NOTE_GF4 NOTE_FS4 +#define NOTE_AF4 NOTE_GS4 +#define NOTE_BF4 NOTE_AS4 +#define NOTE_DF5 NOTE_CS5 +#define NOTE_EF5 NOTE_DS5 +#define NOTE_GF5 NOTE_FS5 +#define NOTE_AF5 NOTE_GS5 +#define NOTE_BF5 NOTE_AS5 +#define NOTE_DF6 NOTE_CS6 +#define NOTE_EF6 NOTE_DS6 +#define NOTE_GF6 NOTE_FS6 +#define NOTE_AF6 NOTE_GS6 +#define NOTE_BF6 NOTE_AS6 +#define NOTE_DF7 NOTE_CS7 +#define NOTE_EF7 NOTE_DS7 +#define NOTE_GF7 NOTE_FS7 +#define NOTE_AF7 NOTE_GS7 +#define NOTE_BF7 NOTE_AS7 +#define NOTE_DF8 NOTE_CS8 +#define NOTE_EF8 NOTE_DS8 +#define NOTE_GF8 NOTE_FS8 +#define NOTE_AF8 NOTE_GS8 +#define NOTE_BF8 NOTE_AS8 + + +#endif diff --git a/quantum/audio/song_list.h b/quantum/audio/song_list.h new file mode 100644 index 0000000000..db2d1a94cd --- /dev/null +++ b/quantum/audio/song_list.h @@ -0,0 +1,179 @@ +/* Copyright 2016 Jack Humbert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ +#include "musical_notes.h" + +#ifndef SONG_LIST_H +#define SONG_LIST_H + +#define COIN_SOUND \ +    E__NOTE(_A5  ),\ +    HD_NOTE(_E6  ), + +#define ODE_TO_JOY                                          \ +    Q__NOTE(_E4), Q__NOTE(_E4), Q__NOTE(_F4), Q__NOTE(_G4), \ +    Q__NOTE(_G4), Q__NOTE(_F4), Q__NOTE(_E4), Q__NOTE(_D4), \ +    Q__NOTE(_C4), Q__NOTE(_C4), Q__NOTE(_D4), Q__NOTE(_E4), \ +    QD_NOTE(_E4), E__NOTE(_D4), H__NOTE(_D4), + +#define ROCK_A_BYE_BABY                            \ +    QD_NOTE(_B4), E__NOTE(_D4), Q__NOTE(_B5),      \ +    H__NOTE(_A5), Q__NOTE(_G5),                    \ +    QD_NOTE(_B4), E__NOTE(_D5), Q__NOTE(_G5),      \ +    H__NOTE(_FS5), + +#define CLOSE_ENCOUNTERS_5_NOTE  \ +	Q__NOTE(_D5),                \ +	Q__NOTE(_E5),                \ +	Q__NOTE(_C5),                \ +	Q__NOTE(_C4),                \ +	Q__NOTE(_G4), + +#define DOE_A_DEER              \ +	QD_NOTE(_C4), E__NOTE(_D4), \ +	QD_NOTE(_E4), E__NOTE(_C4), \ +	Q__NOTE(_E4), Q__NOTE(_C4), \ +	Q__NOTE(_E4), + +/* Requires: PLAY_NOTE_ARRAY(..., ..., STACCATO); */ +#define IN_LIKE_FLINT \ +    E__NOTE(_AS4), E__NOTE(_AS4), QD_NOTE(_B4),  \ +    E__NOTE(_AS4), E__NOTE(_B4),  QD_NOTE(_CS4), \ +    E__NOTE(_B4),  E__NOTE(_CS4), QD_NOTE(_DS4), \ +    E__NOTE(_CS4), E__NOTE(_B4),  QD_NOTE(_AS4), \ +    E__NOTE(_AS4), E__NOTE(_AS4), QD_NOTE(_B4), + +#define GOODBYE_SOUND \ +    E__NOTE(_E7),     \ +    E__NOTE(_A6),     \ +    ED_NOTE(_E6), + +#define STARTUP_SOUND  \ +    ED_NOTE(_E7 ),     \ +    E__NOTE(_CS7),     \ +    E__NOTE(_E6 ),     \ +    E__NOTE(_A6 ),     \ +    M__NOTE(_CS7, 20), + +#define QWERTY_SOUND \ +    E__NOTE(_GS6 ),  \ +    E__NOTE(_A6  ),  \ +    S__NOTE(_REST),  \ +    Q__NOTE(_E7  ), + +#define COLEMAK_SOUND \ +    E__NOTE(_GS6 ),   \ +    E__NOTE(_A6  ),   \ +    S__NOTE(_REST),   \ +    ED_NOTE(_E7  ),   \ +    S__NOTE(_REST),   \ +    ED_NOTE(_GS7 ), + +#define DVORAK_SOUND \ +    E__NOTE(_GS6 ),  \ +    E__NOTE(_A6  ),  \ +    S__NOTE(_REST),  \ +    E__NOTE(_E7  ),  \ +    S__NOTE(_REST),  \ +    E__NOTE(_FS7 ),  \ +    S__NOTE(_REST),  \ +    E__NOTE(_E7  ), + +#define PLOVER_SOUND \ +    E__NOTE(_GS6 ),  \ +    E__NOTE(_A6  ),  \ +    S__NOTE(_REST),  \ +    ED_NOTE(_E7  ),  \ +    S__NOTE(_REST),  \ +    ED_NOTE(_A7  ), + +#define PLOVER_GOODBYE_SOUND \ +    E__NOTE(_GS6 ),  \ +    E__NOTE(_A6  ),  \ +    S__NOTE(_REST),  \ +    ED_NOTE(_A7  ),  \ +    S__NOTE(_REST),  \ +    ED_NOTE(_E7  ), + +#define MUSIC_SCALE_SOUND \ +    E__NOTE(_A5 ),        \ +    E__NOTE(_B5 ),        \ +    E__NOTE(_CS6),        \ +    E__NOTE(_D6 ),        \ +    E__NOTE(_E6 ),        \ +    E__NOTE(_FS6),        \ +    E__NOTE(_GS6),        \ +    E__NOTE(_A6 ), + +#define CAPS_LOCK_ON_SOUND \ +    E__NOTE(_A3),          \ +    E__NOTE(_B3), + +#define CAPS_LOCK_OFF_SOUND \ +    E__NOTE(_B3),           \ +    E__NOTE(_A3), + +#define SCROLL_LOCK_ON_SOUND \ +    E__NOTE(_D4),            \ +    E__NOTE(_E4), + +#define SCROLL_LOCK_OFF_SOUND \ +    E__NOTE(_E4),             \ +    E__NOTE(_D4), + +#define NUM_LOCK_ON_SOUND \ +    E__NOTE(_D5),         \ +    E__NOTE(_E5), + +#define NUM_LOCK_OFF_SOUND \ +    E__NOTE(_E5),          \ +    E__NOTE(_D5), + +#define UNICODE_WINDOWS \ +    E__NOTE(_B5),       \ +    S__NOTE(_E6), + +#define UNICODE_LINUX \ +    E__NOTE(_E6),     \ +    S__NOTE(_B5), + +#define COIN_SOUND \ +    E__NOTE(_A5  ),      \ +    HD_NOTE(_E6  ), + +#define ONE_UP_SOUND \ +    Q__NOTE(_E6  ),  \ +    Q__NOTE(_G6  ),  \ +    Q__NOTE(_E7  ),  \ +    Q__NOTE(_C7  ),  \ +    Q__NOTE(_D7  ),  \ +    Q__NOTE(_G7  ), + +#define SONIC_RING \ +    E__NOTE(_E6),  \ +    E__NOTE(_G6),  \ +    HD_NOTE(_C7), + +#define ZELDA_PUZZLE \ +    Q__NOTE(_G5),     \ +    Q__NOTE(_FS5),    \ +    Q__NOTE(_DS5),     \ +    Q__NOTE(_A4),    \ +    Q__NOTE(_GS4),     \ +    Q__NOTE(_E5),     \ +    Q__NOTE(_GS5),     \ +    HD_NOTE(_C6), + +#endif diff --git a/quantum/audio/voices.c b/quantum/audio/voices.c new file mode 100644 index 0000000000..94147ccb66 --- /dev/null +++ b/quantum/audio/voices.c @@ -0,0 +1,296 @@ +/* Copyright 2016 Jack Humbert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ +#include "voices.h" +#include "audio.h" +#include "stdlib.h" + +// these are imported from audio.c +extern uint16_t envelope_index; +extern float note_timbre; +extern float polyphony_rate; +extern bool glissando; + +voice_type voice = default_voice; + +void set_voice(voice_type v) { +    voice = v; +} + +void voice_iterate() { +    voice = (voice + 1) % number_of_voices; +} + +void voice_deiterate() { +    voice = (voice - 1 + number_of_voices) % number_of_voices; +} + +float voice_envelope(float frequency) { +    // envelope_index ranges from 0 to 0xFFFF, which is preserved at 880.0 Hz +    __attribute__ ((unused)) +    uint16_t compensated_index = (uint16_t)((float)envelope_index * (880.0 / frequency)); + +    switch (voice) { +        case default_voice: +            glissando = false; +            note_timbre = TIMBRE_50; +            polyphony_rate = 0; +	        break; + +    #ifdef AUDIO_VOICES + +        case something: +            glissando = false; +            polyphony_rate = 0; +            switch (compensated_index) { +                case 0 ... 9: +                    note_timbre = TIMBRE_12; +                    break; + +                case 10 ... 19: +                    note_timbre = TIMBRE_25; +                    break; + +                case 20 ... 200: +                    note_timbre = .125 + .125; +                    break; + +                default: +                    note_timbre = .125; +                    break; +            } +            break; + +        case drums: +            glissando = false; +            polyphony_rate = 0; +                // switch (compensated_index) { +                //     case 0 ... 10: +                //         note_timbre = 0.5; +                //         break; +                //     case 11 ... 20: +                //         note_timbre = 0.5 * (21 - compensated_index) / 10; +                //         break; +                //     default: +                //         note_timbre = 0; +                //         break; +                // } +                // frequency = (rand() % (int)(frequency * 1.2 - frequency)) + (frequency * 0.8); + +            if (frequency < 80.0) { + +            } else if (frequency < 160.0) { + +                // Bass drum: 60 - 100 Hz +                frequency = (rand() % (int)(40)) + 60; +                switch (envelope_index) { +                    case 0 ... 10: +                        note_timbre = 0.5; +                        break; +                    case 11 ... 20: +                        note_timbre = 0.5 * (21 - envelope_index) / 10; +                        break; +                    default: +                        note_timbre = 0; +                        break; +                } + +            } else if (frequency < 320.0) { + + +                // Snare drum: 1 - 2 KHz +                frequency = (rand() % (int)(1000)) + 1000; +                switch (envelope_index) { +                    case 0 ... 5: +                        note_timbre = 0.5; +                        break; +                    case 6 ... 20: +                        note_timbre = 0.5 * (21 - envelope_index) / 15; +                        break; +                    default: +                        note_timbre = 0; +                        break; +                } + +            } else if (frequency < 640.0) { + +                // Closed Hi-hat: 3 - 5 KHz +                frequency = (rand() % (int)(2000)) + 3000; +                switch (envelope_index) { +                    case 0 ... 15: +                        note_timbre = 0.5; +                        break; +                    case 16 ... 20: +                        note_timbre = 0.5 * (21 - envelope_index) / 5; +                        break; +                    default: +                        note_timbre = 0; +                        break; +                } + +            } else if (frequency < 1280.0) { + +                // Open Hi-hat: 3 - 5 KHz +                frequency = (rand() % (int)(2000)) + 3000; +                switch (envelope_index) { +                    case 0 ... 35: +                        note_timbre = 0.5; +                        break; +                    case 36 ... 50: +                        note_timbre = 0.5 * (51 - envelope_index) / 15; +                        break; +                    default: +                        note_timbre = 0; +                        break; +                } + +            } +            break; +        case butts_fader: +            glissando = true; +            polyphony_rate = 0; +            switch (compensated_index) { +                case 0 ... 9: +                    frequency = frequency / 4; +                    note_timbre = TIMBRE_12; +	                break; + +                case 10 ... 19: +                    frequency = frequency / 2; +                    note_timbre = TIMBRE_12; +	                break; + +                case 20 ... 200: +                    note_timbre = .125 - pow(((float)compensated_index - 20) / (200 - 20), 2)*.125; +	                break; + +                default: +                    note_timbre = 0; +                	break; +            } +    	    break; + +        // case octave_crunch: +        //     polyphony_rate = 0; +        //     switch (compensated_index) { +        //         case 0 ... 9: +        //         case 20 ... 24: +        //         case 30 ... 32: +        //             frequency = frequency / 2; +        //             note_timbre = TIMBRE_12; +        //         break; + +        //         case 10 ... 19: +        //         case 25 ... 29: +        //         case 33 ... 35: +        //             frequency = frequency * 2; +        //             note_timbre = TIMBRE_12; +	       //          break; + +        //         default: +        //             note_timbre = TIMBRE_12; +        //         	break; +        //     } +	       //  break; + +        case duty_osc: +            // This slows the loop down a substantial amount, so higher notes may freeze +            glissando = true; +            polyphony_rate = 0; +            switch (compensated_index) { +                default: +                    #define OCS_SPEED 10 +                    #define OCS_AMP   .25 +                    // sine wave is slow +                    // note_timbre = (sin((float)compensated_index/10000*OCS_SPEED) * OCS_AMP / 2) + .5; +                    // triangle wave is a bit faster +                    note_timbre = (float)abs((compensated_index*OCS_SPEED % 3000) - 1500) * ( OCS_AMP / 1500 ) + (1 - OCS_AMP) / 2; +                	break; +            } +	        break; + +        case duty_octave_down: +            glissando = true; +            polyphony_rate = 0; +            note_timbre = (envelope_index % 2) * .125 + .375 * 2; +            if ((envelope_index % 4) == 0) +                note_timbre = 0.5; +            if ((envelope_index % 8) == 0) +                note_timbre = 0; +            break; +        case delayed_vibrato: +            glissando = true; +            polyphony_rate = 0; +            note_timbre = TIMBRE_50; +            #define VOICE_VIBRATO_DELAY 150 +            #define VOICE_VIBRATO_SPEED 50 +            switch (compensated_index) { +                case 0 ... VOICE_VIBRATO_DELAY: +                    break; +                default: +                    frequency = frequency * vibrato_lut[(int)fmod((((float)compensated_index - (VOICE_VIBRATO_DELAY + 1))/1000*VOICE_VIBRATO_SPEED), VIBRATO_LUT_LENGTH)]; +                    break; +            } +            break; +        // case delayed_vibrato_octave: +        //     polyphony_rate = 0; +        //     if ((envelope_index % 2) == 1) { +        //         note_timbre = 0.55; +        //     } else { +        //         note_timbre = 0.45; +        //     } +        //     #define VOICE_VIBRATO_DELAY 150 +        //     #define VOICE_VIBRATO_SPEED 50 +        //     switch (compensated_index) { +        //         case 0 ... VOICE_VIBRATO_DELAY: +        //             break; +        //         default: +        //             frequency = frequency * VIBRATO_LUT[(int)fmod((((float)compensated_index - (VOICE_VIBRATO_DELAY + 1))/1000*VOICE_VIBRATO_SPEED), VIBRATO_LUT_LENGTH)]; +        //             break; +        //     } +        //     break; +        // case duty_fifth_down: +        //     note_timbre = 0.5; +        //     if ((envelope_index % 3) == 0) +        //         note_timbre = 0.75; +        //     break; +        // case duty_fourth_down: +        //     note_timbre = 0.0; +        //     if ((envelope_index % 12) == 0) +        //         note_timbre = 0.75; +        //     if (((envelope_index % 12) % 4) != 1) +        //         note_timbre = 0.75; +        //     break; +        // case duty_third_down: +        //     note_timbre = 0.5; +        //     if ((envelope_index % 5) == 0) +        //         note_timbre = 0.75; +        //     break; +        // case duty_fifth_third_down: +        //     note_timbre = 0.5; +        //     if ((envelope_index % 5) == 0) +        //         note_timbre = 0.75; +        //     if ((envelope_index % 3) == 0) +        //         note_timbre = 0.25; +        //     break; + +    #endif + +		default: +   			break; +    } + +    return frequency; +} diff --git a/quantum/audio/voices.h b/quantum/audio/voices.h new file mode 100644 index 0000000000..9403a6b5e7 --- /dev/null +++ b/quantum/audio/voices.h @@ -0,0 +1,50 @@ +/* Copyright 2016 Jack Humbert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ +#include <stdint.h> +#include <stdbool.h> +#include <avr/io.h> +#include <util/delay.h> +#include "luts.h" + +#ifndef VOICES_H +#define VOICES_H + +float voice_envelope(float frequency); + +typedef enum { +    default_voice, +    #ifdef AUDIO_VOICES +    something, +    drums, +    butts_fader, +    octave_crunch, +    duty_osc, +    duty_octave_down, +    delayed_vibrato, +    // delayed_vibrato_octave, +    // duty_fifth_down, +    // duty_fourth_down, +    // duty_third_down, +    // duty_fifth_third_down, +    #endif +    number_of_voices // important that this is last +} voice_type; + +void set_voice(voice_type v); +void voice_iterate(void); +void voice_deiterate(void); + +#endif diff --git a/quantum/audio/wave.h b/quantum/audio/wave.h new file mode 100644 index 0000000000..f15615dd1b --- /dev/null +++ b/quantum/audio/wave.h @@ -0,0 +1,281 @@ +/* Copyright 2016 Jack Humbert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include <avr/io.h> +#include <avr/interrupt.h> +#include <avr/pgmspace.h> + +#define SINE_LENGTH 2048 + +const uint8_t sinewave[] PROGMEM= //2048 values +{ +0x80,0x80,0x80,0x81,0x81,0x81,0x82,0x82, +0x83,0x83,0x83,0x84,0x84,0x85,0x85,0x85, +0x86,0x86,0x87,0x87,0x87,0x88,0x88,0x88, +0x89,0x89,0x8a,0x8a,0x8a,0x8b,0x8b,0x8c, +0x8c,0x8c,0x8d,0x8d,0x8e,0x8e,0x8e,0x8f, +0x8f,0x8f,0x90,0x90,0x91,0x91,0x91,0x92, +0x92,0x93,0x93,0x93,0x94,0x94,0x95,0x95, +0x95,0x96,0x96,0x96,0x97,0x97,0x98,0x98, +0x98,0x99,0x99,0x9a,0x9a,0x9a,0x9b,0x9b, +0x9b,0x9c,0x9c,0x9d,0x9d,0x9d,0x9e,0x9e, +0x9e,0x9f,0x9f,0xa0,0xa0,0xa0,0xa1,0xa1, +0xa2,0xa2,0xa2,0xa3,0xa3,0xa3,0xa4,0xa4, +0xa5,0xa5,0xa5,0xa6,0xa6,0xa6,0xa7,0xa7, +0xa7,0xa8,0xa8,0xa9,0xa9,0xa9,0xaa,0xaa, +0xaa,0xab,0xab,0xac,0xac,0xac,0xad,0xad, +0xad,0xae,0xae,0xae,0xaf,0xaf,0xb0,0xb0, +0xb0,0xb1,0xb1,0xb1,0xb2,0xb2,0xb2,0xb3, +0xb3,0xb4,0xb4,0xb4,0xb5,0xb5,0xb5,0xb6, +0xb6,0xb6,0xb7,0xb7,0xb7,0xb8,0xb8,0xb8, +0xb9,0xb9,0xba,0xba,0xba,0xbb,0xbb,0xbb, +0xbc,0xbc,0xbc,0xbd,0xbd,0xbd,0xbe,0xbe, +0xbe,0xbf,0xbf,0xbf,0xc0,0xc0,0xc0,0xc1, +0xc1,0xc1,0xc2,0xc2,0xc2,0xc3,0xc3,0xc3, +0xc4,0xc4,0xc4,0xc5,0xc5,0xc5,0xc6,0xc6, +0xc6,0xc7,0xc7,0xc7,0xc8,0xc8,0xc8,0xc9, +0xc9,0xc9,0xca,0xca,0xca,0xcb,0xcb,0xcb, +0xcb,0xcc,0xcc,0xcc,0xcd,0xcd,0xcd,0xce, +0xce,0xce,0xcf,0xcf,0xcf,0xcf,0xd0,0xd0, +0xd0,0xd1,0xd1,0xd1,0xd2,0xd2,0xd2,0xd2, +0xd3,0xd3,0xd3,0xd4,0xd4,0xd4,0xd5,0xd5, +0xd5,0xd5,0xd6,0xd6,0xd6,0xd7,0xd7,0xd7, +0xd7,0xd8,0xd8,0xd8,0xd9,0xd9,0xd9,0xd9, +0xda,0xda,0xda,0xda,0xdb,0xdb,0xdb,0xdc, +0xdc,0xdc,0xdc,0xdd,0xdd,0xdd,0xdd,0xde, +0xde,0xde,0xde,0xdf,0xdf,0xdf,0xe0,0xe0, +0xe0,0xe0,0xe1,0xe1,0xe1,0xe1,0xe2,0xe2, +0xe2,0xe2,0xe3,0xe3,0xe3,0xe3,0xe4,0xe4, +0xe4,0xe4,0xe4,0xe5,0xe5,0xe5,0xe5,0xe6, +0xe6,0xe6,0xe6,0xe7,0xe7,0xe7,0xe7,0xe8, +0xe8,0xe8,0xe8,0xe8,0xe9,0xe9,0xe9,0xe9, +0xea,0xea,0xea,0xea,0xea,0xeb,0xeb,0xeb, +0xeb,0xeb,0xec,0xec,0xec,0xec,0xec,0xed, +0xed,0xed,0xed,0xed,0xee,0xee,0xee,0xee, +0xee,0xef,0xef,0xef,0xef,0xef,0xf0,0xf0, +0xf0,0xf0,0xf0,0xf0,0xf1,0xf1,0xf1,0xf1, +0xf1,0xf2,0xf2,0xf2,0xf2,0xf2,0xf2,0xf3, +0xf3,0xf3,0xf3,0xf3,0xf3,0xf4,0xf4,0xf4, +0xf4,0xf4,0xf4,0xf5,0xf5,0xf5,0xf5,0xf5, +0xf5,0xf5,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6, +0xf6,0xf7,0xf7,0xf7,0xf7,0xf7,0xf7,0xf7, +0xf8,0xf8,0xf8,0xf8,0xf8,0xf8,0xf8,0xf8, +0xf9,0xf9,0xf9,0xf9,0xf9,0xf9,0xf9,0xf9, +0xfa,0xfa,0xfa,0xfa,0xfa,0xfa,0xfa,0xfa, +0xfa,0xfa,0xfb,0xfb,0xfb,0xfb,0xfb,0xfb, +0xfb,0xfb,0xfb,0xfb,0xfc,0xfc,0xfc,0xfc, +0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc, +0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, +0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfe,0xfe, +0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe, +0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe, +0xfe,0xfe,0xfe,0xfe,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfe, +0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe, +0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe, +0xfe,0xfe,0xfe,0xfd,0xfd,0xfd,0xfd,0xfd, +0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, +0xfd,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc, +0xfc,0xfc,0xfc,0xfc,0xfc,0xfb,0xfb,0xfb, +0xfb,0xfb,0xfb,0xfb,0xfb,0xfb,0xfb,0xfa, +0xfa,0xfa,0xfa,0xfa,0xfa,0xfa,0xfa,0xfa, +0xfa,0xf9,0xf9,0xf9,0xf9,0xf9,0xf9,0xf9, +0xf9,0xf8,0xf8,0xf8,0xf8,0xf8,0xf8,0xf8, +0xf8,0xf7,0xf7,0xf7,0xf7,0xf7,0xf7,0xf7, +0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf6,0xf5, +0xf5,0xf5,0xf5,0xf5,0xf5,0xf5,0xf4,0xf4, +0xf4,0xf4,0xf4,0xf4,0xf3,0xf3,0xf3,0xf3, +0xf3,0xf3,0xf2,0xf2,0xf2,0xf2,0xf2,0xf2, +0xf1,0xf1,0xf1,0xf1,0xf1,0xf0,0xf0,0xf0, +0xf0,0xf0,0xf0,0xef,0xef,0xef,0xef,0xef, +0xee,0xee,0xee,0xee,0xee,0xed,0xed,0xed, +0xed,0xed,0xec,0xec,0xec,0xec,0xec,0xeb, +0xeb,0xeb,0xeb,0xeb,0xea,0xea,0xea,0xea, +0xea,0xe9,0xe9,0xe9,0xe9,0xe8,0xe8,0xe8, +0xe8,0xe8,0xe7,0xe7,0xe7,0xe7,0xe6,0xe6, +0xe6,0xe6,0xe5,0xe5,0xe5,0xe5,0xe4,0xe4, +0xe4,0xe4,0xe4,0xe3,0xe3,0xe3,0xe3,0xe2, +0xe2,0xe2,0xe2,0xe1,0xe1,0xe1,0xe1,0xe0, +0xe0,0xe0,0xe0,0xdf,0xdf,0xdf,0xde,0xde, +0xde,0xde,0xdd,0xdd,0xdd,0xdd,0xdc,0xdc, +0xdc,0xdc,0xdb,0xdb,0xdb,0xda,0xda,0xda, +0xda,0xd9,0xd9,0xd9,0xd9,0xd8,0xd8,0xd8, +0xd7,0xd7,0xd7,0xd7,0xd6,0xd6,0xd6,0xd5, +0xd5,0xd5,0xd5,0xd4,0xd4,0xd4,0xd3,0xd3, +0xd3,0xd2,0xd2,0xd2,0xd2,0xd1,0xd1,0xd1, +0xd0,0xd0,0xd0,0xcf,0xcf,0xcf,0xcf,0xce, +0xce,0xce,0xcd,0xcd,0xcd,0xcc,0xcc,0xcc, +0xcb,0xcb,0xcb,0xcb,0xca,0xca,0xca,0xc9, +0xc9,0xc9,0xc8,0xc8,0xc8,0xc7,0xc7,0xc7, +0xc6,0xc6,0xc6,0xc5,0xc5,0xc5,0xc4,0xc4, +0xc4,0xc3,0xc3,0xc3,0xc2,0xc2,0xc2,0xc1, +0xc1,0xc1,0xc0,0xc0,0xc0,0xbf,0xbf,0xbf, +0xbe,0xbe,0xbe,0xbd,0xbd,0xbd,0xbc,0xbc, +0xbc,0xbb,0xbb,0xbb,0xba,0xba,0xba,0xb9, +0xb9,0xb8,0xb8,0xb8,0xb7,0xb7,0xb7,0xb6, +0xb6,0xb6,0xb5,0xb5,0xb5,0xb4,0xb4,0xb4, +0xb3,0xb3,0xb2,0xb2,0xb2,0xb1,0xb1,0xb1, +0xb0,0xb0,0xb0,0xaf,0xaf,0xae,0xae,0xae, +0xad,0xad,0xad,0xac,0xac,0xac,0xab,0xab, +0xaa,0xaa,0xaa,0xa9,0xa9,0xa9,0xa8,0xa8, +0xa7,0xa7,0xa7,0xa6,0xa6,0xa6,0xa5,0xa5, +0xa5,0xa4,0xa4,0xa3,0xa3,0xa3,0xa2,0xa2, +0xa2,0xa1,0xa1,0xa0,0xa0,0xa0,0x9f,0x9f, +0x9e,0x9e,0x9e,0x9d,0x9d,0x9d,0x9c,0x9c, +0x9b,0x9b,0x9b,0x9a,0x9a,0x9a,0x99,0x99, +0x98,0x98,0x98,0x97,0x97,0x96,0x96,0x96, +0x95,0x95,0x95,0x94,0x94,0x93,0x93,0x93, +0x92,0x92,0x91,0x91,0x91,0x90,0x90,0x8f, +0x8f,0x8f,0x8e,0x8e,0x8e,0x8d,0x8d,0x8c, +0x8c,0x8c,0x8b,0x8b,0x8a,0x8a,0x8a,0x89, +0x89,0x88,0x88,0x88,0x87,0x87,0x87,0x86, +0x86,0x85,0x85,0x85,0x84,0x84,0x83,0x83, +0x83,0x82,0x82,0x81,0x81,0x81,0x80,0x80, +0x80,0x7f,0x7f,0x7e,0x7e,0x7e,0x7d,0x7d, +0x7c,0x7c,0x7c,0x7b,0x7b,0x7a,0x7a,0x7a, +0x79,0x79,0x78,0x78,0x78,0x77,0x77,0x77, +0x76,0x76,0x75,0x75,0x75,0x74,0x74,0x73, +0x73,0x73,0x72,0x72,0x71,0x71,0x71,0x70, +0x70,0x70,0x6f,0x6f,0x6e,0x6e,0x6e,0x6d, +0x6d,0x6c,0x6c,0x6c,0x6b,0x6b,0x6a,0x6a, +0x6a,0x69,0x69,0x69,0x68,0x68,0x67,0x67, +0x67,0x66,0x66,0x65,0x65,0x65,0x64,0x64, +0x64,0x63,0x63,0x62,0x62,0x62,0x61,0x61, +0x61,0x60,0x60,0x5f,0x5f,0x5f,0x5e,0x5e, +0x5d,0x5d,0x5d,0x5c,0x5c,0x5c,0x5b,0x5b, +0x5a,0x5a,0x5a,0x59,0x59,0x59,0x58,0x58, +0x58,0x57,0x57,0x56,0x56,0x56,0x55,0x55, +0x55,0x54,0x54,0x53,0x53,0x53,0x52,0x52, +0x52,0x51,0x51,0x51,0x50,0x50,0x4f,0x4f, +0x4f,0x4e,0x4e,0x4e,0x4d,0x4d,0x4d,0x4c, +0x4c,0x4b,0x4b,0x4b,0x4a,0x4a,0x4a,0x49, +0x49,0x49,0x48,0x48,0x48,0x47,0x47,0x47, +0x46,0x46,0x45,0x45,0x45,0x44,0x44,0x44, +0x43,0x43,0x43,0x42,0x42,0x42,0x41,0x41, +0x41,0x40,0x40,0x40,0x3f,0x3f,0x3f,0x3e, +0x3e,0x3e,0x3d,0x3d,0x3d,0x3c,0x3c,0x3c, +0x3b,0x3b,0x3b,0x3a,0x3a,0x3a,0x39,0x39, +0x39,0x38,0x38,0x38,0x37,0x37,0x37,0x36, +0x36,0x36,0x35,0x35,0x35,0x34,0x34,0x34, +0x34,0x33,0x33,0x33,0x32,0x32,0x32,0x31, +0x31,0x31,0x30,0x30,0x30,0x30,0x2f,0x2f, +0x2f,0x2e,0x2e,0x2e,0x2d,0x2d,0x2d,0x2d, +0x2c,0x2c,0x2c,0x2b,0x2b,0x2b,0x2a,0x2a, +0x2a,0x2a,0x29,0x29,0x29,0x28,0x28,0x28, +0x28,0x27,0x27,0x27,0x26,0x26,0x26,0x26, +0x25,0x25,0x25,0x25,0x24,0x24,0x24,0x23, +0x23,0x23,0x23,0x22,0x22,0x22,0x22,0x21, +0x21,0x21,0x21,0x20,0x20,0x20,0x1f,0x1f, +0x1f,0x1f,0x1e,0x1e,0x1e,0x1e,0x1d,0x1d, +0x1d,0x1d,0x1c,0x1c,0x1c,0x1c,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1a,0x1a,0x1a,0x1a,0x19, +0x19,0x19,0x19,0x18,0x18,0x18,0x18,0x17, +0x17,0x17,0x17,0x17,0x16,0x16,0x16,0x16, +0x15,0x15,0x15,0x15,0x15,0x14,0x14,0x14, +0x14,0x14,0x13,0x13,0x13,0x13,0x13,0x12, +0x12,0x12,0x12,0x12,0x11,0x11,0x11,0x11, +0x11,0x10,0x10,0x10,0x10,0x10,0xf,0xf, +0xf,0xf,0xf,0xf,0xe,0xe,0xe,0xe, +0xe,0xd,0xd,0xd,0xd,0xd,0xd,0xc, +0xc,0xc,0xc,0xc,0xc,0xb,0xb,0xb, +0xb,0xb,0xb,0xa,0xa,0xa,0xa,0xa, +0xa,0xa,0x9,0x9,0x9,0x9,0x9,0x9, +0x9,0x8,0x8,0x8,0x8,0x8,0x8,0x8, +0x7,0x7,0x7,0x7,0x7,0x7,0x7,0x7, +0x6,0x6,0x6,0x6,0x6,0x6,0x6,0x6, +0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x5, +0x5,0x5,0x4,0x4,0x4,0x4,0x4,0x4, +0x4,0x4,0x4,0x4,0x3,0x3,0x3,0x3, +0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3, +0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2, +0x2,0x2,0x2,0x2,0x2,0x2,0x1,0x1, +0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1, +0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1, +0x1,0x1,0x1,0x1,0x0,0x0,0x0,0x0, +0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, +0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, +0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, +0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, +0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, +0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, +0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1, +0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1, +0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1, +0x1,0x1,0x1,0x2,0x2,0x2,0x2,0x2, +0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2, +0x2,0x3,0x3,0x3,0x3,0x3,0x3,0x3, +0x3,0x3,0x3,0x3,0x3,0x4,0x4,0x4, +0x4,0x4,0x4,0x4,0x4,0x4,0x4,0x5, +0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x5, +0x5,0x6,0x6,0x6,0x6,0x6,0x6,0x6, +0x6,0x7,0x7,0x7,0x7,0x7,0x7,0x7, +0x7,0x8,0x8,0x8,0x8,0x8,0x8,0x8, +0x9,0x9,0x9,0x9,0x9,0x9,0x9,0xa, +0xa,0xa,0xa,0xa,0xa,0xa,0xb,0xb, +0xb,0xb,0xb,0xb,0xc,0xc,0xc,0xc, +0xc,0xc,0xd,0xd,0xd,0xd,0xd,0xd, +0xe,0xe,0xe,0xe,0xe,0xf,0xf,0xf, +0xf,0xf,0xf,0x10,0x10,0x10,0x10,0x10, +0x11,0x11,0x11,0x11,0x11,0x12,0x12,0x12, +0x12,0x12,0x13,0x13,0x13,0x13,0x13,0x14, +0x14,0x14,0x14,0x14,0x15,0x15,0x15,0x15, +0x15,0x16,0x16,0x16,0x16,0x17,0x17,0x17, +0x17,0x17,0x18,0x18,0x18,0x18,0x19,0x19, +0x19,0x19,0x1a,0x1a,0x1a,0x1a,0x1b,0x1b, +0x1b,0x1b,0x1b,0x1c,0x1c,0x1c,0x1c,0x1d, +0x1d,0x1d,0x1d,0x1e,0x1e,0x1e,0x1e,0x1f, +0x1f,0x1f,0x1f,0x20,0x20,0x20,0x21,0x21, +0x21,0x21,0x22,0x22,0x22,0x22,0x23,0x23, +0x23,0x23,0x24,0x24,0x24,0x25,0x25,0x25, +0x25,0x26,0x26,0x26,0x26,0x27,0x27,0x27, +0x28,0x28,0x28,0x28,0x29,0x29,0x29,0x2a, +0x2a,0x2a,0x2a,0x2b,0x2b,0x2b,0x2c,0x2c, +0x2c,0x2d,0x2d,0x2d,0x2d,0x2e,0x2e,0x2e, +0x2f,0x2f,0x2f,0x30,0x30,0x30,0x30,0x31, +0x31,0x31,0x32,0x32,0x32,0x33,0x33,0x33, +0x34,0x34,0x34,0x34,0x35,0x35,0x35,0x36, +0x36,0x36,0x37,0x37,0x37,0x38,0x38,0x38, +0x39,0x39,0x39,0x3a,0x3a,0x3a,0x3b,0x3b, +0x3b,0x3c,0x3c,0x3c,0x3d,0x3d,0x3d,0x3e, +0x3e,0x3e,0x3f,0x3f,0x3f,0x40,0x40,0x40, +0x41,0x41,0x41,0x42,0x42,0x42,0x43,0x43, +0x43,0x44,0x44,0x44,0x45,0x45,0x45,0x46, +0x46,0x47,0x47,0x47,0x48,0x48,0x48,0x49, +0x49,0x49,0x4a,0x4a,0x4a,0x4b,0x4b,0x4b, +0x4c,0x4c,0x4d,0x4d,0x4d,0x4e,0x4e,0x4e, +0x4f,0x4f,0x4f,0x50,0x50,0x51,0x51,0x51, +0x52,0x52,0x52,0x53,0x53,0x53,0x54,0x54, +0x55,0x55,0x55,0x56,0x56,0x56,0x57,0x57, +0x58,0x58,0x58,0x59,0x59,0x59,0x5a,0x5a, +0x5a,0x5b,0x5b,0x5c,0x5c,0x5c,0x5d,0x5d, +0x5d,0x5e,0x5e,0x5f,0x5f,0x5f,0x60,0x60, +0x61,0x61,0x61,0x62,0x62,0x62,0x63,0x63, +0x64,0x64,0x64,0x65,0x65,0x65,0x66,0x66, +0x67,0x67,0x67,0x68,0x68,0x69,0x69,0x69, +0x6a,0x6a,0x6a,0x6b,0x6b,0x6c,0x6c,0x6c, +0x6d,0x6d,0x6e,0x6e,0x6e,0x6f,0x6f,0x70, +0x70,0x70,0x71,0x71,0x71,0x72,0x72,0x73, +0x73,0x73,0x74,0x74,0x75,0x75,0x75,0x76, +0x76,0x77,0x77,0x77,0x78,0x78,0x78,0x79, +0x79,0x7a,0x7a,0x7a,0x7b,0x7b,0x7c,0x7c, +0x7c,0x7d,0x7d,0x7e,0x7e,0x7e,0x7f,0x7f +}; diff --git a/quantum/config_common.h b/quantum/config_common.h new file mode 100644 index 0000000000..c88e02d918 --- /dev/null +++ b/quantum/config_common.h @@ -0,0 +1,103 @@ +/* Copyright 2015-2017 Jack Humbert + * + * 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 CONFIG_DEFINITIONS_H +#define CONFIG_DEFINITIONS_H + +/* diode directions */ +#define COL2ROW       0 +#define ROW2COL       1 +#define CUSTOM_MATRIX 2 /* Disables built-in matrix scanning code */ + +/* I/O pins */ +#ifndef F0 +    #define B0 0x30 +    #define B1 0x31 +    #define B2 0x32 +    #define B3 0x33 +    #define B4 0x34 +    #define B5 0x35 +    #define B6 0x36 +    #define B7 0x37 +    #define C0 0x60 +    #define C1 0x61 +    #define C2 0x62 +    #define C3 0x63 +    #define C4 0x64 +    #define C5 0x65 +    #define C6 0x66 +    #define C7 0x67 +    #define D0 0x90 +    #define D1 0x91 +    #define D2 0x92 +    #define D3 0x93 +    #define D4 0x94 +    #define D5 0x95 +    #define D6 0x96 +    #define D7 0x97 +    #define E0 0xC0 +    #define E1 0xC1 +    #define E2 0xC2 +    #define E3 0xC3 +    #define E4 0xC4 +    #define E5 0xC5 +    #define E6 0xC6 +    #define E7 0xC7 +    #define F0 0xF0 +    #define F1 0xF1 +    #define F2 0xF2 +    #define F3 0xF3 +    #define F4 0xF4 +    #define F5 0xF5 +    #define F6 0xF6 +    #define F7 0xF7 +    #define A0 0x00 +    #define A1 0x01 +    #define A2 0x02 +    #define A3 0x03 +    #define A4 0x04 +    #define A5 0x05 +    #define A6 0x06 +    #define A7 0x07 +#endif + +/* USART configuration */ +#ifdef BLUETOOTH_ENABLE +#   ifdef __AVR_ATmega32U4__ +#      define SERIAL_UART_BAUD 9600 +#      define SERIAL_UART_DATA UDR1 +#      define SERIAL_UART_UBRR (F_CPU / (16UL * SERIAL_UART_BAUD) - 1) +#      define SERIAL_UART_RXD_VECT USART1_RX_vect +#      define SERIAL_UART_TXD_READY (UCSR1A & _BV(UDRE1)) +#      define SERIAL_UART_INIT() do { \ +            /* baud rate */ \ +            UBRR1L = SERIAL_UART_UBRR; \ +            /* baud rate */ \ +            UBRR1H = SERIAL_UART_UBRR >> 8; \ +            /* enable TX */ \ +            UCSR1B = _BV(TXEN1); \ +            /* 8-bit data */ \ +            UCSR1C = _BV(UCSZ11) | _BV(UCSZ10); \ +            sei(); \ +        } while(0) +#   else +#       error "USART configuration is needed." +#   endif +#endif + +#define API_SYSEX_MAX_SIZE 32 + +#endif diff --git a/quantum/dynamic_macro.h b/quantum/dynamic_macro.h new file mode 100644 index 0000000000..045ee95b5f --- /dev/null +++ b/quantum/dynamic_macro.h @@ -0,0 +1,303 @@ +/* Copyright 2016 Jack Humbert + * + * 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/>. + */ + +/* Author: Wojciech Siewierski < wojciech dot siewierski at onet dot pl > */ +#ifndef DYNAMIC_MACROS_H +#define DYNAMIC_MACROS_H + +#include "action_layer.h" + +#ifndef DYNAMIC_MACRO_SIZE +/* May be overridden with a custom value. Be aware that the effective + * macro length is half of this value: each keypress is recorded twice + * because of the down-event and up-event. This is not a bug, it's the + * intended behavior. + * + * Usually it should be fine to set the macro size to at least 256 but + * there have been reports of it being too much in some users' cases, + * so 128 is considered a safe default. + */ +#define DYNAMIC_MACRO_SIZE 128 +#endif + +/* DYNAMIC_MACRO_RANGE must be set as the last element of user's + * "planck_keycodes" enum prior to including this header. This allows + * us to 'extend' it. + */ +enum dynamic_macro_keycodes { +    DYN_REC_START1 = DYNAMIC_MACRO_RANGE, +    DYN_REC_START2, +    DYN_REC_STOP, +    DYN_MACRO_PLAY1, +    DYN_MACRO_PLAY2, +}; + +/* Blink the LEDs to notify the user about some event. */ +void dynamic_macro_led_blink(void) +{ +#ifdef BACKLIGHT_ENABLE +    backlight_toggle(); +    _delay_ms(100); +    backlight_toggle(); +#endif +} + +/* Convenience macros used for retrieving the debug info. All of them + * need a `direction` variable accessible at the call site. + */ +#define DYNAMIC_MACRO_CURRENT_SLOT() (direction > 0 ? 1 : 2) +#define DYNAMIC_MACRO_CURRENT_LENGTH(BEGIN, POINTER) \ +    ((int)(direction * ((POINTER) - (BEGIN)))) +#define DYNAMIC_MACRO_CURRENT_CAPACITY(BEGIN, END2) \ +    ((int)(direction * ((END2) - (BEGIN)) + 1)) + +/** + * Start recording of the dynamic macro. + * + * @param[out] macro_pointer The new macro buffer iterator. + * @param[in]  macro_buffer  The macro buffer used to initialize macro_pointer. + */ +void dynamic_macro_record_start( +    keyrecord_t **macro_pointer, keyrecord_t *macro_buffer) +{ +    dprintln("dynamic macro recording: started"); + +    dynamic_macro_led_blink(); + +    clear_keyboard(); +    layer_clear(); +    *macro_pointer = macro_buffer; +} + +/** + * Play the dynamic macro. + * + * @param macro_buffer[in] The beginning of the macro buffer being played. + * @param macro_end[in]    The element after the last macro buffer element. + * @param direction[in]    Either +1 or -1, which way to iterate the buffer. + */ +void dynamic_macro_play( +    keyrecord_t *macro_buffer, keyrecord_t *macro_end, int8_t direction) +{ +    dprintf("dynamic macro: slot %d playback\n", DYNAMIC_MACRO_CURRENT_SLOT()); + +    uint32_t saved_layer_state = layer_state; + +    clear_keyboard(); +    layer_clear(); + +    while (macro_buffer != macro_end) { +        process_record(macro_buffer); +        macro_buffer += direction; +    } + +    clear_keyboard(); + +    layer_state = saved_layer_state; +} + +/** + * Record a single key in a dynamic macro. + * + * @param macro_buffer[in] The start of the used macro buffer. + * @param macro_pointer[in,out] The current buffer position. + * @param macro2_end[in] The end of the other macro. + * @param direction[in]  Either +1 or -1, which way to iterate the buffer. + * @param record[in]     The current keypress. + */ +void dynamic_macro_record_key( +    keyrecord_t *macro_buffer, +    keyrecord_t **macro_pointer, +    keyrecord_t *macro2_end, +    int8_t direction, +    keyrecord_t *record) +{ +    /* If we've just started recording, ignore all the key releases. */ +    if (!record->event.pressed && *macro_pointer == macro_buffer) { +        dprintln("dynamic macro: ignoring a leading key-up event"); +        return; +    } + +    /* The other end of the other macro is the last buffer element it +     * is safe to use before overwriting the other macro. +     */ +    if (*macro_pointer - direction != macro2_end) { +        **macro_pointer = *record; +        *macro_pointer += direction; +    } else { +        dynamic_macro_led_blink(); +    } + +    dprintf( +        "dynamic macro: slot %d length: %d/%d\n", +        DYNAMIC_MACRO_CURRENT_SLOT(), +        DYNAMIC_MACRO_CURRENT_LENGTH(macro_buffer, *macro_pointer), +        DYNAMIC_MACRO_CURRENT_CAPACITY(macro_buffer, macro2_end)); +} + +/** + * End recording of the dynamic macro. Essentially just update the + * pointer to the end of the macro. + */ +void dynamic_macro_record_end( +    keyrecord_t *macro_buffer, +    keyrecord_t *macro_pointer, +    int8_t direction, +    keyrecord_t **macro_end) +{ +    dynamic_macro_led_blink(); + +    /* Do not save the keys being held when stopping the recording, +     * i.e. the keys used to access the layer DYN_REC_STOP is on. +     */ +    while (macro_pointer != macro_buffer && +           (macro_pointer - direction)->event.pressed) { +        dprintln("dynamic macro: trimming a trailing key-down event"); +        macro_pointer -= direction; +    } + +    dprintf( +        "dynamic macro: slot %d saved, length: %d\n", +        DYNAMIC_MACRO_CURRENT_SLOT(), +        DYNAMIC_MACRO_CURRENT_LENGTH(macro_buffer, macro_pointer)); + +    *macro_end = macro_pointer; +} + +/* Handle the key events related to the dynamic macros. Should be + * called from process_record_user() like this: + * + *   bool process_record_user(uint16_t keycode, keyrecord_t *record) { + *       if (!process_record_dynamic_macro(keycode, record)) { + *           return false; + *       } + *       <...THE REST OF THE FUNCTION...> + *   } + */ +bool process_record_dynamic_macro(uint16_t keycode, keyrecord_t *record) +{ +    /* Both macros use the same buffer but read/write on different +     * ends of it. +     * +     * Macro1 is written left-to-right starting from the beginning of +     * the buffer. +     * +     * Macro2 is written right-to-left starting from the end of the +     * buffer. +     * +     * ¯o_buffer   macro_end +     *  v                   v +     * +------------------------------------------------------------+ +     * |>>>>>> MACRO1 >>>>>>      <<<<<<<<<<<<< MACRO2 <<<<<<<<<<<<<| +     * +------------------------------------------------------------+ +     *                           ^                                 ^ +     *                         r_macro_end                  r_macro_buffer +     * +     * During the recording when one macro encounters the end of the +     * other macro, the recording is stopped. Apart from this, there +     * are no arbitrary limits for the macros' length in relation to +     * each other: for example one can either have two medium sized +     * macros or one long macro and one short macro. Or even one empty +     * and one using the whole buffer. +     */ +    static keyrecord_t macro_buffer[DYNAMIC_MACRO_SIZE]; + +    /* Pointer to the first buffer element after the first macro. +     * Initially points to the very beginning of the buffer since the +     * macro is empty. */ +    static keyrecord_t *macro_end = macro_buffer; + +    /* The other end of the macro buffer. Serves as the beginning of +     * the second macro. */ +    static keyrecord_t *const r_macro_buffer = macro_buffer + DYNAMIC_MACRO_SIZE - 1; + +    /* Like macro_end but for the second macro. */ +    static keyrecord_t *r_macro_end = r_macro_buffer; + +    /* A persistent pointer to the current macro position (iterator) +     * used during the recording. */ +    static keyrecord_t *macro_pointer = NULL; + +    /* 0   - no macro is being recorded right now +     * 1,2 - either macro 1 or 2 is being recorded */ +    static uint8_t macro_id = 0; + +    if (macro_id == 0) { +        /* No macro recording in progress. */ +        if (!record->event.pressed) { +            switch (keycode) { +            case DYN_REC_START1: +                dynamic_macro_record_start(¯o_pointer, macro_buffer); +                macro_id = 1; +                return false; +            case DYN_REC_START2: +                dynamic_macro_record_start(¯o_pointer, r_macro_buffer); +                macro_id = 2; +                return false; +            case DYN_MACRO_PLAY1: +                dynamic_macro_play(macro_buffer, macro_end, +1); +                return false; +            case DYN_MACRO_PLAY2: +                dynamic_macro_play(r_macro_buffer, r_macro_end, -1); +                return false; +            } +        } +    } else { +        /* A macro is being recorded right now. */ +        switch (keycode) { +        case DYN_REC_STOP: +            /* Stop the macro recording. */ +            if (record->event.pressed) { /* Ignore the initial release +                                          * just after the recoding +                                          * starts. */ +                switch (macro_id) { +                case 1: +                    dynamic_macro_record_end(macro_buffer, macro_pointer, +1, ¯o_end); +                    break; +                case 2: +                    dynamic_macro_record_end(r_macro_buffer, macro_pointer, -1, &r_macro_end); +                    break; +                } +                macro_id = 0; +            } +            return false; +        case DYN_MACRO_PLAY1: +        case DYN_MACRO_PLAY2: +            dprintln("dynamic macro: ignoring macro play key while recording"); +            return false; +        default: +            /* Store the key in the macro buffer and process it normally. */ +            switch (macro_id) { +            case 1: +                dynamic_macro_record_key(macro_buffer, ¯o_pointer, r_macro_end, +1, record); +                break; +            case 2: +                dynamic_macro_record_key(r_macro_buffer, ¯o_pointer, macro_end, -1, record); +                break; +            } +            return true; +            break; +        } +    } + +    return true; +} + +#undef DYNAMIC_MACRO_CURRENT_SLOT +#undef DYNAMIC_MACRO_CURRENT_LENGTH +#undef DYNAMIC_MACRO_CURRENT_CAPACITY + +#endif diff --git a/quantum/fauxclicky.c b/quantum/fauxclicky.c new file mode 100644 index 0000000000..c3341ca332 --- /dev/null +++ b/quantum/fauxclicky.c @@ -0,0 +1,61 @@ +/* +Copyright 2017 Priyadi Iman Nurcahyo + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <avr/interrupt.h> +#include <avr/io.h> +#include <timer.h> +#include <fauxclicky.h> +#include <stdbool.h> +#include <musical_notes.h> + +bool fauxclicky_enabled = true; +uint16_t note_start = 0; +bool note_playing = false; +uint16_t note_period = 0; + +void fauxclicky_init() +{ +    // Set port PC6 (OC3A and /OC4A) as output +    DDRC |= _BV(PORTC6); + +    // TCCR3A / TCCR3B: Timer/Counter #3 Control Registers +    TCCR3A = (0 << COM3A1) | (0 << COM3A0) | (1 << WGM31) | (0 << WGM30); +    TCCR3B = (1 << WGM33)  | (1 << WGM32)  | (0 << CS32)  | (1 << CS31) | (0 << CS30); +} + +void fauxclicky_stop() +{ +    FAUXCLICKY_DISABLE_OUTPUT; +    note_playing = false; +} + +void fauxclicky_play(float note[]) { +    if (!fauxclicky_enabled) return; +    if (note_playing) fauxclicky_stop(); +    FAUXCLICKY_TIMER_PERIOD = (uint16_t)(((float)F_CPU) / (note[0] * (float)FAUXCLICKY_CPU_PRESCALER)); +    FAUXCLICKY_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (note[0] * (float)FAUXCLICKY_CPU_PRESCALER)) / (float)2); +    note_playing = true; +    note_period = (note[1] / (float)16) * ((float)60 / (float)FAUXCLICKY_TEMPO) * 1000; +    note_start = timer_read(); +    FAUXCLICKY_ENABLE_OUTPUT; +} + +void fauxclicky_check() { +    if (!note_playing) return; + +    if (timer_elapsed(note_start) > note_period) { +        fauxclicky_stop(); +    } +} diff --git a/quantum/fauxclicky.h b/quantum/fauxclicky.h new file mode 100644 index 0000000000..1a8e188dd5 --- /dev/null +++ b/quantum/fauxclicky.h @@ -0,0 +1,99 @@ +/* +Copyright 2017 Priyadi Iman Nurcahyo + +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/>. +*/ + +#ifdef AUDIO_ENABLE +#error "AUDIO_ENABLE and FAUXCLICKY_ENABLE cannot be both enabled" +#endif + +#include "musical_notes.h" +#include "stdbool.h" + +__attribute__ ((weak)) +float fauxclicky_pressed_note[2] = MUSICAL_NOTE(_D4, 0.25); +__attribute__ ((weak)) +float fauxclicky_released_note[2] = MUSICAL_NOTE(_C4, 0.125); +__attribute__ ((weak)) +float fauxclicky_beep_note[2] = MUSICAL_NOTE(_C4, 0.25); + +bool fauxclicky_enabled; + +// +// tempo in BPM +// + +#ifndef FAUXCLICKY_TEMPO +#define FAUXCLICKY_TEMPO TEMPO_DEFAULT +#endif + +// beep on press +#define FAUXCLICKY_ACTION_PRESS fauxclicky_play(fauxclicky_pressed_note) + +// beep on release +#define FAUXCLICKY_ACTION_RELEASE fauxclicky_play(fauxclicky_released_note) + +// general purpose beep +#define FAUXCLICKY_BEEP fauxclicky_play(fauxclicky_beep_note) + +// enable +#define FAUXCLICKY_ON fauxclicky_enabled = true + +// disable +#define FAUXCLICKY_OFF do { \ +    fauxclicky_enabled = false; \ +    fauxclicky_stop(); \ +} while (0) + +// toggle +#define FAUXCLICKY_TOGGLE do { \ +    if (fauxclicky_enabled) { \ +        FAUXCLICKY_OFF; \ +    } else { \ +        FAUXCLICKY_ON; \ +    } \ +} while (0) + +// +// pin configuration +// + +#ifndef FAUXCLICKY_CPU_PRESCALER +#define FAUXCLICKY_CPU_PRESCALER 8 +#endif + +#ifndef FAUXCLICKY_ENABLE_OUTPUT +#define FAUXCLICKY_ENABLE_OUTPUT TCCR3A |= _BV(COM3A1) +#endif + +#ifndef FAUXCLICKY_DISABLE_OUTPUT +#define FAUXCLICKY_DISABLE_OUTPUT TCCR3A &= ~(_BV(COM3A1) | _BV(COM3A0)) +#endif + +#ifndef FAUXCLICKY_TIMER_PERIOD +#define FAUXCLICKY_TIMER_PERIOD ICR3 +#endif + +#ifndef FAUXCLICKY_DUTY_CYCLE +#define FAUXCLICKY_DUTY_CYCLE OCR3A +#endif + +// +// definitions +// + +void fauxclicky_init(void); +void fauxclicky_stop(void); +void fauxclicky_play(float note[2]); +void fauxclicky_check(void); + diff --git a/quantum/keycode_config.c b/quantum/keycode_config.c new file mode 100644 index 0000000000..eb39c8fe00 --- /dev/null +++ b/quantum/keycode_config.c @@ -0,0 +1,118 @@ +/* Copyright 2016 Jack Humbert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include "keycode_config.h" + +extern keymap_config_t keymap_config; + +uint16_t keycode_config(uint16_t keycode) { + +    switch (keycode) { +        case KC_CAPSLOCK: +        case KC_LOCKING_CAPS: +            if (keymap_config.swap_control_capslock || keymap_config.capslock_to_control) { +                return KC_LCTL; +            } +            return keycode; +        case KC_LCTL: +            if (keymap_config.swap_control_capslock) { +                return KC_CAPSLOCK; +            } +            return KC_LCTL; +        case KC_LALT: +            if (keymap_config.swap_lalt_lgui) { +                if (keymap_config.no_gui) { +                    return KC_NO; +                } +                return KC_LGUI; +            } +            return KC_LALT; +        case KC_LGUI: +            if (keymap_config.swap_lalt_lgui) { +                return KC_LALT; +            } +            if (keymap_config.no_gui) { +                return KC_NO; +            } +            return KC_LGUI; +        case KC_RALT: +            if (keymap_config.swap_ralt_rgui) { +                if (keymap_config.no_gui) { +                    return KC_NO; +                } +                return KC_RGUI; +            } +            return KC_RALT; +        case KC_RGUI: +            if (keymap_config.swap_ralt_rgui) { +                return KC_RALT; +            } +            if (keymap_config.no_gui) { +                return KC_NO; +            } +            return KC_RGUI; +        case KC_GRAVE: +            if (keymap_config.swap_grave_esc) { +                return KC_ESC; +            } +            return KC_GRAVE; +        case KC_ESC: +            if (keymap_config.swap_grave_esc) { +                return KC_GRAVE; +            } +            return KC_ESC; +        case KC_BSLASH: +            if (keymap_config.swap_backslash_backspace) { +                return KC_BSPACE; +            } +            return KC_BSLASH; +        case KC_BSPACE: +            if (keymap_config.swap_backslash_backspace) { +                return KC_BSLASH; +            } +            return KC_BSPACE; +        default: +            return keycode; +    } +} + +uint8_t mod_config(uint8_t mod) { +    keymap_config.raw = eeconfig_read_keymap(); +    if (keymap_config.swap_lalt_lgui) { +        if ((mod & MOD_RGUI) == MOD_LGUI) { +            mod &= ~MOD_LGUI; +            mod |= MOD_LALT; +        } else if ((mod & MOD_RALT) == MOD_LALT) { +            mod &= ~MOD_LALT; +            mod |= MOD_LGUI; +        } +    } +    if (keymap_config.swap_ralt_rgui) { +        if ((mod & MOD_RGUI) == MOD_RGUI) { +            mod &= ~MOD_RGUI; +            mod |= MOD_RALT; +        } else if ((mod & MOD_RALT) == MOD_RALT) { +            mod &= ~MOD_RALT; +            mod |= MOD_RGUI; +        } +    } +    if (keymap_config.no_gui) { +        mod &= ~MOD_LGUI; +        mod &= ~MOD_RGUI; +    } + +    return mod; +}
\ No newline at end of file diff --git a/quantum/keycode_config.h b/quantum/keycode_config.h new file mode 100644 index 0000000000..022f4bd19b --- /dev/null +++ b/quantum/keycode_config.h @@ -0,0 +1,44 @@ +/* Copyright 2016 Jack Humbert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include "eeconfig.h" +#include "keycode.h" +#include "action_code.h" + +#ifndef KEYCODE_CONFIG_H +#define KEYCODE_CONFIG_H + +uint16_t keycode_config(uint16_t keycode); +uint8_t mod_config(uint8_t mod); + +/* NOTE: Not portable. Bit field order depends on implementation */ +typedef union { +    uint16_t raw; +    struct { +        bool swap_control_capslock:1; +        bool capslock_to_control:1; +        bool swap_lalt_lgui:1; +        bool swap_ralt_rgui:1; +        bool no_gui:1; +        bool swap_grave_esc:1; +        bool swap_backslash_backspace:1; +        bool nkro:1; +    }; +} keymap_config_t; + +extern keymap_config_t keymap_config; + +#endif /* KEYCODE_CONFIG_H */ diff --git a/quantum/keymap.h b/quantum/keymap.h new file mode 100644 index 0000000000..5d64be19c8 --- /dev/null +++ b/quantum/keymap.h @@ -0,0 +1,53 @@ +/* +Copyright 2012-2016 Jun Wako <wakojun@gmail.com> + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef KEYMAP_H +#define KEYMAP_H + +#include <stdint.h> +#include <stdbool.h> +#include "action.h" +#if defined(__AVR__) +#include <avr/pgmspace.h> +#endif +#include "keycode.h" +#include "action_macro.h" +#include "report.h" +#include "host.h" +// #include "print.h" +#include "debug.h" +#include "keycode_config.h" + +// ChibiOS uses RESET in its FlagStatus enumeration +// Therefore define it as QK_RESET here, to avoid name collision +#if defined(PROTOCOL_CHIBIOS) +#define RESET QK_RESET +#endif + +#include "quantum_keycodes.h" + +// translates key to keycode +uint16_t keymap_key_to_keycode(uint8_t layer, keypos_t key); + +// translates function id to action +uint16_t keymap_function_id_to_action( uint16_t function_id ); + +extern const uint16_t keymaps[][MATRIX_ROWS][MATRIX_COLS]; +extern const uint16_t fn_actions[]; + + +#endif diff --git a/quantum/keymap_common.c b/quantum/keymap_common.c new file mode 100644 index 0000000000..b1460c53cc --- /dev/null +++ b/quantum/keymap_common.c @@ -0,0 +1,191 @@ +/* +Copyright 2012-2017 Jun Wako <wakojun@gmail.com> + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "keymap.h" +#include "report.h" +#include "keycode.h" +#include "action_layer.h" +#if defined(__AVR__) +#include <util/delay.h> +#include <stdio.h> +#endif +#include "action.h" +#include "action_macro.h" +#include "debug.h" +#include "backlight.h" +#include "quantum.h" + +#ifdef MIDI_ENABLE +	#include "process_midi.h" +#endif + +extern keymap_config_t keymap_config; + +#include <inttypes.h> + +/* converts key to action */ +action_t action_for_key(uint8_t layer, keypos_t key) +{ +    // 16bit keycodes - important +    uint16_t keycode = keymap_key_to_keycode(layer, key); + +    // keycode remapping +    keycode = keycode_config(keycode); + +    action_t action; +    uint8_t action_layer, when, mod; + +    switch (keycode) { +        case KC_FN0 ... KC_FN31: +            action.code = keymap_function_id_to_action(FN_INDEX(keycode)); +            break; +        case KC_A ... KC_EXSEL: +        case KC_LCTRL ... KC_RGUI: +            action.code = ACTION_KEY(keycode); +            break; +        case KC_SYSTEM_POWER ... KC_SYSTEM_WAKE: +            action.code = ACTION_USAGE_SYSTEM(KEYCODE2SYSTEM(keycode)); +            break; +        case KC_AUDIO_MUTE ... KC_MEDIA_REWIND: +            action.code = ACTION_USAGE_CONSUMER(KEYCODE2CONSUMER(keycode)); +            break; +        case KC_MS_UP ... KC_MS_ACCEL2: +            action.code = ACTION_MOUSEKEY(keycode); +            break; +        case KC_TRNS: +            action.code = ACTION_TRANSPARENT; +            break; +        case QK_MODS ... QK_MODS_MAX: ; +            // Has a modifier +            // Split it up +            action.code = ACTION_MODS_KEY(keycode >> 8, keycode & 0xFF); // adds modifier to key +            break; +        case QK_FUNCTION ... QK_FUNCTION_MAX: ; +            // Is a shortcut for function action_layer, pull last 12bits +            // This means we have 4,096 FN macros at our disposal +            action.code = keymap_function_id_to_action( (int)keycode & 0xFFF ); +            break; +        case QK_MACRO ... QK_MACRO_MAX: +            if (keycode & 0x800) // tap macros have upper bit set +                action.code = ACTION_MACRO_TAP(keycode & 0xFF); +            else +                action.code = ACTION_MACRO(keycode & 0xFF); +            break; +        case QK_LAYER_TAP ... QK_LAYER_TAP_MAX: +            action.code = ACTION_LAYER_TAP_KEY((keycode >> 0x8) & 0xF, keycode & 0xFF); +            break; +        case QK_TO ... QK_TO_MAX: ; +            // Layer set "GOTO" +            when = (keycode >> 0x4) & 0x3; +            action_layer = keycode & 0xF; +            action.code = ACTION_LAYER_SET(action_layer, when); +            break; +        case QK_MOMENTARY ... QK_MOMENTARY_MAX: ; +            // Momentary action_layer +            action_layer = keycode & 0xFF; +            action.code = ACTION_LAYER_MOMENTARY(action_layer); +            break; +        case QK_DEF_LAYER ... QK_DEF_LAYER_MAX: ; +            // Set default action_layer +            action_layer = keycode & 0xFF; +            action.code = ACTION_DEFAULT_LAYER_SET(action_layer); +            break; +        case QK_TOGGLE_LAYER ... QK_TOGGLE_LAYER_MAX: ; +            // Set toggle +            action_layer = keycode & 0xFF; +            action.code = ACTION_LAYER_TOGGLE(action_layer); +            break; +        case QK_ONE_SHOT_LAYER ... QK_ONE_SHOT_LAYER_MAX: ; +            // OSL(action_layer) - One-shot action_layer +            action_layer = keycode & 0xFF; +            action.code = ACTION_LAYER_ONESHOT(action_layer); +            break; +        case QK_ONE_SHOT_MOD ... QK_ONE_SHOT_MOD_MAX: ; +            // OSM(mod) - One-shot mod +            mod = keycode & 0xFF; +            action.code = ACTION_MODS_ONESHOT(mod); +            break; +        case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX: +            action.code = ACTION_LAYER_TAP_TOGGLE(keycode & 0xFF); +            break; +        case QK_MOD_TAP ... QK_MOD_TAP_MAX: +            mod = mod_config((keycode >> 0x8) & 0x1F); +            action.code = ACTION_MODS_TAP_KEY(mod, keycode & 0xFF); +            break; +    #ifdef BACKLIGHT_ENABLE +        case BL_0 ... BL_15: +            action.code = ACTION_BACKLIGHT_LEVEL(keycode - BL_0); +            break; +        case BL_DEC: +            action.code = ACTION_BACKLIGHT_DECREASE(); +            break; +        case BL_INC: +            action.code = ACTION_BACKLIGHT_INCREASE(); +            break; +        case BL_TOGG: +            action.code = ACTION_BACKLIGHT_TOGGLE(); +            break; +        case BL_STEP: +            action.code = ACTION_BACKLIGHT_STEP(); +            break; +    #endif +        default: +            action.code = ACTION_NO; +            break; +    } +    return action; +} + +__attribute__ ((weak)) +const uint16_t PROGMEM fn_actions[] = { + +}; + +/* Macro */ +__attribute__ ((weak)) +const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt) +{ +    return MACRO_NONE; +} + +/* Function */ +__attribute__ ((weak)) +void action_function(keyrecord_t *record, uint8_t id, uint8_t opt) +{ +} + +// translates key to keycode +__attribute__ ((weak)) +uint16_t keymap_key_to_keycode(uint8_t layer, keypos_t key) +{ +    // Read entire word (16bits) +    return pgm_read_word(&keymaps[(layer)][(key.row)][(key.col)]); +} + +// translates function id to action +__attribute__ ((weak)) +uint16_t keymap_function_id_to_action( uint16_t function_id ) +{ +    // The compiler sees the empty (weak) fn_actions and generates a warning +    // This function should not be called in that case, so the warning is too strict +    // If this function is called however, the keymap should have overridden fn_actions, and then the compile +    // is comparing against the wrong array +    #pragma GCC diagnostic push +    #pragma GCC diagnostic ignored "-Warray-bounds" +	return pgm_read_word(&fn_actions[function_id]); +    #pragma GCC diagnostic pop +} diff --git a/quantum/keymap_extras/keymap_bepo.h b/quantum/keymap_extras/keymap_bepo.h new file mode 100644 index 0000000000..013559e96d --- /dev/null +++ b/quantum/keymap_extras/keymap_bepo.h @@ -0,0 +1,326 @@ +/* Copyright 2016 Didier Loiseau + * + * 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/>. + */ +/* Keymap macros for the French BÉPO layout - http://bepo.fr */ +#ifndef KEYMAP_BEPO_H +#define KEYMAP_BEPO_H + +#include "keymap.h" + +// Alt gr +#ifndef ALTGR +#define ALTGR(kc)   RALT(kc) +#endif +#ifndef ALGR +#define ALGR(kc)    ALTGR(kc) +#endif +#define BP_ALGR KC_RALT + +// Normal characters +// First row (on usual keyboards) +#define BP_DOLLAR           KC_GRAVE            // $ +#define BP_DLR              BP_DOLLAR +#define BP_DOUBLE_QUOTE     KC_1                // " +#define BP_DQOT             BP_DOUBLE_QUOTE +#define BP_LEFT_GUILLEMET   KC_2                // « +#define BP_LGIL             BP_LEFT_GUILLEMET +#define BP_RIGHT_GUILLEMET  KC_3                // » +#define BP_RGIL             BP_RIGHT_GUILLEMET +#define BP_LEFT_PAREN       KC_4                // ( +#define BP_LPRN             BP_LEFT_PAREN +#define BP_RIGHT_PAREN      KC_5                // ) +#define BP_RPRN             BP_RIGHT_PAREN +#define BP_AT               KC_6                // @ +#define BP_PLUS             KC_7                // + +#define BP_MINUS            KC_8                // - +#define BP_MINS             BP_MINUS +#define BP_SLASH            KC_9                // / +#define BP_SLSH             BP_SLASH +#define BP_ASTERISK         KC_0                // * +#define BP_ASTR             BP_ASTERISK +#define BP_EQUAL            KC_MINUS            // = +#define BP_EQL              BP_EQUAL +#define BP_PERCENT          KC_EQUAL            // % +#define BP_PERC             BP_PERCENT + +// Second row +#define BP_B                KC_Q +#define BP_E_ACUTE          KC_W        // é +#define BP_ECUT             BP_E_ACUTE +#define BP_P                KC_E +#define BP_O                KC_R +#define BP_E_GRAVE          KC_T        // è +#define BP_EGRV             BP_E_GRAVE +#define BP_DEAD_CIRCUMFLEX  KC_Y        // dead ^ +#define BP_DCRC             BP_DEAD_CIRCUMFLEX +#define BP_V                KC_U +#define BP_D                KC_I +#define BP_L                KC_O +#define BP_J                KC_P +#define BP_Z                KC_LBRACKET +#define BP_W                KC_RBRACKET + +// Third row +#define BP_A            KC_A +#define BP_U            KC_S +#define BP_I            KC_D +#define BP_E            KC_F +#define BP_COMMA        KC_G        // , +#define BP_COMM         BP_COMMA +#define BP_C            KC_H +#define BP_T            KC_J +#define BP_S            KC_K +#define BP_R            KC_L +#define BP_N            KC_SCOLON +#define BP_M            KC_QUOTE +#define BP_C_CEDILLA    KC_BSLASH   // ç +#define BP_CCED         BP_C_CEDILLA + +// Fourth row +#define BP_E_CIRCUMFLEX     KC_NONUS_BSLASH // ê +#define BP_ECRC             BP_E_CIRCUMFLEX +#define BP_A_GRAVE          KC_Z            // à +#define BP_AGRV             BP_A_GRAVE +#define BP_Y                KC_X +#define BP_X                KC_C +#define BP_DOT              KC_V            // . +#define BP_K                KC_B +#define BP_APOSTROPHE       KC_N +#define BP_APOS             BP_APOSTROPHE   // ' +#define BP_Q                KC_M +#define BP_G                KC_COMMA +#define BP_H                KC_DOT +#define BP_F                KC_SLASH + +// Shifted characters +// First row +#define BP_HASH     LSFT(BP_DOLLAR)     // # +#define BP_1        LSFT(KC_1) +#define BP_2        LSFT(KC_2) +#define BP_3        LSFT(KC_3) +#define BP_4        LSFT(KC_4) +#define BP_5        LSFT(KC_5) +#define BP_6        LSFT(KC_6) +#define BP_7        LSFT(KC_7) +#define BP_8        LSFT(KC_8) +#define BP_9        LSFT(KC_9) +#define BP_0        LSFT(KC_0) +#define BP_DEGREE   LSFT(BP_EQUAL)      // ° +#define BP_DEGR     BP_DEGREE +#define BP_GRAVE    LSFT(BP_PERCENT)    // ` +#define BP_GRV      BP_GRAVE + +// Second row +#define BP_EXCLAIM  LSFT(BP_DEAD_CIRCUMFLEX)    // ! +#define BP_EXLM     BP_EXCLAIM + +// Third row +#define BP_SCOLON   LSFT(BP_COMMA)  // ; +#define BP_SCLN     BP_SCOLON + +// Fourth row +#define BP_COLON    LSFT(BP_DOT)    // : +#define BP_COLN     BP_COLON +#define BP_QUESTION LSFT(BP_APOS)  // ? +#define BP_QEST     BP_QUESTION + +// Space bar +#define BP_NON_BREAKING_SPACE   LSFT(KC_SPACE) +#define BP_NBSP                 BP_NON_BREAKING_SPACE + +// AltGr-ed characters +// First row +#define BP_EN_DASH          ALTGR(BP_DOLLAR)    // – +#define BP_NDSH             BP_EN_DASH +#define BP_EM_DASH          ALTGR(KC_1)         // — +#define BP_MDSH             BP_EM_DASH +#define BP_LESS             ALTGR(KC_2)         // < +#define BP_GREATER          ALTGR(KC_3)         // > +#define BP_GRTR             BP_GREATER +#define BP_LBRACKET         ALTGR(KC_4)         // [ +#define BP_LBRC             BP_LBRACKET +#define BP_RBRACKET         ALTGR(KC_5)         // ] +#define BP_RBRC             BP_RBRACKET +#define BP_CIRCUMFLEX       ALTGR(KC_6)         // ^ +#define BP_CIRC             BP_CIRCUMFLEX +#define BP_PLUS_MINUS       ALTGR(KC_7)         // ± +#define BP_PSMS             BP_PLUS_MINUS +#define BP_MATH_MINUS       ALTGR(KC_8)         // − +#define BP_MMNS             BP_MATH_MINUS +#define BP_OBELUS           ALTGR(KC_9)         // ÷ +#define BP_OBEL             BP_OBELUS +// more conventional name of the symbol +#define BP_DIVISION_SIGN    BP_OBELUS +#define BP_DVSN             BP_DIVISION_SIGN +#define BP_TIMES            ALTGR(KC_0)         // × +#define BP_TIMS             BP_TIMES +#define BP_DIFFERENT        ALTGR(BP_EQUAL)     // ≠ +#define BP_DIFF             BP_DIFFERENT +#define BP_PERMILLE         ALTGR(BP_PERCENT)   // ‰ +#define BP_PMIL             BP_PERMILLE + +// Second row +#define BP_PIPE                 ALTGR(BP_B)         // | +#define BP_DEAD_ACUTE           ALTGR(BP_E_ACUTE)   // dead ´ +#define BP_DACT                 BP_DEAD_ACUTE +#define BP_AMPERSAND            ALTGR(BP_P)         // & +#define BP_AMPR                 BP_AMPERSAND +#define BP_OE_LIGATURE          ALTGR(BP_O)         // œ +#define BP_OE                   BP_OE_LIGATURE +#define BP_DEAD_GRAVE           ALTGR(BP_E_GRAVE)   // ` +#define BP_DGRV                 BP_DEAD_GRAVE +#define BP_INVERTED_EXCLAIM     ALTGR(BP_DEAD_CIRCUMFLEX)   // ¡ +#define BP_IXLM                 BP_INVERTED_EXCLAIM +#define BP_DEAD_CARON           ALTGR(BP_V)         // dead ˇ +#define BP_DCAR                 BP_DEAD_CARON +#define BP_ETH                  ALTGR(BP_D)         // ð +#define BP_DEAD_SLASH           ALTGR(BP_L)         // dead / +#define BP_DSLH                 BP_DEAD_SLASH +#define BP_IJ_LIGATURE          ALTGR(BP_J)         // ij +#define BP_IJ                   BP_IJ_LIGATURE +#define BP_SCHWA                ALTGR(BP_Z)         // ə +#define BP_SCWA                 BP_SCHWA +#define BP_DEAD_BREVE           ALTGR(BP_W)         // dead ˘ +#define BP_DBRV                 BP_DEAD_BREVE + +// Third row +#define BP_AE_LIGATURE              ALTGR(BP_A)         // æ +#define BP_AE                       BP_AE_LIGATURE +#define BP_U_GRAVE                  ALTGR(BP_U)           // ù +#define BP_UGRV                     BP_U_GRAVE +#define BP_DEAD_TREMA               ALTGR(BP_I)         // dead ¨ (trema/umlaut/diaresis) +#define BP_DTRM                     BP_DEAD_TREMA +#define BP_EURO                     ALTGR(BP_E)         // € +#define BP_TYPOGRAPHICAL_APOSTROPHE ALTGR(BP_COMMMA)    // ’ +#define BP_TAPO                     BP_TYPOGRAPHICAL_APOSTROPHE +#define BP_COPYRIGHT                ALTGR(BP_C)         // © +#define BP_CPRT                     BP_COPYRIGHT +#define BP_THORN                    ALTGR(BP_T)         // þ +#define BP_THRN                     BP_THORN +#define BP_SHARP_S                  ALTGR(BP_S)         // ß +#define BP_SRPS                     BP_SHARP_S +#define BP_REGISTERED_TRADEMARK     ALTGR(BP_R)         // ® +#define BP_RTM                      BP_REGISTERED_TRADEMARK +#define BP_DEAD_TILDE               ALTGR(BP_N)         // dead ~ +#define BP_DTLD                     BP_DEAD_TILDE +#define BP_DEAD_MACRON              ALTGR(BP_M)         // dead ¯ +#define BP_DMCR                     BP_DEAD_MACRON +#define BP_DEAD_CEDILLA             ALTGR(BP_C_CEDILLA) // dead ¸ +#define BP_DCED                     BP_DEAD_CEDILLA + +// Fourth row +#define BP_NONUS_SLASH          ALTGR(BP_E_CIRCUMFLEX)  // / on non-us backslash key (102nd key, ê in bépo) +#define BP_NUSL                 BP_NONUS_SLASH +#define BP_BACKSLASH            ALTGR(BP_A_GRAVE)       /* \ */ +#define BP_BSLS                 BP_BACKSLASH +#define BP_LEFT_CURLY_BRACE     ALTGR(BP_Y)             // { +#define BP_LCBR                 BP_LEFT_CURLY_BRACE +#define BP_RIGHT_CURLY_BRACE    ALTGR(BP_X)             // } +#define BP_RCBR                 BP_RIGHT_CURLY_BRACE +#define BP_ELLIPSIS             ALTGR(BP_DOT)           // … +#define BP_ELPS                 BP_ELLIPSIS +#define BP_TILDE                ALTGR(BP_K)             // ~ +#define BP_TILD                 BP_TILDE +#define BP_INVERTED_QUESTION    ALTGR(BP_QUESTION)      // ¿ +#define BP_IQST                 BP_INVERTED_QUESTION +#define BP_DEAD_RING            ALTGR(BP_Q)             // dead ° +#define BP_DRNG                 BP_DEAD_RING +#define BP_DEAD_GREEK           ALTGR(BP_G)             // dead Greek key (following key will make a Greek letter) +#define BP_DGRK                 BP_DEAD_GREEK +#define BP_DAGGER               ALTGR(BP_H)             // † +#define BP_DAGR                 BP_DAGGER +#define BP_DEAD_OGONEK          ALTGR(BP_F)             // dead ˛ +#define BP_DOGO                 BP_DEAD_OGONEK + +// Space bar +#define BP_UNDERSCORE   ALTGR(KC_SPACE)     // _ +#define BP_UNDS         BP_UNDERSCORE + +// AltGr-Shifted characters (different from capitalised AltGr-ed characters) +// First row +#define BP_PARAGRAPH            ALTGR(BP_HASH)      // ¶ +#define BP_PARG                 BP_PARAGRAPH +#define BP_LOW_DOUBLE_QUOTE     ALTGR(BP_1)         // „ +#define BP_LWQT                 BP_LOW_DOUBLE_QUOTE +#define BP_LEFT_DOUBLE_QUOTE    ALTGR(BP_2)         // “ +#define BP_LDQT                 BP_LEFT_DOUBLE_QUOTE +#define BP_RIGHT_DOUBLE_QUOTE   ALTGR(BP_3)         // ” +#define BP_RDQT                 BP_RIGHT_DOUBLE_QUOTE +#define BP_LESS_OR_EQUAL        ALTGR(BP_4)         // ≤ +#define BP_LEQL                 BP_LESS_OR_EQUAL +#define BP_GREATER_OR_EQUAL     ALTGR(BP_5)         // ≥ +#define BP_GEQL                 BP_GREATER_OR_EQUAL +// nothing on ALTGR(BP_6) +#define BP_NEGATION             ALTGR(BP_7)         // ¬ +#define BP_NEGT                 BP_NEGATION +#define BP_ONE_QUARTER          ALTGR(BP_8)         // ¼ +#define BP_1QRT                 BP_ONE_QUARTER +#define BP_ONE_HALF             ALTGR(BP_9)         // ½ +#define BP_1HLF                 BP_ONE_HALF +#define BP_THREE_QUARTERS       ALTGR(BP_0)         // ¾ +#define BP_3QRT                 BP_THREE_QUARTERS +#define BP_MINUTES              ALTGR(BP_DEGREE)    // ′ +#define BP_MNUT                 BP_MINUTES +#define BP_SECONDS              ALTGR(BP_GRAVE)     // ″ +#define BP_SCND                 BP_SECONDS + +// Second row +#define BP_BROKEN_PIPE          LSFT(BP_PIPE)           // ¦ +#define BP_BPIP                 BP_BROKEN_PIPE +#define BP_DEAD_DOUBLE_ACUTE    LSFT(BP_DEAD_ACUTE)     // ˝ +#define BP_DDCT                 BP_DEAD_DOUBLE_ACUTE +#define BP_SECTION              ALTGR(LSFT(BP_P))       // § +#define BP_SECT                 BP_SECTION +// LSFT(BP_DEAD_GRAVE) is actually the same character as LSFT(BP_PERCENT) +#define BP_GRAVE_BIS            LSFT(BP_DEAD_GRAVE)     // ` +#define BP_GRVB                 BP_GRAVE_BIS + +// Third row +#define BP_DEAD_DOT_ABOVE       LSFT(BP_DEAD_TREMA)     // dead ˙ +#define BP_DDTA                 BP_DEAD_DOT_ABOVE +#define BP_DEAD_CURRENCY        LSFT(BP_EURO)           // dead ¤ (next key will generate a currency code like ¥ or £) +#define BP_DCUR                 BP_DEAD_CURRENCY +#define BP_DEAD_HORN            LSFT(ALTGR(BP_COMMA))   // dead ̛ +#define BP_DHRN                 BP_DEAD_HORN +#define BP_LONG_S               LSFT(ALTGR(BP_C))       // ſ +#define BP_LNGS                 BP_LONG_S +#define BP_TRADEMARK            LSFT(BP_REGISTERED_TRADEMARK)   // ™ +#define BP_TM                   BP_TRADEMARK +#define BP_ORDINAL_INDICATOR_O  LSFT(ALTGR(BP_M))               // º +#define BP_ORDO                 BP_ORDINAL_INDICATOR_O +#define BP_DEAD_COMMA           LSFT(BP_DEAD_CEDILLA)   // dead ˛ +#define BP_DCOM                 BP_DEAD_COMMA + +// Fourth row +#define BP_LEFT_QUOTE           LSFT(ALTGR(BP_Y))       // ‘ +#define BP_LQOT                 BP_LEFT_QUOTE +#define BP_RIGHT_QUOTE          LSFT(ALTGR(BP_X))       // ’ +#define BP_RQOT                 BP_RIGHT_QUOTE +#define BP_INTERPUNCT           LSFT(ALTGR(BP_DOT))     // · +#define BP_IPCT                 BP_INTERPUNCT +#define BP_DEAD_HOOK_ABOVE      LSFT(ALTGR(BP_QUESTION))    // dead ̉ +#define BP_DHKA                 BP_DEAD_HOOK_ABOVE +#define BP_DEAD_UNDERDOT        LSFT(BP_DEAD_RING)      // dead ̣ +#define BP_DUDT                 BP_DEAD_UNDERDOT +#define BP_DOUBLE_DAGGER        LSFT(BP_DAGGER)         // ‡ +#define BP_DDGR                 BP_DOUBLE_DAGGER +#define BP_ORDINAL_INDICATOR_A  LSFT(ALTGR(BP_F))       // ª +#define BP_ORDA                 BP_ORDINAL_INDICATOR_A + +// Space bar +#define BP_NARROW_NON_BREAKING_SPACE    ALTGR(BP_NON_BREAKING_SPACE) +#define BP_NNBS                         BP_NARROW_NON_BREAKING_SPACE + +#endif diff --git a/quantum/keymap_extras/keymap_br_abnt2.h b/quantum/keymap_extras/keymap_br_abnt2.h new file mode 100644 index 0000000000..b001139dd4 --- /dev/null +++ b/quantum/keymap_extras/keymap_br_abnt2.h @@ -0,0 +1,74 @@ +/* Copyright 2017 Potiguar Faga + * + * 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 KEYMAP_BR_ABNT2_H +#define KEYMAP_BR_ABNT2_H + +#include "keymap_common.h" + +/* Scan codes for the Brazilian ABNT2 keyboard layout */ + +#define BR_CCDL KC_SCLN      //  Ç   same scancode as ;: on US layout +#define BR_SCLN KC_SLSH      //  ;:  same scancode as /? on US layout +#define BR_QUOT KC_GRV       //  '"  same scancode as `~ on US layout +#define BR_TILD KC_QUOT      //  ~^  dead keys, same scancode as '" on US layout +#define BR_ACUT KC_LBRC      //  ´`  dead keys, same scancode as [{ on US layout +#define BR_LBRC KC_RBRC      //  [{  same scancode as ]} on US layout +#define BR_RBRC KC_BSLS      //  ]}  same scancode as \| on US layout +#define BR_BSLS KC_NUBS      //  \|  uses the non-US hash scancode (#~, sometimes §±) +#define BR_SLSH KC_INT1      //  /?  uses the INTL1 scancode + +#define BR_COLN LSFT(BR_SCLN)   // shifted : +#define BR_DQT  LSFT(BR_QUOT)   // shifted " +#define BR_CIRC LSFT(BR_TILD)   // shifted ^ (dead key) +#define BR_GRAV LSFT(BR_ACUT)   // shifted ` (dead key) +#define BR_LCBR LSFT(BR_LBRC)   // shifted { +#define BR_RCBR LSFT(BR_RBRC)   // shifted } +#define BR_PIPE LSFT(BR_BSLS)   // shifted | +#define BR_QUES LSFT(BR_SLSH)   // shifted ? +#define BR_TRMA LSFT(KC_6)      // shifted ¨ (dead key - trema accent) + +// On the ABNT2 the keypad comma and the keypad dot scancodes are switched +// (presumably because in Brazil comma is used as the decimal separator) +#define BR_KPDT KC_KP_COMMA  //  keypad . +#define BR_KPCM KC_KP_DOT    //  keypad , + +#define BR_1UP    LALT(KC_1)      // 1 superscript                    ¹   alt+1 +#define BR_2UP    LALT(KC_2)      // 2 superscript                    ²   alt+2 +#define BR_3UP    LALT(KC_3)      // 3 superscript                    ³   alt+3 +#define BR_PND    LALT(KC_4)      // Pound sign                       £   alt+4 +#define BR_CENT   LALT(KC_5)      // Cent sign                        ¢   alt+5 +#define BR_NOT    LALT(KC_6)      // Not sign                         ¬   alt+6 +#define BR_SECT   LALT(KC_EQL)    // Section sign                     §   alt+= +#define BR_FORD   LALT(BR_LBRC)   // Feminine Ordinal Sign            ª   alt+[ +#define BR_MORD   LALT(BR_RBRC)   // Masculine Ordinal Sign           º   alt+] +#define BR_DGRE   LALT(BR_SLSH)   // Degree sign                      °   alt+/ + +#define BR_EURO   LALT(KC_E)      // Euro sign                        €   alt+e +#define BR_NDTD   LALT(BR_TILD)   // Non-dead key tilde               ~   alt+~ +#define BR_NDAC   LALT(BR_ACUT)   // Non-dead key acute accent        ´   alt+´ +#define BR_NDGV   LALT(BR_QUOT)   // Non-dead key grave accent        `   alt+' +#define BR_NDCR   LALT(BR_CIRC)   // Non-dead key circumflex accent   ^   alt+^ (alt+shift+~) +#define BR_NDTR   LALT(BR_TRMA)   // Non-dead key trema accent        ¨   alt+¨ (alt+shift+6) + +// For 101-key keyboard layouts, the ABNT2 layout allows +// the slash and question mark to be typed using alt+q and alt+w. +// The shortcuts are provided here for completeness' sake, +// but it's recommended to use BR_SLSH and BR_QUES instead +#define BR_ASLS   LALT(KC_Q) +#define BR_AQST   LALT(KC_W) + +#endif diff --git a/quantum/keymap_extras/keymap_canadian_multilingual.h b/quantum/keymap_extras/keymap_canadian_multilingual.h new file mode 100644 index 0000000000..1d45bee32e --- /dev/null +++ b/quantum/keymap_extras/keymap_canadian_multilingual.h @@ -0,0 +1,270 @@ +/* Copyright 2016 Didier Loiseau + * + * 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 KEYMAP_CANADIAN_MULTILINGUAG_H +#define KEYMAP_CANADIAN_MULTILINGUAG_H + +#include "keymap.h" + +// Alt gr +#ifndef ALTGR +#define ALTGR(kc)   RALT(kc) +#endif +#ifndef ALGR +#define ALGR(kc)    ALTGR(kc) +#endif + +#define CSA_ALTGR   KC_RALT +#define CSA_ALGR    CSA_ALTGR + +#ifndef GR2A +#define GR2A(kc)    RCTL(kc) +#endif + +// Normal characters +// First row +#define CSA_SLASH   KC_GRV      // / +#define CSA_SLSH    CSA_SLASH + +// Second row +#define CSA_DEAD_CIRCUMFLEX     KC_LBRACKET         // dead ^ +#define CSA_DCRC                CSA_DEAD_CIRCUMFLEX +#define CSA_C_CEDILLA           KC_RBRACKET         // Ç +#define CSA_CCED                CSA_C_CEDILLA + +// Third row +#define CSA_E_GRAVE     KC_QUOT     // è +#define CSA_EGRV        CSA_E_GRAVE +#define CSA_A_GRAVE     KC_BSLASH   // à +#define CSA_AGRV        CSA_A_GRAVE + +// Fourth row +#define CSA_U_GRAVE     KC_NONUS_BSLASH     // ù +#define CSA_UGRV        CSA_U_GRAVE +#define CSA_E_ACUTE     KC_SLSH             // é +#define CSA_ECUT        CSA_E_ACUTE + +// Shifted characters +// First row +#define CSA_BACKSLASH   LSFT(CSA_SLASH) /* \ */ +#define CSA_BSLS        CSA_BACKSLASH +#define CSA_QUESTION    LSFT(KC_6)      // ? +#define CSA_QEST        CSA_QUESTION + +// Second row +#define CSA_DEAD_TREMA  LSFT(CSA_DEAD_CIRCUMFLEX)    // dead trema/umlaut/diaresis for ä ë ï ö ü +#define CSA_DTRM        CSA_DEAD_TREMA + +// Third row +// all same as US-QWERTY, or capitalised character of the non-shifted key + +// Fourth row +#define CSA_APOSTROPHE      LSFT(KC_COMMA)  // ' +#define CSA_APOS            CSA_APOSTROPHE +#define CSA_DOUBLE_QUOTE    LSFT(KC_DOT)    // " +#define CSA_DQOT            CSA_DOUBLE_QUOTE + +// Alt Gr-ed characters +// First row +#define CSA_PIPE                ALTGR(CSA_SLASH)        // | +#define CSA_CURRENCY            ALTGR(KC_4)             // ¤ +#define CSA_CURR                CSA_CURRENCY +#define CSA_LEFT_CURLY_BRACE    ALTGR(KC_7)             // { +#define CSA_LCBR                CSA_LEFT_CURLY_BRACE +#define CSA_RIGHT_CURLY_BRACE   ALTGR(KC_8)             // } +#define CSA_RCBR                CSA_RIGHT_CURLY_BRACE +#define CSA_LBRACKET            ALTGR(KC_9)             // [ +#define CSA_LBRC                CSA_LBRACKET +#define CSA_RBRACKET            ALTGR(KC_0)             // ] +#define CSA_RBRC                CSA_RBRACKET +#define CSA_NEGATION            ALTGR(KC_EQUAL)         // ¬ +#define CSA_NEGT                CSA_NEGATION + +// Second row +// euro symbol not available on Linux? (X.org) +#define CSA_EURO        ALTGR(KC_E)                 // € +#define CSA_DEAD_GRAVE  ALTGR(CSA_DEAD_CIRCUMFLEX) +#define CSA_DGRV        CSA_DEAD_GRAVE              // dead ` +#define CSA_DEAD_TILDE  ALTGR(CSA_C_CEDILLA)        // ~ +#define CSA_DTLD        CSA_DEAD_TILDE + +// Third row +#define CSA_DEGREE  ALTGR(KC_SCOLON)    // ° +#define CSA_DEGR    CSA_DEGREE + +// Fourth row +#define CSA_LEFT_GUILLEMET      ALTGR(KC_Z)         // « +#define CSA_LGIL                CSA_LEFT_GUILLEMET +#define CSA_RIGHT_GUILLEMET     ALTGR(KC_X)         // » +#define CSA_RGIL                CSA_RIGHT_GUILLEMET +#define CSA_LESS                ALTGR(KC_COMMA)     // < +#define CSA_GREATER             ALTGR(KC_DOT)       // > +#define CSA_GRTR                CSA_GREATER + +// Space bar +#define CSA_NON_BREAKING_SPACE  ALTGR(KC_SPACE) +#define CSA_NBSP                CSA_NON_BREAKING_SPACE + +// GR2A-ed characters +// First row +#define CSA_SUPERSCRIPT_ONE     GR2A(KC_1)  // ¹ +#define CSA_SUP1                CSA_SUPERSCRIPT_ONE +#define CSA_SUPERSCRIPT_TWO     GR2A(KC_2)  // ² +#define CSA_SUP2                CSA_SUPERSCRIPT_TWO +#define CSA_SUPERSCRIPT_THREE   GR2A(KC_3)  // ³ +#define CSA_SUP3                CSA_SUPERSCRIPT_THREE +#define CSA_ONE_QUARTER         GR2A(KC_4)  // ¼ +#define CSA_1QRT                CSA_ONE_QUARTER +#define CSA_ONE_HALF            GR2A(KC_5)  // ½ +#define CSA_1HLF                CSA_ONE_HALF +#define CSA_THREE_QUARTERS      GR2A(KC_6)  // ¾ +#define CSA_3QRT                CSA_THREE_QUARTERS +// nothing on 7-0 and - +#define CSA_DEAD_CEDILLA        GR2A(KC_EQUAL)  // dead ¸ +#define CSA_DCED                CSA_DEAD_CEDILLA + +// Second row +#define CSA_OMEGA           GR2A(KC_Q)  // ω +#define CSA_OMEG            CSA_OMEGA +#define CSA_L_STROKE        GR2A(KC_W)  // ł +#define CSA_LSTK            CSA_L_STROKE +#define CSA_OE_LIGATURE     GR2A(KC_E)  // œ +#define CSA_OE              CSA_OE_LIGATURE +#define CSA_PARAGRAPH       GR2A(KC_R)  // ¶ +#define CSA_PARG            CSA_PARAGRAPH +#define CSA_T_STROKE        GR2A(KC_T)  // ŧ +#define CSA_LEFT_ARROW      GR2A(KC_Y)  // ← +#define CSA_LARW            CSA_LEFT_ARROW +#define CSA_DOWN_ARROW      GR2A(KC_U)  // ↓ +#define CSA_DARW            CSA_DOWN_ARROW +#define CSA_RIGHT_ARROW     GR2A(KC_I)  // → +#define CSA_RARW            CSA_RIGHT_ARROW +#define CSA_O_STROKE        GR2A(KC_O)  // ø +#define CSA_OSTK            CSA_O_STROKE +#define CSA_THORN           GR2A(KC_P)  // þ +#define CSA_THRN            CSA_THORN +// nothing on ^ +#define CSA_TILDE           GR2A(CSA_C_CEDILLA)  // dead ~ +#define CSA_TILD            CSA_TILDE + +// Third row +#define CSA_AE_LIGATURE     GR2A(KC_A)      // æ +#define CSA_AE              CSA_AE_LIGATURE +#define CSA_SHARP_S         GR2A(KC_S)      // ß +#define CSA_SRPS            CSA_SHARP_S +#define CSA_ETH             GR2A(KC_D)      // ð +// nothing on F +#define CSA_ENG             GR2A(KC_G)      // ŋ +#define CSA_H_SRTOKE        GR2A(KC_H)      // ħ +#define CSA_HSTK            CSA_H_SRTOKE +#define CSA_IJ_LIGATURE     GR2A(KC_J)      // ij +#define CSA_IJ              CSA_IJ_LIGATURE +#define CSA_KRA             GR2A(KC_K)      // ĸ +#define CSA_L_FLOWN_DOT     GR2A(KC_L)      // ŀ +#define CSA_LFLD            CSA_L_FLOWN_DOT +#define CSA_DEAD_ACUTE      GR2A(KC_SCLN)   // dead acute accent +#define CSA_DACT            CSA_DEAD_ACUTE +// nothing on È & À + +// Fourth row +#define CSA_CENT                GR2A(KC_C)  // ¢ +#define CSA_LEFT_DOUBLE_QUOTE   GR2A(KC_V)  // “ +#define CSA_LDQT                CSA_LEFT_DOUBLE_QUOTE +#define CSA_RIGHT_DOUBLE_QUOTE  GR2A(KC_B)  // ” +#define CSA_RDQT                CSA_RIGHT_DOUBLE_QUOTE +#define CSA_N_APOSTROPHE        GR2A(KC_N)  // ʼn (deprecated unicode codepoint) +#define CSA_NAPO                CSA_N_APOSTROPHE +#define CSA_MU                  GR2A(KC_M)  // μ +#define CSA_HORIZONTAL_BAR      GR2A(KC_COMMA)  // ― +#define CSA_HZBR                CSA_HORIZONTAL_BAR +#define CSA_DEAD_DOT_ABOVE      GR2A(KC_DOT)    // dead ˙ +#define CSA_DDTA                CSA_DEAD_DOT_ABOVE + +// GR2A-shifted characters (different from capitalised GR2A-ed characters) +// First row +#define CSA_SOFT_HYPHEN         GR2A(LSFT(CSA_SLASH))   // soft-hyphen, appears as a hyphen in wrapped word +#define CSA_SHYP                CSA_SOFT_HYPHEN +#define CSA_INVERTED_EXCLAIM    GR2A(KC_EXCLAIM)    // ¡ +#define CSA_IXLM                CSA_INVERTED_EXCLAIM +// nothing on 2 +#define CSA_POUND               GR2A(LSFT(KC_3))    // £ +#define CSA_GBP                 CSA_POUND_SIGN +// already on ALTGR(KC_E) +#define CSA_EURO_BIS            GR2A(LSFT(KC_4))    // € +#define CSA_EURB                CSA_EURO_BIS +#define CSA_THREE_EIGHTHS       GR2A(LSFT(KC_5))    // ⅜ +#define CSA_3ON8                CSA_THREE_EIGHTHS +#define CSA_FIVE_EIGHTHS        GR2A(LSFT(KC_6))    // ⅝ +#define CSA_5ON8                CSA_FIVE_EIGHTHS +#define CSA_SEVEN_EIGHTHS       GR2A(LSFT(KC_7))    // ⅞ +#define CSA_7ON8                CSA_SEVEN_EIGHTHS +#define CSA_TRADEMARK           GR2A(LSFT(KC_8))    // ™ +#define CSA_TM                  CSA_TRADEMARK +#define CSA_PLUS_MINUS          GR2A(LSFT(KC_9))    // ± +#define CSA_PSMS                CSA_PLUS_MINUS +// nothing on 0 +#define CSA_INVERTED_QUESTION   GR2A(LSFT(KC_MINUS))    // ¿ +#define CSA_IQST                CSA_INVERTED_QUESTION +#define CSA_DEAD_OGONEK         GR2A(LSFT(KC_EQUAL))    // dead ˛ +#define CSA_DOGO                CSA_DEAD_OGONEK + +// Second row +#define CSA_REGISTERED_TRADEMARK    GR2A(LSFT(KC_R))        // ® +#define CSA_RTM                     CSA_REGISTERED_TRADEMARK +#define CSA_YEN                     GR2A(LSFT(KC_Y))        // ¥ +#define CSA_YUAN                    CSA_YEN +#define CSA_UP_ARROW                LSFT(CSA_DOWN_ARROW)    // ↑ +#define CSA_DOTLESS_I               GR2A(LSFT(KC_I))        // ı +#define CSA_DLSI                    CSA_DOTLESS_I +#define CSA_DEAD_RING               GR2A(LSFT(CSA_DCRC))    // dead ° +#define CSA_DRNG                    CSA_DEAD_RING +#define CSA_DEAD_MACRON             GR2A(LSFT(CSA_C_CEDILLA))   // dead ¯ +#define CSA_DMCR                    CSA_DEAD_MACRON + +// Third row +#define CSA_SECTION                 GR2A(LSFT(KC_S))        // § +#define CSA_SECT                    CSA_SECTION +#define CSA_ORDINAL_INDICATOR_A     GR2A(LSFT(KC_F))        // ª +#define CSA_ORDA                    CSA_ORDINAL_INDICATOR_A +#define CSA_DEAD_DOUBLE_ACUTE       LSFT(CSA_DEAD_ACUTE)    // ˝ +#define CSA_DDCT                    CSA_DEAD_DOUBLE_ACUTE +#define CSA_DEAD_CARON              GR2A(LSFT(CSA_E_GRAVE)) // dead ˇ +#define CSA_DCAR                    CSA_DEAD_CARON +#define CSA_DEAD_BREVE              GR2A(LSFT(CSA_A_GRAVE)) // dead ˘ +#define CSA_DBRV                    CSA_DEAD_BREVE + +// Fourth row +#define CSA_BROKEN_PIPE         GR2A(LSFT(CSA_U_GRAVE)) // ¦ +#define CSA_BPIP                CSA_BROKEN_PIPE +#define CSA_COPYRIGHT           GR2A(LSFT(KC_C))        // © +#define CSA_CPRT                CSA_COPYRIGHT +#define CSA_LEFT_QUOTE          GR2A(LSFT(KC_V))        // ‘ +#define CSA_LQOT                CSA_LEFT_QUOTE +#define CSA_RIGHT_QUOTE         GR2A(LSFT(KC_B))        // ’ +#define CSA_RQOT                CSA_RIGHT_QUOTE +#define CSA_EIGHTH_NOTE         GR2A(LSFT(KC_N))        // ♪ +#define CSA_8NOT                CSA_EIGHTH_NOTE +#define CSA_ORDINAL_INDICATOR_O GR2A(LSFT(KC_M))        // º +#define CSA_ORDO                CSA_ORDINAL_INDICATOR_O +#define CSA_TIMES               GR2A(LSFT(KC_COMMA))    // × +#define CSA_TIMS                CSA_TIMES +#define CSA_OBELUS              GR2A(LSFT(KC_DOT))      // ÷ +#define CSA_OBEL                CSA_OBELUS +// more conventional name of the symbol +#define CSA_DIVISION_SIGN       CSA_OBELUS +#define CSA_DVSN                CSA_DIVISION_SIGN +// TODO GR2A(LSFT(CSA_E_ACUTE)) + +#endif diff --git a/quantum/keymap_extras/keymap_colemak.h b/quantum/keymap_extras/keymap_colemak.h new file mode 100644 index 0000000000..2d3f9c06a5 --- /dev/null +++ b/quantum/keymap_extras/keymap_colemak.h @@ -0,0 +1,90 @@ +/* Copyright 2015-2016 Jack Humbert + * + * 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 KEYMAP_COLEMAK_H +#define KEYMAP_COLEMAK_H + +#include "keymap.h" +// For software implementation of colemak +#define CM_Q    KC_Q +#define CM_W    KC_W +#define CM_F    KC_E +#define CM_P    KC_R +#define CM_G    KC_T +#define CM_J    KC_Y +#define CM_L    KC_U +#define CM_U    KC_I +#define CM_Y    KC_O +#define CM_SCLN KC_P + +#define CM_A    KC_A +#define CM_R    KC_S +#define CM_S    KC_D +#define CM_T    KC_F +#define CM_D    KC_G +#define CM_H    KC_H +#define CM_N    KC_J +#define CM_E    KC_K +#define CM_I    KC_L +#define CM_O    KC_SCLN +#define CM_COLN LSFT(CM_SCLN) + +#define CM_Z    KC_Z +#define CM_X    KC_X +#define CM_C    KC_C +#define CM_V    KC_V +#define CM_B    KC_B +#define CM_K    KC_N +#define CM_M    KC_M +#define CM_COMM KC_COMM +#define CM_DOT  KC_DOT +#define CM_SLSH KC_SLSH + +// Make it easy to support these in macros +// TODO: change macro implementation so these aren't needed +#define KC_CM_Q    CM_Q +#define KC_CM_W    CM_W +#define KC_CM_F    CM_F +#define KC_CM_P    CM_P +#define KC_CM_G    CM_G +#define KC_CM_J    CM_J +#define KC_CM_L    CM_L +#define KC_CM_U    CM_U +#define KC_CM_Y    CM_Y +#define KC_CM_SCLN CM_SCLN + +#define KC_CM_A    CM_A +#define KC_CM_R    CM_R +#define KC_CM_S    CM_S +#define KC_CM_T    CM_T +#define KC_CM_D    CM_D +#define KC_CM_H    CM_H +#define KC_CM_N    CM_N +#define KC_CM_E    CM_E +#define KC_CM_I    CM_I +#define KC_CM_O    CM_O + +#define KC_CM_Z    CM_Z +#define KC_CM_X    CM_X +#define KC_CM_C    CM_C +#define KC_CM_V    CM_V +#define KC_CM_B    CM_B +#define KC_CM_K    CM_K +#define KC_CM_M    CM_M +#define KC_CM_COMM CM_COMM +#define KC_CM_DOT  CM_DOT +#define KC_CM_SLSH CM_SLSH + +#endif diff --git a/quantum/keymap_extras/keymap_dvorak.h b/quantum/keymap_extras/keymap_dvorak.h new file mode 100644 index 0000000000..b1d5604baf --- /dev/null +++ b/quantum/keymap_extras/keymap_dvorak.h @@ -0,0 +1,100 @@ +/* Copyright 2015-2016 Jack Humbert + * + * 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 KEYMAP_DVORAK_H +#define KEYMAP_DVORAK_H + +#include "keymap.h" + +// Normal characters +#define DV_GRV	KC_GRV +#define DV_1	KC_1 +#define DV_2	KC_2 +#define DV_3	KC_3 +#define DV_4	KC_4 +#define DV_5	KC_5 +#define DV_6	KC_6 +#define DV_7	KC_7 +#define DV_8	KC_8 +#define DV_9	KC_9 +#define DV_0	KC_0 +#define DV_LBRC	KC_MINS +#define DV_RBRC	KC_EQL + +#define DV_QUOT	KC_Q +#define DV_COMM	KC_W +#define DV_DOT	KC_E +#define DV_P	KC_R +#define DV_Y	KC_T +#define	DV_F	KC_Y +#define DV_G	KC_U +#define DV_C	KC_I +#define	DV_R	KC_O +#define DV_L	KC_P +#define DV_SLSH	KC_LBRC +#define DV_EQL	KC_RBRC +#define DV_BSLS	KC_BSLS + +#define DV_A	KC_A +#define DV_O	KC_S +#define DV_E	KC_D +#define DV_U	KC_F +#define DV_I	KC_G +#define DV_D	KC_H +#define DV_H	KC_J +#define DV_T	KC_K +#define DV_N	KC_L +#define DV_S	KC_SCLN +#define DV_MINS	KC_QUOT + +#define DV_SCLN	KC_Z +#define DV_Q	KC_X +#define DV_J	KC_C +#define DV_K	KC_V +#define DV_X	KC_B +#define DV_B	KC_N +#define DV_M	KC_M +#define DV_W	KC_COMM +#define DV_V	KC_DOT +#define DV_Z	KC_SLSH + +// Shifted characters +#define DV_TILD	LSFT(DV_GRV) +#define DV_EXLM	LSFT(DV_1) +#define DV_AT	LSFT(DV_2) +#define DV_HASH	LSFT(DV_3) +#define DV_DLR	LSFT(DV_4) +#define DV_PERC	LSFT(DV_5) +#define DV_CIRC	LSFT(DV_6) +#define DV_AMPR	LSFT(DV_7) +#define DV_ASTR	LSFT(DV_8) +#define DV_LPRN	LSFT(DV_9) +#define DV_RPRN	LSFT(DV_0) +#define DV_LCBR	LSFT(DV_LBRC) +#define DV_RCBR	LSFT(DV_RBRC) + +#define DV_DQUO	LSFT(DV_QUOT) +#define DV_LABK	LSFT(DV_COMM) +#define DV_RABK	LSFT(DV_DOT) + +#define DV_QUES	LSFT(DV_SLSH) +#define DV_PLUS	LSFT(DV_EQL) +#define DV_PIPE	LSFT(DV_BSLS) + +#define DV_UNDS	LSFT(DV_MINS) + +#define DV_COLN	LSFT(DV_SCLN) + +#endif diff --git a/quantum/keymap_extras/keymap_dvp.h b/quantum/keymap_extras/keymap_dvp.h new file mode 100644 index 0000000000..50e2d1f461 --- /dev/null +++ b/quantum/keymap_extras/keymap_dvp.h @@ -0,0 +1,98 @@ +/* Copyright 2016 Artyom Mironov + * + * 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 KEYMAP_DVP_H +#define KEYMAP_DVP_H + +#include "keymap.h" + +// Normal characters +#define DP_DLR	KC_GRV +#define DP_AMPR	KC_1 +#define DP_LBRC	KC_2 +#define DP_LCBR	KC_3 +#define DP_RCBR	KC_4 +#define DP_LPRN	KC_5 +#define DP_EQL	KC_6 +#define DP_ASTR	KC_7 +#define DP_RPRN	KC_8 +#define DP_PLUS	KC_9 +#define DP_RBRC	KC_0 +#define DP_EXLM	KC_MINS +#define DP_HASH	KC_EQL + +#define DP_SCLN	KC_Q +#define DP_COMM	KC_W +#define DP_DOT	KC_E +#define DP_P	KC_R +#define DP_Y	KC_T +#define DP_F	KC_Y +#define DP_G	KC_U +#define DP_C	KC_I +#define DP_R	KC_O +#define DP_L	KC_P +#define DP_SLSH	KC_LBRC +#define DP_AT	KC_RBRC +#define DP_BSLS	KC_BSLS + +#define DP_A	KC_A +#define DP_O	KC_S +#define DP_E	KC_D +#define DP_U	KC_F +#define DP_I	KC_G +#define DP_D	KC_H +#define DP_H	KC_J +#define DP_T	KC_K +#define DP_N	KC_L +#define DP_S	KC_SCLN +#define DP_MINS	KC_QUOT + +#define DP_QUOT	KC_Z +#define DP_Q	KC_X +#define DP_J	KC_C +#define DP_K	KC_V +#define DP_X	KC_B +#define DP_B	KC_N +#define DP_M	KC_M +#define DP_W	KC_COMM +#define DP_V	KC_DOT +#define DP_Z	KC_SLSH + +// Shifted characters +#define DP_TILD	LSFT(DP_DLR) +#define DP_PERC	LSFT(DP_AMPR) +#define DP_7	LSFT(DP_LBRC) +#define DP_5	LSFT(DP_LCBR) +#define DP_3	LSFT(DP_RCBR) +#define DP_1	LSFT(DP_LPRN) +#define DP_9	LSFT(DP_EQL) +#define DP_0	LSFT(DP_ASTR) +#define DP_2	LSFT(DP_RPRN) +#define DP_4	LSFT(DP_PLUS) +#define DP_6	LSFT(DP_RBRC) +#define DP_8	LSFT(DP_EXLM) +#define DP_GRV	LSFT(DP_HASH) + +#define DP_COLN	LSFT(DP_SCLN) +#define DP_LABK	LSFT(DP_COMM) +#define DP_RABK	LSFT(DP_DOT) +#define DP_QUES	LSFT(DP_SLSH) +#define DP_CIRC	LSFT(DP_AT) +#define DP_PIPE	LSFT(DP_BSLS) +#define DP_UNDS	LSFT(DP_MINS) +#define DP_DQUO	LSFT(DP_QUOT) + +#endif diff --git a/quantum/keymap_extras/keymap_fr_ch.h b/quantum/keymap_extras/keymap_fr_ch.h new file mode 100644 index 0000000000..c0ca832a6f --- /dev/null +++ b/quantum/keymap_extras/keymap_fr_ch.h @@ -0,0 +1,113 @@ +/* Copyright 2016 Vincent Pochet + * + * 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 KEYMAP_FR_CH +#define KEYMAP_FR_CH + +#include "keymap.h" + +// Alt gr +#define ALGR(kc) RALT(kc) +#define FR_CH_ALGR KC_RALT + +// normal characters +#define FR_CH_Z KC_Y +#define FR_CH_Y KC_Z + +#define FR_CH_A KC_A +#define FR_CH_B KC_B +#define FR_CH_C KC_C +#define FR_CH_D KC_D +#define FR_CH_E KC_E +#define FR_CH_F KC_F +#define FR_CH_G KC_G +#define FR_CH_H KC_H +#define FR_CH_I KC_I +#define FR_CH_J KC_J +#define FR_CH_K KC_K +#define FR_CH_L KC_L +#define FR_CH_M KC_M +#define FR_CH_N KC_N +#define FR_CH_O KC_O +#define FR_CH_P KC_P +#define FR_CH_Q KC_Q +#define FR_CH_R KC_R +#define FR_CH_S KC_S +#define FR_CH_T KC_T +#define FR_CH_U KC_U +#define FR_CH_V KC_V +#define FR_CH_W KC_W +#define FR_CH_X KC_X + +#define FR_CH_0 KC_0 +#define FR_CH_1 KC_1 +#define FR_CH_2 KC_2 +#define FR_CH_3 KC_3 +#define FR_CH_4 KC_4 +#define FR_CH_5 KC_5 +#define FR_CH_6 KC_6 +#define FR_CH_7 KC_7 +#define FR_CH_8 KC_8 +#define FR_CH_9 KC_9 + +#define FR_CH_DOT KC_DOT +#define FR_CH_COMM KC_COMM + +#define FR_CH_QUOT KC_MINS +#define FR_CH_AE KC_QUOT +#define FR_CH_UE KC_LBRC +#define FR_CH_OE KC_SCLN + +#define FR_CH_CIRC KC_EQL // accent circumflex ^ and grave ` and ~ +#define FR_CH_LESS KC_NUBS // < and > and backslash +#define FR_CH_MINS KC_SLSH // - and _ +#define FR_CH_DLR KC_BSLS // $, £ and } +#define FR_CH_PARA KC_GRV // § and ring ° +#define FR_CH_DIAE KC_RBRC // accent ¨ + +// shifted characters +#define FR_CH_RING LSFT(KC_GRV) // ° +#define FR_CH_EXLM LSFT(KC_RBRC) // ! +#define FR_CH_PLUS LSFT(KC_1) // + +#define FR_CH_DQOT LSFT(KC_2) // " +#define FR_CH_ASTR LSFT(KC_3) // * +#define FR_CH_PERC LSFT(KC_5) // % +#define FR_CH_AMPR LSFT(KC_6) // & +#define FR_CH_SLSH LSFT(KC_7) // / +#define FR_CH_LPRN LSFT(KC_8) // ( +#define FR_CH_RPRN LSFT(KC_9) // ) +#define FR_CH_EQL  LSFT(KC_0) // = +#define FR_CH_QST  LSFT(FR_CH_QUOT) // ? +#define FR_CH_MORE LSFT(FR_CH_LESS) // > +#define FR_CH_COLN LSFT(KC_DOT) // : +#define FR_CH_SCLN LSFT(KC_COMM) // ; +#define FR_CH_UNDS LSFT(FR_CH_MINS) // _ +#define FR_CH_CCED LSFT(KC_4) // ç +#define FR_CH_GRV  LSFT(FR_CH_CIRC) // accent grave ` + +// Alt Gr-ed characters +#define FR_CH_LCBR ALGR(KC_QUOT) // { +#define FR_CH_LBRC ALGR(KC_LBRC) // [ +#define FR_CH_RBRC ALGR(KC_9) // ] +#define FR_CH_RCBR ALGR(KC_0) // } +#define FR_CH_BSLS ALGR(FR_CH_LESS) // backslash +#define FR_CH_AT   ALGR(KC_2) // @ +#define FR_CH_EURO ALGR(KC_E) // € +#define FR_CH_TILD ALGR(FR_CH_CIRC) // ~ +#define FR_CH_PIPE ALGR(KC_1) // | +#define FR_CH_HASH ALGR(KC_3) // # +#define FR_CH_ACUT ALGR(FR_CH_QUOT) // accent acute ´ + +#endif diff --git a/quantum/keymap_extras/keymap_french.h b/quantum/keymap_extras/keymap_french.h new file mode 100644 index 0000000000..3308dc5f77 --- /dev/null +++ b/quantum/keymap_extras/keymap_french.h @@ -0,0 +1,100 @@ +/* Copyright 2015-2016 Jack Humbert + * + * 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 KEYMAP_FRENCH_H +#define KEYMAP_FRENCH_H + +#include "keymap.h" + +// Alt gr +#ifndef ALGR +#define ALGR(kc) RALT(kc) +#endif +#define NO_ALGR KC_RALT + +// Normal characters +#define FR_SUP2	KC_GRV +#define FR_AMP	KC_1 +#define FR_EACU	KC_2 +#define FR_QUOT	KC_3 +#define FR_APOS	KC_4 +#define FR_LPRN	KC_5 +#define FR_MINS	KC_6 +#define FR_EGRV	KC_7 +#define FR_UNDS	KC_8 +#define FR_CCED	KC_9 +#define FR_AGRV	KC_0 +#define FR_RPRN	KC_MINS +#define FR_EQL	KC_EQL + +#define FR_A 	KC_Q +#define FR_Z	KC_W +#define	FR_CIRC	KC_LBRC +#define FR_DLR	KC_RBRC + +#define FR_Q 	KC_A +#define FR_M 	KC_SCLN +#define FR_UGRV	KC_QUOT +#define FR_ASTR	KC_NUHS + +#define FR_LESS	KC_NUBS +#define FR_W	KC_Z +#define FR_COMM	KC_M +#define FR_SCLN	KC_COMM +#define FR_COLN	KC_DOT +#define FR_EXLM	KC_SLSH + +// Shifted characters +#define FR_1 	LSFT(KC_1) +#define FR_2 	LSFT(KC_2) +#define FR_3 	LSFT(KC_3) +#define FR_4 	LSFT(KC_4) +#define FR_5 	LSFT(KC_5) +#define FR_6 	LSFT(KC_6) +#define FR_7 	LSFT(KC_7) +#define FR_8 	LSFT(KC_8) +#define FR_9 	LSFT(KC_9) +#define FR_0 	LSFT(KC_0) +#define FR_OVRR	LSFT(FR_RPRN) +#define FR_PLUS LSFT(FR_EQL) + +#define FR_UMLT	LSFT(FR_CIRC) +#define FR_PND	LSFT(FR_DLR) +#define	FR_PERC	LSFT(FR_UGRV) +#define FR_MU 	LSFT(FR_ASTR) + +#define FR_GRTR	LSFT(FR_LESS) +#define FR_QUES	LSFT(FR_COMM) +#define FR_DOT	LSFT(FR_SCLN) +#define FR_SLSH	LSFT(FR_COLN) +#define FR_SECT	LSFT(FR_EXLM) + +// Alt Gr-ed characters +#define FR_TILD	ALGR(KC_2) +#define FR_HASH	ALGR(KC_3) +#define FR_LCBR ALGR(KC_4) +#define FR_LBRC	ALGR(KC_5) +#define FR_PIPE ALGR(KC_6) +#define FR_GRV 	ALGR(KC_7) +#define FR_BSLS	ALGR(KC_8) +#define FR_CCIRC	ALGR(KC_9) +#define FR_AT 	ALGR(KC_0) +#define FR_RBRC	ALGR(FR_RPRN) +#define FR_RCBR ALGR(FR_EQL) + +#define FR_EURO	ALGR(KC_E) +#define FR_BULT	ALGR(FR_DLR) + +#endif diff --git a/quantum/keymap_extras/keymap_french_osx.h b/quantum/keymap_extras/keymap_french_osx.h new file mode 100644 index 0000000000..ecade3fe98 --- /dev/null +++ b/quantum/keymap_extras/keymap_french_osx.h @@ -0,0 +1,92 @@ +/* Copyright 2016 Sébastien Pérochon + * + * 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 KEYMAP_FRENCH_OSX_H +#define KEYMAP_FRENCH_OSX_H + +#include "keymap.h" + +// Normal characters +#define FR_AT 	KC_GRV +#define FR_AMP	KC_1 +#define FR_EACU	KC_2 +#define FR_QUOT	KC_3 +#define FR_APOS	KC_4 +#define FR_LPRN	KC_5 +#define FR_SECT	KC_6 +#define FR_EGRV	KC_7 +#define FR_EXLM	KC_8 +#define FR_CCED	KC_9 +#define FR_AGRV	KC_0 +#define FR_RPRN	KC_MINS +#define FR_MINS	KC_EQL + +#define FR_A 	KC_Q +#define FR_Z	KC_W +#define FR_CIRC	KC_LBRC +#define FR_DLR	KC_RBRC + +#define FR_Q 	KC_A +#define FR_M 	KC_SCLN +#define FR_UGRV	KC_QUOT +#define FR_GRV	KC_NUHS + +#define FR_LESS	KC_NUBS +#define FR_W	KC_Z +#define FR_COMM	KC_M +#define FR_SCLN	KC_COMM +#define FR_COLN	KC_DOT +#define FR_EQL	KC_SLSH + +// Shifted characters +#define FR_HASH	LSFT(KC_GRV) +#define FR_1 	LSFT(KC_1) +#define FR_2 	LSFT(KC_2) +#define FR_3 	LSFT(KC_3) +#define FR_4 	LSFT(KC_4) +#define FR_5 	LSFT(KC_5) +#define FR_6 	LSFT(KC_6) +#define FR_7 	LSFT(KC_7) +#define FR_8 	LSFT(KC_8) +#define FR_9 	LSFT(KC_9) +#define FR_0 	LSFT(KC_0) +#define FR_UNDS	LSFT(FR_MINS) + +#define FR_UMLT	LSFT(FR_CIRC) +#define FR_ASTR	LSFT(FR_DLR) + +#define FR_PERC	LSFT(FR_UGRV) +#define FR_PND	LSFT(FR_GRV) + +#define FR_GRTR	LSFT(FR_LESS) +#define FR_QUES	LSFT(FR_COMM) +#define FR_DOT	LSFT(FR_SCLN) +#define FR_SLSH	LSFT(FR_COLN) +#define FR_PLUS	LSFT(FR_EQL) + +// Alted characters +#define FR_LCBR	LALT(KC_5) +#define FR_RCBR	LALT(FR_RPRN) +#define FR_EURO	LALT(KC_E) +#define FR_BULT	LALT(FR_DLR) +#define FR_TILD	LALT(KC_N) + +// Shift+Alt-ed characters +#define FR_LBRC	LSFT(LALT(KC_5)) +#define FR_RBRC	LSFT(LALT(FR_RPRN)) +#define FR_PIPE	LSFT(LALT(KC_L)) +#define FR_BSLS	LSFT(LALT(FR_COLN)) + +#endif diff --git a/quantum/keymap_extras/keymap_german.h b/quantum/keymap_extras/keymap_german.h new file mode 100644 index 0000000000..e007c26ef5 --- /dev/null +++ b/quantum/keymap_extras/keymap_german.h @@ -0,0 +1,115 @@ +/* Copyright 2015-2016 Matthias Schmidtt + * + * 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 KEYMAP_GERMAN +#define KEYMAP_GERMAN + +#include "keymap.h" + +// Alt gr +#define ALGR(kc) RALT(kc) +#define DE_ALGR KC_RALT + +// normal characters +#define DE_Z KC_Y +#define DE_Y KC_Z + +#define DE_A KC_A +#define DE_B KC_B +#define DE_C KC_C +#define DE_D KC_D +#define DE_E KC_E +#define DE_F KC_F +#define DE_G KC_G +#define DE_H KC_H +#define DE_I KC_I +#define DE_J KC_J +#define DE_K KC_K +#define DE_L KC_L +#define DE_M KC_M +#define DE_N KC_N +#define DE_O KC_O +#define DE_P KC_P +#define DE_Q KC_Q +#define DE_R KC_R +#define DE_S KC_S +#define DE_T KC_T +#define DE_U KC_U +#define DE_V KC_V +#define DE_W KC_W +#define DE_X KC_X + +#define DE_0 KC_0 +#define DE_1 KC_1 +#define DE_2 KC_2 +#define DE_3 KC_3 +#define DE_4 KC_4 +#define DE_5 KC_5 +#define DE_6 KC_6 +#define DE_7 KC_7 +#define DE_8 KC_8 +#define DE_9 KC_9 + +#define DE_DOT KC_DOT +#define DE_COMM KC_COMM + +#define DE_SS KC_MINS +#define DE_AE KC_QUOT +#define DE_UE KC_LBRC +#define DE_OE KC_SCLN + +#define DE_CIRC KC_GRAVE // accent circumflex ^ and ring ° +#define DE_ACUT KC_EQL // accent acute ´ and grave ` +#define DE_PLUS KC_RBRC // + and * and ~ +#define DE_HASH KC_BSLS // # and ' +#define DE_LESS KC_NUBS // < and > and | +#define DE_MINS KC_SLSH // - and _ + +// shifted characters +#define DE_RING LSFT(DE_CIRC) // ° +#define DE_EXLM LSFT(KC_1) // ! +#define DE_DQOT LSFT(KC_2) // " +#define DE_PARA LSFT(KC_3) // § +#define DE_DLR  LSFT(KC_4) // $ +#define DE_PERC LSFT(KC_5) // % +#define DE_AMPR LSFT(KC_6) // & +#define DE_SLSH LSFT(KC_7) // / +#define DE_LPRN LSFT(KC_8) // ( +#define DE_RPRN LSFT(KC_9) // ) +#define DE_EQL  LSFT(KC_0) // = +#define DE_QST  LSFT(DE_SS) // ? +#define DE_GRV  LSFT(DE_ACUT) // ` +#define DE_ASTR LSFT(DE_PLUS) // * +#define DE_QUOT LSFT(DE_HASH) // ' +#define DE_MORE LSFT(DE_LESS) // > +#define DE_COLN LSFT(KC_DOT) // : +#define DE_SCLN LSFT(KC_COMM) // ; +#define DE_UNDS LSFT(DE_MINS) // _ + +// Alt Gr-ed characters +#define DE_SQ2 ALGR(KC_2) // ² +#define DE_SQ3 ALGR(KC_3) // ³ +#define DE_LCBR ALGR(KC_7) // { +#define DE_LBRC ALGR(KC_8) // [ +#define DE_RBRC ALGR(KC_9) // ] +#define DE_RCBR ALGR(KC_0) // } +#define DE_BSLS ALGR(DE_SS) // backslash +#define DE_AT  ALGR(KC_Q) // @ +#define DE_EURO ALGR(KC_E) // € +#define DE_TILD ALGR(DE_PLUS) // ~ +#define DE_PIPE ALGR(DE_LESS) // | + +#endif diff --git a/quantum/keymap_extras/keymap_german_ch.h b/quantum/keymap_extras/keymap_german_ch.h new file mode 100644 index 0000000000..67350d6602 --- /dev/null +++ b/quantum/keymap_extras/keymap_german_ch.h @@ -0,0 +1,121 @@ +/* Copyright 2016 heartsekai + * + * 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 KEYMAP_SWISS_GERMAN +#define KEYMAP_SWISS_GERMAN + +#include "keymap.h" + +// Alt gr +#define ALGR(kc) RALT(kc) +#define CH_ALGR KC_RALT + +// normal characters +#define CH_Z KC_Y +#define CH_Y KC_Z + +#define CH_A KC_A +#define CH_B KC_B +#define CH_C KC_C +#define CH_D KC_D +#define CH_E KC_E +#define CH_F KC_F +#define CH_G KC_G +#ifdef CH_H +// The ChibiOS ch.h file defines this... +#undef CH_H +#endif +#define CH_H KC_H +#define CH_I KC_I +#define CH_J KC_J +#define CH_K KC_K +#define CH_L KC_L +#define CH_M KC_M +#define CH_N KC_N +#define CH_O KC_O +#define CH_P KC_P +#define CH_Q KC_Q +#define CH_R KC_R +#define CH_S KC_S +#define CH_T KC_T +#define CH_U KC_U +#define CH_V KC_V +#define CH_W KC_W +#define CH_X KC_X + +#define CH_0 KC_0 +#define CH_1 KC_1 +#define CH_2 KC_2 +#define CH_3 KC_3 +#define CH_4 KC_4 +#define CH_5 KC_5 +#define CH_6 KC_6 +#define CH_7 KC_7 +#define CH_8 KC_8 +#define CH_9 KC_9 + +#define CH_DOT KC_DOT +#define CH_COMM KC_COMM + +#define CH_QUOT KC_MINS // ' ? ´ +#define CH_AE KC_QUOT +#define CH_UE KC_LBRC +#define CH_OE KC_SCLN + +#define CH_PARA KC_GRAVE // secction sign § and ° +#define CH_CARR KC_EQL // carret ^ ` ~ +#define CH_DIER KC_RBRC // dieresis ¨ ! ] +#define CH_DLR KC_BSLS // $ £ } +#define CH_LESS KC_NUBS // < and > and backslash +#define CH_MINS KC_SLSH // - and _ + +// shifted characters +#define CH_RING LSFT(CH_PARA) // ° +#define CH_PLUS LSFT(KC_1) // + +#define CH_DQOT LSFT(KC_2) // " +#define CH_PAST LSFT(KC_3) // * +#define CH_CELA  LSFT(KC_4) // ç +#define CH_PERC LSFT(KC_5) // % +#define CH_AMPR LSFT(KC_6) // & +#define CH_SLSH LSFT(KC_7) // / +#define CH_LPRN LSFT(KC_8) // ( +#define CH_RPRN LSFT(KC_9) // ) +#define CH_EQL  LSFT(KC_0) // = +#define CH_QST  LSFT(CH_QUOT) // ? +#define CH_GRV  LSFT(CH_CARR) // ` +#define CH_EXLM LSFT(CH_DIER) // ! +#define CH_POND LSFT(CH_DLR) // £ +#define CH_MORE LSFT(CH_LESS) // > +#define CH_COLN LSFT(KC_DOT) // : +#define CH_SCLN LSFT(KC_COMM) // ; +#define CH_UNDS LSFT(CH_MINS) // _ + +// Alt Gr-ed characters +#define CH_BRBR ALGR(KC_1) // ¦ brocken bar +#define CH_AT ALGR(KC_2) // @ +#define CH_HASH ALGR(KC_3) // # +#define CH_NOTL ALGR(KC_6) // ¬ negative logic +#define CH_PIPE ALGR(KC_7) // | +#define CH_CENT ALGR(KC_8) // ¢ cent +#define CH_ACUT ALGR(CH_QUOT) // ´ +#define CH_TILD ALGR(CH_CARR) // ~ +#define CH_EURO ALGR(KC_E) // € +#define CH_LBRC ALGR(CH_UE) // [ +#define CH_RBRC ALGR(CH_DIER) // ] +#define CH_LCBR ALGR(CH_AE) // { +#define CH_RCBR ALGR(CH_DLR) // } +#define CH_BSLS ALGR(CH_LESS) // backslash + +#endif diff --git a/quantum/keymap_extras/keymap_german_osx.h b/quantum/keymap_extras/keymap_german_osx.h new file mode 100644 index 0000000000..798bb75798 --- /dev/null +++ b/quantum/keymap_extras/keymap_german_osx.h @@ -0,0 +1,112 @@ +/* Copyright 2016 Stephen Bösebeck + * + * 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 KEYMAP_GERMAN_OSX +#define KEYMAP_GERMAN_OSX + +#include "keymap.h" + +// Alt gr + +// normal characters +#define DE_OSX_Z KC_Y +#define DE_OSX_Y KC_Z + +#define DE_OSX_A KC_A +#define DE_OSX_B KC_B +#define DE_OSX_C KC_C +#define DE_OSX_D KC_D +#define DE_OSX_E KC_E +#define DE_OSX_F KC_F +#define DE_OSX_G KC_G +#define DE_OSX_H KC_H +#define DE_OSX_I KC_I +#define DE_OSX_J KC_J +#define DE_OSX_K KC_K +#define DE_OSX_L KC_L +#define DE_OSX_M KC_M +#define DE_OSX_N KC_N +#define DE_OSX_O KC_O +#define DE_OSX_P KC_P +#define DE_OSX_Q KC_Q +#define DE_OSX_R KC_R +#define DE_OSX_S KC_S +#define DE_OSX_T KC_T +#define DE_OSX_U KC_U +#define DE_OSX_V KC_V +#define DE_OSX_W KC_W +#define DE_OSX_X KC_X + +#define DE_OSX_0 KC_0 +#define DE_OSX_1 KC_1 +#define DE_OSX_2 KC_2 +#define DE_OSX_3 KC_3 +#define DE_OSX_4 KC_4 +#define DE_OSX_5 KC_5 +#define DE_OSX_6 KC_6 +#define DE_OSX_7 KC_7 +#define DE_OSX_8 KC_8 +#define DE_OSX_9 KC_9 + +#define DE_OSX_DOT KC_DOT +#define DE_OSX_COMM KC_COMM + +#define DE_OSX_SS KC_MINS +#define DE_OSX_AE KC_QUOT +#define DE_OSX_UE KC_LBRC +#define DE_OSX_OE KC_SCLN + +#define DE_OSX_CIRC KC_NUBS // accent circumflex ^ and ring ° +#define DE_OSX_ACUT KC_EQL // accent acute ´ and grave ` +#define DE_OSX_PLUS KC_RBRC // + and * and ~ +#define DE_OSX_HASH KC_BSLS // # and ' +#define DE_OSX_LESS KC_GRV // < and > and | +#define DE_OSX_MINS KC_SLSH // - and _ + +// shifted characters +#define DE_OSX_RING LSFT(DE_OSX_CIRC) // ° +#define DE_OSX_EXLM LSFT(KC_1) // ! +#define DE_OSX_DQOT LSFT(KC_2) // " +#define DE_OSX_PARA LSFT(KC_3) // § +#define DE_OSX_DLR  LSFT(KC_4) // $ +#define DE_OSX_PERC LSFT(KC_5) // % +#define DE_OSX_AMPR LSFT(KC_6) // & +#define DE_OSX_SLSH LSFT(KC_7) // / +#define DE_OSX_LPRN LSFT(KC_8) // ( +#define DE_OSX_RPRN LSFT(KC_9) // ) +#define DE_OSX_EQL  LSFT(KC_0) // = +#define DE_OSX_QST  LSFT(DE_OSX_SS) // ? +#define DE_OSX_GRV  LSFT(DE_OSX_ACUT) // ` +#define DE_OSX_ASTR LSFT(DE_OSX_PLUS) // * +#define DE_OSX_QUOT LSFT(DE_OSX_HASH) // ' +#define DE_OSX_MORE LSFT(DE_OSX_LESS) // > +#define DE_OSX_COLN LSFT(KC_DOT) // : +#define DE_OSX_SCLN LSFT(KC_COMM) // ; +#define DE_OSX_UNDS LSFT(DE_OSX_MINS) // _ + +// Alt-ed characters +//#define DE_OSX_SQ2 LALT(KC_2) // ² +//#define DE_OSX_SQ3 LALT(KC_3) // ³ +#define DE_OSX_LCBR LALT(KC_8) // { +#define DE_OSX_LBRC LALT(KC_5) // [ +#define DE_OSX_RBRC LALT(KC_6) // ] +#define DE_OSX_RCBR LALT(KC_9) // } +#define DE_OSX_BSLS LALT(LSFT(KC_7)) // backslash +#define DE_OSX_AT  LALT(DE_OSX_L) // @ +#define DE_OSX_EURO LALT(KC_E) // € +#define DE_OSX_TILD LALT(DE_OSX_N) // ~ +#define DE_OSX_PIPE LALT(DE_OSX_7) // | + +#endif diff --git a/quantum/keymap_extras/keymap_jp.h b/quantum/keymap_extras/keymap_jp.h new file mode 100644 index 0000000000..fb74bce8d4 --- /dev/null +++ b/quantum/keymap_extras/keymap_jp.h @@ -0,0 +1,77 @@ +/* Copyright 2016 h-youhei + * + * 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/>. + * + * JP106-layout (Japanese Standard) + * + * For more information, see + * http://www2d.biglobe.ne.jp/~msyk/keyboard/layout/usbkeycode.html + * note: This website is written in Japanese. + */ + + +#ifndef KEYMAP_JP_H +#define KEYMAP_JP_H + + +#include "keymap.h" + + +#define JP_ZHTG KC_GRV  // hankaku/zenkaku|kanzi +#define JP_YEN  KC_INT3 // yen, | +#define JP_CIRC KC_EQL  // ^, ~ +#define JP_AT   KC_LBRC // @, ` +#define JP_LBRC KC_RBRC // [, { +#define JP_COLN KC_QUOT // :, * +#define JP_RBRC KC_NUHS // ], } +#define JP_BSLS KC_INT1 // \, _ +#define JP_MHEN KC_INT5 // muhenkan +#define JP_HENK KC_INT4 // henkan +#define JP_KANA KC_INT2 // katakana/hiragana|ro-mazi + + +//Aliases for shifted symbols +#define JP_DQT  LSFT(KC_2)    // " +#define JP_AMPR LSFT(KC_6)    // & +#define JP_QUOT LSFT(KC_7)    // ' +#define JP_LPRN LSFT(KC_8)    // ( +#define JP_RPRN LSFT(KC_9)    // ) +#define JP_EQL  LSFT(KC_MINS) // = +#define JP_TILD LSFT(JP_CIRC) // ~ +#define JP_PIPE LSFT(JP_YEN)  // | +#define JP_GRV  LSFT(JP_AT)   // ` +#define JP_LCBR LSFT(JP_LBRC) // { +#define JP_PLUS LSFT(KC_SCLN) // + +#define JP_ASTR LSFT(JP_COLN) // * +#define JP_RCBR LSFT(JP_RBRC) // } +#define JP_UNDS LSFT(JP_BSLS) // _ + + +// These symbols are correspond to US101-layout. +#define JP_MINS KC_MINS // - +#define JP_SCLN KC_SCLN // ; +#define JP_COMM KC_COMM // , +#define JP_DOT  KC_DOT  // . +#define JP_SLSH KC_SLSH // / +// shifted +#define JP_EXLM KC_EXLM // ! +#define JP_HASH KC_HASH // # +#define JP_DLR  KC_DLR  // $ +#define JP_PERC KC_PERC // % +#define JP_LT   KC_LT   // < +#define JP_GT   KC_GT   // > +#define JP_QUES KC_QUES // ? + + +#endif diff --git a/quantum/keymap_extras/keymap_neo2.h b/quantum/keymap_extras/keymap_neo2.h new file mode 100644 index 0000000000..174f4a6eec --- /dev/null +++ b/quantum/keymap_extras/keymap_neo2.h @@ -0,0 +1,78 @@ +/* Copyright 2016 Matthias Schmitt + * + * 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 KEYMAP_NEO2 +#define KEYMAP_NEO2 + +#include "keymap.h" +#include "keymap_german.h" + +#define NEO_A KC_D +#define NEO_B KC_N +#define NEO_C KC_R +#define NEO_D DE_OE +#define NEO_E KC_F +#define NEO_F KC_O +#define NEO_G KC_I +#define NEO_H KC_U +#define NEO_I KC_S +#define NEO_J DE_MINS +#define NEO_K DE_Z +#define NEO_L KC_E +#define NEO_M KC_M +#define NEO_N KC_J +#define NEO_O KC_G +#define NEO_P KC_V +#define NEO_Q KC_P +#define NEO_R KC_K +#define NEO_S KC_H +#define NEO_T KC_L +#define NEO_U KC_A +#define NEO_V KC_W +#define NEO_W KC_T +#define NEO_X KC_Q +#define NEO_Y DE_AE +#define NEO_Z KC_B +#define NEO_AE KC_C +#define NEO_OE KC_X +#define NEO_UE DE_Y +#define NEO_SS DE_UE + +#define NEO_DOT DE_DOT +#define NEO_COMM DE_COMM + +#define NEO_1 DE_1 +#define NEO_2 DE_2 +#define NEO_3 DE_3 +#define NEO_4 DE_4 +#define NEO_5 DE_5 +#define NEO_6 DE_6 +#define NEO_7 DE_7 +#define NEO_8 DE_8 +#define NEO_9 DE_9 +#define NEO_0 DE_0 +#define NEO_MINS DE_SS + +#define NEO_ACUT DE_PLUS +#define NEO_GRV DE_ACUT +#define NEO_CIRC DE_CIRC + +#define NEO_L1_L KC_CAPS +#define NEO_L1_R DE_HASH + +#define NEO_L2_L DE_LESS +#define NEO_L2_R DE_ALGR + +#endif diff --git a/quantum/keymap_extras/keymap_nordic.h b/quantum/keymap_extras/keymap_nordic.h new file mode 100644 index 0000000000..6b34db5588 --- /dev/null +++ b/quantum/keymap_extras/keymap_nordic.h @@ -0,0 +1,74 @@ +/* Copyright 2015-2016 Jack Humbert + * + * 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 KEYMAP_NORDIC_H +#define KEYMAP_NORDIC_H + +#include "keymap.h" + +// Alt gr +#define ALGR(kc) RALT(kc) +#define NO_ALGR KC_RALT + +// Normal characters +#define NO_HALF	KC_GRV +#define NO_PLUS	KC_MINS +#define NO_ACUT	KC_EQL + +#define NO_AM	KC_LBRC +#define NO_QUOT	KC_RBRC // this is the "umlaut" char on Nordic keyboards, Apple layout +#define NO_AE	KC_SCLN +#define NO_OSLH	KC_QUOT +#define	NO_APOS	KC_NUHS + +#define NO_LESS	KC_NUBS +#define NO_MINS KC_SLSH + +// Shifted characters +#define NO_SECT LSFT(NO_HALF) +#define NO_QUO2	LSFT(KC_2) +#define NO_BULT LSFT(KC_4) +#define NO_AMPR	LSFT(KC_6) +#define NO_SLSH LSFT(KC_7) +#define NO_LPRN	LSFT(KC_8) +#define NO_RPRN	LSFT(KC_9) +#define NO_EQL	LSFT(KC_0) +#define NO_QUES	LSFT(NO_PLUS) +#define NO_GRV	LSFT(NO_ACUT) + +#define NO_CIRC LSFT(NO_QUOT) + +#define NO_GRTR	LSFT(NO_LESS) +#define NO_SCLN LSFT(KC_COMM) +#define NO_COLN LSFT(KC_DOT) +#define NO_UNDS LSFT(NO_MINS) + +// Alt Gr-ed characters +#define NO_AT	ALGR(KC_2) +#define NO_PND	ALGR(KC_3) +#define NO_DLR	ALGR(KC_4) +#define NO_LCBR ALGR(KC_7) +#define NO_LBRC ALGR(KC_8) +#define NO_RBRC ALGR(KC_9) +#define NO_RCBR	ALGR(KC_0) +#define NO_PIPE ALGR(KC_NUBS) + +#define NO_EURO ALGR(KC_E) +#define NO_TILD ALGR(NO_QUOT) + +#define NO_BSLS ALGR(KC_MINS) +#define NO_MU 	ALGR(KC_M) + +#endif diff --git a/quantum/keymap_extras/keymap_norwegian.h b/quantum/keymap_extras/keymap_norwegian.h new file mode 100644 index 0000000000..b7128973aa --- /dev/null +++ b/quantum/keymap_extras/keymap_norwegian.h @@ -0,0 +1,56 @@ +/* Copyright 2016 Jack Humbert + * + * 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 KEYMAP_NORWEGIAN_H +#define KEYMAP_NORWEGIAN_H + +#include "keymap_nordic.h" + +// There are slight differrences in the keyboards in the nordic contries + +// Norwegian redifinitions from the nordic keyset +#undef NO_ACUT +#define NO_ACUT	ALGR(NO_BSLS)  // ´ +#undef NO_AE +#define NO_AE	KC_QUOT  // æ +#undef NO_BSLS +#define NO_BSLS KC_EQL  // '\' +#undef NO_CIRC +#define NO_CIRC LSFT(KC_RBRC)  // ^ +#undef NO_GRV +#define NO_GRV	LSFT(NO_BSLS)  // +#undef NO_OSLH +#define NO_OSLH	KC_SCLN  // ø +#undef NO_PIPE +#define NO_PIPE KC_GRV  // | + +// Additional norwegian keys not defined in the nordic keyset +#define NO_AA	KC_LBRC  // å +#define NO_ASTR LSFT(KC_BSLS)  // * + +// Norwegian unique MAC characters +#define NO_ACUT_MAC KC_EQL  // = +#define NO_APOS_MAC KC_NUBS  // ' +#define NO_AT_MAC   KC_BSLS  // @ +#define NO_BSLS_MAC ALGR(LSFT(KC_7)) // '\' +#define NO_DLR_MAC  LSFT(KC_4) // $ +#define NO_GRV_MAC ALGR(NO_BSLS) // ` +#define NO_GRTR_MAC LSFT(KC_GRV)  // > +#define NO_LCBR_MAC ALGR(LSFT(KC_8))  // } +#define NO_LESS_MAC KC_GRV  // > +#define NO_PIPE_MAC ALGR(KC_7)  // | +#define NO_RCBR_MAC ALGR(LSFT(KC_9))  // } + +#endif diff --git a/quantum/keymap_extras/keymap_plover.h b/quantum/keymap_extras/keymap_plover.h new file mode 100644 index 0000000000..de6d8c53f3 --- /dev/null +++ b/quantum/keymap_extras/keymap_plover.h @@ -0,0 +1,47 @@ +/* Copyright 2016 James Kay + * + * 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 KEYMAP_PLOVER_H +#define KEYMAP_PLOVER_H + +#include "keymap.h" + +#define PV_NUM  KC_1 +#define PV_LS   KC_Q +#define PV_LT   KC_W +#define PV_LP   KC_E +#define PV_LH   KC_R +#define PV_LK   KC_S +#define PV_LW   KC_D +#define PV_LR   KC_F + +#define PV_STAR KC_Y +#define PV_RF   KC_U +#define PV_RP   KC_I +#define PV_RL   KC_O +#define PV_RT   KC_P +#define PV_RD   KC_LBRC +#define PV_RR   KC_J +#define PV_RB   KC_K +#define PV_RG   KC_L +#define PV_RS   KC_SCLN +#define PV_RZ   KC_QUOT + +#define PV_A    KC_C +#define PV_O    KC_V +#define PV_E    KC_N +#define PV_U    KC_M + +#endif diff --git a/quantum/keymap_extras/keymap_spanish.h b/quantum/keymap_extras/keymap_spanish.h new file mode 100644 index 0000000000..224db7be16 --- /dev/null +++ b/quantum/keymap_extras/keymap_spanish.h @@ -0,0 +1,77 @@ +/* Copyright 2015-2016 Jack Humbert + * + * 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 KEYMAP_SPANISH_H +#define KEYMAP_SPANISH_H + +#include "keymap.h" + +// Alt gr +#define ALGR(kc) RALT(kc) +#define NO_ALGR KC_RALT + +// Normal characters +#define ES_OVRR KC_GRV +#define ES_APOS	KC_MINS +#define ES_IEXL	KC_EQL + +#define ES_GRV	KC_LBRC +#define ES_PLUS	KC_RBRC + +#define ES_NTIL	KC_SCLN +#define ES_ACUT	KC_QUOT +#define ES_CCED	KC_NUHS + +#define ES_LESS	KC_NUBS +#define ES_MINS	KC_SLSH + +// Shifted characters +#define ES_ASML	LSFT(ES_OVRR) +#define ES_QUOT	LSFT(KC_2) +#define ES_OVDT	LSFT(KC_3) +#define ES_AMPR	LSFT(KC_6) +#define ES_SLSH LSFT(KC_7) +#define ES_LPRN LSFT(KC_8) +#define ES_RPRN LSFT(KC_9) +#define ES_EQL	LSFT(KC_0) +#define ES_QUES	LSFT(ES_APOS) +#define ES_IQUE	LSFT(ES_IEXL) + +#define ES_CIRC	LSFT(ES_GRV) +#define ES_ASTR	LSFT(ES_PLUS) + +#define ES_UMLT	LSFT(ES_GRV) + +#define ES_GRTR	LSFT(ES_LESS) +#define ES_SCLN	LSFT(KC_COMM) +#define ES_COLN	LSFT(KC_DOT) +#define ES_UNDS	LSFT(ES_MINS) + +// Alt Gr-ed characters +#define ES_BSLS	ALGR(ES_OVRR) +#define ES_PIPE	ALGR(KC_1) +#define ES_AT 	ALGR(KC_2) +#define ES_HASH	ALGR(KC_3) +#define ES_TILD	ALGR(ES_NTIL) +#define ES_EURO	ALGR(KC_5) +#define ES_NOT	ALGR(KC_6) + +#define ES_LBRC	ALGR(ES_GRV) +#define ES_RBRC ALGR(ES_PLUS) + +#define ES_LCBR	ALGR(ES_ACUT) +#define ES_RCBR	ALGR(ES_CCED) + +#endif diff --git a/quantum/keymap_extras/keymap_swedish.h b/quantum/keymap_extras/keymap_swedish.h new file mode 100644 index 0000000000..dcfad720d0 --- /dev/null +++ b/quantum/keymap_extras/keymap_swedish.h @@ -0,0 +1,52 @@ +/* Copyright 2017 Andreas Lindhé + * + * 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 KEYMAP_SWEDISH_H +#define KEYMAP_SWEDISH_H + +#include "keymap_nordic.h" + +// There are slight differrences in the keyboards in the nordic contries + +// Swedish redifinitions from the nordic keyset +#undef  NO_AE +#define NO_AE   KC_QUOT  // ä +#undef  NO_CIRC +#define NO_CIRC LSFT(KC_RBRC)  // ^ +#undef  NO_GRV +#define NO_GRV  LSFT(NO_BSLS)  // +#undef  NO_OSLH +#define NO_OSLH KC_SCLN  // ö + +// Additional Swedish keys not defined in the nordic keyset +#define NO_AA   KC_LBRC  // å +#define NO_ASTR LSFT(KC_BSLS)  // * + +// Norwegian unique MAC characters (not vetted for Swedish) +#define NO_ACUT_MAC KC_EQL  // = +#define NO_APOS_MAC KC_NUBS  // ' +#define NO_AT_MAC   KC_BSLS  // @ +#define NO_BSLS_MAC ALGR(LSFT(KC_7)) // '\' +#define NO_DLR_MAC  LSFT(KC_4) // $ +#define NO_GRV_MAC ALGR(NO_BSLS) // ` +#define NO_GRTR_MAC LSFT(KC_GRV)  // > +#define NO_LCBR_MAC ALGR(LSFT(KC_8))  // } +#define NO_LESS_MAC KC_GRV  // > +#define NO_PIPE_MAC ALGR(KC_7)  // | +#define NO_RCBR_MAC ALGR(LSFT(KC_9))  // } + +#endif + diff --git a/quantum/keymap_extras/keymap_uk.h b/quantum/keymap_extras/keymap_uk.h new file mode 100644 index 0000000000..9d02efe04a --- /dev/null +++ b/quantum/keymap_extras/keymap_uk.h @@ -0,0 +1,51 @@ +/* Copyright 2015-2016 Jack Humbert + * + * 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 KEYMAP_UK_H +#define KEYMAP_UK_H + +#include "keymap.h" + +// Alt gr +#define ALGR(kc) RALT(kc) +#define NO_ALGR KC_RALT + +// Normal characters +#define UK_HASH KC_NUHS + +#define UK_BSLS	KC_NUBS + +// Shifted characters +#define UK_NOT	LSFT(KC_GRV) +#define UK_QUOT	LSFT(KC_2) +#define UK_PND	LSFT(KC_3) + +#define UK_AT	LSFT(KC_QUOT) +#define UK_TILD	LSFT(KC_NUHS) + +#define UK_PIPE	LSFT(KC_NUBS) + +// Alt Gr-ed characters +#define UK_BRKP	ALGR(KC_GRV) +#define UK_EURO	ALGR(KC_4) + +#define UK_EACT	ALGR(KC_E) +#define UK_UACT	ALGR(KC_U) +#define UK_IACT ALGR(KC_I) +#define UK_OACT ALGR(KC_O) + +#define UK_AACT ALGR(KC_A) + +#endif diff --git a/quantum/keymap_extras/sendstring_colemak.h b/quantum/keymap_extras/sendstring_colemak.h new file mode 100644 index 0000000000..fa9ace9290 --- /dev/null +++ b/quantum/keymap_extras/sendstring_colemak.h @@ -0,0 +1,41 @@ +/* Copyright 2016 Jack Humbert + * + * 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/>. + */ +/* Sendstring definitions for the Colemak layout */ +#ifndef SENDSTRING_COLEMAK +#define SENDSTRING_COLEMAK + +#include "keymap_colemak.h" + +const uint8_t ascii_to_keycode_lut[0x80] PROGMEM = { +    0, 0, 0, 0, 0, 0, 0, 0, +    KC_BSPC, KC_TAB, KC_ENT, 0, 0, 0, 0, 0, +    0, 0, 0, 0, 0, 0, 0, 0, +    0, 0, 0, KC_ESC, 0, 0, 0, 0, +    KC_SPC, KC_1, KC_QUOT, KC_3, KC_4, KC_5, KC_7, KC_QUOT, +    KC_9, KC_0, KC_8, KC_EQL, KC_COMM, KC_MINS, KC_DOT, KC_SLSH, +    KC_0, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, +    KC_8, KC_9, CM_SCLN, CM_SCLN, KC_COMM, KC_EQL, KC_DOT, KC_SLSH, +    KC_2, CM_A, CM_B, CM_C, CM_D, CM_E, CM_F, CM_G, +    CM_H, CM_I, CM_J, CM_K, CM_L, CM_M, CM_N, CM_O, +    CM_P, CM_Q, CM_R, CM_S, CM_T, CM_U, CM_V, CM_W, +    CM_X, CM_Y, CM_Z, KC_LBRC, KC_BSLS, KC_RBRC, KC_6, KC_MINS, +    KC_GRV, CM_A, CM_B, CM_C, CM_D, CM_E, CM_F, CM_G, +    CM_H, CM_I, CM_J, CM_K, CM_L, CM_M, CM_N, CM_O, +    CM_P, CM_Q, CM_R, CM_S, CM_T, CM_U, CM_V, CM_W, +    CM_X, CM_Y, CM_Z, KC_LBRC, KC_BSLS, KC_RBRC, KC_GRV, KC_DEL +}; + +#endif diff --git a/quantum/keymap_extras/sendstring_dvorak.h b/quantum/keymap_extras/sendstring_dvorak.h new file mode 100644 index 0000000000..f5c5c818b8 --- /dev/null +++ b/quantum/keymap_extras/sendstring_dvorak.h @@ -0,0 +1,41 @@ +/* Copyright 2016 Jack Humbert + * + * 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/>. + */ +/* Sendstring definitions for the Dvorak layout */ +#ifndef SENDSTRING_DVORAK +#define SENDSTRING_DVORAK + +#include "keymap_dvorak.h" + +const uint8_t ascii_to_keycode_lut[0x80] PROGMEM = { +    0, 0, 0, 0, 0, 0, 0, 0, +    KC_BSPC, KC_TAB, KC_ENT, 0, 0, 0, 0, 0, +    0, 0, 0, 0, 0, 0, 0, 0, +    0, 0, 0, KC_ESC, 0, 0, 0, 0, +    KC_SPC, DV_1, DV_QUOT, DV_3, DV_4, DV_5, DV_7, DV_QUOT, +    DV_9, DV_0, DV_8, DV_EQL, DV_COMM, DV_MINS, DV_DOT, DV_SLSH, +    DV_0, DV_1, DV_2, DV_3, DV_4, DV_5, DV_6, DV_7, +    DV_8, DV_9, DV_SCLN, DV_SCLN, DV_COMM, DV_EQL, DV_DOT, DV_SLSH, +    DV_2, DV_A, DV_B, DV_C, DV_D, DV_E, DV_F, DV_G, +    DV_H, DV_I, DV_J, DV_K, DV_L, DV_M, DV_N, DV_O, +    DV_P, DV_Q, DV_R, DV_S, DV_T, DV_U, DV_V, DV_W, +    DV_X, DV_Y, DV_Z, DV_LBRC, DV_BSLS, DV_RBRC, DV_6, DV_MINS, +    DV_GRV, DV_A, DV_B, DV_C, DV_D, DV_E, DV_F, DV_G, +    DV_H, DV_I, DV_J, DV_K, DV_L, DV_M, DV_N, DV_O, +    DV_P, DV_Q, DV_R, DV_S, DV_T, DV_U, DV_V, DV_W, +    DV_X, DV_Y, DV_Z, DV_LBRC, DV_BSLS, DV_RBRC, DV_GRV, KC_DEL +}; + +#endif diff --git a/quantum/keymap_extras/sendstring_jis.h b/quantum/keymap_extras/sendstring_jis.h new file mode 100644 index 0000000000..c5a38c6a5b --- /dev/null +++ b/quantum/keymap_extras/sendstring_jis.h @@ -0,0 +1,58 @@ +/* Copyright 2016 Jack Humbert + * + * 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/>. + */ +/* Sendstring definitions for the JIS keyboard layout */ +#ifndef SENDSTRING_JIS +#define SENDSTRING_JIS + +const bool ascii_to_shift_lut[0x80] PROGMEM = { +    0, 0, 0, 0, 0, 0, 0, 0, +    0, 0, 0, 0, 0, 0, 0, 0, +    0, 0, 0, 0, 0, 0, 0, 0, +    0, 0, 0, 0, 0, 0, 0, 0, +    0, 1, 1, 1, 1, 1, 1, 1, +    1, 1, 1, 1, 0, 0, 0, 0, +    0, 0, 0, 0, 0, 0, 0, 0, +    0, 0, 0, 0, 1, 1, 1, 1, +    0, 1, 1, 1, 1, 1, 1, 1, +    1, 1, 1, 1, 1, 1, 1, 1, +    1, 1, 1, 1, 1, 1, 1, 1, +    1, 1, 1, 0, 0, 0, 0, 1, +    1, 0, 0, 0, 0, 0, 0, 0, +    0, 0, 0, 0, 0, 0, 0, 0, +    0, 0, 0, 0, 0, 0, 0, 0, +    0, 0, 0, 1, 1, 1, 1, 0 +}; + +const uint8_t ascii_to_keycode_lut[0x80] PROGMEM = { +    0, 0, 0, 0, 0, 0, 0, 0, +    KC_BSPC, KC_TAB, KC_ENT, 0, 0, 0, 0, 0, +    0, 0, 0, 0, 0, 0, 0, 0, +    0, 0, 0, KC_ESC, 0, 0, 0, 0, +    KC_SPC, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, +    KC_8, KC_9, KC_QUOT, KC_SCLN, KC_COMM, KC_MINS, KC_DOT, KC_SLSH, +    KC_0, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, +    KC_8, KC_9, KC_QUOT, KC_SCLN, KC_COMM, KC_MINS, KC_DOT, KC_SLSH, +    KC_LBRC, KC_A, KC_B, KC_C, KC_D, KC_E, KC_F, KC_G, +    KC_H, KC_I, KC_J, KC_K, KC_L, KC_M, KC_N, KC_O, +    KC_P, KC_Q, KC_R, KC_S, KC_T, KC_U, KC_V, KC_W, +    KC_X, KC_Y, KC_Z, KC_RBRC, KC_JYEN, KC_BSLS, KC_EQL, KC_RO, +    KC_LBRC, KC_A, KC_B, KC_C, KC_D, KC_E, KC_F, KC_G, +    KC_H, KC_I, KC_J, KC_K, KC_L, KC_M, KC_N, KC_O, +    KC_P, KC_Q, KC_R, KC_S, KC_T, KC_U, KC_V, KC_W, +    KC_X, KC_Y, KC_Z, KC_RBRC, KC_JYEN, KC_BSLS, KC_EQL, KC_DEL, +}; + +#endif diff --git a/quantum/led_tables.c b/quantum/led_tables.c new file mode 100644 index 0000000000..b99f262097 --- /dev/null +++ b/quantum/led_tables.c @@ -0,0 +1,71 @@ +/* +Copyright 2017 Fred Sundvik + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "led_tables.h" + + +#ifdef USE_CIE1931_CURVE +// Lightness curve using the CIE 1931 lightness formula +//Generated by the python script provided in http://jared.geek.nz/2013/feb/linear-led-pwm +const uint8_t CIE1931_CURVE[] PROGMEM = { +    0, 0, 0, 0, 0, 1, 1, 1, 1, 1, +    1, 1, 1, 1, 2, 2, 2, 2, 2, 2, +    2, 2, 2, 3, 3, 3, 3, 3, 3, 3, +    3, 4, 4, 4, 4, 4, 4, 5, 5, 5, +    5, 5, 6, 6, 6, 6, 6, 7, 7, 7, +    7, 8, 8, 8, 8, 9, 9, 9, 10, 10, +    10, 10, 11, 11, 11, 12, 12, 12, 13, 13, +    13, 14, 14, 15, 15, 15, 16, 16, 17, 17, +    17, 18, 18, 19, 19, 20, 20, 21, 21, 22, +    22, 23, 23, 24, 24, 25, 25, 26, 26, 27, +    28, 28, 29, 29, 30, 31, 31, 32, 32, 33, +    34, 34, 35, 36, 37, 37, 38, 39, 39, 40, +    41, 42, 43, 43, 44, 45, 46, 47, 47, 48, +    49, 50, 51, 52, 53, 54, 54, 55, 56, 57, +    58, 59, 60, 61, 62, 63, 64, 65, 66, 67, +    68, 70, 71, 72, 73, 74, 75, 76, 77, 79, +    80, 81, 82, 83, 85, 86, 87, 88, 90, 91, +    92, 94, 95, 96, 98, 99, 100, 102, 103, 105, +    106, 108, 109, 110, 112, 113, 115, 116, 118, 120, +    121, 123, 124, 126, 128, 129, 131, 132, 134, 136, +    138, 139, 141, 143, 145, 146, 148, 150, 152, 154, +    155, 157, 159, 161, 163, 165, 167, 169, 171, 173, +    175, 177, 179, 181, 183, 185, 187, 189, 191, 193, +    196, 198, 200, 202, 204, 207, 209, 211, 214, 216, +    218, 220, 223, 225, 228, 230, 232, 235, 237, 240, +    242, 245, 247, 250, 252, 255, +    }; +#endif + +#ifdef USE_LED_BREATHING_TABLE +const uint8_t LED_BREATHING_TABLE[] PROGMEM = { +  0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 9, +  10, 11, 12, 14, 15, 17, 18, 20, 21, 23, 25, 27, 29, 31, 33, 35, +  37, 40, 42, 44, 47, 49, 52, 54, 57, 59, 62, 65, 67, 70, 73, 76, +  79, 82, 85, 88, 90, 93, 97, 100, 103, 106, 109, 112, 115, 118, 121, 124, +  127, 131, 134, 137, 140, 143, 146, 149, 152, 155, 158, 162, 165, 167, 170, 173, +  176, 179, 182, 185, 188, 190, 193, 196, 198, 201, 203, 206, 208, 211, 213, 215, +  218, 220, 222, 224, 226, 228, 230, 232, 234, 235, 237, 238, 240, 241, 243, 244, +  245, 246, 248, 249, 250, 250, 251, 252, 253, 253, 254, 254, 254, 255, 255, 255, +  255, 255, 255, 255, 254, 254, 254, 253, 253, 252, 251, 250, 250, 249, 248, 246, +  245, 244, 243, 241, 240, 238, 237, 235, 234, 232, 230, 228, 226, 224, 222, 220, +  218, 215, 213, 211, 208, 206, 203, 201, 198, 196, 193, 190, 188, 185, 182, 179, +  176, 173, 170, 167, 165, 162, 158, 155, 152, 149, 146, 143, 140, 137, 134, 131, +  128, 124, 121, 118, 115, 112, 109, 106, 103, 100, 97, 93, 90, 88, 85, 82, +  79, 76, 73, 70, 67, 65, 62, 59, 57, 54, 52, 49, 47, 44, 42, 40, +  37, 35, 33, 31, 29, 27, 25, 23, 21, 20, 18, 17, 15, 14, 12, 11, +  10, 9, 7, 6, 5, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0 +}; +#endif diff --git a/quantum/led_tables.h b/quantum/led_tables.h new file mode 100644 index 0000000000..af49bf3323 --- /dev/null +++ b/quantum/led_tables.h @@ -0,0 +1,30 @@ +/* +Copyright 2017 Fred Sundvik + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef LED_TABLES_H +#define LED_TABLES_H + +#include "progmem.h" +#include <stdint.h> + +#ifdef USE_CIE1931_CURVE +extern const uint8_t CIE1931_CURVE[] PROGMEM; +#endif + +#ifdef USE_LED_BREATHING_TABLE +extern const uint8_t LED_BREATHING_TABLE[] PROGMEM; +#endif + +#endif diff --git a/quantum/light_ws2812.c b/quantum/light_ws2812.c new file mode 100755 index 0000000000..2506e3d8ec --- /dev/null +++ b/quantum/light_ws2812.c @@ -0,0 +1,342 @@ +/* +* light weight WS2812 lib V2.0b +* +* Controls WS2811/WS2812/WS2812B RGB-LEDs +* Author: Tim (cpldcpu@gmail.com) +* +* Jan 18th, 2014  v2.0b Initial Version +* Nov 29th, 2015  v2.3  Added SK6812RGBW support +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "light_ws2812.h" +#include <avr/interrupt.h> +#include <avr/io.h> +#include <util/delay.h> +#include "debug.h" + +#ifdef RGBW_BB_TWI + +// Port for the I2C +#define I2C_DDR DDRD +#define I2C_PIN PIND +#define I2C_PORT PORTD + +// Pins to be used in the bit banging +#define I2C_CLK 0 +#define I2C_DAT 1 + +#define I2C_DATA_HI()\ +I2C_DDR &= ~ (1 << I2C_DAT);\ +I2C_PORT |= (1 << I2C_DAT); +#define I2C_DATA_LO()\ +I2C_DDR |= (1 << I2C_DAT);\ +I2C_PORT &= ~ (1 << I2C_DAT); + +#define I2C_CLOCK_HI()\ +I2C_DDR &= ~ (1 << I2C_CLK);\ +I2C_PORT |= (1 << I2C_CLK); +#define I2C_CLOCK_LO()\ +I2C_DDR |= (1 << I2C_CLK);\ +I2C_PORT &= ~ (1 << I2C_CLK); + +#define I2C_DELAY 1 + +void I2C_WriteBit(unsigned char c) +{ +    if (c > 0) +    { +        I2C_DATA_HI(); +    } +    else +    { +        I2C_DATA_LO(); +    } + +    I2C_CLOCK_HI(); +    _delay_us(I2C_DELAY); + +    I2C_CLOCK_LO(); +    _delay_us(I2C_DELAY); + +    if (c > 0) +    { +        I2C_DATA_LO(); +    } + +    _delay_us(I2C_DELAY); +} + +// Inits bitbanging port, must be called before using the functions below +// +void I2C_Init(void) +{ +    I2C_PORT &= ~ ((1 << I2C_DAT) | (1 << I2C_CLK)); + +    I2C_CLOCK_HI(); +    I2C_DATA_HI(); + +    _delay_us(I2C_DELAY); +} + +// Send a START Condition +// +void I2C_Start(void) +{ +    // set both to high at the same time +    I2C_DDR &= ~ ((1 << I2C_DAT) | (1 << I2C_CLK)); +    _delay_us(I2C_DELAY); + +    I2C_DATA_LO(); +    _delay_us(I2C_DELAY); + +    I2C_CLOCK_LO(); +    _delay_us(I2C_DELAY); +} + +// Send a STOP Condition +// +void I2C_Stop(void) +{ +    I2C_CLOCK_HI(); +    _delay_us(I2C_DELAY); + +    I2C_DATA_HI(); +    _delay_us(I2C_DELAY); +} + +// write a byte to the I2C slave device +// +unsigned char I2C_Write(unsigned char c) +{ +    for (char i = 0; i < 8; i++) +    { +        I2C_WriteBit(c & 128); + +        c <<= 1; +    } + +     +    I2C_WriteBit(0); +    _delay_us(I2C_DELAY); +    _delay_us(I2C_DELAY); +   +    // _delay_us(I2C_DELAY); +    //return I2C_ReadBit(); +    return 0; +} + + +#endif + +// Setleds for standard RGB +void inline ws2812_setleds(LED_TYPE *ledarray, uint16_t leds) +{ +   // ws2812_setleds_pin(ledarray,leds, _BV(ws2812_pin)); +   ws2812_setleds_pin(ledarray,leds, _BV(RGB_DI_PIN & 0xF)); +} + +void inline ws2812_setleds_pin(LED_TYPE *ledarray, uint16_t leds, uint8_t pinmask) +{ +  // ws2812_DDRREG |= pinmask; // Enable DDR +  // new universal format (DDR) +  _SFR_IO8((RGB_DI_PIN >> 4) + 1) |= pinmask; + +  ws2812_sendarray_mask((uint8_t*)ledarray,leds+leds+leds,pinmask); +  _delay_us(50); +} + +// Setleds for SK6812RGBW +void inline ws2812_setleds_rgbw(LED_TYPE *ledarray, uint16_t leds) +{ + +  #ifdef RGBW_BB_TWI +    uint8_t sreg_prev, twcr_prev; +    sreg_prev=SREG; +    twcr_prev=TWCR; +    cli(); +    TWCR &= ~(1<<TWEN); +    I2C_Init(); +    I2C_Start(); +    I2C_Write(0x84); +    uint16_t datlen = leds<<2; +    uint8_t curbyte; +    uint8_t * data = (uint8_t*)ledarray; +    while (datlen--) { +      curbyte=*data++; +      I2C_Write(curbyte); +    } +    I2C_Stop(); +    SREG=sreg_prev; +    TWCR=twcr_prev; +  #endif + + +  // ws2812_DDRREG |= _BV(ws2812_pin); // Enable DDR +  // new universal format (DDR) +  _SFR_IO8((RGB_DI_PIN >> 4) + 1) |= _BV(RGB_DI_PIN & 0xF); + +  ws2812_sendarray_mask((uint8_t*)ledarray,leds<<2,_BV(RGB_DI_PIN & 0xF)); + + +  #ifndef RGBW_BB_TWI +    _delay_us(80); +  #endif +} + +void ws2812_sendarray(uint8_t *data,uint16_t datlen) +{ +  ws2812_sendarray_mask(data,datlen,_BV(RGB_DI_PIN & 0xF)); +} + +/* +  This routine writes an array of bytes with RGB values to the Dataout pin +  using the fast 800kHz clockless WS2811/2812 protocol. +*/ + +// Timing in ns +#define w_zeropulse   350 +#define w_onepulse    900 +#define w_totalperiod 1250 + +// Fixed cycles used by the inner loop +#define w_fixedlow    2 +#define w_fixedhigh   4 +#define w_fixedtotal  8 + +// Insert NOPs to match the timing, if possible +#define w_zerocycles    (((F_CPU/1000)*w_zeropulse          )/1000000) +#define w_onecycles     (((F_CPU/1000)*w_onepulse    +500000)/1000000) +#define w_totalcycles   (((F_CPU/1000)*w_totalperiod +500000)/1000000) + +// w1 - nops between rising edge and falling edge - low +#define w1 (w_zerocycles-w_fixedlow) +// w2   nops between fe low and fe high +#define w2 (w_onecycles-w_fixedhigh-w1) +// w3   nops to complete loop +#define w3 (w_totalcycles-w_fixedtotal-w1-w2) + +#if w1>0 +  #define w1_nops w1 +#else +  #define w1_nops  0 +#endif + +// The only critical timing parameter is the minimum pulse length of the "0" +// Warn or throw error if this timing can not be met with current F_CPU settings. +#define w_lowtime ((w1_nops+w_fixedlow)*1000000)/(F_CPU/1000) +#if w_lowtime>550 +   #error "Light_ws2812: Sorry, the clock speed is too low. Did you set F_CPU correctly?" +#elif w_lowtime>450 +   #warning "Light_ws2812: The timing is critical and may only work on WS2812B, not on WS2812(S)." +   #warning "Please consider a higher clockspeed, if possible" +#endif + +#if w2>0 +#define w2_nops w2 +#else +#define w2_nops  0 +#endif + +#if w3>0 +#define w3_nops w3 +#else +#define w3_nops  0 +#endif + +#define w_nop1  "nop      \n\t" +#define w_nop2  "rjmp .+0 \n\t" +#define w_nop4  w_nop2 w_nop2 +#define w_nop8  w_nop4 w_nop4 +#define w_nop16 w_nop8 w_nop8 + +void inline ws2812_sendarray_mask(uint8_t *data,uint16_t datlen,uint8_t maskhi) +{ +  uint8_t curbyte,ctr,masklo; +  uint8_t sreg_prev; + +  // masklo  =~maskhi&ws2812_PORTREG; +  // maskhi |=        ws2812_PORTREG; +  masklo  =~maskhi&_SFR_IO8((RGB_DI_PIN >> 4) + 2); +  maskhi |=        _SFR_IO8((RGB_DI_PIN >> 4) + 2); +  sreg_prev=SREG; +  cli(); + +  while (datlen--) { +    curbyte=(*data++); + +    asm volatile( +    "       ldi   %0,8  \n\t" +    "loop%=:            \n\t" +    "       out   %2,%3 \n\t"    //  '1' [01] '0' [01] - re +#if (w1_nops&1) +w_nop1 +#endif +#if (w1_nops&2) +w_nop2 +#endif +#if (w1_nops&4) +w_nop4 +#endif +#if (w1_nops&8) +w_nop8 +#endif +#if (w1_nops&16) +w_nop16 +#endif +    "       sbrs  %1,7  \n\t"    //  '1' [03] '0' [02] +    "       out   %2,%4 \n\t"    //  '1' [--] '0' [03] - fe-low +    "       lsl   %1    \n\t"    //  '1' [04] '0' [04] +#if (w2_nops&1) +  w_nop1 +#endif +#if (w2_nops&2) +  w_nop2 +#endif +#if (w2_nops&4) +  w_nop4 +#endif +#if (w2_nops&8) +  w_nop8 +#endif +#if (w2_nops&16) +  w_nop16 +#endif +    "       out   %2,%4 \n\t"    //  '1' [+1] '0' [+1] - fe-high +#if (w3_nops&1) +w_nop1 +#endif +#if (w3_nops&2) +w_nop2 +#endif +#if (w3_nops&4) +w_nop4 +#endif +#if (w3_nops&8) +w_nop8 +#endif +#if (w3_nops&16) +w_nop16 +#endif + +    "       dec   %0    \n\t"    //  '1' [+2] '0' [+2] +    "       brne  loop%=\n\t"    //  '1' [+3] '0' [+4] +    :	"=&d" (ctr) +    :	"r" (curbyte), "I" (_SFR_IO_ADDR(_SFR_IO8((RGB_DI_PIN >> 4) + 2))), "r" (maskhi), "r" (masklo) +    ); +  } + +  SREG=sreg_prev; +} diff --git a/quantum/light_ws2812.h b/quantum/light_ws2812.h new file mode 100755 index 0000000000..60924a0fb6 --- /dev/null +++ b/quantum/light_ws2812.h @@ -0,0 +1,91 @@ +/* + * light weight WS2812 lib include + * + * Version 2.3  - Nev 29th 2015 + * Author: Tim (cpldcpu@gmail.com) + * + * Please do not change this file! All configuration is handled in "ws2812_config.h" + * + * 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 LIGHT_WS2812_H_ +#define LIGHT_WS2812_H_ + +#include <avr/io.h> +#include <avr/interrupt.h> +//#include "ws2812_config.h" +//#include "i2cmaster.h" + +#ifdef RGBW +  #define LED_TYPE struct cRGBW +#else +  #define LED_TYPE struct cRGB +#endif + + +/* + *  Structure of the LED array + * + * cRGB:     RGB  for WS2812S/B/C/D, SK6812, SK6812Mini, SK6812WWA, APA104, APA106 + * cRGBW:    RGBW for SK6812RGBW + */ + +struct cRGB  { uint8_t g; uint8_t r; uint8_t b; }; +struct cRGBW { uint8_t g; uint8_t r; uint8_t b; uint8_t w;}; + + + +/* User Interface + * + * Input: + *         ledarray:           An array of GRB data describing the LED colors + *         number_of_leds:     The number of LEDs to write + *         pinmask (optional): Bitmask describing the output bin. e.g. _BV(PB0) + * + * The functions will perform the following actions: + *         - Set the data-out pin as output + *         - Send out the LED data + *         - Wait 50�s to reset the LEDs + */ + +void ws2812_setleds     (LED_TYPE *ledarray, uint16_t number_of_leds); +void ws2812_setleds_pin (LED_TYPE *ledarray, uint16_t number_of_leds,uint8_t pinmask); +void ws2812_setleds_rgbw(LED_TYPE *ledarray, uint16_t number_of_leds); + +/* + * Old interface / Internal functions + * + * The functions take a byte-array and send to the data output as WS2812 bitstream. + * The length is the number of bytes to send - three per LED. + */ + +void ws2812_sendarray     (uint8_t *array,uint16_t length); +void ws2812_sendarray_mask(uint8_t *array,uint16_t length, uint8_t pinmask); + + +/* + * Internal defines + */ +#ifndef CONCAT +#define CONCAT(a, b)            a ## b +#endif +#ifndef CONCAT_EXP +#define CONCAT_EXP(a, b)   CONCAT(a, b) +#endif + +// #define ws2812_PORTREG  CONCAT_EXP(PORT,ws2812_port) +// #define ws2812_DDRREG   CONCAT_EXP(DDR,ws2812_port) + +#endif /* LIGHT_WS2812_H_ */ diff --git a/quantum/matrix.c b/quantum/matrix.c new file mode 100644 index 0000000000..5337e2626b --- /dev/null +++ b/quantum/matrix.c @@ -0,0 +1,412 @@ +/* +Copyright 2012-2017 Jun Wako, Jack Humbert + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ +#include <stdint.h> +#include <stdbool.h> +#if defined(__AVR__) +#include <avr/io.h> +#endif +#include "wait.h" +#include "print.h" +#include "debug.h" +#include "util.h" +#include "matrix.h" +#include "timer.h" + + +/* Set 0 if debouncing isn't needed */ + +#ifndef DEBOUNCING_DELAY +#   define DEBOUNCING_DELAY 5 +#endif + +#if (DEBOUNCING_DELAY > 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) +#elif (MATRIX_COLS <= 16) +#    define print_matrix_header()  print("\nr/c 0123456789ABCDEF\n") +#    define print_matrix_row(row)  print_bin_reverse16(matrix_get_row(row)) +#    define matrix_bitpop(i)       bitpop16(matrix[i]) +#    define ROW_SHIFTER ((uint16_t)1) +#elif (MATRIX_COLS <= 32) +#    define print_matrix_header()  print("\nr/c 0123456789ABCDEF0123456789ABCDEF\n") +#    define print_matrix_row(row)  print_bin_reverse32(matrix_get_row(row)) +#    define matrix_bitpop(i)       bitpop32(matrix[i]) +#    define ROW_SHIFTER  ((uint32_t)1) +#endif + +#ifdef MATRIX_MASKED +    extern const matrix_row_t matrix_mask[]; +#endif + +#if (DIODE_DIRECTION == ROW2COL) || (DIODE_DIRECTION == COL2ROW) +static const uint8_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS; +static const uint8_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS; +#endif + +/* 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_quantum(void) { +    matrix_init_kb(); +} + +__attribute__ ((weak)) +void matrix_scan_quantum(void) { +    matrix_scan_kb(); +} + +__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_power_up(void) { +// #if (DIODE_DIRECTION == COL2ROW) +//     for (int8_t r = MATRIX_ROWS - 1; r >= 0; --r) { +//         /* DDRxn */ +//         _SFR_IO8((row_pins[r] >> 4) + 1) |= _BV(row_pins[r] & 0xF); +//         toggle_row(r); +//     } +//     for (int8_t c = MATRIX_COLS - 1; c >= 0; --c) { +//         /* PORTxn */ +//         _SFR_IO8((col_pins[c] >> 4) + 2) |= _BV(col_pins[c] & 0xF); +//     } +// #elif (DIODE_DIRECTION == ROW2COL) +//     for (int8_t c = MATRIX_COLS - 1; c >= 0; --c) { +//         /* DDRxn */ +//         _SFR_IO8((col_pins[c] >> 4) + 1) |= _BV(col_pins[c] & 0xF); +//         toggle_col(c); +//     } +//     for (int8_t r = MATRIX_ROWS - 1; r >= 0; --r) { +//         /* PORTxn */ +//         _SFR_IO8((row_pins[r] >> 4) + 2) |= _BV(row_pins[r] & 0xF); +//     } +// #endif +// } + +void matrix_init(void) { + +    // To use PORTF disable JTAG with writing JTD bit twice within four cycles. +    #if  (defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__) || defined(__AVR_ATmega32U4__)) +        MCUCR |= _BV(JTD); +        MCUCR |= _BV(JTD); +    #endif + +    // initialize row and col +#if (DIODE_DIRECTION == COL2ROW) +    unselect_rows(); +    init_cols(); +#elif (DIODE_DIRECTION == ROW2COL) +    unselect_cols(); +    init_rows(); +#endif + +    // 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) +{ + +#if (DIODE_DIRECTION == COL2ROW) + +    // Set row, read cols +    for (uint8_t current_row = 0; current_row < MATRIX_ROWS; current_row++) { +#       if (DEBOUNCING_DELAY > 0) +            bool matrix_changed = read_cols_on_row(matrix_debouncing, current_row); + +            if (matrix_changed) { +                debouncing = true; +                debouncing_time = timer_read(); +            } + +#       else +            read_cols_on_row(matrix, current_row); +#       endif + +    } + +#elif (DIODE_DIRECTION == ROW2COL) + +    // Set col, read rows +    for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++) { +#       if (DEBOUNCING_DELAY > 0) +            bool matrix_changed = read_rows_on_col(matrix_debouncing, current_col); +            if (matrix_changed) { +                debouncing = true; +                debouncing_time = timer_read(); +            } +#       else +             read_rows_on_col(matrix, current_col); +#       endif + +    } + +#endif + +#   if (DEBOUNCING_DELAY > 0) +        if (debouncing && (timer_elapsed(debouncing_time) > DEBOUNCING_DELAY)) { +            for (uint8_t i = 0; i < MATRIX_ROWS; i++) { +                matrix[i] = matrix_debouncing[i]; +            } +            debouncing = false; +        } +#   endif + +    matrix_scan_quantum(); +    return 1; +} + +bool matrix_is_modified(void) +{ +#if (DEBOUNCING_DELAY > 0) +    if (debouncing) return false; +#endif +    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) +{ +    // Matrix mask lets you disable switches in the returned matrix data. For example, if you have a +    // switch blocker installed and the switch is always pressed. +#ifdef MATRIX_MASKED +    return matrix[row] & matrix_mask[row]; +#else +    return matrix[row]; +#endif +} + +void matrix_print(void) +{ +    print_matrix_header(); + +    for (uint8_t row = 0; row < MATRIX_ROWS; row++) { +        phex(row); print(": "); +        print_matrix_row(row); +        print("\n"); +    } +} + +uint8_t matrix_key_count(void) +{ +    uint8_t count = 0; +    for (uint8_t i = 0; i < MATRIX_ROWS; i++) { +        count += matrix_bitpop(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 < MATRIX_ROWS; 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 < MATRIX_ROWS; 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 < MATRIX_ROWS; 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 diff --git a/quantum/pincontrol.h b/quantum/pincontrol.h new file mode 100644 index 0000000000..d77977ebe2 --- /dev/null +++ b/quantum/pincontrol.h @@ -0,0 +1,52 @@ +/* Copyright 2016 Wez Furlong + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ +#pragma once +// Some helpers for controlling gpio pins +#include <avr/io.h> + +enum { +  PinDirectionInput = 0, +  PinDirectionOutput = 1, +  PinLevelHigh = 1, +  PinLevelLow = 0, +}; + +// ex: pinMode(B0, PinDirectionOutput); +static inline void pinMode(uint8_t pin, int mode) { +  uint8_t bv = _BV(pin & 0xf); +  if (mode == PinDirectionOutput) { +    _SFR_IO8((pin >> 4) + 1) |= bv; +  } else { +    _SFR_IO8((pin >> 4) + 1) &= ~bv; +    _SFR_IO8((pin >> 4) + 2) &= ~bv; +  } +} + +// ex: digitalWrite(B0, PinLevelHigh); +static inline void digitalWrite(uint8_t pin, int mode) { +  uint8_t bv = _BV(pin & 0xf); +  if (mode == PinLevelHigh) { +    _SFR_IO8((pin >> 4) + 2) |= bv; +  } else { +    _SFR_IO8((pin >> 4) + 2) &= ~bv; +  } +} + +// Return true if the pin is HIGH +// digitalRead(B0) +static inline bool digitalRead(uint8_t pin) { +  return _SFR_IO8(pin >> 4) & _BV(pin & 0xf); +} diff --git a/quantum/process_keycode/process_audio.c b/quantum/process_keycode/process_audio.c new file mode 100644 index 0000000000..0b6380ed39 --- /dev/null +++ b/quantum/process_keycode/process_audio.c @@ -0,0 +1,62 @@ +#include "audio.h" +#include "process_audio.h" + +static float compute_freq_for_midi_note(uint8_t note) +{ +    // https://en.wikipedia.org/wiki/MIDI_tuning_standard +    return pow(2.0, (note - 69) / 12.0) * 440.0f; +} + +bool process_audio(uint16_t keycode, keyrecord_t *record) { + +    if (keycode == AU_ON && record->event.pressed) { +      audio_on(); +      return false; +    } + +    if (keycode == AU_OFF && record->event.pressed) { +      audio_off(); +      return false; +    } + +    if (keycode == AU_TOG && record->event.pressed) { +        if (is_audio_on()) +        { +            audio_off(); +        } +        else +        { +            audio_on(); +        } +        return false; +    } + +    if (keycode == MUV_IN && record->event.pressed) { +        voice_iterate(); +        music_scale_user(); +        return false; +    } + +    if (keycode == MUV_DE && record->event.pressed) { +        voice_deiterate(); +        music_scale_user(); +        return false; +    } + +    return true; +} + +void process_audio_noteon(uint8_t note) { +    play_note(compute_freq_for_midi_note(note), 0xF); +} + +void process_audio_noteoff(uint8_t note) { +    stop_note(compute_freq_for_midi_note(note)); +} + +void process_audio_all_notes_off(void) { +    stop_all_notes(); +} + +__attribute__ ((weak)) +void audio_on_user() {}
\ No newline at end of file diff --git a/quantum/process_keycode/process_audio.h b/quantum/process_keycode/process_audio.h new file mode 100644 index 0000000000..7ac15b7330 --- /dev/null +++ b/quantum/process_keycode/process_audio.h @@ -0,0 +1,11 @@ +#ifndef PROCESS_AUDIO_H +#define PROCESS_AUDIO_H + +bool process_audio(uint16_t keycode, keyrecord_t *record); +void process_audio_noteon(uint8_t note); +void process_audio_noteoff(uint8_t note); +void process_audio_all_notes_off(void); + +void audio_on_user(void); + +#endif
\ No newline at end of file diff --git a/quantum/process_keycode/process_chording.c b/quantum/process_keycode/process_chording.c new file mode 100644 index 0000000000..6c6ebe300a --- /dev/null +++ b/quantum/process_keycode/process_chording.c @@ -0,0 +1,76 @@ +/* Copyright 2016 Jack Humbert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include "process_chording.h" + +bool keys_chord(uint8_t keys[]) { +  uint8_t keys_size = sizeof(keys)/sizeof(keys[0]); +  bool pass = true; +  uint8_t in = 0; +  for (uint8_t i = 0; i < chord_key_count; i++) { +    bool found = false; +    for (uint8_t j = 0; j < keys_size; j++) { +      if (chord_keys[i] == (keys[j] & 0xFF)) { +        in++; // detects key in chord +        found = true; +        break; +      } +    } +    if (found) +      continue; +    if (chord_keys[i] != 0)  { +      pass = false; // makes sure rest are blank +    } +  } +  return (pass && (in == keys_size)); +} + +bool process_chording(uint16_t keycode, keyrecord_t *record) { +  if (keycode >= QK_CHORDING && keycode <= QK_CHORDING_MAX) { +    if (record->event.pressed) { +      if (!chording) { +        chording = true; +        for (uint8_t i = 0; i < CHORDING_MAX; i++) +          chord_keys[i] = 0; +        chord_key_count = 0; +        chord_key_down = 0; +      } +      chord_keys[chord_key_count] = (keycode & 0xFF); +      chord_key_count++; +      chord_key_down++; +      return false; +    } else { +      if (chording) { +        chord_key_down--; +        if (chord_key_down == 0) { +          chording = false; +          // Chord Dictionary +          if (keys_chord((uint8_t[]){KC_ENTER, KC_SPACE})) { +            register_code(KC_A); +            unregister_code(KC_A); +            return false; +          } +          for (uint8_t i = 0; i < chord_key_count; i++) { +            register_code(chord_keys[i]); +            unregister_code(chord_keys[i]); +            return false; +          } +        } +      } +    } +  } +  return true; +} diff --git a/quantum/process_keycode/process_chording.h b/quantum/process_keycode/process_chording.h new file mode 100644 index 0000000000..8c0f4862a8 --- /dev/null +++ b/quantum/process_keycode/process_chording.h @@ -0,0 +1,32 @@ +/* Copyright 2016 Jack Humbert + * + * 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 PROCESS_CHORDING_H +#define PROCESS_CHORDING_H + +#include "quantum.h" + +// Chording stuff +#define CHORDING_MAX 4 +bool chording = false; + +uint8_t chord_keys[CHORDING_MAX] = {0}; +uint8_t chord_key_count = 0; +uint8_t chord_key_down = 0; + +bool process_chording(uint16_t keycode, keyrecord_t *record); + +#endif diff --git a/quantum/process_keycode/process_combo.c b/quantum/process_keycode/process_combo.c new file mode 100644 index 0000000000..58d45add22 --- /dev/null +++ b/quantum/process_keycode/process_combo.c @@ -0,0 +1,150 @@ +/* Copyright 2016 Jack Humbert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include "process_combo.h" +#include "print.h" + + +#define COMBO_TIMER_ELAPSED -1 + + +__attribute__ ((weak)) +combo_t key_combos[] = { + +}; + +__attribute__ ((weak)) +void process_combo_event(uint8_t combo_index, bool pressed) { + +} + +static uint8_t current_combo_index = 0; + +static inline void send_combo(uint16_t action, bool pressed) +{ +    if (action) { +        if (pressed) { +            register_code16(action); +        } else { +            unregister_code16(action); +        } +    } else { +        process_combo_event(current_combo_index, pressed); +    } +} + +#define ALL_COMBO_KEYS_ARE_DOWN     (((1<<count)-1) == combo->state) +#define NO_COMBO_KEYS_ARE_DOWN      (0 == combo->state) +#define KEY_STATE_DOWN(key)         do{ combo->state |= (1<<key); } while(0) +#define KEY_STATE_UP(key)           do{ combo->state &= ~(1<<key); } while(0) +static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *record)  +{ +    uint8_t count = 0; +    uint8_t index = -1; +    /* Find index of keycode and number of combo keys */ +    for (const uint16_t *keys = combo->keys; ;++count) { +        uint16_t key = pgm_read_word(&keys[count]); +        if (keycode == key) index = count; +        if (COMBO_END == key) break; +    } + +    /* Return if not a combo key */ +    if (-1 == (int8_t)index) return false; + +    /* The combos timer is used to signal whether the combo is active */ +    bool is_combo_active = COMBO_TIMER_ELAPSED == combo->timer ? false : true; + +    if (record->event.pressed) { +        KEY_STATE_DOWN(index); + +        if (is_combo_active) { +            if (ALL_COMBO_KEYS_ARE_DOWN) { /* Combo was pressed */ +                send_combo(combo->keycode, true); +                combo->timer = COMBO_TIMER_ELAPSED; +            } else { /* Combo key was pressed */ +                combo->timer = timer_read(); +#ifdef COMBO_ALLOW_ACTION_KEYS +                combo->prev_record = *record; +#else +                combo->prev_key = keycode; +#endif +            } +        } +    } else { +        if (ALL_COMBO_KEYS_ARE_DOWN) { /* Combo was released */ +            send_combo(combo->keycode, false); +        } + +        if (is_combo_active) { /* Combo key was tapped */ +#ifdef COMBO_ALLOW_ACTION_KEYS +            record->event.pressed = true; +            process_action(record, store_or_get_action(record->event.pressed, record->event.key)); +            record->event.pressed = false; +            process_action(record, store_or_get_action(record->event.pressed, record->event.key)); +#else +            register_code16(keycode); +            send_keyboard_report(); +            unregister_code16(keycode); +#endif +            combo->timer = 0;             +        } + +        KEY_STATE_UP(index);         +    } + +    if (NO_COMBO_KEYS_ARE_DOWN) { +        combo->timer = 0; +    } + +    return is_combo_active; +} + +bool process_combo(uint16_t keycode, keyrecord_t *record) +{ +    bool is_combo_key = false; + +    for (current_combo_index = 0; current_combo_index < COMBO_COUNT; ++current_combo_index) { +        combo_t *combo = &key_combos[current_combo_index]; +        is_combo_key |= process_single_combo(combo, keycode, record); +    }     + +    return !is_combo_key; +} + +void matrix_scan_combo(void) +{ +    for (int i = 0; i < COMBO_COUNT; ++i) { +        combo_t *combo = &key_combos[i]; +        if (combo->timer &&  +            combo->timer != COMBO_TIMER_ELAPSED &&  +            timer_elapsed(combo->timer) > COMBO_TERM) { +             +            /* This disables the combo, meaning key events for this +             * combo will be handled by the next processors in the chain  +             */ +            combo->timer = COMBO_TIMER_ELAPSED; + +#ifdef COMBO_ALLOW_ACTION_KEYS +            process_action(&combo->prev_record,  +                store_or_get_action(combo->prev_record.event.pressed,  +                                    combo->prev_record.event.key)); +#else +            unregister_code16(combo->prev_key); +            register_code16(combo->prev_key); +#endif +        } +    } +} diff --git a/quantum/process_keycode/process_combo.h b/quantum/process_keycode/process_combo.h new file mode 100644 index 0000000000..a5dbd788a4 --- /dev/null +++ b/quantum/process_keycode/process_combo.h @@ -0,0 +1,59 @@ +/* Copyright 2016 Jack Humbert + * + * 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 PROCESS_COMBO_H +#define PROCESS_COMBO_H + +#include <stdint.h> +#include "progmem.h" +#include "quantum.h" + +typedef struct +{ +    const uint16_t *keys; +    uint16_t keycode;         +#ifdef EXTRA_EXTRA_LONG_COMBOS +    uint32_t state; +#elif EXTRA_LONG_COMBOS +    uint16_t state; +#else +    uint8_t state; +#endif +    uint16_t timer; +#ifdef COMBO_ALLOW_ACTION_KEYS +    keyrecord_t prev_record; +#else +    uint16_t prev_key; +#endif +} combo_t; + + +#define COMBO(ck, ca)       {.keys = &(ck)[0], .keycode = (ca)} +#define COMBO_ACTION(ck)    {.keys = &(ck)[0]} + +#define COMBO_END 0 +#ifndef COMBO_COUNT +#define COMBO_COUNT 0 +#endif +#ifndef COMBO_TERM +#define COMBO_TERM TAPPING_TERM +#endif + +bool process_combo(uint16_t keycode, keyrecord_t *record); +void matrix_scan_combo(void); +void process_combo_event(uint8_t combo_index, bool pressed); + +#endif diff --git a/quantum/process_keycode/process_leader.c b/quantum/process_keycode/process_leader.c new file mode 100644 index 0000000000..473906d657 --- /dev/null +++ b/quantum/process_keycode/process_leader.c @@ -0,0 +1,54 @@ +/* Copyright 2016 Jack Humbert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include "process_leader.h" + +__attribute__ ((weak)) +void leader_start(void) {} + +__attribute__ ((weak)) +void leader_end(void) {} + +// Leader key stuff +bool leading = false; +uint16_t leader_time = 0; + +uint16_t leader_sequence[5] = {0, 0, 0, 0, 0}; +uint8_t leader_sequence_size = 0; + +bool process_leader(uint16_t keycode, keyrecord_t *record) { +  // Leader key set-up +  if (record->event.pressed) { +    if (!leading && keycode == KC_LEAD) { +      leader_start(); +      leading = true; +      leader_time = timer_read(); +      leader_sequence_size = 0; +      leader_sequence[0] = 0; +      leader_sequence[1] = 0; +      leader_sequence[2] = 0; +      leader_sequence[3] = 0; +      leader_sequence[4] = 0; +      return false; +    } +    if (leading && timer_elapsed(leader_time) < LEADER_TIMEOUT) { +      leader_sequence[leader_sequence_size] = keycode; +      leader_sequence_size++; +      return false; +    } +  } +  return true; +} diff --git a/quantum/process_keycode/process_leader.h b/quantum/process_keycode/process_leader.h new file mode 100644 index 0000000000..da7a3d2ef7 --- /dev/null +++ b/quantum/process_keycode/process_leader.h @@ -0,0 +1,39 @@ +/* Copyright 2016 Jack Humbert + * + * 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 PROCESS_LEADER_H +#define PROCESS_LEADER_H + +#include "quantum.h" + +bool process_leader(uint16_t keycode, keyrecord_t *record); + +void leader_start(void); +void leader_end(void); + +#ifndef LEADER_TIMEOUT +  #define LEADER_TIMEOUT 200 +#endif +#define SEQ_ONE_KEY(key) if (leader_sequence[0] == (key) && leader_sequence[1] == 0 && leader_sequence[2] == 0 && leader_sequence[3] == 0 && leader_sequence[4] == 0) +#define SEQ_TWO_KEYS(key1, key2) if (leader_sequence[0] == (key1) && leader_sequence[1] == (key2) && leader_sequence[2] == 0 && leader_sequence[3] == 0 && leader_sequence[4] == 0) +#define SEQ_THREE_KEYS(key1, key2, key3) if (leader_sequence[0] == (key1) && leader_sequence[1] == (key2) && leader_sequence[2] == (key3) && leader_sequence[3] == 0 && leader_sequence[4] == 0) +#define SEQ_FOUR_KEYS(key1, key2, key3, key4) if (leader_sequence[0] == (key1) && leader_sequence[1] == (key2) && leader_sequence[2] == (key3) && leader_sequence[3] == (key4) && leader_sequence[4] == 0) +#define SEQ_FIVE_KEYS(key1, key2, key3, key4, key5) if (leader_sequence[0] == (key1) && leader_sequence[1] == (key2) && leader_sequence[2] == (key3) && leader_sequence[3] == (key4) && leader_sequence[4] == (key5)) + +#define LEADER_EXTERNS() extern bool leading; extern uint16_t leader_time; extern uint16_t leader_sequence[5]; extern uint8_t leader_sequence_size +#define LEADER_DICTIONARY() if (leading && timer_elapsed(leader_time) > LEADER_TIMEOUT) + +#endif diff --git a/quantum/process_keycode/process_midi.c b/quantum/process_keycode/process_midi.c new file mode 100644 index 0000000000..9184feaae8 --- /dev/null +++ b/quantum/process_keycode/process_midi.c @@ -0,0 +1,253 @@ +/* Copyright 2016 Jack Humbert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ +#include "process_midi.h" + +#ifdef MIDI_ENABLE +#include "midi.h" + +#ifdef MIDI_BASIC + +void process_midi_basic_noteon(uint8_t note)  +{ +    midi_send_noteon(&midi_device, 0, note, 128); +} + +void process_midi_basic_noteoff(uint8_t note) +{ +    midi_send_noteoff(&midi_device, 0, note, 0); +} + +void process_midi_all_notes_off(void) +{ +    midi_send_cc(&midi_device, 0, 0x7B, 0); +} + +#endif // MIDI_BASIC + +#ifdef MIDI_ADVANCED + +#include "timer.h" + +static uint8_t tone_status[MIDI_TONE_COUNT]; + +static uint8_t midi_modulation; +static int8_t midi_modulation_step; +static uint16_t midi_modulation_timer; + +inline uint8_t compute_velocity(uint8_t setting) +{ +    return (setting + 1) * (128 / (MIDI_VELOCITY_MAX - MIDI_VELOCITY_MIN + 1)); +} + +void midi_init(void) +{ +    midi_config.octave = MI_OCT_2 - MIDI_OCTAVE_MIN; +    midi_config.transpose = 0; +    midi_config.velocity = (MIDI_VELOCITY_MAX - MIDI_VELOCITY_MIN); +    midi_config.channel = 0; +    midi_config.modulation_interval = 8; + +    for (uint8_t i = 0; i < MIDI_TONE_COUNT; i++) +    { +        tone_status[i] = MIDI_INVALID_NOTE; +    } + +    midi_modulation = 0; +    midi_modulation_step = 0; +    midi_modulation_timer = 0; +} + +void midi_task(void) +{ +    if (timer_elapsed(midi_modulation_timer) < midi_config.modulation_interval) +        return; +    midi_modulation_timer = timer_read(); + +    if (midi_modulation_step != 0) +    { +        dprintf("midi modulation %d\n", midi_modulation); +        midi_send_cc(&midi_device, midi_config.channel, 0x1, midi_modulation); + +        if (midi_modulation_step < 0 && midi_modulation < -midi_modulation_step) { +            midi_modulation = 0; +            midi_modulation_step = 0; +            return; +        } + +        midi_modulation += midi_modulation_step; + +        if (midi_modulation > 127) +            midi_modulation = 127; +    } +} + +uint8_t midi_compute_note(uint16_t keycode) +{ +    return 12 * midi_config.octave + (keycode - MIDI_TONE_MIN) + midi_config.transpose; +} + +bool process_midi(uint16_t keycode, keyrecord_t *record) +{ +    switch (keycode) { +        case MIDI_TONE_MIN ... MIDI_TONE_MAX: +        { +            uint8_t channel = midi_config.channel; +            uint8_t tone = keycode - MIDI_TONE_MIN; +            uint8_t velocity = compute_velocity(midi_config.velocity); +            if (record->event.pressed) { +                uint8_t note = midi_compute_note(keycode); +                midi_send_noteon(&midi_device, channel, note, velocity); +                dprintf("midi noteon channel:%d note:%d velocity:%d\n", channel, note, velocity); +                tone_status[tone] = note; +            } +            else { +                uint8_t note = tone_status[tone]; +                if (note != MIDI_INVALID_NOTE) +                { +                    midi_send_noteoff(&midi_device, channel, note, velocity); +                    dprintf("midi noteoff channel:%d note:%d velocity:%d\n", channel, note, velocity); +                } +                tone_status[tone] = MIDI_INVALID_NOTE; +            } +            return false; +        } +        case MIDI_OCTAVE_MIN ... MIDI_OCTAVE_MAX: +            if (record->event.pressed) { +                midi_config.octave = keycode - MIDI_OCTAVE_MIN; +                dprintf("midi octave %d\n", midi_config.octave); +            } +            return false; +        case MI_OCTD: +            if (record->event.pressed && midi_config.octave > 0) { +                midi_config.octave--; +                dprintf("midi octave %d\n", midi_config.octave); +            } +            return false; +        case MI_OCTU: +            if (record->event.pressed && midi_config.octave < (MIDI_OCTAVE_MAX - MIDI_OCTAVE_MIN)) { +                midi_config.octave++; +                dprintf("midi octave %d\n", midi_config.octave); +            } +            return false; +        case MIDI_TRANSPOSE_MIN ... MIDI_TRANSPOSE_MAX: +            if (record->event.pressed) { +                midi_config.transpose = keycode - MI_TRNS_0; +                dprintf("midi transpose %d\n", midi_config.transpose); +            } +            return false; +        case MI_TRNSD: +            if (record->event.pressed && midi_config.transpose > (MIDI_TRANSPOSE_MIN - MI_TRNS_0)) { +                midi_config.transpose--; +                dprintf("midi transpose %d\n", midi_config.transpose); +            } +            return false; +        case MI_TRNSU: +            if (record->event.pressed && midi_config.transpose < (MIDI_TRANSPOSE_MAX - MI_TRNS_0)) { +                const bool positive = midi_config.transpose > 0; +                midi_config.transpose++; +                if (positive && midi_config.transpose < 0) +                    midi_config.transpose--; +                dprintf("midi transpose %d\n", midi_config.transpose); +            } +            return false; +        case MIDI_VELOCITY_MIN ... MIDI_VELOCITY_MAX: +            if (record->event.pressed) { +                midi_config.velocity = keycode - MIDI_VELOCITY_MIN; +                dprintf("midi velocity %d\n", midi_config.velocity); +            } +            return false; +        case MI_VELD: +            if (record->event.pressed && midi_config.velocity > 0) { +                midi_config.velocity--; +                dprintf("midi velocity %d\n", midi_config.velocity); +            } +            return false; +        case MI_VELU: +            if (record->event.pressed) { +                midi_config.velocity++; +                dprintf("midi velocity %d\n", midi_config.velocity); +            } +            return false; +        case MIDI_CHANNEL_MIN ... MIDI_CHANNEL_MAX: +            if (record->event.pressed) { +                midi_config.channel = keycode - MIDI_CHANNEL_MIN; +                dprintf("midi channel %d\n", midi_config.channel); +            } +            return false; +        case MI_CHD: +            if (record->event.pressed) { +                midi_config.channel--; +                dprintf("midi channel %d\n", midi_config.channel); +            } +            return false; +        case MI_CHU: +            if (record->event.pressed) { +                midi_config.channel++; +                dprintf("midi channel %d\n", midi_config.channel); +            } +            return false; +        case MI_ALLOFF: +            if (record->event.pressed) { +                midi_send_cc(&midi_device, midi_config.channel, 0x7B, 0); +                dprintf("midi all notes off\n"); +            } +            return false; +        case MI_SUS: +            midi_send_cc(&midi_device, midi_config.channel, 0x40, record->event.pressed ? 127 : 0); +            dprintf("midi sustain %d\n", record->event.pressed); +            return false; +        case MI_PORT: +            midi_send_cc(&midi_device, midi_config.channel, 0x41, record->event.pressed ? 127 : 0); +            dprintf("midi portamento %d\n", record->event.pressed); +            return false; +        case MI_SOST: +            midi_send_cc(&midi_device, midi_config.channel, 0x42, record->event.pressed ? 127 : 0); +            dprintf("midi sostenuto %d\n", record->event.pressed); +            return false; +        case MI_SOFT: +            midi_send_cc(&midi_device, midi_config.channel, 0x43, record->event.pressed ? 127 : 0); +            dprintf("midi soft %d\n", record->event.pressed); +            return false; +        case MI_LEG: +            midi_send_cc(&midi_device, midi_config.channel, 0x43, record->event.pressed ? 127 : 0); +            dprintf("midi legato %d\n", record->event.pressed); +            return false; +        case MI_MOD: +            midi_modulation_step = record->event.pressed ? 1 : -1; +            return false; +        case MI_MODSD: +            if (record->event.pressed) { +                midi_config.modulation_interval++; +                // prevent overflow +                if (midi_config.modulation_interval == 0) +                    midi_config.modulation_interval--; +                dprintf("midi modulation interval %d\n", midi_config.modulation_interval); +            } +            return false; +        case MI_MODSU: +            if (record->event.pressed && midi_config.modulation_interval > 0) { +                midi_config.modulation_interval--; +                dprintf("midi modulation interval %d\n", midi_config.modulation_interval); +            } +            return false; +    }; + +    return true; +} + +#endif // MIDI_ADVANCED + +#endif // MIDI_ENABLE diff --git a/quantum/process_keycode/process_midi.h b/quantum/process_keycode/process_midi.h new file mode 100644 index 0000000000..ccac8981a6 --- /dev/null +++ b/quantum/process_keycode/process_midi.h @@ -0,0 +1,56 @@ +/* Copyright 2016 Jack Humbert + * + * 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 PROCESS_MIDI_H +#define PROCESS_MIDI_H + +#include "quantum.h" + +#ifdef MIDI_ENABLE + +#ifdef MIDI_BASIC +void process_midi_basic_noteon(uint8_t note); +void process_midi_basic_noteoff(uint8_t note); +void process_midi_all_notes_off(void); +#endif + +#ifdef MIDI_ADVANCED +typedef union { +  uint32_t raw; +  struct { +    uint8_t octave              :4; +    int8_t transpose            :4; +    uint8_t velocity            :4; +    uint8_t channel             :4; +    uint8_t modulation_interval :4; +  }; +} midi_config_t; + +midi_config_t midi_config; + +void midi_init(void); +void midi_task(void); +bool process_midi(uint16_t keycode, keyrecord_t *record); + +#define MIDI_INVALID_NOTE 0xFF +#define MIDI_TONE_COUNT (MIDI_TONE_MAX - MIDI_TONE_MIN + 1) + +uint8_t midi_compute_note(uint16_t keycode); +#endif // MIDI_ADVANCED + +#endif // MIDI_ENABLE + +#endif diff --git a/quantum/process_keycode/process_music.c b/quantum/process_keycode/process_music.c new file mode 100644 index 0000000000..217dca2807 --- /dev/null +++ b/quantum/process_keycode/process_music.c @@ -0,0 +1,205 @@ +/* Copyright 2016 Jack Humbert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ +#include "process_music.h" + +#ifdef AUDIO_ENABLE +#include "process_audio.h" +#endif +#if defined(MIDI_ENABLE) && defined(MIDI_BASIC) +#include "process_midi.h" +#endif + +#if defined(AUDIO_ENABLE) || (defined(MIDI_ENABLE) && defined(MIDI_BASIC)) + +bool music_activated = false; +uint8_t music_starting_note = 0x0C; +int music_offset = 7; + +// music sequencer +static bool music_sequence_recording = false; +static bool music_sequence_recorded = false; +static bool music_sequence_playing = false; +static uint8_t music_sequence[16] = {0}; +static uint8_t music_sequence_count = 0; +static uint8_t music_sequence_position = 0; + +static uint16_t music_sequence_timer = 0; +static uint16_t music_sequence_interval = 100; + +static void music_noteon(uint8_t note) { +    #ifdef AUDIO_ENABLE +    process_audio_noteon(note); +    #endif +    #if defined(MIDI_ENABLE) && defined(MIDI_BASIC) +    process_midi_basic_noteon(note); +    #endif +} + +static void music_noteoff(uint8_t note) { +    #ifdef AUDIO_ENABLE +    process_audio_noteoff(note); +    #endif +    #if defined(MIDI_ENABLE) && defined(MIDI_BASIC) +    process_midi_basic_noteoff(note); +    #endif +} + +void music_all_notes_off(void) { +    #ifdef AUDIO_ENABLE +    process_audio_all_notes_off(); +    #endif +    #if defined(MIDI_ENABLE) && defined(MIDI_BASIC) +    process_midi_all_notes_off(); +    #endif +} + +bool process_music(uint16_t keycode, keyrecord_t *record) { + +    if (keycode == MU_ON && record->event.pressed) { +        music_on(); +        return false; +    } + +    if (keycode == MU_OFF && record->event.pressed) { +        music_off(); +        return false; +    } + +    if (keycode == MU_TOG && record->event.pressed) { +        if (music_activated) +        { +            music_off(); +        } +        else +        { +            music_on(); +        } +        return false; +    } + +    if (music_activated) { + +      if (keycode == KC_LCTL && record->event.pressed) { // Start recording +        music_all_notes_off(); +        music_sequence_recording = true; +        music_sequence_recorded = false; +        music_sequence_playing = false; +        music_sequence_count = 0; +        return false; +      } + +      if (keycode == KC_LALT && record->event.pressed) { // Stop recording/playing +        music_all_notes_off(); +        if (music_sequence_recording) { // was recording +          music_sequence_recorded = true; +        } +        music_sequence_recording = false; +        music_sequence_playing = false; +        return false; +      } + +      if (keycode == KC_LGUI && record->event.pressed && music_sequence_recorded) { // Start playing +        music_all_notes_off(); +        music_sequence_recording = false; +        music_sequence_playing = true; +        music_sequence_position = 0; +        music_sequence_timer = 0; +        return false; +      } + +      if (keycode == KC_UP) { +        if (record->event.pressed) +            music_sequence_interval-=10; +        return false; +      } + +      if (keycode == KC_DOWN) { +        if (record->event.pressed) +            music_sequence_interval+=10; +        return false; +      } + +      #define MUSIC_MODE_GUITAR + +      #ifdef MUSIC_MODE_CHROMATIC +      uint8_t note = (music_starting_note + record->event.key.col + music_offset - 3)+12*(MATRIX_ROWS - record->event.key.row); +      #elif defined(MUSIC_MODE_GUITAR) +      uint8_t note = (music_starting_note + record->event.key.col + music_offset + 32)+5*(MATRIX_ROWS - record->event.key.row); +      #elif defined(MUSIC_MODE_VIOLIN) +      uint8_t note = (music_starting_note + record->event.key.col + music_offset + 32)+7*(MATRIX_ROWS - record->event.key.row); +      #else +      uint8_t note = (music_starting_note + SCALE[record->event.key.col + music_offset] - 3)+12*(MATRIX_ROWS - record->event.key.row); +      #endif + +      if (record->event.pressed) { +        music_noteon(note); +        if (music_sequence_recording) { +          music_sequence[music_sequence_count] = note; +          music_sequence_count++; +        } +      } else { +        music_noteoff(note); +      } + +      if (keycode < 0xFF) // ignores all normal keycodes, but lets RAISE, LOWER, etc through +        return false; +    } + +    return true; +} + +bool is_music_on(void) { +    return (music_activated != 0); +} + +void music_toggle(void) { +    if (!music_activated) { +        music_on(); +    } else { +        music_off(); +    } +} + +void music_on(void) { +    music_activated = 1; +    music_on_user(); +} + +void music_off(void) { +    music_activated = 0; +    music_all_notes_off(); +} + +void matrix_scan_music(void) { +  if (music_sequence_playing) { +    if ((music_sequence_timer == 0) || (timer_elapsed(music_sequence_timer) > music_sequence_interval)) { +      music_sequence_timer = timer_read(); +      uint8_t prev_note = music_sequence[(music_sequence_position - 1 < 0)?(music_sequence_position - 1 + music_sequence_count):(music_sequence_position - 1)]; +      uint8_t next_note = music_sequence[music_sequence_position]; +      music_noteoff(prev_note); +      music_noteon(next_note); +      music_sequence_position = (music_sequence_position + 1) % music_sequence_count; +    } +  } +} + +__attribute__ ((weak)) +void music_on_user() {} + +__attribute__ ((weak)) +void music_scale_user() {} + +#endif // defined(AUDIO_ENABLE) || (defined(MIDI_ENABLE) && defined(MIDI_BASIC))
\ No newline at end of file diff --git a/quantum/process_keycode/process_music.h b/quantum/process_keycode/process_music.h new file mode 100644 index 0000000000..8dfbf041f4 --- /dev/null +++ b/quantum/process_keycode/process_music.h @@ -0,0 +1,47 @@ +/* Copyright 2016 Jack Humbert + * + * 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 PROCESS_MUSIC_H +#define PROCESS_MUSIC_H + +#include "quantum.h" + +#if defined(AUDIO_ENABLE) || (defined(MIDI_ENABLE) && defined(MIDI_BASIC)) + +bool process_music(uint16_t keycode, keyrecord_t *record); + +bool is_music_on(void); +void music_toggle(void); +void music_on(void); +void music_off(void); + +void music_on_user(void); +void music_scale_user(void); +void music_all_notes_off(void); + +void matrix_scan_music(void); + +#ifndef SCALE +#define SCALE (int8_t []){ 0 + (12*0), 2 + (12*0), 4 + (12*0), 5 + (12*0), 7 + (12*0), 9 + (12*0), 11 + (12*0), \ +                           0 + (12*1), 2 + (12*1), 4 + (12*1), 5 + (12*1), 7 + (12*1), 9 + (12*1), 11 + (12*1), \ +                           0 + (12*2), 2 + (12*2), 4 + (12*2), 5 + (12*2), 7 + (12*2), 9 + (12*2), 11 + (12*2), \ +                           0 + (12*3), 2 + (12*3), 4 + (12*3), 5 + (12*3), 7 + (12*3), 9 + (12*3), 11 + (12*3), \ +                           0 + (12*4), 2 + (12*4), 4 + (12*4), 5 + (12*4), 7 + (12*4), 9 + (12*4), 11 + (12*4), } +#endif + +#endif // defined(AUDIO_ENABLE) || (defined(MIDI_ENABLE) && defined(MIDI_BASIC)) + +#endif diff --git a/quantum/process_keycode/process_printer.c b/quantum/process_keycode/process_printer.c new file mode 100644 index 0000000000..613af70183 --- /dev/null +++ b/quantum/process_keycode/process_printer.c @@ -0,0 +1,270 @@ +/* Copyright 2016 Jack Humbert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include "process_printer.h" +#include "action_util.h" + +bool printing_enabled = false; +uint8_t character_shift = 0; + +void enable_printing(void) { +	printing_enabled = true; +	serial_init(); +} + +void disable_printing(void) { +	printing_enabled = false; +} + +uint8_t shifted_numbers[10] = {0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28, 0x29}; + +// uint8_t keycode_to_ascii[0xFF][2]; + +// keycode_to_ascii[KC_MINS] = {0x2D, 0x5F}; + +void print_char(char c) { +	USB_Disable(); +	serial_send(c); +	USB_Init(); +} + +void print_string(char c[]) { +	for(uint8_t i = 0; i < strlen(c); i++) +		print_char(c[i]); +} + +void print_box_string(const char text[]) { +	size_t len = strlen(text); +	char out[len * 3 + 8]; +	out[0] = 0xDA; +	for (uint8_t i = 0; i < len; i++) { +		out[i+1] = 0xC4; +	} +	out[len + 1] = 0xBF; +	out[len + 2] = '\n'; + +	out[len + 3] = 0xB3; +	for (uint8_t i = 0; i < len; i++) { +		out[len + 4 + i] = text[i]; +	} +	out[len * 2 + 4] = 0xB3; +	out[len * 2 + 5] = '\n'; + + +	out[len * 2 + 6] = 0xC0; +	for (uint8_t i = 0; i < len; i++) { +		out[len * 2 + 7 + i] = 0xC4; +	} +	out[len * 3 + 7] = 0xD9; +	out[len * 3 + 8] = '\n'; + +	print_string(out);  +} + +bool process_printer(uint16_t keycode, keyrecord_t *record) { +	if (keycode == PRINT_ON) { +		enable_printing(); +		return false; +	} +	if (keycode == PRINT_OFF) { +		disable_printing(); +		return false; +	} + +	if (printing_enabled) { +		switch(keycode) { +			case KC_EXLM ... KC_RPRN: +			case KC_UNDS: +			case KC_PLUS: +			case KC_LCBR: +			case KC_RCBR: +			case KC_PIPE: +			case KC_TILD: +				keycode &= 0xFF; +			case KC_LSFT: +			case KC_RSFT: +				if (record->event.pressed) { +					character_shift++; +				} else { +					character_shift--; +				} +				return false; +			break; +		} + +		switch(keycode) { +			case KC_F1: +				if (record->event.pressed) { +					print_box_string("This is a line of text!"); +				} +				return false; +			case KC_ESC: +				if (record->event.pressed) { +					print_char(0x1B); +				} +				return false; +			break; +			case KC_SPC: +				if (record->event.pressed) { +					print_char(0x20); +				} +				return false; +			break; +			case KC_A ... KC_Z: +				if (record->event.pressed) { +					if (character_shift) { +						print_char(0x41 + (keycode - KC_A)); +					} else { +						print_char(0x61 + (keycode - KC_A)); +					} +				} +				return false; +			break; +			case KC_1 ... KC_0: +				if (record->event.pressed) { +					if (character_shift) { +							print_char(shifted_numbers[keycode - KC_1]); +					} else { +							print_char(0x30 + ((keycode - KC_1 + 1) % 10)); +					} +				} +				return false; +			break; +			case KC_ENT: +				if (record->event.pressed) { +					if (character_shift) { +						print_char(0x0C); +					} else { +						print_char(0x0A); +					} +				} +				return false; +			break; +			case KC_BSPC: +				if (record->event.pressed) { +					if (character_shift) { +						print_char(0x18); +					} else { +						print_char(0x1A); +					} +				} +				return false; +			break; +			case KC_DOT: +				if (record->event.pressed) { +					if (character_shift) { +						print_char(0x3E); +					} else { +						print_char(0x2E); +					} +				} +				return false; +			break; +			case KC_COMM: +				if (record->event.pressed) { +					if (character_shift) { +						print_char(0x3C); +					} else { +						print_char(0x2C); +					} +				} +				return false; +			break; +			case KC_SLSH: +				if (record->event.pressed) { +					if (character_shift) { +						print_char(0x3F); +					} else { +						print_char(0x2F); +					} +				} +				return false; +			break; +			case KC_QUOT: +				if (record->event.pressed) { +					if (character_shift) { +						print_char(0x22); +					} else { +						print_char(0x27); +					} +				} +				return false; +			break; +			case KC_GRV: +				if (record->event.pressed) { +					if (character_shift) { +						print_char(0x7E); +					} else { +						print_char(0x60); +					} +				} +				return false; +			break; +			case KC_MINS: +				if (record->event.pressed) { +					if (character_shift) { +						print_char(0x5F); +					} else { +						print_char(0x2D); +					} +				} +				return false; +			break; +			case KC_EQL: +				if (record->event.pressed) { +					if (character_shift) { +						print_char(0x2B); +					} else { +						print_char(0x3D); +					} +				} +				return false; +			break; +			case KC_LBRC: +				if (record->event.pressed) { +					if (character_shift) { +						print_char(0x7B); +					} else { +						print_char(0x5B); +					} +				} +				return false; +			break; +			case KC_RBRC: +				if (record->event.pressed) { +					if (character_shift) { +						print_char(0x7D); +					} else { +						print_char(0x5D); +					} +				} +				return false; +			break; +			case KC_BSLS: +				if (record->event.pressed) { +					if (character_shift) { +						print_char(0x7C); +					} else { +						print_char(0x5C); +					} +				} +				return false; +			break; +		} +	} +	return true; + +} diff --git a/quantum/process_keycode/process_printer.h b/quantum/process_keycode/process_printer.h new file mode 100644 index 0000000000..71d3a4b56a --- /dev/null +++ b/quantum/process_keycode/process_printer.h @@ -0,0 +1,26 @@ +/* Copyright 2016 Jack Humbert + * + * 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 PROCESS_PRINTER_H +#define PROCESS_PRINTER_H + +#include "quantum.h" + +#include "protocol/serial.h" + +bool process_printer(uint16_t keycode, keyrecord_t *record); + +#endif diff --git a/quantum/process_keycode/process_printer_bb.c b/quantum/process_keycode/process_printer_bb.c new file mode 100644 index 0000000000..3a00f169d8 --- /dev/null +++ b/quantum/process_keycode/process_printer_bb.c @@ -0,0 +1,276 @@ +/* Copyright 2016 Jack Humbert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include "process_printer.h" +#include "action_util.h" + +bool printing_enabled = false; +uint8_t character_shift = 0; + +#define SERIAL_PIN_DDR DDRD +#define SERIAL_PIN_PORT PORTD +#define SERIAL_PIN_MASK _BV(PD3) +#define SERIAL_DELAY 52 + +inline static +void serial_delay(void) { +  _delay_us(SERIAL_DELAY); +} + +inline static +void serial_high(void) { +  SERIAL_PIN_PORT |= SERIAL_PIN_MASK; +} + +inline static +void serial_low(void) { +  SERIAL_PIN_PORT &= ~SERIAL_PIN_MASK; +} + +inline static +void serial_output(void) { +  SERIAL_PIN_DDR |= SERIAL_PIN_MASK; +} + + +void enable_printing() { +	printing_enabled = true; +	serial_output(); +	serial_high(); +} + +void disable_printing() { +	printing_enabled = false; +} + +uint8_t shifted_numbers[10] = {0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28, 0x29}; + +// uint8_t keycode_to_ascii[0xFF][2]; + +// keycode_to_ascii[KC_MINS] = {0x2D, 0x5F}; + +void print_char(char c) { +  uint8_t b = 8; +  serial_output(); +  while( b-- ) { +    if(c & (1 << b)) { +      serial_high(); +    } else { +      serial_low(); +    } +    serial_delay(); +  } +} + +void print_string(char c[]) { +	for(uint8_t i = 0; i < strlen(c); i++) +		print_char(c[i]); +} + +bool process_printer(uint16_t keycode, keyrecord_t *record) { +	if (keycode == PRINT_ON) { +		enable_printing(); +		return false; +	} +	if (keycode == PRINT_OFF) { +		disable_printing(); +		return false; +	} + +	if (printing_enabled) { +		switch(keycode) { +			case KC_EXLM ... KC_RPRN: +			case KC_UNDS: +			case KC_PLUS: +			case KC_LCBR: +			case KC_RCBR: +			case KC_PIPE: +			case KC_TILD: +				keycode &= 0xFF; +			case KC_LSFT: +			case KC_RSFT: +				if (record->event.pressed) { +					character_shift++; +				} else { +					character_shift--; +				} +				return false; +			break; +		} + +		switch(keycode) { +			case KC_F1: +				if (record->event.pressed) { +					print_string("This is a line of text!\n\n\n"); +				} +				return false; +			case KC_ESC: +				if (record->event.pressed) { +					print_char(0x1B); +				} +				return false; +			break; +			case KC_SPC: +				if (record->event.pressed) { +					print_char(0x20); +				} +				return false; +			break; +			case KC_A ... KC_Z: +				if (record->event.pressed) { +					if (character_shift) { +						print_char(0x41 + (keycode - KC_A)); +					} else { +						print_char(0x61 + (keycode - KC_A)); +					} +				} +				return false; +			break; +			case KC_1 ... KC_0: +				if (record->event.pressed) { +					if (character_shift) { +							print_char(shifted_numbers[keycode - KC_1]); +					} else { +							print_char(0x30 + ((keycode - KC_1 + 1) % 10)); +					} +				} +				return false; +			break; +			case KC_ENT: +				if (record->event.pressed) { +					if (character_shift) { +						print_char(0x0C); +					} else { +						print_char(0x0A); +					} +				} +				return false; +			break; +			case KC_BSPC: +				if (record->event.pressed) { +					if (character_shift) { +						print_char(0x18); +					} else { +						print_char(0x1A); +					} +				} +				return false; +			break; +			case KC_DOT: +				if (record->event.pressed) { +					if (character_shift) { +						print_char(0x3E); +					} else { +						print_char(0x2E); +					} +				} +				return false; +			break; +			case KC_COMM: +				if (record->event.pressed) { +					if (character_shift) { +						print_char(0x3C); +					} else { +						print_char(0x2C); +					} +				} +				return false; +			break; +			case KC_SLSH: +				if (record->event.pressed) { +					if (character_shift) { +						print_char(0x3F); +					} else { +						print_char(0x2F); +					} +				} +				return false; +			break; +			case KC_QUOT: +				if (record->event.pressed) { +					if (character_shift) { +						print_char(0x22); +					} else { +						print_char(0x27); +					} +				} +				return false; +			break; +			case KC_GRV: +				if (record->event.pressed) { +					if (character_shift) { +						print_char(0x7E); +					} else { +						print_char(0x60); +					} +				} +				return false; +			break; +			case KC_MINS: +				if (record->event.pressed) { +					if (character_shift) { +						print_char(0x5F); +					} else { +						print_char(0x2D); +					} +				} +				return false; +			break; +			case KC_EQL: +				if (record->event.pressed) { +					if (character_shift) { +						print_char(0x2B); +					} else { +						print_char(0x3D); +					} +				} +				return false; +			break; +			case KC_LBRC: +				if (record->event.pressed) { +					if (character_shift) { +						print_char(0x7B); +					} else { +						print_char(0x5B); +					} +				} +				return false; +			break; +			case KC_RBRC: +				if (record->event.pressed) { +					if (character_shift) { +						print_char(0x7D); +					} else { +						print_char(0x5D); +					} +				} +				return false; +			break; +			case KC_BSLS: +				if (record->event.pressed) { +					if (character_shift) { +						print_char(0x7C); +					} else { +						print_char(0x5C); +					} +				} +				return false; +			break; +		} +	} +	return true; + +} diff --git a/quantum/process_keycode/process_tap_dance.c b/quantum/process_keycode/process_tap_dance.c new file mode 100644 index 0000000000..4fd45810bb --- /dev/null +++ b/quantum/process_keycode/process_tap_dance.c @@ -0,0 +1,166 @@ +/* Copyright 2016 Jack Humbert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ +#include "quantum.h" +#include "action_tapping.h" + +uint8_t get_oneshot_mods(void); + +static uint16_t last_td; +static int8_t highest_td = -1; + +void qk_tap_dance_pair_finished (qk_tap_dance_state_t *state, void *user_data) { +  qk_tap_dance_pair_t *pair = (qk_tap_dance_pair_t *)user_data; + +  if (state->count == 1) { +    register_code16 (pair->kc1); +  } else if (state->count == 2) { +    register_code16 (pair->kc2); +  } +} + +void qk_tap_dance_pair_reset (qk_tap_dance_state_t *state, void *user_data) { +  qk_tap_dance_pair_t *pair = (qk_tap_dance_pair_t *)user_data; + +  if (state->count == 1) { +    unregister_code16 (pair->kc1); +  } else if (state->count == 2) { +    unregister_code16 (pair->kc2); +  } +} + +static inline void _process_tap_dance_action_fn (qk_tap_dance_state_t *state, +                                                 void *user_data, +                                                 qk_tap_dance_user_fn_t fn) +{ +  if (fn) { +    fn(state, user_data); +  } +} + +static inline void process_tap_dance_action_on_each_tap (qk_tap_dance_action_t *action) +{ +  _process_tap_dance_action_fn (&action->state, action->user_data, action->fn.on_each_tap); +} + +static inline void process_tap_dance_action_on_dance_finished (qk_tap_dance_action_t *action) +{ +  if (action->state.finished) +    return; +  action->state.finished = true; +  add_mods(action->state.oneshot_mods); +  send_keyboard_report(); +  _process_tap_dance_action_fn (&action->state, action->user_data, action->fn.on_dance_finished); +} + +static inline void process_tap_dance_action_on_reset (qk_tap_dance_action_t *action) +{ +  _process_tap_dance_action_fn (&action->state, action->user_data, action->fn.on_reset); +  del_mods(action->state.oneshot_mods); +  send_keyboard_report(); +} + +bool process_tap_dance(uint16_t keycode, keyrecord_t *record) { +  uint16_t idx = keycode - QK_TAP_DANCE; +  qk_tap_dance_action_t *action; + +  if (last_td && last_td != keycode) { +    (&tap_dance_actions[last_td - QK_TAP_DANCE])->state.interrupted = true; +  } + +  switch(keycode) { +  case QK_TAP_DANCE ... QK_TAP_DANCE_MAX: +    if ((int16_t)idx > highest_td) +      highest_td = idx; +    action = &tap_dance_actions[idx]; + +    action->state.pressed = record->event.pressed; +    if (record->event.pressed) { +      action->state.keycode = keycode; +      action->state.count++; +      action->state.timer = timer_read(); +      action->state.oneshot_mods = get_oneshot_mods(); +      process_tap_dance_action_on_each_tap (action); + +      if (last_td && last_td != keycode) { +        qk_tap_dance_action_t *paction = &tap_dance_actions[last_td - QK_TAP_DANCE]; +        paction->state.interrupted = true; +        process_tap_dance_action_on_dance_finished (paction); +        reset_tap_dance (&paction->state); +      } + +      last_td = keycode; +    } + +    break; + +  default: +    if (!record->event.pressed) +      return true; + +    if (highest_td == -1) +      return true; + +    for (int i = 0; i <= highest_td; i++) { +      action = &tap_dance_actions[i]; +      if (action->state.count == 0) +        continue; +      action->state.interrupted = true; +      process_tap_dance_action_on_dance_finished (action); +      reset_tap_dance (&action->state); +    } +    break; +  } + +  return true; +} + + + +void matrix_scan_tap_dance () { +  if (highest_td == -1) +    return; +  uint16_t tap_user_defined; + +for (uint8_t i = 0; i <= highest_td; i++) { +    qk_tap_dance_action_t *action = &tap_dance_actions[i]; +    if(action->custom_tapping_term > 0 ) { +      tap_user_defined = action->custom_tapping_term; +    } +    else{ +      tap_user_defined = TAPPING_TERM; +    } +    if (action->state.count && timer_elapsed (action->state.timer) > tap_user_defined) { +      process_tap_dance_action_on_dance_finished (action); +      reset_tap_dance (&action->state); +    } +  } +} + +void reset_tap_dance (qk_tap_dance_state_t *state) { +  qk_tap_dance_action_t *action; + +  if (state->pressed) +    return; + +  action = &tap_dance_actions[state->keycode - QK_TAP_DANCE]; + +  process_tap_dance_action_on_reset (action); + +  state->count = 0; +  state->interrupted = false; +  state->finished = false; +  last_td = 0; +} diff --git a/quantum/process_keycode/process_tap_dance.h b/quantum/process_keycode/process_tap_dance.h new file mode 100644 index 0000000000..f42c154a05 --- /dev/null +++ b/quantum/process_keycode/process_tap_dance.h @@ -0,0 +1,95 @@ +/* Copyright 2016 Jack Humbert + * + * 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 PROCESS_TAP_DANCE_H +#define PROCESS_TAP_DANCE_H + +#ifdef TAP_DANCE_ENABLE + +#include <stdbool.h> +#include <inttypes.h> + +typedef struct +{ +  uint8_t count; +  uint8_t oneshot_mods; +  uint16_t keycode; +  uint16_t timer; +  bool interrupted; +  bool pressed; +  bool finished; +} qk_tap_dance_state_t; + +#define TD(n) (QK_TAP_DANCE + n) + +typedef void (*qk_tap_dance_user_fn_t) (qk_tap_dance_state_t *state, void *user_data); + +typedef struct +{ +  struct { +    qk_tap_dance_user_fn_t on_each_tap; +    qk_tap_dance_user_fn_t on_dance_finished; +    qk_tap_dance_user_fn_t on_reset; +  } fn; +  qk_tap_dance_state_t state; +  uint16_t custom_tapping_term; +  void *user_data; +} qk_tap_dance_action_t; + +typedef struct +{ +  uint16_t kc1; +  uint16_t kc2; +} qk_tap_dance_pair_t; + +#define ACTION_TAP_DANCE_DOUBLE(kc1, kc2) { \ +    .fn = { NULL, qk_tap_dance_pair_finished, qk_tap_dance_pair_reset }, \ +    .user_data = (void *)&((qk_tap_dance_pair_t) { kc1, kc2 }),  \ +  } + +#define ACTION_TAP_DANCE_FN(user_fn) {  \ +    .fn = { NULL, user_fn, NULL }, \ +    .user_data = NULL, \ +  } + +#define ACTION_TAP_DANCE_FN_ADVANCED(user_fn_on_each_tap, user_fn_on_dance_finished, user_fn_on_dance_reset) { \ +    .fn = { user_fn_on_each_tap, user_fn_on_dance_finished, user_fn_on_dance_reset }, \ +    .user_data = NULL, \ +  } + +#define ACTION_TAP_DANCE_FN_ADVANCED_TIME(user_fn_on_each_tap, user_fn_on_dance_finished, user_fn_on_dance_reset, tap_specific_tapping_term) { \ +    .fn = { user_fn_on_each_tap, user_fn_on_dance_finished, user_fn_on_dance_reset }, \ +    .user_data = NULL, \ +    .custom_tapping_term = tap_specific_tapping_term, \ +  } + +extern qk_tap_dance_action_t tap_dance_actions[]; + +/* To be used internally */ + +bool process_tap_dance(uint16_t keycode, keyrecord_t *record); +void matrix_scan_tap_dance (void); +void reset_tap_dance (qk_tap_dance_state_t *state); + +void qk_tap_dance_pair_finished (qk_tap_dance_state_t *state, void *user_data); +void qk_tap_dance_pair_reset (qk_tap_dance_state_t *state, void *user_data); + +#else + +#define TD(n) KC_NO + +#endif + +#endif diff --git a/quantum/process_keycode/process_ucis.c b/quantum/process_keycode/process_ucis.c new file mode 100644 index 0000000000..86c0937f5e --- /dev/null +++ b/quantum/process_keycode/process_ucis.c @@ -0,0 +1,149 @@ +/* Copyright 2017 Jack Humbert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include "process_ucis.h" + +qk_ucis_state_t qk_ucis_state; + +void qk_ucis_start(void) { +  qk_ucis_state.count = 0; +  qk_ucis_state.in_progress = true; + +  qk_ucis_start_user(); +} + +__attribute__((weak)) +void qk_ucis_start_user(void) { +  unicode_input_start(); +  register_hex(0x2328); +  unicode_input_finish(); +} + +static bool is_uni_seq(char *seq) { +  uint8_t i; + +  for (i = 0; seq[i]; i++) { +    uint16_t code; +    if (('1' <= seq[i]) && (seq[i] <= '0')) +      code = seq[i] - '1' + KC_1; +    else +      code = seq[i] - 'a' + KC_A; + +    if (i > qk_ucis_state.count || qk_ucis_state.codes[i] != code) +      return false; +  } + +  return (qk_ucis_state.codes[i] == KC_ENT || +          qk_ucis_state.codes[i] == KC_SPC); +} + +__attribute__((weak)) +void qk_ucis_symbol_fallback (void) { +  for (uint8_t i = 0; i < qk_ucis_state.count - 1; i++) { +    uint8_t code = qk_ucis_state.codes[i]; +    register_code(code); +    unregister_code(code); +    wait_ms(UNICODE_TYPE_DELAY); +  } +} + +void register_ucis(const char *hex) { +  for(int i = 0; hex[i]; i++) { +    uint8_t kc = 0; +    char c = hex[i]; + +    switch (c) { +    case '0': +      kc = KC_0; +      break; +    case '1' ... '9': +      kc = c - '1' + KC_1; +      break; +    case 'a' ... 'f': +      kc = c - 'a' + KC_A; +      break; +    case 'A' ... 'F': +      kc = c - 'A' + KC_A; +      break; +    } + +    if (kc) { +      register_code (kc); +      unregister_code (kc); +      wait_ms (UNICODE_TYPE_DELAY); +    } +  } +} + +bool process_ucis (uint16_t keycode, keyrecord_t *record) { +  uint8_t i; + +  if (!qk_ucis_state.in_progress) +    return true; + +  if (qk_ucis_state.count >= UCIS_MAX_SYMBOL_LENGTH && +      !(keycode == KC_BSPC || keycode == KC_ESC || keycode == KC_SPC || keycode == KC_ENT)) { +    return false; +  } + +  if (!record->event.pressed) +    return true; + +  qk_ucis_state.codes[qk_ucis_state.count] = keycode; +  qk_ucis_state.count++; + +  if (keycode == KC_BSPC) { +    if (qk_ucis_state.count >= 2) { +      qk_ucis_state.count -= 2; +      return true; +    } else { +      qk_ucis_state.count--; +      return false; +    } +  } + +  if (keycode == KC_ENT || keycode == KC_SPC || keycode == KC_ESC) { +    bool symbol_found = false; + +    for (i = qk_ucis_state.count; i > 0; i--) { +      register_code (KC_BSPC); +      unregister_code (KC_BSPC); +      wait_ms(UNICODE_TYPE_DELAY); +    } + +    if (keycode == KC_ESC) { +      qk_ucis_state.in_progress = false; +      return false; +    } + +    unicode_input_start(); +    for (i = 0; ucis_symbol_table[i].symbol; i++) { +      if (is_uni_seq (ucis_symbol_table[i].symbol)) { +        symbol_found = true; +        register_ucis(ucis_symbol_table[i].code + 2); +        break; +      } +    } +    if (!symbol_found) { +      qk_ucis_symbol_fallback(); +    } +    unicode_input_finish(); + +    qk_ucis_state.in_progress = false; +    return false; +  } +  return true; +} diff --git a/quantum/process_keycode/process_ucis.h b/quantum/process_keycode/process_ucis.h new file mode 100644 index 0000000000..3f736a709f --- /dev/null +++ b/quantum/process_keycode/process_ucis.h @@ -0,0 +1,51 @@ +/* Copyright 2017 Jack Humbert + * + * 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 PROCESS_UCIS_H +#define PROCESS_UCIS_H + +#include "quantum.h" +#include "process_unicode_common.h" + +#ifndef UCIS_MAX_SYMBOL_LENGTH +#define UCIS_MAX_SYMBOL_LENGTH 32 +#endif + +typedef struct { +  char *symbol; +  char *code; +} qk_ucis_symbol_t; + +typedef struct { +  uint8_t count; +  uint16_t codes[UCIS_MAX_SYMBOL_LENGTH]; +  bool in_progress:1; +} qk_ucis_state_t; + +extern qk_ucis_state_t qk_ucis_state; + +#define UCIS_TABLE(...) {__VA_ARGS__, {NULL, NULL}} +#define UCIS_SYM(name, code) {name, #code} + +extern const qk_ucis_symbol_t ucis_symbol_table[]; + +void qk_ucis_start(void); +void qk_ucis_start_user(void); +void qk_ucis_symbol_fallback (void); +void register_ucis(const char *hex); +bool process_ucis (uint16_t keycode, keyrecord_t *record); + +#endif diff --git a/quantum/process_keycode/process_unicode.c b/quantum/process_keycode/process_unicode.c new file mode 100644 index 0000000000..fd008eca12 --- /dev/null +++ b/quantum/process_keycode/process_unicode.c @@ -0,0 +1,35 @@ +/* Copyright 2016 Jack Humbert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ +#include "process_unicode.h" +#include "action_util.h" +#include "eeprom.h" + +static uint8_t first_flag = 0; + +bool process_unicode(uint16_t keycode, keyrecord_t *record) { +  if (keycode > QK_UNICODE && record->event.pressed) { +    if (first_flag == 0) { +      set_unicode_input_mode(eeprom_read_byte(EECONFIG_UNICODEMODE)); +      first_flag = 1; +    } +    uint16_t unicode = keycode & 0x7FFF; +    unicode_input_start(); +    register_hex(unicode); +    unicode_input_finish(); +  } +  return true; +} + diff --git a/quantum/process_keycode/process_unicode.h b/quantum/process_keycode/process_unicode.h new file mode 100644 index 0000000000..c525b74f03 --- /dev/null +++ b/quantum/process_keycode/process_unicode.h @@ -0,0 +1,24 @@ +/* Copyright 2016 Jack Humbert + * + * 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 PROCESS_UNICODE_H +#define PROCESS_UNICODE_H + +#include "quantum.h" +#include "process_unicode_common.h" + +bool process_unicode(uint16_t keycode, keyrecord_t *record); + +#endif diff --git a/quantum/process_keycode/process_unicode_common.c b/quantum/process_keycode/process_unicode_common.c new file mode 100644 index 0000000000..84b5d673dd --- /dev/null +++ b/quantum/process_keycode/process_unicode_common.c @@ -0,0 +1,116 @@ +/* Copyright 2017 Jack Humbert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include "process_unicode_common.h" +#include "eeprom.h" + +static uint8_t input_mode; +uint8_t mods; + +void set_unicode_input_mode(uint8_t os_target) +{ +  input_mode = os_target; +  eeprom_update_byte(EECONFIG_UNICODEMODE, os_target); +} + +uint8_t get_unicode_input_mode(void) { +  return input_mode; +} + +__attribute__((weak)) +void unicode_input_start (void) { +  // save current mods +  mods = keyboard_report->mods; + +  // unregister all mods to start from clean state +  if (mods & MOD_BIT(KC_LSFT)) unregister_code(KC_LSFT); +  if (mods & MOD_BIT(KC_RSFT)) unregister_code(KC_RSFT); +  if (mods & MOD_BIT(KC_LCTL)) unregister_code(KC_LCTL); +  if (mods & MOD_BIT(KC_RCTL)) unregister_code(KC_RCTL); +  if (mods & MOD_BIT(KC_LALT)) unregister_code(KC_LALT); +  if (mods & MOD_BIT(KC_RALT)) unregister_code(KC_RALT); +  if (mods & MOD_BIT(KC_LGUI)) unregister_code(KC_LGUI); +  if (mods & MOD_BIT(KC_RGUI)) unregister_code(KC_RGUI); + +  switch(input_mode) { +  case UC_OSX: +    register_code(KC_LALT); +    break; +  case UC_LNX: +    register_code(KC_LCTL); +    register_code(KC_LSFT); +    register_code(KC_U); +    unregister_code(KC_U); +    unregister_code(KC_LSFT); +    unregister_code(KC_LCTL); +    break; +  case UC_WIN: +    register_code(KC_LALT); +    register_code(KC_PPLS); +    unregister_code(KC_PPLS); +    break; +  case UC_WINC: +    register_code(KC_RALT); +    unregister_code(KC_RALT); +    register_code(KC_U); +    unregister_code(KC_U); +  } +  wait_ms(UNICODE_TYPE_DELAY); +} + +__attribute__((weak)) +void unicode_input_finish (void) { +  switch(input_mode) { +    case UC_OSX: +    case UC_WIN: +      unregister_code(KC_LALT); +      break; +    case UC_LNX: +      register_code(KC_SPC); +      unregister_code(KC_SPC); +      break; +  } + +  // reregister previously set mods +  if (mods & MOD_BIT(KC_LSFT)) register_code(KC_LSFT); +  if (mods & MOD_BIT(KC_RSFT)) register_code(KC_RSFT); +  if (mods & MOD_BIT(KC_LCTL)) register_code(KC_LCTL); +  if (mods & MOD_BIT(KC_RCTL)) register_code(KC_RCTL); +  if (mods & MOD_BIT(KC_LALT)) register_code(KC_LALT); +  if (mods & MOD_BIT(KC_RALT)) register_code(KC_RALT); +  if (mods & MOD_BIT(KC_LGUI)) register_code(KC_LGUI); +  if (mods & MOD_BIT(KC_RGUI)) register_code(KC_RGUI); +} + +__attribute__((weak)) +uint16_t hex_to_keycode(uint8_t hex) +{ +  if (hex == 0x0) { +    return KC_0; +  } else if (hex < 0xA) { +    return KC_1 + (hex - 0x1); +  } else { +    return KC_A + (hex - 0xA); +  } +} + +void register_hex(uint16_t hex) { +  for(int i = 3; i >= 0; i--) { +    uint8_t digit = ((hex >> (i*4)) & 0xF); +    register_code(hex_to_keycode(digit)); +    unregister_code(hex_to_keycode(digit)); +  } +} diff --git a/quantum/process_keycode/process_unicode_common.h b/quantum/process_keycode/process_unicode_common.h new file mode 100644 index 0000000000..f5be1da5cb --- /dev/null +++ b/quantum/process_keycode/process_unicode_common.h @@ -0,0 +1,148 @@ +/* Copyright 2017 Jack Humbert + * + * 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 PROCESS_UNICODE_COMMON_H +#define PROCESS_UNICODE_COMMON_H + +#include "quantum.h" + +#ifndef UNICODE_TYPE_DELAY +#define UNICODE_TYPE_DELAY 10 +#endif + +__attribute__ ((unused)) +static uint8_t input_mode; + +void set_unicode_input_mode(uint8_t os_target); +uint8_t get_unicode_input_mode(void); +void unicode_input_start(void); +void unicode_input_finish(void); +void register_hex(uint16_t hex); + +#define UC_OSX 0  // Mac OS X +#define UC_LNX 1  // Linux +#define UC_WIN 2  // Windows 'HexNumpad' +#define UC_BSD 3  // BSD (not implemented) +#define UC_WINC 4 // WinCompose https://github.com/samhocevar/wincompose + +#define UC_BSPC	UC(0x0008) + +#define UC_SPC	UC(0x0020) + +#define UC_EXLM	UC(0x0021) +#define UC_DQUT	UC(0x0022) +#define UC_HASH	UC(0x0023) +#define UC_DLR	UC(0x0024) +#define UC_PERC	UC(0x0025) +#define UC_AMPR	UC(0x0026) +#define UC_QUOT	UC(0x0027) +#define UC_LPRN	UC(0x0028) +#define UC_RPRN	UC(0x0029) +#define UC_ASTR	UC(0x002A) +#define UC_PLUS	UC(0x002B) +#define UC_COMM	UC(0x002C) +#define UC_DASH	UC(0x002D) +#define UC_DOT	UC(0x002E) +#define UC_SLSH	UC(0x002F) + +#define UC_0	UC(0x0030) +#define UC_1	UC(0x0031) +#define UC_2	UC(0x0032) +#define UC_3	UC(0x0033) +#define UC_4	UC(0x0034) +#define UC_5	UC(0x0035) +#define UC_6	UC(0x0036) +#define UC_7	UC(0x0037) +#define UC_8	UC(0x0038) +#define UC_9	UC(0x0039) + +#define UC_COLN UC(0x003A) +#define UC_SCLN UC(0x003B) +#define UC_LT	UC(0x003C) +#define UC_EQL	UC(0x003D) +#define UC_GT	UC(0x003E) +#define UC_QUES	UC(0x003F) +#define UC_AT 	UC(0x0040) + +#define UC_A 	UC(0x0041) +#define UC_B 	UC(0x0042) +#define UC_C 	UC(0x0043) +#define UC_D 	UC(0x0044) +#define UC_E 	UC(0x0045) +#define UC_F 	UC(0x0046) +#define UC_G 	UC(0x0047) +#define UC_H 	UC(0x0048) +#define UC_I 	UC(0x0049) +#define UC_J 	UC(0x004A) +#define UC_K 	UC(0x004B) +#define UC_L 	UC(0x004C) +#define UC_M 	UC(0x004D) +#define UC_N 	UC(0x004E) +#define UC_O 	UC(0x004F) +#define UC_P 	UC(0x0050) +#define UC_Q 	UC(0x0051) +#define UC_R 	UC(0x0052) +#define UC_S 	UC(0x0053) +#define UC_T 	UC(0x0054) +#define UC_U 	UC(0x0055) +#define UC_V 	UC(0x0056) +#define UC_W 	UC(0x0057) +#define UC_X 	UC(0x0058) +#define UC_Y 	UC(0x0059) +#define UC_Z 	UC(0x005A) + +#define UC_LBRC	UC(0x005B) +#define UC_BSLS	UC(0x005C) +#define UC_RBRC	UC(0x005D) +#define UC_CIRM	UC(0x005E) +#define UC_UNDR	UC(0x005F) + +#define UC_GRV 	UC(0x0060) + +#define UC_a 	UC(0x0061) +#define UC_b 	UC(0x0062) +#define UC_c 	UC(0x0063) +#define UC_d 	UC(0x0064) +#define UC_e 	UC(0x0065) +#define UC_f 	UC(0x0066) +#define UC_g 	UC(0x0067) +#define UC_h 	UC(0x0068) +#define UC_i 	UC(0x0069) +#define UC_j 	UC(0x006A) +#define UC_k 	UC(0x006B) +#define UC_l 	UC(0x006C) +#define UC_m 	UC(0x006D) +#define UC_n 	UC(0x006E) +#define UC_o 	UC(0x006F) +#define UC_p 	UC(0x0070) +#define UC_q 	UC(0x0071) +#define UC_r 	UC(0x0072) +#define UC_s 	UC(0x0073) +#define UC_t 	UC(0x0074) +#define UC_u 	UC(0x0075) +#define UC_v 	UC(0x0076) +#define UC_w 	UC(0x0077) +#define UC_x 	UC(0x0078) +#define UC_y 	UC(0x0079) +#define UC_z 	UC(0x007A) + +#define UC_LCBR	UC(0x007B) +#define UC_PIPE	UC(0x007C) +#define UC_RCBR	UC(0x007D) +#define UC_TILD	UC(0x007E) +#define UC_DEL	UC(0x007F) + +#endif diff --git a/quantum/process_keycode/process_unicodemap.c b/quantum/process_keycode/process_unicodemap.c new file mode 100644 index 0000000000..75f35112b1 --- /dev/null +++ b/quantum/process_keycode/process_unicodemap.c @@ -0,0 +1,72 @@ +/* Copyright 2017 Jack Humbert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include "process_unicodemap.h" +#include "process_unicode_common.h" + +__attribute__((weak)) +const uint32_t PROGMEM unicode_map[] = { +}; + +void register_hex32(uint32_t hex) { +  bool onzerostart = true; +  for(int i = 7; i >= 0; i--) { +    if (i <= 3) { +      onzerostart = false; +    } +    uint8_t digit = ((hex >> (i*4)) & 0xF); +    if (digit == 0) { +      if (!onzerostart) { +        register_code(hex_to_keycode(digit)); +        unregister_code(hex_to_keycode(digit)); +      } +    } else { +      register_code(hex_to_keycode(digit)); +      unregister_code(hex_to_keycode(digit)); +      onzerostart = false; +    } +  } +} + +__attribute__((weak)) +void unicode_map_input_error() {} + +bool process_unicode_map(uint16_t keycode, keyrecord_t *record) { +  uint8_t input_mode = get_unicode_input_mode(); +  if ((keycode & QK_UNICODE_MAP) == QK_UNICODE_MAP && record->event.pressed) { +    const uint32_t* map = unicode_map; +    uint16_t index = keycode - QK_UNICODE_MAP; +    uint32_t code = pgm_read_dword(&map[index]); +    if (code > 0xFFFF && code <= 0x10ffff && input_mode == UC_OSX) { +      // Convert to UTF-16 surrogate pair +      code -= 0x10000; +      uint32_t lo = code & 0x3ff; +      uint32_t hi = (code & 0xffc00) >> 10; +      unicode_input_start(); +      register_hex32(hi + 0xd800); +      register_hex32(lo + 0xdc00); +      unicode_input_finish(); +    } else if ((code > 0x10ffff && input_mode == UC_OSX) || (code > 0xFFFFF && input_mode == UC_LNX)) { +      // when character is out of range supported by the OS +      unicode_map_input_error(); +    } else { +      unicode_input_start(); +      register_hex32(code); +      unicode_input_finish(); +    } +  } +  return true; +} diff --git a/quantum/process_keycode/process_unicodemap.h b/quantum/process_keycode/process_unicodemap.h new file mode 100644 index 0000000000..929c88c0b6 --- /dev/null +++ b/quantum/process_keycode/process_unicodemap.h @@ -0,0 +1,25 @@ +/* Copyright 2017 Jack Humbert + * + * 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 PROCESS_UNICODEMAP_H +#define PROCESS_UNICODEMAP_H + +#include "quantum.h" +#include "process_unicode_common.h" + +void unicode_map_input_error(void); +bool process_unicode_map(uint16_t keycode, keyrecord_t *record); +#endif diff --git a/quantum/quantum.c b/quantum/quantum.c new file mode 100644 index 0000000000..36e586d316 --- /dev/null +++ b/quantum/quantum.c @@ -0,0 +1,1060 @@ +/* Copyright 2016-2017 Jack Humbert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include "quantum.h" +#ifdef PROTOCOL_LUFA +#include "outputselect.h" +#endif + +#ifndef TAPPING_TERM +#define TAPPING_TERM 200 +#endif + +#include "backlight.h" +extern backlight_config_t backlight_config; + +#ifdef FAUXCLICKY_ENABLE +#include "fauxclicky.h" +#endif + +static void do_code16 (uint16_t code, void (*f) (uint8_t)) { +  switch (code) { +  case QK_MODS ... QK_MODS_MAX: +    break; +  default: +    return; +  } + +  if (code & QK_LCTL) +    f(KC_LCTL); +  if (code & QK_LSFT) +    f(KC_LSFT); +  if (code & QK_LALT) +    f(KC_LALT); +  if (code & QK_LGUI) +    f(KC_LGUI); + +  if (code < QK_RMODS_MIN) return; + +  if (code & QK_RCTL) +    f(KC_RCTL); +  if (code & QK_RSFT) +    f(KC_RSFT); +  if (code & QK_RALT) +    f(KC_RALT); +  if (code & QK_RGUI) +    f(KC_RGUI); +} + +static inline void qk_register_weak_mods(uint8_t kc) { +    add_weak_mods(MOD_BIT(kc)); +    send_keyboard_report(); +} + +static inline void qk_unregister_weak_mods(uint8_t kc) { +    del_weak_mods(MOD_BIT(kc)); +    send_keyboard_report(); +} + +static inline void qk_register_mods(uint8_t kc) { +    add_weak_mods(MOD_BIT(kc)); +    send_keyboard_report(); +} + +static inline void qk_unregister_mods(uint8_t kc) { +    del_weak_mods(MOD_BIT(kc)); +    send_keyboard_report(); +} + +void register_code16 (uint16_t code) { +  if (IS_MOD(code) || code == KC_NO) { +      do_code16 (code, qk_register_mods); +  } else { +      do_code16 (code, qk_register_weak_mods); +  } +  register_code (code); +} + +void unregister_code16 (uint16_t code) { +  unregister_code (code); +  if (IS_MOD(code) || code == KC_NO) { +      do_code16 (code, qk_unregister_mods); +  } else { +      do_code16 (code, qk_unregister_weak_mods); +  } +} + +__attribute__ ((weak)) +bool process_action_kb(keyrecord_t *record) { +  return true; +} + +__attribute__ ((weak)) +bool process_record_kb(uint16_t keycode, keyrecord_t *record) { +  return process_record_user(keycode, record); +} + +__attribute__ ((weak)) +bool process_record_user(uint16_t keycode, keyrecord_t *record) { +  return true; +} + +void reset_keyboard(void) { +  clear_keyboard(); +#if defined(AUDIO_ENABLE) || (defined(MIDI_ENABLE) && defined(MIDI_ENABLE_BASIC)) +  music_all_notes_off(); +  shutdown_user(); +#endif +  wait_ms(250); +#ifdef CATERINA_BOOTLOADER +  *(uint16_t *)0x0800 = 0x7777; // these two are a-star-specific +#endif +  bootloader_jump(); +} + +// Shift / paren setup + +#ifndef LSPO_KEY +  #define LSPO_KEY KC_9 +#endif +#ifndef RSPC_KEY +  #define RSPC_KEY KC_0 +#endif + +static bool shift_interrupted[2] = {0, 0}; +static uint16_t scs_timer[2] = {0, 0}; + +bool process_record_quantum(keyrecord_t *record) { + +  /* This gets the keycode from the key pressed */ +  keypos_t key = record->event.key; +  uint16_t keycode; + +  #if !defined(NO_ACTION_LAYER) && defined(PREVENT_STUCK_MODIFIERS) +    /* TODO: Use store_or_get_action() or a similar function. */ +    if (!disable_action_cache) { +      uint8_t layer; + +      if (record->event.pressed) { +        layer = layer_switch_get_layer(key); +        update_source_layers_cache(key, layer); +      } else { +        layer = read_source_layers_cache(key); +      } +      keycode = keymap_key_to_keycode(layer, key); +    } else +  #endif +    keycode = keymap_key_to_keycode(layer_switch_get_layer(key), key); + +    // This is how you use actions here +    // if (keycode == KC_LEAD) { +    //   action_t action; +    //   action.code = ACTION_DEFAULT_LAYER_SET(0); +    //   process_action(record, action); +    //   return false; +    // } + +  if (!( +    process_record_kb(keycode, record) && +  #if defined(MIDI_ENABLE) && defined(MIDI_ADVANCED) +    process_midi(keycode, record) && +  #endif +  #ifdef AUDIO_ENABLE +    process_audio(keycode, record) && +  #endif +  #if defined(AUDIO_ENABLE) || (defined(MIDI_ENABLE) && defined(MIDI_BASIC)) +    process_music(keycode, record) && +  #endif +  #ifdef TAP_DANCE_ENABLE +    process_tap_dance(keycode, record) && +  #endif +  #ifndef DISABLE_LEADER +    process_leader(keycode, record) && +  #endif +  #ifndef DISABLE_CHORDING +    process_chording(keycode, record) && +  #endif +  #ifdef COMBO_ENABLE +    process_combo(keycode, record) && +  #endif +  #ifdef UNICODE_ENABLE +    process_unicode(keycode, record) && +  #endif +  #ifdef UCIS_ENABLE +    process_ucis(keycode, record) && +  #endif +  #ifdef PRINTING_ENABLE +    process_printer(keycode, record) && +  #endif +  #ifdef UNICODEMAP_ENABLE +    process_unicode_map(keycode, record) && +  #endif +      true)) { +    return false; +  } + +  // Shift / paren setup + +  switch(keycode) { +    case RESET: +      if (record->event.pressed) { +        reset_keyboard(); +      } +	  return false; +      break; +    case DEBUG: +      if (record->event.pressed) { +          print("\nDEBUG: enabled.\n"); +          debug_enable = true; +      } +	  return false; +      break; +  #ifdef FAUXCLICKY_ENABLE +  case FC_TOG: +    if (record->event.pressed) { +      FAUXCLICKY_TOGGLE; +    } +    return false; +    break; +  case FC_ON: +    if (record->event.pressed) { +      FAUXCLICKY_ON; +    } +    return false; +    break; +  case FC_OFF: +    if (record->event.pressed) { +      FAUXCLICKY_OFF; +    } +    return false; +    break; +  #endif +	#ifdef RGBLIGHT_ENABLE +	case RGB_TOG: +		if (record->event.pressed) { +			rgblight_toggle(); +      } +	  return false; +      break; +	case RGB_MOD: +		if (record->event.pressed) { +			rgblight_step(); +      } +	  return false; +      break; +	case RGB_HUI: +		if (record->event.pressed) { +			rgblight_increase_hue(); +      } +	  return false; +      break; +	case RGB_HUD: +		if (record->event.pressed) { +			rgblight_decrease_hue(); +      } +	  return false; +      break; +	case RGB_SAI: +		if (record->event.pressed) { +			rgblight_increase_sat(); +      } +	  return false; +      break; +	case RGB_SAD: +		if (record->event.pressed) { +			rgblight_decrease_sat(); +      } +	  return false; +      break; +	case RGB_VAI: +		if (record->event.pressed) { +			rgblight_increase_val(); +      } +	  return false; +      break; +	case RGB_VAD: +		if (record->event.pressed) { +			rgblight_decrease_val(); +      } +	  return false; +      break; +	#endif +    #ifdef PROTOCOL_LUFA +    case OUT_AUTO: +      if (record->event.pressed) { +        set_output(OUTPUT_AUTO); +      } +      return false; +      break; +    case OUT_USB: +      if (record->event.pressed) { +        set_output(OUTPUT_USB); +      } +      return false; +      break; +    #ifdef BLUETOOTH_ENABLE +    case OUT_BT: +      if (record->event.pressed) { +        set_output(OUTPUT_BLUETOOTH); +      } +      return false; +      break; +    #endif +    #endif +    case MAGIC_SWAP_CONTROL_CAPSLOCK ... MAGIC_TOGGLE_NKRO: +      if (record->event.pressed) { +        // MAGIC actions (BOOTMAGIC without the boot) +        if (!eeconfig_is_enabled()) { +            eeconfig_init(); +        } +        /* keymap config */ +        keymap_config.raw = eeconfig_read_keymap(); +        switch (keycode) +        { +          case MAGIC_SWAP_CONTROL_CAPSLOCK: +            keymap_config.swap_control_capslock = true; +            break; +          case MAGIC_CAPSLOCK_TO_CONTROL: +            keymap_config.capslock_to_control = true; +            break; +          case MAGIC_SWAP_LALT_LGUI: +            keymap_config.swap_lalt_lgui = true; +            break; +          case MAGIC_SWAP_RALT_RGUI: +            keymap_config.swap_ralt_rgui = true; +            break; +          case MAGIC_NO_GUI: +            keymap_config.no_gui = true; +            break; +          case MAGIC_SWAP_GRAVE_ESC: +            keymap_config.swap_grave_esc = true; +            break; +          case MAGIC_SWAP_BACKSLASH_BACKSPACE: +            keymap_config.swap_backslash_backspace = true; +            break; +          case MAGIC_HOST_NKRO: +            keymap_config.nkro = true; +            break; +          case MAGIC_SWAP_ALT_GUI: +            keymap_config.swap_lalt_lgui = true; +            keymap_config.swap_ralt_rgui = true; +            break; +          case MAGIC_UNSWAP_CONTROL_CAPSLOCK: +            keymap_config.swap_control_capslock = false; +            break; +          case MAGIC_UNCAPSLOCK_TO_CONTROL: +            keymap_config.capslock_to_control = false; +            break; +          case MAGIC_UNSWAP_LALT_LGUI: +            keymap_config.swap_lalt_lgui = false; +            break; +          case MAGIC_UNSWAP_RALT_RGUI: +            keymap_config.swap_ralt_rgui = false; +            break; +          case MAGIC_UNNO_GUI: +            keymap_config.no_gui = false; +            break; +          case MAGIC_UNSWAP_GRAVE_ESC: +            keymap_config.swap_grave_esc = false; +            break; +          case MAGIC_UNSWAP_BACKSLASH_BACKSPACE: +            keymap_config.swap_backslash_backspace = false; +            break; +          case MAGIC_UNHOST_NKRO: +            keymap_config.nkro = false; +            break; +          case MAGIC_UNSWAP_ALT_GUI: +            keymap_config.swap_lalt_lgui = false; +            keymap_config.swap_ralt_rgui = false; +            break; +          case MAGIC_TOGGLE_NKRO: +            keymap_config.nkro = !keymap_config.nkro; +            break; +          default: +            break; +        } +        eeconfig_update_keymap(keymap_config.raw); +        clear_keyboard(); // clear to prevent stuck keys + +        return false; +      } +      break; +    case KC_LSPO: { +      if (record->event.pressed) { +        shift_interrupted[0] = false; +        scs_timer[0] = timer_read (); +        register_mods(MOD_BIT(KC_LSFT)); +      } +      else { +        #ifdef DISABLE_SPACE_CADET_ROLLOVER +          if (get_mods() & MOD_BIT(KC_RSFT)) { +            shift_interrupted[0] = true; +            shift_interrupted[1] = true; +          } +        #endif +        if (!shift_interrupted[0] && timer_elapsed(scs_timer[0]) < TAPPING_TERM) { +          register_code(LSPO_KEY); +          unregister_code(LSPO_KEY); +        } +        unregister_mods(MOD_BIT(KC_LSFT)); +      } +      return false; +      // break; +    } + +    case KC_RSPC: { +      if (record->event.pressed) { +        shift_interrupted[1] = false; +        scs_timer[1] = timer_read (); +        register_mods(MOD_BIT(KC_RSFT)); +      } +      else { +        #ifdef DISABLE_SPACE_CADET_ROLLOVER +          if (get_mods() & MOD_BIT(KC_LSFT)) { +            shift_interrupted[0] = true; +            shift_interrupted[1] = true; +          } +        #endif +        if (!shift_interrupted[1] && timer_elapsed(scs_timer[1]) < TAPPING_TERM) { +          register_code(RSPC_KEY); +          unregister_code(RSPC_KEY); +        } +        unregister_mods(MOD_BIT(KC_RSFT)); +      } +      return false; +      // break; +    } +    case GRAVE_ESC: { +      void (*method)(uint8_t) = (record->event.pressed) ? &add_key : &del_key; +      uint8_t shifted = get_mods() & ((MOD_BIT(KC_LSHIFT)|MOD_BIT(KC_RSHIFT) +                                      |MOD_BIT(KC_LGUI)|MOD_BIT(KC_RGUI))); + +      method(shifted ? KC_GRAVE : KC_ESCAPE); +      send_keyboard_report();  +    } +    default: { +      shift_interrupted[0] = true; +      shift_interrupted[1] = true; +      break; +    } +  } + +  return process_action_kb(record); +} + +__attribute__ ((weak)) +const bool ascii_to_shift_lut[0x80] PROGMEM = { +    0, 0, 0, 0, 0, 0, 0, 0, +    0, 0, 0, 0, 0, 0, 0, 0, +    0, 0, 0, 0, 0, 0, 0, 0, +    0, 0, 0, 0, 0, 0, 0, 0, +    0, 1, 1, 1, 1, 1, 1, 0, +    1, 1, 1, 1, 0, 0, 0, 0, +    0, 0, 0, 0, 0, 0, 0, 0, +    0, 0, 1, 0, 1, 0, 1, 1, +    1, 1, 1, 1, 1, 1, 1, 1, +    1, 1, 1, 1, 1, 1, 1, 1, +    1, 1, 1, 1, 1, 1, 1, 1, +    1, 1, 1, 0, 0, 0, 1, 1, +    0, 0, 0, 0, 0, 0, 0, 0, +    0, 0, 0, 0, 0, 0, 0, 0, +    0, 0, 0, 0, 0, 0, 0, 0, +    0, 0, 0, 1, 1, 1, 1, 0 +}; + +__attribute__ ((weak)) +const uint8_t ascii_to_keycode_lut[0x80] PROGMEM = { +    0, 0, 0, 0, 0, 0, 0, 0, +    KC_BSPC, KC_TAB, KC_ENT, 0, 0, 0, 0, 0, +    0, 0, 0, 0, 0, 0, 0, 0, +    0, 0, 0, KC_ESC, 0, 0, 0, 0, +    KC_SPC, KC_1, KC_QUOT, KC_3, KC_4, KC_5, KC_7, KC_QUOT, +    KC_9, KC_0, KC_8, KC_EQL, KC_COMM, KC_MINS, KC_DOT, KC_SLSH, +    KC_0, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, +    KC_8, KC_9, KC_SCLN, KC_SCLN, KC_COMM, KC_EQL, KC_DOT, KC_SLSH, +    KC_2, KC_A, KC_B, KC_C, KC_D, KC_E, KC_F, KC_G, +    KC_H, KC_I, KC_J, KC_K, KC_L, KC_M, KC_N, KC_O, +    KC_P, KC_Q, KC_R, KC_S, KC_T, KC_U, KC_V, KC_W, +    KC_X, KC_Y, KC_Z, KC_LBRC, KC_BSLS, KC_RBRC, KC_6, KC_MINS, +    KC_GRV, KC_A, KC_B, KC_C, KC_D, KC_E, KC_F, KC_G, +    KC_H, KC_I, KC_J, KC_K, KC_L, KC_M, KC_N, KC_O, +    KC_P, KC_Q, KC_R, KC_S, KC_T, KC_U, KC_V, KC_W, +    KC_X, KC_Y, KC_Z, KC_LBRC, KC_BSLS, KC_RBRC, KC_GRV, KC_DEL +}; + +void send_string(const char *str) { +  send_string_with_delay(str, 0); +} + +void send_string_with_delay(const char *str, uint8_t interval) { +    while (1) { +        uint8_t keycode; +        uint8_t ascii_code = pgm_read_byte(str); +        if (!ascii_code) break; +        keycode = pgm_read_byte(&ascii_to_keycode_lut[ascii_code]); +        if (pgm_read_byte(&ascii_to_shift_lut[ascii_code])) { +            register_code(KC_LSFT); +            register_code(keycode); +            unregister_code(keycode); +            unregister_code(KC_LSFT); +        } +        else { +            register_code(keycode); +            unregister_code(keycode); +        } +        ++str; +        // interval +        { uint8_t ms = interval; while (ms--) wait_ms(1); } +    } +} + +void update_tri_layer(uint8_t layer1, uint8_t layer2, uint8_t layer3) { +  if (IS_LAYER_ON(layer1) && IS_LAYER_ON(layer2)) { +    layer_on(layer3); +  } else { +    layer_off(layer3); +  } +} + +void tap_random_base64(void) { +  #if defined(__AVR_ATmega32U4__) +    uint8_t key = (TCNT0 + TCNT1 + TCNT3 + TCNT4) % 64; +  #else +    uint8_t key = rand() % 64; +  #endif +  switch (key) { +    case 0 ... 25: +      register_code(KC_LSFT); +      register_code(key + KC_A); +      unregister_code(key + KC_A); +      unregister_code(KC_LSFT); +      break; +    case 26 ... 51: +      register_code(key - 26 + KC_A); +      unregister_code(key - 26 + KC_A); +      break; +    case 52: +      register_code(KC_0); +      unregister_code(KC_0); +      break; +    case 53 ... 61: +      register_code(key - 53 + KC_1); +      unregister_code(key - 53 + KC_1); +      break; +    case 62: +      register_code(KC_LSFT); +      register_code(KC_EQL); +      unregister_code(KC_EQL); +      unregister_code(KC_LSFT); +      break; +    case 63: +      register_code(KC_SLSH); +      unregister_code(KC_SLSH); +      break; +  } +} + +void matrix_init_quantum() { +  #ifdef BACKLIGHT_ENABLE +    backlight_init_ports(); +  #endif +  matrix_init_kb(); +} + +void matrix_scan_quantum() { +  #ifdef AUDIO_ENABLE +    matrix_scan_music(); +  #endif + +  #ifdef TAP_DANCE_ENABLE +    matrix_scan_tap_dance(); +  #endif + +  #ifdef COMBO_ENABLE +    matrix_scan_combo(); +  #endif + +  #if defined(BACKLIGHT_ENABLE) && defined(BACKLIGHT_PIN) +    backlight_task(); +  #endif + +  matrix_scan_kb(); +} + +#if defined(BACKLIGHT_ENABLE) && defined(BACKLIGHT_PIN) + +static const uint8_t backlight_pin = BACKLIGHT_PIN; + +#if BACKLIGHT_PIN == B7 +#  define COM1x1 COM1C1 +#  define OCR1x  OCR1C +#elif BACKLIGHT_PIN == B6 +#  define COM1x1 COM1B1 +#  define OCR1x  OCR1B +#elif BACKLIGHT_PIN == B5 +#  define COM1x1 COM1A1 +#  define OCR1x  OCR1A +#else +#  define NO_BACKLIGHT_CLOCK +#endif + +#ifndef BACKLIGHT_ON_STATE +#define BACKLIGHT_ON_STATE 0 +#endif + +__attribute__ ((weak)) +void backlight_init_ports(void) +{ + +  // Setup backlight pin as output and output to on state. +  // DDRx |= n +  _SFR_IO8((backlight_pin >> 4) + 1) |= _BV(backlight_pin & 0xF); +  #if BACKLIGHT_ON_STATE == 0 +    // PORTx &= ~n +    _SFR_IO8((backlight_pin >> 4) + 2) &= ~_BV(backlight_pin & 0xF); +  #else +    // PORTx |= n +    _SFR_IO8((backlight_pin >> 4) + 2) |= _BV(backlight_pin & 0xF); +  #endif + +  #ifndef NO_BACKLIGHT_CLOCK +    // Use full 16-bit resolution. +    ICR1 = 0xFFFF; + +    // I could write a wall of text here to explain... but TL;DW +    // Go read the ATmega32u4 datasheet. +    // And this: http://blog.saikoled.com/post/43165849837/secret-konami-cheat-code-to-high-resolution-pwm-on + +    // Pin PB7 = OCR1C (Timer 1, Channel C) +    // Compare Output Mode = Clear on compare match, Channel C = COM1C1=1 COM1C0=0 +    // (i.e. start high, go low when counter matches.) +    // WGM Mode 14 (Fast PWM) = WGM13=1 WGM12=1 WGM11=1 WGM10=0 +    // Clock Select = clk/1 (no prescaling) = CS12=0 CS11=0 CS10=1 + +    TCCR1A = _BV(COM1x1) | _BV(WGM11); // = 0b00001010; +    TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10); // = 0b00011001; +  #endif + +  backlight_init(); +  #ifdef BACKLIGHT_BREATHING +    breathing_defaults(); +  #endif +} + +__attribute__ ((weak)) +void backlight_set(uint8_t level) +{ +  // Prevent backlight blink on lowest level +  // #if BACKLIGHT_ON_STATE == 0 +  //   // PORTx &= ~n +  //   _SFR_IO8((backlight_pin >> 4) + 2) &= ~_BV(backlight_pin & 0xF); +  // #else +  //   // PORTx |= n +  //   _SFR_IO8((backlight_pin >> 4) + 2) |= _BV(backlight_pin & 0xF); +  // #endif + +  if ( level == 0 ) { +    #ifndef NO_BACKLIGHT_CLOCK +      // Turn off PWM control on backlight pin, revert to output low. +      TCCR1A &= ~(_BV(COM1x1)); +      OCR1x = 0x0; +    #else +      // #if BACKLIGHT_ON_STATE == 0 +      //   // PORTx |= n +      //   _SFR_IO8((backlight_pin >> 4) + 2) |= _BV(backlight_pin & 0xF); +      // #else +      //   // PORTx &= ~n +      //   _SFR_IO8((backlight_pin >> 4) + 2) &= ~_BV(backlight_pin & 0xF); +      // #endif +    #endif +  }  +  #ifndef NO_BACKLIGHT_CLOCK +    else if ( level == BACKLIGHT_LEVELS ) { +      // Turn on PWM control of backlight pin +      TCCR1A |= _BV(COM1x1); +      // Set the brightness +      OCR1x = 0xFFFF; +    }  +    else { +      // Turn on PWM control of backlight pin +      TCCR1A |= _BV(COM1x1); +      // Set the brightness +      OCR1x = 0xFFFF >> ((BACKLIGHT_LEVELS - level) * ((BACKLIGHT_LEVELS + 1) / 2)); +    } +  #endif + +  #ifdef BACKLIGHT_BREATHING +    breathing_intensity_default(); +  #endif +} + +uint8_t backlight_tick = 0; + +void backlight_task(void) { +  #ifdef NO_BACKLIGHT_CLOCK +  if ((0xFFFF >> ((BACKLIGHT_LEVELS - backlight_config.level) * ((BACKLIGHT_LEVELS + 1) / 2))) & (1 << backlight_tick)) {  +    #if BACKLIGHT_ON_STATE == 0 +      // PORTx &= ~n +      _SFR_IO8((backlight_pin >> 4) + 2) &= ~_BV(backlight_pin & 0xF); +    #else +      // PORTx |= n +      _SFR_IO8((backlight_pin >> 4) + 2) |= _BV(backlight_pin & 0xF); +    #endif +  } else { +    #if BACKLIGHT_ON_STATE == 0 +      // PORTx |= n +      _SFR_IO8((backlight_pin >> 4) + 2) |= _BV(backlight_pin & 0xF); +    #else +      // PORTx &= ~n +      _SFR_IO8((backlight_pin >> 4) + 2) &= ~_BV(backlight_pin & 0xF); +    #endif +  } +  backlight_tick = (backlight_tick + 1) % 16; +  #endif +} + +#ifdef BACKLIGHT_BREATHING + +#define BREATHING_NO_HALT  0 +#define BREATHING_HALT_OFF 1 +#define BREATHING_HALT_ON  2 + +static uint8_t breath_intensity; +static uint8_t breath_speed; +static uint16_t breathing_index; +static uint8_t breathing_halt; + +void breathing_enable(void) +{ +    if (get_backlight_level() == 0) +    { +        breathing_index = 0; +    } +    else +    { +        // Set breathing_index to be at the midpoint (brightest point) +        breathing_index = 0x20 << breath_speed; +    } + +    breathing_halt = BREATHING_NO_HALT; + +    // Enable breathing interrupt +    TIMSK1 |= _BV(OCIE1A); +} + +void breathing_pulse(void) +{ +    if (get_backlight_level() == 0) +    { +        breathing_index = 0; +    } +    else +    { +        // Set breathing_index to be at the midpoint + 1 (brightest point) +        breathing_index = 0x21 << breath_speed; +    } + +    breathing_halt = BREATHING_HALT_ON; + +    // Enable breathing interrupt +    TIMSK1 |= _BV(OCIE1A); +} + +void breathing_disable(void) +{ +    // Disable breathing interrupt +    TIMSK1 &= ~_BV(OCIE1A); +    backlight_set(get_backlight_level()); +} + +void breathing_self_disable(void) +{ +    if (get_backlight_level() == 0) +    { +        breathing_halt = BREATHING_HALT_OFF; +    } +    else +    { +        breathing_halt = BREATHING_HALT_ON; +    } + +    //backlight_set(get_backlight_level()); +} + +void breathing_toggle(void) +{ +    if (!is_breathing()) +    { +        if (get_backlight_level() == 0) +        { +            breathing_index = 0; +        } +        else +        { +            // Set breathing_index to be at the midpoint + 1 (brightest point) +            breathing_index = 0x21 << breath_speed; +        } + +        breathing_halt = BREATHING_NO_HALT; +    } + +    // Toggle breathing interrupt +    TIMSK1 ^= _BV(OCIE1A); + +    // Restore backlight level +    if (!is_breathing()) +    { +        backlight_set(get_backlight_level()); +    } +} + +bool is_breathing(void) +{ +    return (TIMSK1 && _BV(OCIE1A)); +} + +void breathing_intensity_default(void) +{ +    //breath_intensity = (uint8_t)((uint16_t)100 * (uint16_t)get_backlight_level() / (uint16_t)BACKLIGHT_LEVELS); +    breath_intensity = ((BACKLIGHT_LEVELS - get_backlight_level()) * ((BACKLIGHT_LEVELS + 1) / 2)); +} + +void breathing_intensity_set(uint8_t value) +{ +    breath_intensity = value; +} + +void breathing_speed_default(void) +{ +    breath_speed = 4; +} + +void breathing_speed_set(uint8_t value) +{ +    bool is_breathing_now = is_breathing(); +    uint8_t old_breath_speed = breath_speed; + +    if (is_breathing_now) +    { +        // Disable breathing interrupt +        TIMSK1 &= ~_BV(OCIE1A); +    } + +    breath_speed = value; + +    if (is_breathing_now) +    { +        // Adjust index to account for new speed +        breathing_index = (( (uint8_t)( (breathing_index) >> old_breath_speed ) ) & 0x3F) << breath_speed; + +        // Enable breathing interrupt +        TIMSK1 |= _BV(OCIE1A); +    } + +} + +void breathing_speed_inc(uint8_t value) +{ +    if ((uint16_t)(breath_speed - value) > 10 ) +    { +        breathing_speed_set(0); +    } +    else +    { +        breathing_speed_set(breath_speed - value); +    } +} + +void breathing_speed_dec(uint8_t value) +{ +    if ((uint16_t)(breath_speed + value) > 10 ) +    { +        breathing_speed_set(10); +    } +    else +    { +        breathing_speed_set(breath_speed + value); +    } +} + +void breathing_defaults(void) +{ +    breathing_intensity_default(); +    breathing_speed_default(); +    breathing_halt = BREATHING_NO_HALT; +} + +/* Breathing Sleep LED brighness(PWM On period) table + * (64[steps] * 4[duration]) / 64[PWM periods/s] = 4 second breath cycle + * + * http://www.wolframalpha.com/input/?i=%28sin%28+x%2F64*pi%29**8+*+255%2C+x%3D0+to+63 + * (0..63).each {|x| p ((sin(x/64.0*PI)**8)*255).to_i } + */ +static const uint8_t breathing_table[64] PROGMEM = { +  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   1,   2,   4,   6,  10, + 15,  23,  32,  44,  58,  74,  93, 113, 135, 157, 179, 199, 218, 233, 245, 252, +255, 252, 245, 233, 218, 199, 179, 157, 135, 113,  93,  74,  58,  44,  32,  23, + 15,  10,   6,   4,   2,   1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, +}; + +ISR(TIMER1_COMPA_vect) +{ +    // OCR1x = (pgm_read_byte(&breathing_table[ ( (uint8_t)( (breathing_index++) >> breath_speed ) ) & 0x3F ] )) * breath_intensity; + + +    uint8_t local_index = ( (uint8_t)( (breathing_index++) >> breath_speed ) ) & 0x3F; + +    if (((breathing_halt == BREATHING_HALT_ON) && (local_index == 0x20)) || ((breathing_halt == BREATHING_HALT_OFF) && (local_index == 0x3F))) +    { +        // Disable breathing interrupt +        TIMSK1 &= ~_BV(OCIE1A); +    } + +    OCR1x = (uint16_t)(((uint16_t)pgm_read_byte(&breathing_table[local_index]) * 257)) >> breath_intensity; + +} + + + +#endif // breathing + +#else // backlight + +__attribute__ ((weak)) +void backlight_init_ports(void) +{ + +} + +__attribute__ ((weak)) +void backlight_set(uint8_t level) +{ + +} + +#endif // backlight + + +// Functions for spitting out values +// + +void send_dword(uint32_t number) { // this might not actually work +    uint16_t word = (number >> 16); +    send_word(word); +    send_word(number & 0xFFFFUL); +} + +void send_word(uint16_t number) { +    uint8_t byte = number >> 8; +    send_byte(byte); +    send_byte(number & 0xFF); +} + +void send_byte(uint8_t number) { +    uint8_t nibble = number >> 4; +    send_nibble(nibble); +    send_nibble(number & 0xF); +} + +void send_nibble(uint8_t number) { +    switch (number) { +        case 0: +            register_code(KC_0); +            unregister_code(KC_0); +            break; +        case 1 ... 9: +            register_code(KC_1 + (number - 1)); +            unregister_code(KC_1 + (number - 1)); +            break; +        case 0xA ... 0xF: +            register_code(KC_A + (number - 0xA)); +            unregister_code(KC_A + (number - 0xA)); +            break; +    } +} + + +__attribute__((weak)) +uint16_t hex_to_keycode(uint8_t hex) +{ +  if (hex == 0x0) { +    return KC_0; +  } else if (hex < 0xA) { +    return KC_1 + (hex - 0x1); +  } else { +    return KC_A + (hex - 0xA); +  } +} + +void api_send_unicode(uint32_t unicode) { +#ifdef API_ENABLE +    uint8_t chunk[4]; +    dword_to_bytes(unicode, chunk); +    MT_SEND_DATA(DT_UNICODE, chunk, 5); +#endif +} + +__attribute__ ((weak)) +void led_set_user(uint8_t usb_led) { + +} + +__attribute__ ((weak)) +void led_set_kb(uint8_t usb_led) { +    led_set_user(usb_led); +} + +__attribute__ ((weak)) +void led_init_ports(void) +{ + +} + +__attribute__ ((weak)) +void led_set(uint8_t usb_led) +{ + +  // Example LED Code +  // +    // // Using PE6 Caps Lock LED +    // if (usb_led & (1<<USB_LED_CAPS_LOCK)) +    // { +    //     // Output high. +    //     DDRE |= (1<<6); +    //     PORTE |= (1<<6); +    // } +    // else +    // { +    //     // Output low. +    //     DDRE &= ~(1<<6); +    //     PORTE &= ~(1<<6); +    // } + +  led_set_kb(usb_led); +} + + +//------------------------------------------------------------------------------ +// Override these functions in your keymap file to play different tunes on +// different events such as startup and bootloader jump + +__attribute__ ((weak)) +void startup_user() {} + +__attribute__ ((weak)) +void shutdown_user() {} + +//------------------------------------------------------------------------------ diff --git a/quantum/quantum.h b/quantum/quantum.h new file mode 100644 index 0000000000..e00fe23464 --- /dev/null +++ b/quantum/quantum.h @@ -0,0 +1,159 @@ +/* Copyright 2016-2017 Erez Zukerman, Jack Humbert + * + * 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 QUANTUM_H +#define QUANTUM_H + +#if defined(__AVR__) +#include <avr/pgmspace.h> +#include <avr/io.h> +#include <avr/interrupt.h> +#endif +#include "wait.h" +#include "matrix.h" +#include "keymap.h" +#ifdef BACKLIGHT_ENABLE +    #include "backlight.h" +#endif +#ifdef RGBLIGHT_ENABLE +  #include "rgblight.h" +#endif +#include "action_layer.h" +#include "eeconfig.h" +#include <stddef.h> +#include "bootloader.h" +#include "timer.h" +#include "config_common.h" +#include "led.h" +#include "action_util.h" +#include <stdlib.h> +#include "print.h" + + +extern uint32_t default_layer_state; + +#ifndef NO_ACTION_LAYER +	extern uint32_t layer_state; +#endif + +#ifdef MIDI_ENABLE +	#include <lufa.h> +#ifdef MIDI_ADVANCED +	#include "process_midi.h" +#endif +#endif // MIDI_ENABLE + +#ifdef AUDIO_ENABLE + 	#include "process_audio.h" +#endif + +#if defined(AUDIO_ENABLE) || (defined(MIDI_ENABLE) && defined(MIDI_BASIC)) +	#include "process_music.h" +#endif + +#ifndef DISABLE_LEADER +	#include "process_leader.h" +#endif + +#define DISABLE_CHORDING +#ifndef DISABLE_CHORDING +	#include "process_chording.h" +#endif + +#ifdef UNICODE_ENABLE +	#include "process_unicode.h" +#endif + +#ifdef UCIS_ENABLE +	#include "process_ucis.h" +#endif + +#ifdef UNICODEMAP_ENABLE +	#include "process_unicodemap.h" +#endif + +#include "process_tap_dance.h" + +#ifdef PRINTING_ENABLE +	#include "process_printer.h" +#endif + +#ifdef COMBO_ENABLE +	#include "process_combo.h" +#endif + +#define SEND_STRING(str) send_string(PSTR(str)) +extern const bool ascii_to_shift_lut[0x80]; +extern const uint8_t ascii_to_keycode_lut[0x80]; +void send_string(const char *str); +void send_string_with_delay(const char *str, uint8_t interval); + +// For tri-layer +void update_tri_layer(uint8_t layer1, uint8_t layer2, uint8_t layer3); + +void tap_random_base64(void); + +#define IS_LAYER_ON(layer)  (layer_state & (1UL << (layer))) +#define IS_LAYER_OFF(layer) (~layer_state & (1UL << (layer))) + +void matrix_init_kb(void); +void matrix_scan_kb(void); +void matrix_init_user(void); +void matrix_scan_user(void); +bool process_action_kb(keyrecord_t *record); +bool process_record_kb(uint16_t keycode, keyrecord_t *record); +bool process_record_user(uint16_t keycode, keyrecord_t *record); + +void reset_keyboard(void); + +void startup_user(void); +void shutdown_user(void); + +void register_code16 (uint16_t code); +void unregister_code16 (uint16_t code); + +#ifdef BACKLIGHT_ENABLE +void backlight_init_ports(void); +void backlight_task(void); + +#ifdef BACKLIGHT_BREATHING +void breathing_enable(void); +void breathing_pulse(void); +void breathing_disable(void); +void breathing_self_disable(void); +void breathing_toggle(void); +bool is_breathing(void); + +void breathing_defaults(void); +void breathing_intensity_default(void); +void breathing_speed_default(void); +void breathing_speed_set(uint8_t value); +void breathing_speed_inc(uint8_t value); +void breathing_speed_dec(uint8_t value); +#endif + +#endif +void send_dword(uint32_t number); +void send_word(uint16_t number); +void send_byte(uint8_t number); +void send_nibble(uint8_t number); +uint16_t hex_to_keycode(uint8_t hex); + +void led_set_user(uint8_t usb_led); +void led_set_kb(uint8_t usb_led); + +void api_send_unicode(uint32_t unicode); + +#endif diff --git a/quantum/quantum_keycodes.h b/quantum/quantum_keycodes.h new file mode 100644 index 0000000000..6038e31c46 --- /dev/null +++ b/quantum/quantum_keycodes.h @@ -0,0 +1,603 @@ +/* Copyright 2016-2017 Jack Humbert + * + * 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 QUANTUM_KEYCODES_H +#define QUANTUM_KEYCODES_H + +#ifndef MIDI_ENABLE_STRICT +#define MIDI_ENABLE_STRICT 0 +#endif + +#if !MIDI_ENABLE_STRICT || (defined(MIDI_ENABLE) && defined(MIDI_ADVANCED)) +#ifndef MIDI_TONE_KEYCODE_OCTAVES +#define MIDI_TONE_KEYCODE_OCTAVES 3 +#endif +#endif + +enum quantum_keycodes { +    // Ranges used in shortucuts - not to be used directly +    QK_TMK                = 0x0000, +    QK_TMK_MAX            = 0x00FF, +    QK_MODS               = 0x0100, +    QK_LCTL               = 0x0100, +    QK_LSFT               = 0x0200, +    QK_LALT               = 0x0400, +    QK_LGUI               = 0x0800, +    QK_RMODS_MIN          = 0x1000, +    QK_RCTL               = 0x1100, +    QK_RSFT               = 0x1200, +    QK_RALT               = 0x1400, +    QK_RGUI               = 0x1800, +    QK_MODS_MAX           = 0x1FFF, +    QK_FUNCTION           = 0x2000, +    QK_FUNCTION_MAX       = 0x2FFF, +    QK_MACRO              = 0x3000, +    QK_MACRO_MAX          = 0x3FFF, +    QK_LAYER_TAP          = 0x4000, +    QK_LAYER_TAP_MAX      = 0x4FFF, +    QK_TO                 = 0x5000, +    QK_TO_MAX             = 0x50FF, +    QK_MOMENTARY          = 0x5100, +    QK_MOMENTARY_MAX      = 0x51FF, +    QK_DEF_LAYER          = 0x5200, +    QK_DEF_LAYER_MAX      = 0x52FF, +    QK_TOGGLE_LAYER       = 0x5300, +    QK_TOGGLE_LAYER_MAX   = 0x53FF, +    QK_ONE_SHOT_LAYER     = 0x5400, +    QK_ONE_SHOT_LAYER_MAX = 0x54FF, +    QK_ONE_SHOT_MOD       = 0x5500, +    QK_ONE_SHOT_MOD_MAX   = 0x55FF, +#ifndef DISABLE_CHORDING +    QK_CHORDING           = 0x5600, +    QK_CHORDING_MAX       = 0x56FF, +#endif +    QK_TAP_DANCE          = 0x5700, +    QK_TAP_DANCE_MAX      = 0x57FF, +    QK_LAYER_TAP_TOGGLE   = 0x5800, +    QK_LAYER_TAP_TOGGLE_MAX = 0x58FF, +    QK_MOD_TAP            = 0x6000, +    QK_MOD_TAP_MAX        = 0x7FFF, +#if defined(UNICODEMAP_ENABLE) && defined(UNICODE_ENABLE) +    #error "Cannot enable both UNICODEMAP && UNICODE" +#endif +#ifdef UNICODE_ENABLE +    QK_UNICODE            = 0x8000, +    QK_UNICODE_MAX        = 0xFFFF, +#endif +#ifdef UNICODEMAP_ENABLE +    QK_UNICODE_MAP        = 0x8000, +    QK_UNICODE_MAP_MAX    = 0x83FF, +#endif + +    // Loose keycodes - to be used directly + +    RESET = 0x5C00, +    DEBUG, +    MAGIC_SWAP_CONTROL_CAPSLOCK, +    MAGIC_CAPSLOCK_TO_CONTROL, +    MAGIC_SWAP_LALT_LGUI, +    MAGIC_SWAP_RALT_RGUI, +    MAGIC_NO_GUI, +    MAGIC_SWAP_GRAVE_ESC, +    MAGIC_SWAP_BACKSLASH_BACKSPACE, +    MAGIC_HOST_NKRO, +    MAGIC_SWAP_ALT_GUI, +    MAGIC_UNSWAP_CONTROL_CAPSLOCK, +    MAGIC_UNCAPSLOCK_TO_CONTROL, +    MAGIC_UNSWAP_LALT_LGUI, +    MAGIC_UNSWAP_RALT_RGUI, +    MAGIC_UNNO_GUI, +    MAGIC_UNSWAP_GRAVE_ESC, +    MAGIC_UNSWAP_BACKSLASH_BACKSPACE, +    MAGIC_UNHOST_NKRO, +    MAGIC_UNSWAP_ALT_GUI, +    MAGIC_TOGGLE_NKRO, +    GRAVE_ESC, + +    // Leader key +#ifndef DISABLE_LEADER +    KC_LEAD, +#endif + +    // Audio on/off/toggle +    AU_ON, +    AU_OFF, +    AU_TOG, + +#ifdef FAUXCLICKY_ENABLE +    // Faux clicky +    FC_ON, +    FC_OFF, +    FC_TOG, +#endif + +    // Music mode on/off/toggle +    MU_ON, +    MU_OFF, +    MU_TOG, + +    // Music voice iterate +    MUV_IN, +    MUV_DE, + +    // Midi +#if !MIDI_ENABLE_STRICT || (defined(MIDI_ENABLE) && defined(MIDI_BASIC)) +    MI_ON,  // send midi notes when music mode is enabled +    MI_OFF, // don't send midi notes when music mode is enabled +#endif + +#if !MIDI_ENABLE_STRICT || (defined(MIDI_ENABLE) && defined(MIDI_ADVANCED)) +    MIDI_TONE_MIN, + +#if !MIDI_ENABLE_STRICT || MIDI_TONE_KEYCODE_OCTAVES > 0 +    MI_C = MIDI_TONE_MIN, +    MI_Cs, +    MI_Db = MI_Cs, +    MI_D, +    MI_Ds, +    MI_Eb = MI_Ds, +    MI_E, +    MI_F, +    MI_Fs, +    MI_Gb = MI_Fs, +    MI_G, +    MI_Gs, +    MI_Ab = MI_Gs, +    MI_A, +    MI_As, +    MI_Bb = MI_As, +    MI_B, +#endif + +#if !MIDI_ENABLE_STRICT || MIDI_TONE_KEYCODE_OCTAVES > 1 +    MI_C_1, +    MI_Cs_1, +    MI_Db_1 = MI_Cs_1, +    MI_D_1, +    MI_Ds_1, +    MI_Eb_1 = MI_Ds_1, +    MI_E_1, +    MI_F_1, +    MI_Fs_1, +    MI_Gb_1 = MI_Fs_1, +    MI_G_1, +    MI_Gs_1, +    MI_Ab_1 = MI_Gs_1, +    MI_A_1, +    MI_As_1, +    MI_Bb_1 = MI_As_1, +    MI_B_1, +#endif + +#if !MIDI_ENABLE_STRICT || MIDI_TONE_KEYCODE_OCTAVES > 2 +    MI_C_2, +    MI_Cs_2, +    MI_Db_2 = MI_Cs_2, +    MI_D_2, +    MI_Ds_2, +    MI_Eb_2 = MI_Ds_2, +    MI_E_2, +    MI_F_2, +    MI_Fs_2, +    MI_Gb_2 = MI_Fs_2, +    MI_G_2, +    MI_Gs_2, +    MI_Ab_2 = MI_Gs_2, +    MI_A_2, +    MI_As_2, +    MI_Bb_2 = MI_As_2, +    MI_B_2, +#endif + +#if !MIDI_ENABLE_STRICT || MIDI_TONE_KEYCODE_OCTAVES > 3 +    MI_C_3, +    MI_Cs_3, +    MI_Db_3 = MI_Cs_3, +    MI_D_3, +    MI_Ds_3, +    MI_Eb_3 = MI_Ds_3, +    MI_E_3, +    MI_F_3, +    MI_Fs_3, +    MI_Gb_3 = MI_Fs_3, +    MI_G_3, +    MI_Gs_3, +    MI_Ab_3 = MI_Gs_3, +    MI_A_3, +    MI_As_3, +    MI_Bb_3 = MI_As_3, +    MI_B_3, +#endif + +#if !MIDI_ENABLE_STRICT || MIDI_TONE_KEYCODE_OCTAVES > 4 +    MI_C_4, +    MI_Cs_4, +    MI_Db_4 = MI_Cs_4, +    MI_D_4, +    MI_Ds_4, +    MI_Eb_4 = MI_Ds_4, +    MI_E_4, +    MI_F_4, +    MI_Fs_4, +    MI_Gb_4 = MI_Fs_4, +    MI_G_4, +    MI_Gs_4, +    MI_Ab_4 = MI_Gs_4, +    MI_A_4, +    MI_As_4, +    MI_Bb_4 = MI_As_4, +    MI_B_4, +#endif + +#if !MIDI_ENABLE_STRICT || MIDI_TONE_KEYCODE_OCTAVES > 5 +    MI_C_5, +    MI_Cs_5, +    MI_Db_5 = MI_Cs_5, +    MI_D_5, +    MI_Ds_5, +    MI_Eb_5 = MI_Ds_5, +    MI_E_5, +    MI_F_5, +    MI_Fs_5, +    MI_Gb_5 = MI_Fs_5, +    MI_G_5, +    MI_Gs_5, +    MI_Ab_5 = MI_Gs_5, +    MI_A_5, +    MI_As_5, +    MI_Bb_5 = MI_As_5, +    MI_B_5, +#endif + +#if !MIDI_ENABLE_STRICT || MIDI_TONE_KEYCODE_OCTAVES > 5 +    MIDI_TONE_MAX = MI_B_5, +#elif MIDI_TONE_KEYCODE_OCTAVES > 4 +    MIDI_TONE_MAX = MI_B_4, +#elif MIDI_TONE_KEYCODE_OCTAVES > 3 +    MIDI_TONE_MAX = MI_B_3, +#elif MIDI_TONE_KEYCODE_OCTAVES > 2 +    MIDI_TONE_MAX = MI_B_2, +#elif MIDI_TONE_KEYCODE_OCTAVES > 1 +    MIDI_TONE_MAX = MI_B_1, +#elif MIDI_TONE_KEYCODE_OCTAVES > 0 +    MIDI_TONE_MAX = MI_B, +#endif + +    MIDI_OCTAVE_MIN, +    MI_OCT_N2 = MIDI_OCTAVE_MIN, +    MI_OCT_N1, +    MI_OCT_0, +    MI_OCT_1, +    MI_OCT_2, +    MI_OCT_3, +    MI_OCT_4, +    MI_OCT_5, +    MI_OCT_6, +    MI_OCT_7, +    MIDI_OCTAVE_MAX = MI_OCT_7, +    MI_OCTD, // octave down +    MI_OCTU, // octave up + +    MIDI_TRANSPOSE_MIN, +    MI_TRNS_N6 = MIDI_TRANSPOSE_MIN, +    MI_TRNS_N5, +    MI_TRNS_N4, +    MI_TRNS_N3, +    MI_TRNS_N2, +    MI_TRNS_N1, +    MI_TRNS_0, +    MI_TRNS_1, +    MI_TRNS_2, +    MI_TRNS_3, +    MI_TRNS_4, +    MI_TRNS_5, +    MI_TRNS_6, +    MIDI_TRANSPOSE_MAX = MI_TRNS_6, +    MI_TRNSD, // transpose down +    MI_TRNSU, // transpose up + +    MIDI_VELOCITY_MIN, +    MI_VEL_1 = MIDI_VELOCITY_MIN, +    MI_VEL_2, +    MI_VEL_3, +    MI_VEL_4, +    MI_VEL_5, +    MI_VEL_6, +    MI_VEL_7, +    MI_VEL_8, +    MI_VEL_9, +    MI_VEL_10, +    MIDI_VELOCITY_MAX = MI_VEL_10, +    MI_VELD, // velocity down +    MI_VELU, // velocity up + +    MIDI_CHANNEL_MIN, +    MI_CH1 = MIDI_CHANNEL_MIN, +    MI_CH2, +    MI_CH3, +    MI_CH4, +    MI_CH5, +    MI_CH6, +    MI_CH7, +    MI_CH8, +    MI_CH9, +    MI_CH10, +    MI_CH11, +    MI_CH12, +    MI_CH13, +    MI_CH14, +    MI_CH15, +    MI_CH16, +    MIDI_CHANNEL_MAX = MI_CH16, +    MI_CHD, // previous channel +    MI_CHU, // next channel + +    MI_ALLOFF, // all notes off + +    MI_SUS, // sustain +    MI_PORT, // portamento +    MI_SOST, // sostenuto +    MI_SOFT, // soft pedal +    MI_LEG,  // legato + +    MI_MOD, // modulation +    MI_MODSD, // decrease modulation speed +    MI_MODSU, // increase modulation speed +#endif // MIDI_ADVANCED + +    // Backlight functionality +    BL_0, +    BL_1, +    BL_2, +    BL_3, +    BL_4, +    BL_5, +    BL_6, +    BL_7, +    BL_8, +    BL_9, +    BL_10, +    BL_11, +    BL_12, +    BL_13, +    BL_14, +    BL_15, +    BL_DEC, +    BL_INC, +    BL_TOGG, +    BL_STEP, + +    // RGB functionality +    RGB_TOG, +    RGB_MOD, +    RGB_HUI, +    RGB_HUD, +    RGB_SAI, +    RGB_SAD, +    RGB_VAI, +    RGB_VAD, + +    // Left shift, open paren +    KC_LSPO, + +    // Right shift, close paren +    KC_RSPC, + +    // Printing +    PRINT_ON, +    PRINT_OFF, + +    // output selection +    OUT_AUTO, +    OUT_USB, +#ifdef BLUETOOTH_ENABLE +    OUT_BT, +#endif + +    // always leave at the end +    SAFE_RANGE +}; + +// Ability to use mods in layouts +#define LCTL(kc) (kc | QK_LCTL) +#define LSFT(kc) (kc | QK_LSFT) +#define LALT(kc) (kc | QK_LALT) +#define LGUI(kc) (kc | QK_LGUI) +#define RCTL(kc) (kc | QK_RCTL) +#define RSFT(kc) (kc | QK_RSFT) +#define RALT(kc) (kc | QK_RALT) +#define RGUI(kc) (kc | QK_RGUI) + +#define HYPR(kc) (kc | QK_LCTL | QK_LSFT | QK_LALT | QK_LGUI) +#define MEH(kc)  (kc | QK_LCTL | QK_LSFT | QK_LALT) +#define LCAG(kc) (kc | QK_LCTL | QK_LALT | QK_LGUI) +#define ALTG(kc) (kc | QK_RCTL | QK_RALT) +#define SCMD(kc) (kc | QK_LGUI | QK_LSFT) +#define SWIN(kc) SCMD(kc) +#define LCA(kc) (kc | QK_LCTL | QK_LALT) + +#define MOD_HYPR 0xf +#define MOD_MEH 0x7 + + +// Aliases for shifted symbols +// Each key has a 4-letter code, and some have longer aliases too. +// While the long aliases are descriptive, the 4-letter codes +// make for nicer grid layouts (everything lines up), and are +// the preferred style for Quantum. +#define KC_TILD LSFT(KC_GRV)    // ~ +#define KC_TILDE    KC_TILD + +#define KC_EXLM LSFT(KC_1)      // ! +#define KC_EXCLAIM  KC_EXLM + +#define KC_AT   LSFT(KC_2)      // @ + +#define KC_HASH LSFT(KC_3)      // # + +#define KC_DLR  LSFT(KC_4)      // $ +#define KC_DOLLAR   KC_DLR + +#define KC_PERC LSFT(KC_5)      // % +#define KC_PERCENT  KC_PERC + +#define KC_CIRC LSFT(KC_6)      // ^ +#define KC_CIRCUMFLEX   KC_CIRC + +#define KC_AMPR LSFT(KC_7)      // & +#define KC_AMPERSAND    KC_AMPR + +#define KC_ASTR LSFT(KC_8)      // * +#define KC_ASTERISK KC_ASTR + +#define KC_LPRN LSFT(KC_9)      // ( +#define KC_LEFT_PAREN   KC_LPRN + +#define KC_RPRN LSFT(KC_0)      // ) +#define KC_RIGHT_PAREN  KC_RPRN + +#define KC_UNDS LSFT(KC_MINS)   // _ +#define KC_UNDERSCORE   KC_UNDS + +#define KC_PLUS LSFT(KC_EQL)    // + + +#define KC_LCBR LSFT(KC_LBRC)   // { +#define KC_LEFT_CURLY_BRACE KC_LCBR + +#define KC_RCBR LSFT(KC_RBRC)   // } +#define KC_RIGHT_CURLY_BRACE    KC_RCBR + +#define KC_LABK LSFT(KC_COMM)   // < +#define KC_LEFT_ANGLE_BRACKET   KC_LABK + +#define KC_RABK LSFT(KC_DOT)    // > +#define KC_RIGHT_ANGLE_BRACKET  KC_RABK + +#define KC_COLN LSFT(KC_SCLN)   // : +#define KC_COLON    KC_COLN + +#define KC_PIPE LSFT(KC_BSLS)   // | + +#define KC_LT LSFT(KC_COMM)     // < + +#define KC_GT LSFT(KC_DOT)      // > + +#define KC_QUES LSFT(KC_SLSH)   // ? +#define KC_QUESTION KC_QUES + +#define KC_DQT LSFT(KC_QUOT)   // " +#define KC_DOUBLE_QUOTE KC_DQT +#define KC_DQUO KC_DQT + +#define KC_DELT KC_DELETE // Del key (four letter code) + +// Alias for function layers than expand past FN31 +#define FUNC(kc) (kc | QK_FUNCTION) + +// Aliases +#define S(kc) LSFT(kc) +#define F(kc) FUNC(kc) + +#define M(kc) (kc | QK_MACRO) + +#define MACROTAP(kc) (kc | QK_MACRO | FUNC_TAP<<8) +#define MACRODOWN(...) (record->event.pressed ? MACRO(__VA_ARGS__) : MACRO_NONE) + +#define KC_GESC GRAVE_ESC + + +// L-ayer, T-ap - 256 keycode max, 16 layer max +#define LT(layer, kc) (kc | QK_LAYER_TAP | ((layer & 0xF) << 8)) + +#define AG_SWAP MAGIC_SWAP_ALT_GUI +#define AG_NORM MAGIC_UNSWAP_ALT_GUI + +#define BL_ON  BL_9 +#define BL_OFF BL_0 + +// GOTO layer - 16 layers max +// when: +// ON_PRESS    = 1 +// ON_RELEASE  = 2 +// Unless you have a good reason not to do so, prefer  ON_PRESS (1) as your default. +// In fact, we changed it to assume ON_PRESS for sanity/simplicity. If needed, you can add your own +// keycode modeled after the old version, kept below for this. +/* #define TO(layer, when) (layer | QK_TO | (when << 0x4)) */ +#define TO(layer) (layer | QK_TO | (ON_PRESS << 0x4)) + +// Momentary switch layer - 256 layer max +#define MO(layer) (layer | QK_MOMENTARY) + +// Set default layer - 256 layer max +#define DF(layer) (layer | QK_DEF_LAYER) + +// Toggle to layer - 256 layer max +#define TG(layer) (layer | QK_TOGGLE_LAYER) + +// One-shot layer - 256 layer max +#define OSL(layer) (layer | QK_ONE_SHOT_LAYER) + +// One-shot mod +#define OSM(mod) ((mod) | QK_ONE_SHOT_MOD) + +// Layer tap-toggle +#define TT(layer) (layer | QK_LAYER_TAP_TOGGLE) + +// M-od, T-ap - 256 keycode max +#define MT(mod, kc) (kc | QK_MOD_TAP | (((mod) & 0x1F) << 8)) + +#define CTL_T(kc) MT(MOD_LCTL, kc) +#define LCTL_T(kc) MT(MOD_LCTL, kc) +#define RCTL_T(kc) MT(MOD_RCTL, kc) + +#define SFT_T(kc) MT(MOD_LSFT, kc) +#define LSFT_T(kc) MT(MOD_LSFT, kc) +#define RSFT_T(kc) MT(MOD_RSFT, kc) + +#define ALT_T(kc) MT(MOD_LALT, kc) +#define LALT_T(kc) MT(MOD_LALT, kc) +#define RALT_T(kc) MT(MOD_RALT, kc) +#define ALGR_T(kc) MT(MOD_RALT, kc) // dual-function AltGR + +#define GUI_T(kc) MT(MOD_LGUI, kc) +#define LGUI_T(kc) MT(MOD_LGUI, kc) +#define RGUI_T(kc) MT(MOD_RGUI, kc) + +#define C_S_T(kc) MT((MOD_LCTL | MOD_LSFT), kc) // Control + Shift e.g. for gnome-terminal +#define MEH_T(kc) MT((MOD_LCTL | MOD_LSFT | MOD_LALT), kc) // Meh is a less hyper version of the Hyper key -- doesn't include Win or Cmd, so just alt+shift+ctrl +#define LCAG_T(kc) MT((MOD_LCTL | MOD_LALT | MOD_LGUI), kc) // Left control alt and gui +#define RCAG_T(kc) MT((MOD_RCTL | MOD_RALT | MOD_RGUI), kc) // Right control alt and gui +#define ALL_T(kc) MT((MOD_LCTL | MOD_LSFT | MOD_LALT | MOD_LGUI), kc) // see http://brettterpstra.com/2012/12/08/a-useful-caps-lock-key/ +#define SCMD_T(kc) MT((MOD_LGUI | MOD_LSFT), kc) +#define SWIN_T(kc) SCMD_T(kc) +#define LCA_T(kc) MT((MOD_LCTL | MOD_LALT), kc) // Left control and left alt + +// Dedicated keycode versions for Hyper and Meh, if you want to use them as standalone keys rather than mod-tap +#define KC_HYPR HYPR(KC_NO) +#define KC_MEH  MEH(KC_NO) + +#ifdef UNICODE_ENABLE +    // For sending unicode codes. +    // You may not send codes over 7FFF -- this supports most of UTF8. +    // To have a key that sends out Œ, go UC(0x0152) +    #define UNICODE(n) (n | QK_UNICODE) +    #define UC(n) UNICODE(n) +#endif + +#ifdef UNICODEMAP_ENABLE +    #define X(n) (n | QK_UNICODE_MAP) +#endif + +#endif // QUANTUM_KEYCODES_H diff --git a/quantum/rgblight.c b/quantum/rgblight.c new file mode 100644 index 0000000000..4eec2a7762 --- /dev/null +++ b/quantum/rgblight.c @@ -0,0 +1,613 @@ +/* Copyright 2016-2017 Yang Liu + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ +#include <avr/eeprom.h> +#include <avr/interrupt.h> +#include <util/delay.h> +#include "progmem.h" +#include "timer.h" +#include "rgblight.h" +#include "debug.h" +#include "led_tables.h" + + +__attribute__ ((weak)) +const uint8_t RGBLED_BREATHING_INTERVALS[] PROGMEM = {30, 20, 10, 5}; +__attribute__ ((weak)) +const uint8_t RGBLED_RAINBOW_MOOD_INTERVALS[] PROGMEM = {120, 60, 30}; +__attribute__ ((weak)) +const uint8_t RGBLED_RAINBOW_SWIRL_INTERVALS[] PROGMEM = {100, 50, 20}; +__attribute__ ((weak)) +const uint8_t RGBLED_SNAKE_INTERVALS[] PROGMEM = {100, 50, 20}; +__attribute__ ((weak)) +const uint8_t RGBLED_KNIGHT_INTERVALS[] PROGMEM = {100, 50, 20}; +__attribute__ ((weak)) +const uint16_t RGBLED_GRADIENT_RANGES[] PROGMEM = {360, 240, 180, 120, 90}; + +rgblight_config_t rgblight_config; +rgblight_config_t inmem_config; + +LED_TYPE led[RGBLED_NUM]; +uint8_t rgblight_inited = 0; +bool rgblight_timer_enabled = false; + +void sethsv(uint16_t hue, uint8_t sat, uint8_t val, LED_TYPE *led1) { +  uint8_t r = 0, g = 0, b = 0, base, color; + +  if (sat == 0) { // Acromatic color (gray). Hue doesn't mind. +    r = val; +    g = val; +    b = val; +  } else { +    base = ((255 - sat) * val) >> 8; +    color = (val - base) * (hue % 60) / 60; + +    switch (hue / 60) { +      case 0: +        r = val; +        g = base + color; +        b = base; +        break; +      case 1: +        r = val - color; +        g = val; +        b = base; +        break; +      case 2: +        r = base; +        g = val; +        b = base + color; +        break; +      case 3: +        r = base; +        g = val - color; +        b = val; +        break; +      case 4: +        r = base + color; +        g = base; +        b = val; +        break; +      case 5: +        r = val; +        g = base; +        b = val - color; +        break; +    } +  } +  r = pgm_read_byte(&CIE1931_CURVE[r]); +  g = pgm_read_byte(&CIE1931_CURVE[g]); +  b = pgm_read_byte(&CIE1931_CURVE[b]); + +  setrgb(r, g, b, led1); +} + +void setrgb(uint8_t r, uint8_t g, uint8_t b, LED_TYPE *led1) { +  (*led1).r = r; +  (*led1).g = g; +  (*led1).b = b; +} + + +uint32_t eeconfig_read_rgblight(void) { +  return eeprom_read_dword(EECONFIG_RGBLIGHT); +} +void eeconfig_update_rgblight(uint32_t val) { +  eeprom_update_dword(EECONFIG_RGBLIGHT, val); +} +void eeconfig_update_rgblight_default(void) { +  dprintf("eeconfig_update_rgblight_default\n"); +  rgblight_config.enable = 1; +  rgblight_config.mode = 1; +  rgblight_config.hue = 0; +  rgblight_config.sat = 255; +  rgblight_config.val = 255; +  eeconfig_update_rgblight(rgblight_config.raw); +} +void eeconfig_debug_rgblight(void) { +  dprintf("rgblight_config eprom\n"); +  dprintf("rgblight_config.enable = %d\n", rgblight_config.enable); +  dprintf("rghlight_config.mode = %d\n", rgblight_config.mode); +  dprintf("rgblight_config.hue = %d\n", rgblight_config.hue); +  dprintf("rgblight_config.sat = %d\n", rgblight_config.sat); +  dprintf("rgblight_config.val = %d\n", rgblight_config.val); +} + +void rgblight_init(void) { +  debug_enable = 1; // Debug ON! +  dprintf("rgblight_init called.\n"); +  rgblight_inited = 1; +  dprintf("rgblight_init start!\n"); +  if (!eeconfig_is_enabled()) { +    dprintf("rgblight_init eeconfig is not enabled.\n"); +    eeconfig_init(); +    eeconfig_update_rgblight_default(); +  } +  rgblight_config.raw = eeconfig_read_rgblight(); +  if (!rgblight_config.mode) { +    dprintf("rgblight_init rgblight_config.mode = 0. Write default values to EEPROM.\n"); +    eeconfig_update_rgblight_default(); +    rgblight_config.raw = eeconfig_read_rgblight(); +  } +  eeconfig_debug_rgblight(); // display current eeprom values + +  #ifdef RGBLIGHT_ANIMATIONS +    rgblight_timer_init(); // setup the timer +  #endif + +  if (rgblight_config.enable) { +    rgblight_mode(rgblight_config.mode); +  } +} + +void rgblight_update_dword(uint32_t dword) { +  rgblight_config.raw = dword; +  eeconfig_update_rgblight(rgblight_config.raw); +  if (rgblight_config.enable) +    rgblight_mode(rgblight_config.mode); +  else { +    #ifdef RGBLIGHT_ANIMATIONS +      rgblight_timer_disable(); +    #endif +      rgblight_set(); +  } +} + +void rgblight_increase(void) { +  uint8_t mode = 0; +  if (rgblight_config.mode < RGBLIGHT_MODES) { +    mode = rgblight_config.mode + 1; +  } +  rgblight_mode(mode); +} +void rgblight_decrease(void) { +  uint8_t mode = 0; +  // Mode will never be < 1. If it ever is, eeprom needs to be initialized. +  if (rgblight_config.mode > 1) { +    mode = rgblight_config.mode - 1; +  } +  rgblight_mode(mode); +} +void rgblight_step(void) { +  uint8_t mode = 0; +  mode = rgblight_config.mode + 1; +  if (mode > RGBLIGHT_MODES) { +    mode = 1; +  } +  rgblight_mode(mode); +} +void rgblight_step_reverse(void) { +  uint8_t mode = 0; +  mode = rgblight_config.mode - 1; +  if (mode < 1) { +    mode = RGBLIGHT_MODES; +  } +  rgblight_mode(mode); +} + +void rgblight_mode(uint8_t mode) { +  if (!rgblight_config.enable) { +    return; +  } +  if (mode < 1) { +    rgblight_config.mode = 1; +  } else if (mode > RGBLIGHT_MODES) { +    rgblight_config.mode = RGBLIGHT_MODES; +  } else { +    rgblight_config.mode = mode; +  } +  eeconfig_update_rgblight(rgblight_config.raw); +  xprintf("rgblight mode: %u\n", rgblight_config.mode); +  if (rgblight_config.mode == 1) { +    #ifdef RGBLIGHT_ANIMATIONS +      rgblight_timer_disable(); +    #endif +  } else if (rgblight_config.mode >= 2 && rgblight_config.mode <= 24) { +    // MODE 2-5, breathing +    // MODE 6-8, rainbow mood +    // MODE 9-14, rainbow swirl +    // MODE 15-20, snake +    // MODE 21-23, knight + +    #ifdef RGBLIGHT_ANIMATIONS +      rgblight_timer_enable(); +    #endif +  } else if (rgblight_config.mode >= 25 && rgblight_config.mode <= 34) { +    // MODE 25-34, static gradient + +    #ifdef RGBLIGHT_ANIMATIONS +      rgblight_timer_disable(); +    #endif +  } +  rgblight_sethsv(rgblight_config.hue, rgblight_config.sat, rgblight_config.val); +} + +void rgblight_toggle(void) { +  rgblight_config.enable ^= 1; +  eeconfig_update_rgblight(rgblight_config.raw); +  xprintf("rgblight toggle: rgblight_config.enable = %u\n", rgblight_config.enable); +  if (rgblight_config.enable) { +    rgblight_mode(rgblight_config.mode); +  } else { +    #ifdef RGBLIGHT_ANIMATIONS +      rgblight_timer_disable(); +    #endif +    _delay_ms(50); +    rgblight_set(); +  } +} + +void rgblight_enable(void) { +  rgblight_config.enable = 1; +  eeconfig_update_rgblight(rgblight_config.raw); +  xprintf("rgblight enable: rgblight_config.enable = %u\n", rgblight_config.enable); +  rgblight_mode(rgblight_config.mode); +} + + +void rgblight_increase_hue(void) { +  uint16_t hue; +  hue = (rgblight_config.hue+RGBLIGHT_HUE_STEP) % 360; +  rgblight_sethsv(hue, rgblight_config.sat, rgblight_config.val); +} +void rgblight_decrease_hue(void) { +  uint16_t hue; +  if (rgblight_config.hue-RGBLIGHT_HUE_STEP < 0) { +    hue = (rgblight_config.hue + 360 - RGBLIGHT_HUE_STEP) % 360; +  } else { +    hue = (rgblight_config.hue - RGBLIGHT_HUE_STEP) % 360; +  } +  rgblight_sethsv(hue, rgblight_config.sat, rgblight_config.val); +} +void rgblight_increase_sat(void) { +  uint8_t sat; +  if (rgblight_config.sat + RGBLIGHT_SAT_STEP > 255) { +    sat = 255; +  } else { +    sat = rgblight_config.sat + RGBLIGHT_SAT_STEP; +  } +  rgblight_sethsv(rgblight_config.hue, sat, rgblight_config.val); +} +void rgblight_decrease_sat(void) { +  uint8_t sat; +  if (rgblight_config.sat - RGBLIGHT_SAT_STEP < 0) { +    sat = 0; +  } else { +    sat = rgblight_config.sat - RGBLIGHT_SAT_STEP; +  } +  rgblight_sethsv(rgblight_config.hue, sat, rgblight_config.val); +} +void rgblight_increase_val(void) { +  uint8_t val; +  if (rgblight_config.val + RGBLIGHT_VAL_STEP > 255) { +    val = 255; +  } else { +    val = rgblight_config.val + RGBLIGHT_VAL_STEP; +  } +  rgblight_sethsv(rgblight_config.hue, rgblight_config.sat, val); +} +void rgblight_decrease_val(void) { +  uint8_t val; +  if (rgblight_config.val - RGBLIGHT_VAL_STEP < 0) { +    val = 0; +  } else { +    val = rgblight_config.val - RGBLIGHT_VAL_STEP; +  } +  rgblight_sethsv(rgblight_config.hue, rgblight_config.sat, val); +} + +void rgblight_sethsv_noeeprom(uint16_t hue, uint8_t sat, uint8_t val) { +  inmem_config.raw = rgblight_config.raw; +  if (rgblight_config.enable) { +    LED_TYPE tmp_led; +    sethsv(hue, sat, val, &tmp_led); +    inmem_config.hue = hue; +    inmem_config.sat = sat; +    inmem_config.val = val; +    // dprintf("rgblight set hue [MEMORY]: %u,%u,%u\n", inmem_config.hue, inmem_config.sat, inmem_config.val); +    rgblight_setrgb(tmp_led.r, tmp_led.g, tmp_led.b); +  } +} +void rgblight_sethsv(uint16_t hue, uint8_t sat, uint8_t val) { +  if (rgblight_config.enable) { +    if (rgblight_config.mode == 1) { +      // same static color +      rgblight_sethsv_noeeprom(hue, sat, val); +    } else { +      // all LEDs in same color +      if (rgblight_config.mode >= 2 && rgblight_config.mode <= 5) { +        // breathing mode, ignore the change of val, use in memory value instead +        val = rgblight_config.val; +      } else if (rgblight_config.mode >= 6 && rgblight_config.mode <= 14) { +        // rainbow mood and rainbow swirl, ignore the change of hue +        hue = rgblight_config.hue; +      } else if (rgblight_config.mode >= 25 && rgblight_config.mode <= 34) { +        // static gradient +        uint16_t _hue; +        int8_t direction = ((rgblight_config.mode - 25) % 2) ? -1 : 1; +        uint16_t range = pgm_read_word(&RGBLED_GRADIENT_RANGES[(rgblight_config.mode - 25) / 2]); +        for (uint8_t i = 0; i < RGBLED_NUM; i++) { +          _hue = (range / RGBLED_NUM * i * direction + hue + 360) % 360; +          dprintf("rgblight rainbow set hsv: %u,%u,%d,%u\n", i, _hue, direction, range); +          sethsv(_hue, sat, val, (LED_TYPE *)&led[i]); +        } +        rgblight_set(); +      } +    } +    rgblight_config.hue = hue; +    rgblight_config.sat = sat; +    rgblight_config.val = val; +    eeconfig_update_rgblight(rgblight_config.raw); +    xprintf("rgblight set hsv [EEPROM]: %u,%u,%u\n", rgblight_config.hue, rgblight_config.sat, rgblight_config.val); +  } +} + +void rgblight_setrgb(uint8_t r, uint8_t g, uint8_t b) { +  // dprintf("rgblight set rgb: %u,%u,%u\n", r,g,b); +  for (uint8_t i = 0; i < RGBLED_NUM; i++) { +    led[i].r = r; +    led[i].g = g; +    led[i].b = b; +  } +  rgblight_set(); +} + +__attribute__ ((weak)) +void rgblight_set(void) { +  if (rgblight_config.enable) { +    #ifdef RGBW +      ws2812_setleds_rgbw(led, RGBLED_NUM); +    #else +      ws2812_setleds(led, RGBLED_NUM); +    #endif +  } else { +    for (uint8_t i = 0; i < RGBLED_NUM; i++) { +      led[i].r = 0; +      led[i].g = 0; +      led[i].b = 0; +    } +    #ifdef RGBW +      ws2812_setleds_rgbw(led, RGBLED_NUM); +    #else +      ws2812_setleds(led, RGBLED_NUM); +    #endif +  } +} + +#ifdef RGBLIGHT_ANIMATIONS + +// Animation timer -- AVR Timer3 +void rgblight_timer_init(void) { +  // static uint8_t rgblight_timer_is_init = 0; +  // if (rgblight_timer_is_init) { +  //   return; +  // } +  // rgblight_timer_is_init = 1; +  // /* Timer 3 setup */ +  // TCCR3B = _BV(WGM32) // CTC mode OCR3A as TOP +  //       | _BV(CS30); // Clock selelct: clk/1 +  // /* Set TOP value */ +  // uint8_t sreg = SREG; +  // cli(); +  // OCR3AH = (RGBLED_TIMER_TOP >> 8) & 0xff; +  // OCR3AL = RGBLED_TIMER_TOP & 0xff; +  // SREG = sreg; + +  rgblight_timer_enabled = true; +} +void rgblight_timer_enable(void) { +  rgblight_timer_enabled = true; +  dprintf("TIMER3 enabled.\n"); +} +void rgblight_timer_disable(void) { +  rgblight_timer_enabled = false; +  dprintf("TIMER3 disabled.\n"); +} +void rgblight_timer_toggle(void) { +  rgblight_timer_enabled ^= rgblight_timer_enabled; +  dprintf("TIMER3 toggled.\n"); +} + +void rgblight_show_solid_color(uint8_t r, uint8_t g, uint8_t b) { +  rgblight_enable(); +  rgblight_mode(1); +  rgblight_setrgb(r, g, b); +} + +void rgblight_task(void) { +  if (rgblight_timer_enabled) { +    // mode = 1, static light, do nothing here +    if (rgblight_config.mode >= 2 && rgblight_config.mode <= 5) { +      // mode = 2 to 5, breathing mode +      rgblight_effect_breathing(rgblight_config.mode - 2); +    } else if (rgblight_config.mode >= 6 && rgblight_config.mode <= 8) { +      // mode = 6 to 8, rainbow mood mod +      rgblight_effect_rainbow_mood(rgblight_config.mode - 6); +    } else if (rgblight_config.mode >= 9 && rgblight_config.mode <= 14) { +      // mode = 9 to 14, rainbow swirl mode +      rgblight_effect_rainbow_swirl(rgblight_config.mode - 9); +    } else if (rgblight_config.mode >= 15 && rgblight_config.mode <= 20) { +      // mode = 15 to 20, snake mode +      rgblight_effect_snake(rgblight_config.mode - 15); +    } else if (rgblight_config.mode >= 21 && rgblight_config.mode <= 23) { +      // mode = 21 to 23, knight mode +      rgblight_effect_knight(rgblight_config.mode - 21); +    } else if (rgblight_config.mode == 24) { +      // mode = 24, christmas mode +      rgblight_effect_christmas(); +    } +  } +} + +// Effects +void rgblight_effect_breathing(uint8_t interval) { +  static uint8_t pos = 0; +  static uint16_t last_timer = 0; + +  if (timer_elapsed(last_timer) < pgm_read_byte(&RGBLED_BREATHING_INTERVALS[interval])) { +    return; +  } +  last_timer = timer_read(); + +  rgblight_sethsv_noeeprom(rgblight_config.hue, rgblight_config.sat, pgm_read_byte(&LED_BREATHING_TABLE[pos])); +  pos = (pos + 1) % 256; +} +void rgblight_effect_rainbow_mood(uint8_t interval) { +  static uint16_t current_hue = 0; +  static uint16_t last_timer = 0; + +  if (timer_elapsed(last_timer) < pgm_read_byte(&RGBLED_RAINBOW_MOOD_INTERVALS[interval])) { +    return; +  } +  last_timer = timer_read(); +  rgblight_sethsv_noeeprom(current_hue, rgblight_config.sat, rgblight_config.val); +  current_hue = (current_hue + 1) % 360; +} +void rgblight_effect_rainbow_swirl(uint8_t interval) { +  static uint16_t current_hue = 0; +  static uint16_t last_timer = 0; +  uint16_t hue; +  uint8_t i; +  if (timer_elapsed(last_timer) < pgm_read_byte(&RGBLED_RAINBOW_MOOD_INTERVALS[interval / 2])) { +    return; +  } +  last_timer = timer_read(); +  for (i = 0; i < RGBLED_NUM; i++) { +    hue = (360 / RGBLED_NUM * i + current_hue) % 360; +    sethsv(hue, rgblight_config.sat, rgblight_config.val, (LED_TYPE *)&led[i]); +  } +  rgblight_set(); + +  if (interval % 2) { +    current_hue = (current_hue + 1) % 360; +  } else { +    if (current_hue - 1 < 0) { +      current_hue = 359; +    } else { +      current_hue = current_hue - 1; +    } +  } +} +void rgblight_effect_snake(uint8_t interval) { +  static uint8_t pos = 0; +  static uint16_t last_timer = 0; +  uint8_t i, j; +  int8_t k; +  int8_t increment = 1; +  if (interval % 2) { +    increment = -1; +  } +  if (timer_elapsed(last_timer) < pgm_read_byte(&RGBLED_SNAKE_INTERVALS[interval / 2])) { +    return; +  } +  last_timer = timer_read(); +  for (i = 0; i < RGBLED_NUM; i++) { +    led[i].r = 0; +    led[i].g = 0; +    led[i].b = 0; +    for (j = 0; j < RGBLIGHT_EFFECT_SNAKE_LENGTH; j++) { +      k = pos + j * increment; +      if (k < 0) { +        k = k + RGBLED_NUM; +      } +      if (i == k) { +        sethsv(rgblight_config.hue, rgblight_config.sat, (uint8_t)(rgblight_config.val*(RGBLIGHT_EFFECT_SNAKE_LENGTH-j)/RGBLIGHT_EFFECT_SNAKE_LENGTH), (LED_TYPE *)&led[i]); +      } +    } +  } +  rgblight_set(); +  if (increment == 1) { +    if (pos - 1 < 0) { +      pos = RGBLED_NUM - 1; +    } else { +      pos -= 1; +    } +  } else { +    pos = (pos + 1) % RGBLED_NUM; +  } +} +void rgblight_effect_knight(uint8_t interval) { +  static int8_t pos = 0; +  static uint16_t last_timer = 0; +  uint8_t i, j, cur; +  int8_t k; +  LED_TYPE preled[RGBLED_NUM]; +  static int8_t increment = -1; +  if (timer_elapsed(last_timer) < pgm_read_byte(&RGBLED_KNIGHT_INTERVALS[interval])) { +    return; +  } +  last_timer = timer_read(); +  for (i = 0; i < RGBLED_NUM; i++) { +    preled[i].r = 0; +    preled[i].g = 0; +    preled[i].b = 0; +    for (j = 0; j < RGBLIGHT_EFFECT_KNIGHT_LENGTH; j++) { +      k = pos + j * increment; +      if (k < 0) { +        k = 0; +      } +      if (k >= RGBLED_NUM) { +        k = RGBLED_NUM - 1; +      } +      if (i == k) { +        sethsv(rgblight_config.hue, rgblight_config.sat, rgblight_config.val, (LED_TYPE *)&preled[i]); +      } +    } +  } +  if (RGBLIGHT_EFFECT_KNIGHT_OFFSET) { +    for (i = 0; i < RGBLED_NUM; i++) { +      cur = (i + RGBLIGHT_EFFECT_KNIGHT_OFFSET) % RGBLED_NUM; +      led[i].r = preled[cur].r; +      led[i].g = preled[cur].g; +      led[i].b = preled[cur].b; +    } +  } +  rgblight_set(); +  if (increment == 1) { +    if (pos - 1 < 0 - RGBLIGHT_EFFECT_KNIGHT_LENGTH) { +      pos = 0 - RGBLIGHT_EFFECT_KNIGHT_LENGTH; +      increment = -1; +    } else { +      pos -= 1; +    } +  } else { +    if (pos + 1 > RGBLED_NUM + RGBLIGHT_EFFECT_KNIGHT_LENGTH) { +      pos = RGBLED_NUM + RGBLIGHT_EFFECT_KNIGHT_LENGTH - 1; +      increment = 1; +    } else { +      pos += 1; +    } +  } +} + + +void rgblight_effect_christmas(void) { +  static uint16_t current_offset = 0; +  static uint16_t last_timer = 0; +  uint16_t hue; +  uint8_t i; +  if (timer_elapsed(last_timer) < RGBLIGHT_EFFECT_CHRISTMAS_INTERVAL) { +    return; +  } +  last_timer = timer_read(); +  current_offset = (current_offset + 1) % 2; +  for (i = 0; i < RGBLED_NUM; i++) { +    hue = 0 + ((i/RGBLIGHT_EFFECT_CHRISTMAS_STEP + current_offset) % 2) * 120; +    sethsv(hue, rgblight_config.sat, rgblight_config.val, (LED_TYPE *)&led[i]); +  } +  rgblight_set(); +} + +#endif diff --git a/quantum/rgblight.h b/quantum/rgblight.h new file mode 100644 index 0000000000..92130192ce --- /dev/null +++ b/quantum/rgblight.h @@ -0,0 +1,129 @@ +/* Copyright 2017 Yang Liu + * + * 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 RGBLIGHT_H +#define RGBLIGHT_H + +#ifdef RGBLIGHT_ANIMATIONS +	#define RGBLIGHT_MODES 34 +#else +	#define RGBLIGHT_MODES 1 +#endif + +#ifndef RGBLIGHT_EFFECT_SNAKE_LENGTH +#define RGBLIGHT_EFFECT_SNAKE_LENGTH 7 +#endif + +#ifndef RGBLIGHT_EFFECT_KNIGHT_LENGTH +#define RGBLIGHT_EFFECT_KNIGHT_LENGTH 7 +#endif +#ifndef RGBLIGHT_EFFECT_KNIGHT_OFFSET +#define RGBLIGHT_EFFECT_KNIGHT_OFFSET 9 +#endif + +#ifndef RGBLIGHT_EFFECT_DUALKNIGHT_LENGTH +#define RGBLIGHT_EFFECT_DUALKNIGHT_LENGTH 4 +#endif + +#ifndef RGBLIGHT_EFFECT_CHRISTMAS_INTERVAL +#define RGBLIGHT_EFFECT_CHRISTMAS_INTERVAL 1000 +#endif + +#ifndef RGBLIGHT_EFFECT_CHRISTMAS_STEP +#define RGBLIGHT_EFFECT_CHRISTMAS_STEP 2 +#endif + +#ifndef RGBLIGHT_HUE_STEP +#define RGBLIGHT_HUE_STEP 10 +#endif +#ifndef RGBLIGHT_SAT_STEP +#define RGBLIGHT_SAT_STEP 17 +#endif +#ifndef RGBLIGHT_VAL_STEP +#define RGBLIGHT_VAL_STEP 17 +#endif + +#define RGBLED_TIMER_TOP F_CPU/(256*64) +// #define RGBLED_TIMER_TOP 0xFF10 + +#include <stdint.h> +#include <stdbool.h> +#include "eeconfig.h" +#include "light_ws2812.h" + +extern LED_TYPE led[RGBLED_NUM]; + +extern const uint8_t RGBLED_BREATHING_INTERVALS[4] PROGMEM; +extern const uint8_t RGBLED_RAINBOW_MOOD_INTERVALS[3] PROGMEM; +extern const uint8_t RGBLED_RAINBOW_SWIRL_INTERVALS[3] PROGMEM; +extern const uint8_t RGBLED_SNAKE_INTERVALS[3] PROGMEM; +extern const uint8_t RGBLED_KNIGHT_INTERVALS[3] PROGMEM; + +typedef union { +  uint32_t raw; +  struct { +    bool     enable  :1; +    uint8_t  mode    :6; +    uint16_t hue     :9; +    uint8_t  sat     :8; +    uint8_t  val     :8; +  }; +} rgblight_config_t; + +void rgblight_init(void); +void rgblight_increase(void); +void rgblight_decrease(void); +void rgblight_toggle(void); +void rgblight_enable(void); +void rgblight_step(void); +void rgblight_step_reverse(void); +void rgblight_mode(uint8_t mode); +void rgblight_set(void); +void rgblight_update_dword(uint32_t dword); +void rgblight_increase_hue(void); +void rgblight_decrease_hue(void); +void rgblight_increase_sat(void); +void rgblight_decrease_sat(void); +void rgblight_increase_val(void); +void rgblight_decrease_val(void); +void rgblight_sethsv(uint16_t hue, uint8_t sat, uint8_t val); +void rgblight_setrgb(uint8_t r, uint8_t g, uint8_t b); + +uint32_t eeconfig_read_rgblight(void); +void eeconfig_update_rgblight(uint32_t val); +void eeconfig_update_rgblight_default(void); +void eeconfig_debug_rgblight(void); + +void sethsv(uint16_t hue, uint8_t sat, uint8_t val, LED_TYPE *led1); +void setrgb(uint8_t r, uint8_t g, uint8_t b, LED_TYPE *led1); +void rgblight_sethsv_noeeprom(uint16_t hue, uint8_t sat, uint8_t val); + +#define EZ_RGB(val) rgblight_show_solid_color((val >> 16) & 0xFF, (val >> 8) & 0xFF, val & 0xFF) +void rgblight_show_solid_color(uint8_t r, uint8_t g, uint8_t b); + +void rgblight_task(void); + +void rgblight_timer_init(void); +void rgblight_timer_enable(void); +void rgblight_timer_disable(void); +void rgblight_timer_toggle(void); +void rgblight_effect_breathing(uint8_t interval); +void rgblight_effect_rainbow_mood(uint8_t interval); +void rgblight_effect_rainbow_swirl(uint8_t interval); +void rgblight_effect_snake(uint8_t interval); +void rgblight_effect_knight(uint8_t interval); +void rgblight_effect_christmas(void); + +#endif diff --git a/quantum/serial_link/LICENSE b/quantum/serial_link/LICENSE new file mode 100644 index 0000000000..d13cc4b26a --- /dev/null +++ b/quantum/serial_link/LICENSE @@ -0,0 +1,19 @@ +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/quantum/serial_link/README.md b/quantum/serial_link/README.md new file mode 100644 index 0000000000..e8490e290d --- /dev/null +++ b/quantum/serial_link/README.md @@ -0,0 +1 @@ +# qmk_serial_link
\ No newline at end of file diff --git a/quantum/serial_link/protocol/byte_stuffer.c b/quantum/serial_link/protocol/byte_stuffer.c new file mode 100644 index 0000000000..2c87d64c29 --- /dev/null +++ b/quantum/serial_link/protocol/byte_stuffer.c @@ -0,0 +1,142 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Fred Sundvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "serial_link/protocol/byte_stuffer.h" +#include "serial_link/protocol/frame_validator.h" +#include "serial_link/protocol/physical.h" +#include <stdbool.h> + +// This implements the "Consistent overhead byte stuffing protocol" +// https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing +// http://www.stuartcheshire.org/papers/COBSforToN.pdf + +typedef struct byte_stuffer_state { +    uint16_t next_zero; +    uint16_t data_pos; +    bool long_frame; +    uint8_t data[MAX_FRAME_SIZE]; +}byte_stuffer_state_t; + +static byte_stuffer_state_t states[NUM_LINKS]; + +void init_byte_stuffer_state(byte_stuffer_state_t* state) { +    state->next_zero = 0; +    state->data_pos = 0; +    state->long_frame = false; +} + +void init_byte_stuffer(void) { +    int i; +    for (i=0;i<NUM_LINKS;i++) { +        init_byte_stuffer_state(&states[i]); +    } +} + +void byte_stuffer_recv_byte(uint8_t link, uint8_t data) { +    byte_stuffer_state_t* state = &states[link]; +    // Start of a new frame +    if (state->next_zero == 0) { +        state->next_zero = data; +        state->long_frame = data == 0xFF; +        state->data_pos = 0; +        return; +    } + +    state->next_zero--; +    if (data == 0) { +        if (state->next_zero == 0) { +            // The frame is completed +            if (state->data_pos > 0) { +                validator_recv_frame(link, state->data, state->data_pos); +            } +        } +        else { +            // The frame is invalid, so reset +            init_byte_stuffer_state(state); +        } +    } +    else { +        if (state->data_pos == MAX_FRAME_SIZE) { +            // We exceeded our maximum frame size +            // therefore there's nothing else to do than reset to a new frame +            state->next_zero = data; +            state->long_frame = data == 0xFF; +            state->data_pos = 0; +        } +        else if (state->next_zero == 0) { +            if (state->long_frame) { +                // This is part of a long frame, so continue +                state->next_zero = data; +                state->long_frame = data == 0xFF; +            } +            else { +                // Special case for zeroes +                state->next_zero = data; +                state->data[state->data_pos++] = 0; +            } +        } +        else { +            state->data[state->data_pos++] = data; +        } +    } +} + +static void send_block(uint8_t link, uint8_t* start, uint8_t* end, uint8_t num_non_zero) { +    send_data(link, &num_non_zero, 1); +    if (end > start) { +        send_data(link, start, end-start); +    } +} + +void byte_stuffer_send_frame(uint8_t link, uint8_t* data, uint16_t size) { +    const uint8_t zero = 0; +    if (size > 0) { +        uint16_t num_non_zero = 1; +        uint8_t* end = data + size; +        uint8_t* start = data; +        while (data < end) { +            if (num_non_zero == 0xFF) { +                // There's more data after big non-zero block +                // So send it, and start a new block +                send_block(link, start, data, num_non_zero); +                start = data; +                num_non_zero = 1; +            } +            else { +                if (*data == 0) { +                    // A zero encountered, so send the block +                    send_block(link, start, data, num_non_zero); +                    start = data + 1; +                    num_non_zero = 1; +                } +                else { +                    num_non_zero++; +                } +                ++data; +            } +        } +        send_block(link, start, data, num_non_zero); +        send_data(link, &zero, 1); +    } +} diff --git a/quantum/serial_link/protocol/byte_stuffer.h b/quantum/serial_link/protocol/byte_stuffer.h new file mode 100644 index 0000000000..97e8968564 --- /dev/null +++ b/quantum/serial_link/protocol/byte_stuffer.h @@ -0,0 +1,37 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Fred Sundvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef SERIAL_LINK_BYTE_STUFFER_H +#define SERIAL_LINK_BYTE_STUFFER_H + +#include <stdint.h> + +#define MAX_FRAME_SIZE 1024 +#define NUM_LINKS 2 + +void init_byte_stuffer(void); +void byte_stuffer_recv_byte(uint8_t link, uint8_t data); +void byte_stuffer_send_frame(uint8_t link, uint8_t* data, uint16_t size); + +#endif diff --git a/quantum/serial_link/protocol/frame_router.c b/quantum/serial_link/protocol/frame_router.c new file mode 100644 index 0000000000..04b8c2e75c --- /dev/null +++ b/quantum/serial_link/protocol/frame_router.c @@ -0,0 +1,69 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Fred Sundvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "serial_link/protocol/frame_router.h" +#include "serial_link/protocol/transport.h" +#include "serial_link/protocol/frame_validator.h" + +static bool is_master; + +void router_set_master(bool master) { +   is_master = master; +} + +void route_incoming_frame(uint8_t link, uint8_t* data, uint16_t size){ +    if (is_master) { +        if (link == DOWN_LINK) { +            transport_recv_frame(data[size-1], data, size - 1); +        } +    } +    else { +        if (link == UP_LINK) { +            if (data[size-1] & 1) { +                transport_recv_frame(0, data, size - 1); +            } +            data[size-1] >>= 1; +            validator_send_frame(DOWN_LINK, data, size); +        } +        else { +            data[size-1]++; +            validator_send_frame(UP_LINK, data, size); +        } +    } +} + +void router_send_frame(uint8_t destination, uint8_t* data, uint16_t size) { +    if (destination == 0) { +        if (!is_master) { +            data[size] = 1; +            validator_send_frame(UP_LINK, data, size + 1); +        } +    } +    else { +        if (is_master) { +            data[size] = destination; +            validator_send_frame(DOWN_LINK, data, size + 1); +        } +    } +} diff --git a/quantum/serial_link/protocol/frame_router.h b/quantum/serial_link/protocol/frame_router.h new file mode 100644 index 0000000000..712250ff35 --- /dev/null +++ b/quantum/serial_link/protocol/frame_router.h @@ -0,0 +1,38 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Fred Sundvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef SERIAL_LINK_FRAME_ROUTER_H +#define SERIAL_LINK_FRAME_ROUTER_H + +#include <stdint.h> +#include <stdbool.h> + +#define UP_LINK 0 +#define DOWN_LINK 1 + +void router_set_master(bool master); +void route_incoming_frame(uint8_t link, uint8_t* data, uint16_t size); +void router_send_frame(uint8_t destination, uint8_t* data, uint16_t size); + +#endif diff --git a/quantum/serial_link/protocol/frame_validator.c b/quantum/serial_link/protocol/frame_validator.c new file mode 100644 index 0000000000..474f80ee8e --- /dev/null +++ b/quantum/serial_link/protocol/frame_validator.c @@ -0,0 +1,121 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Fred Sundvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "serial_link/protocol/frame_validator.h" +#include "serial_link/protocol/frame_router.h" +#include "serial_link/protocol/byte_stuffer.h" +#include <string.h> + +const uint32_t poly8_lookup[256] = +{ + 0, 0x77073096, 0xEE0E612C, 0x990951BA, + 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, + 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, + 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, + 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, + 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, + 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, + 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, + 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, + 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, + 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, + 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, + 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, + 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, + 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, + 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, + 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, + 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, + 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, + 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, + 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, + 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, + 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, + 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, + 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, + 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, + 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, + 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, + 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, + 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, + 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D +}; + +static uint32_t crc32_byte(uint8_t *p, uint32_t bytelength) +{ +    uint32_t crc = 0xffffffff; +    while (bytelength-- !=0) crc = poly8_lookup[((uint8_t) crc ^ *(p++))] ^ (crc >> 8); +    // return (~crc); also works +    return (crc ^ 0xffffffff); +} + +void validator_recv_frame(uint8_t link, uint8_t* data, uint16_t size) { +    if (size > 4) { +        uint32_t frame_crc; +        memcpy(&frame_crc, data + size -4, 4); +        uint32_t expected_crc = crc32_byte(data, size - 4); +        if (frame_crc == expected_crc) { +            route_incoming_frame(link, data, size-4); +        } +    } +} + +void validator_send_frame(uint8_t link, uint8_t* data, uint16_t size) { +    uint32_t crc = crc32_byte(data, size); +    memcpy(data + size, &crc, 4); +    byte_stuffer_send_frame(link, data, size + 4); +} diff --git a/quantum/serial_link/protocol/frame_validator.h b/quantum/serial_link/protocol/frame_validator.h new file mode 100644 index 0000000000..4a910d510b --- /dev/null +++ b/quantum/serial_link/protocol/frame_validator.h @@ -0,0 +1,34 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Fred Sundvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef SERIAL_LINK_FRAME_VALIDATOR_H +#define SERIAL_LINK_FRAME_VALIDATOR_H + +#include <stdint.h> + +void validator_recv_frame(uint8_t link, uint8_t* data, uint16_t size); +// The buffer pointed to by the data needs 4 additional bytes +void validator_send_frame(uint8_t link, uint8_t* data, uint16_t size); + +#endif diff --git a/quantum/serial_link/protocol/physical.h b/quantum/serial_link/protocol/physical.h new file mode 100644 index 0000000000..425e06cdd2 --- /dev/null +++ b/quantum/serial_link/protocol/physical.h @@ -0,0 +1,30 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Fred Sundvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef SERIAL_LINK_PHYSICAL_H +#define SERIAL_LINK_PHYSICAL_H + +void send_data(uint8_t link, const uint8_t* data, uint16_t size); + +#endif diff --git a/quantum/serial_link/protocol/transport.c b/quantum/serial_link/protocol/transport.c new file mode 100644 index 0000000000..ff795fe201 --- /dev/null +++ b/quantum/serial_link/protocol/transport.c @@ -0,0 +1,128 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Fred Sundvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "serial_link/protocol/transport.h" +#include "serial_link/protocol/frame_router.h" +#include "serial_link/protocol/triple_buffered_object.h" +#include <string.h> + +#define MAX_REMOTE_OBJECTS 16 +static remote_object_t* remote_objects[MAX_REMOTE_OBJECTS]; +static uint32_t num_remote_objects = 0; + +void reinitialize_serial_link_transport(void) { +    num_remote_objects = 0; +} + +void add_remote_objects(remote_object_t** _remote_objects, uint32_t _num_remote_objects) { +    unsigned int i; +    for(i=0;i<_num_remote_objects;i++) { +        remote_object_t* obj = _remote_objects[i]; +        remote_objects[num_remote_objects++] = obj; +        if (obj->object_type == MASTER_TO_ALL_SLAVES) { +            triple_buffer_object_t* tb = (triple_buffer_object_t*)obj->buffer; +            triple_buffer_init(tb); +            uint8_t* start = obj->buffer + LOCAL_OBJECT_SIZE(obj->object_size); +            tb = (triple_buffer_object_t*)start; +            triple_buffer_init(tb); +        } +        else if(obj->object_type == MASTER_TO_SINGLE_SLAVE) { +            uint8_t* start = obj->buffer; +            unsigned int j; +            for (j=0;j<NUM_SLAVES;j++) { +                triple_buffer_object_t* tb = (triple_buffer_object_t*)start; +                triple_buffer_init(tb); +                start += LOCAL_OBJECT_SIZE(obj->object_size); +            } +            triple_buffer_object_t* tb = (triple_buffer_object_t*)start; +            triple_buffer_init(tb); +        } +        else { +            uint8_t* start = obj->buffer; +            triple_buffer_object_t* tb = (triple_buffer_object_t*)start; +            triple_buffer_init(tb); +            start += LOCAL_OBJECT_SIZE(obj->object_size); +            unsigned int j; +            for (j=0;j<NUM_SLAVES;j++) { +                tb = (triple_buffer_object_t*)start; +                triple_buffer_init(tb); +                start += REMOTE_OBJECT_SIZE(obj->object_size); +            } +        } +    } +} + +void transport_recv_frame(uint8_t from, uint8_t* data, uint16_t size) { +    uint8_t id = data[size-1]; +    if (id < num_remote_objects) { +        remote_object_t* obj = remote_objects[id]; +        if (obj->object_size == size - 1) { +            uint8_t* start; +            if (obj->object_type == MASTER_TO_ALL_SLAVES) { +                start = obj->buffer + LOCAL_OBJECT_SIZE(obj->object_size); +            } +            else if(obj->object_type == SLAVE_TO_MASTER) { +                start = obj->buffer + LOCAL_OBJECT_SIZE(obj->object_size); +                start += (from - 1) * REMOTE_OBJECT_SIZE(obj->object_size); +            } +            else { +                start = obj->buffer + NUM_SLAVES * LOCAL_OBJECT_SIZE(obj->object_size); +            } +            triple_buffer_object_t* tb = (triple_buffer_object_t*)start; +            void* ptr = triple_buffer_begin_write_internal(obj->object_size, tb); +            memcpy(ptr, data, size - 1); +            triple_buffer_end_write_internal(tb); +        } +    } +} + +void update_transport(void) { +    unsigned int i; +    for(i=0;i<num_remote_objects;i++) { +        remote_object_t* obj = remote_objects[i]; +        if (obj->object_type == MASTER_TO_ALL_SLAVES || obj->object_type == SLAVE_TO_MASTER) { +            triple_buffer_object_t* tb = (triple_buffer_object_t*)obj->buffer; +            uint8_t* ptr = (uint8_t*)triple_buffer_read_internal(obj->object_size + LOCAL_OBJECT_EXTRA, tb); +            if (ptr) { +                ptr[obj->object_size] = i; +                uint8_t dest = obj->object_type == MASTER_TO_ALL_SLAVES ? 0xFF : 0; +                router_send_frame(dest, ptr, obj->object_size + 1); +            } +        } +        else { +            uint8_t* start = obj->buffer; +            unsigned int j; +            for (j=0;j<NUM_SLAVES;j++) { +                triple_buffer_object_t* tb = (triple_buffer_object_t*)start; +                uint8_t* ptr = (uint8_t*)triple_buffer_read_internal(obj->object_size + LOCAL_OBJECT_EXTRA, tb); +                if (ptr) { +                    ptr[obj->object_size] = i; +                    uint8_t dest = j + 1; +                    router_send_frame(dest, ptr, obj->object_size + 1); +                } +                start += LOCAL_OBJECT_SIZE(obj->object_size); +            } +        } +    } +} diff --git a/quantum/serial_link/protocol/transport.h b/quantum/serial_link/protocol/transport.h new file mode 100644 index 0000000000..2c5d890b21 --- /dev/null +++ b/quantum/serial_link/protocol/transport.h @@ -0,0 +1,152 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Fred Sundvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef SERIAL_LINK_TRANSPORT_H +#define SERIAL_LINK_TRANSPORT_H + +#include "serial_link/protocol/triple_buffered_object.h" +#include "serial_link/system/serial_link.h" + +#define NUM_SLAVES 8 +#define LOCAL_OBJECT_EXTRA 16 + +// master -> slave = 1 local(target all), 1 remote object +// slave -> master = 1 local(target 0), multiple remote objects +// master -> single slave (multiple local, target id), 1 remote object +typedef enum { +    MASTER_TO_ALL_SLAVES, +    MASTER_TO_SINGLE_SLAVE, +    SLAVE_TO_MASTER, +} remote_object_type; + +typedef struct { +    remote_object_type object_type; +    uint16_t object_size; +    uint8_t buffer[] __attribute__((aligned(4))); +} remote_object_t; + +#define REMOTE_OBJECT_SIZE(objectsize) \ +    (sizeof(triple_buffer_object_t) + objectsize * 3) +#define LOCAL_OBJECT_SIZE(objectsize) \ +    (sizeof(triple_buffer_object_t) + (objectsize + LOCAL_OBJECT_EXTRA) * 3) + +#define REMOTE_OBJECT_HELPER(name, type, num_local, num_remote) \ +typedef struct { \ +    remote_object_t object; \ +    uint8_t buffer[ \ +        num_remote * REMOTE_OBJECT_SIZE(sizeof(type)) + \ +        num_local * LOCAL_OBJECT_SIZE(sizeof(type))]; \ +} remote_object_##name##_t; + +#define MASTER_TO_ALL_SLAVES_OBJECT(name, type) \ +    REMOTE_OBJECT_HELPER(name, type, 1, 1) \ +    remote_object_##name##_t remote_object_##name = { \ +        .object = { \ +            .object_type = MASTER_TO_ALL_SLAVES, \ +            .object_size = sizeof(type), \ +        } \ +    }; \ +    type* begin_write_##name(void) { \ +        remote_object_t* obj = (remote_object_t*)&remote_object_##name; \ +        triple_buffer_object_t* tb = (triple_buffer_object_t*)obj->buffer; \ +        return (type*)triple_buffer_begin_write_internal(sizeof(type) + LOCAL_OBJECT_EXTRA, tb); \ +    }\ +    void end_write_##name(void) { \ +        remote_object_t* obj = (remote_object_t*)&remote_object_##name; \ +        triple_buffer_object_t* tb = (triple_buffer_object_t*)obj->buffer; \ +        triple_buffer_end_write_internal(tb); \ +        signal_data_written(); \ +    }\ +    type* read_##name(void) { \ +        remote_object_t* obj = (remote_object_t*)&remote_object_##name; \ +        uint8_t* start = obj->buffer + LOCAL_OBJECT_SIZE(obj->object_size);\ +        triple_buffer_object_t* tb = (triple_buffer_object_t*)start; \ +        return (type*)triple_buffer_read_internal(obj->object_size, tb); \ +    } + +#define MASTER_TO_SINGLE_SLAVE_OBJECT(name, type) \ +    REMOTE_OBJECT_HELPER(name, type, NUM_SLAVES, 1) \ +    remote_object_##name##_t remote_object_##name = { \ +        .object = { \ +            .object_type = MASTER_TO_SINGLE_SLAVE, \ +            .object_size = sizeof(type), \ +        } \ +    }; \ +    type* begin_write_##name(uint8_t slave) { \ +        remote_object_t* obj = (remote_object_t*)&remote_object_##name; \ +        uint8_t* start = obj->buffer;\ +        start += slave * LOCAL_OBJECT_SIZE(obj->object_size); \ +        triple_buffer_object_t* tb = (triple_buffer_object_t*)start; \ +        return (type*)triple_buffer_begin_write_internal(sizeof(type) + LOCAL_OBJECT_EXTRA, tb); \ +    }\ +    void end_write_##name(uint8_t slave) { \ +        remote_object_t* obj = (remote_object_t*)&remote_object_##name; \ +        uint8_t* start = obj->buffer;\ +        start += slave * LOCAL_OBJECT_SIZE(obj->object_size); \ +        triple_buffer_object_t* tb = (triple_buffer_object_t*)start; \ +        triple_buffer_end_write_internal(tb); \ +        signal_data_written(); \ +    }\ +    type* read_##name() { \ +        remote_object_t* obj = (remote_object_t*)&remote_object_##name; \ +        uint8_t* start = obj->buffer + NUM_SLAVES * LOCAL_OBJECT_SIZE(obj->object_size);\ +        triple_buffer_object_t* tb = (triple_buffer_object_t*)start; \ +        return (type*)triple_buffer_read_internal(obj->object_size, tb); \ +    } + +#define SLAVE_TO_MASTER_OBJECT(name, type) \ +    REMOTE_OBJECT_HELPER(name, type, 1, NUM_SLAVES) \ +    remote_object_##name##_t remote_object_##name = { \ +        .object = { \ +            .object_type = SLAVE_TO_MASTER, \ +            .object_size = sizeof(type), \ +        } \ +    }; \ +    type* begin_write_##name(void) { \ +        remote_object_t* obj = (remote_object_t*)&remote_object_##name; \ +        triple_buffer_object_t* tb = (triple_buffer_object_t*)obj->buffer; \ +        return (type*)triple_buffer_begin_write_internal(sizeof(type) + LOCAL_OBJECT_EXTRA, tb); \ +    }\ +    void end_write_##name(void) { \ +        remote_object_t* obj = (remote_object_t*)&remote_object_##name; \ +        triple_buffer_object_t* tb = (triple_buffer_object_t*)obj->buffer; \ +        triple_buffer_end_write_internal(tb); \ +        signal_data_written(); \ +    }\ +    type* read_##name(uint8_t slave) { \ +        remote_object_t* obj = (remote_object_t*)&remote_object_##name; \ +        uint8_t* start = obj->buffer + LOCAL_OBJECT_SIZE(obj->object_size);\ +        start+=slave * REMOTE_OBJECT_SIZE(obj->object_size); \ +        triple_buffer_object_t* tb = (triple_buffer_object_t*)start; \ +        return (type*)triple_buffer_read_internal(obj->object_size, tb); \ +    } + +#define REMOTE_OBJECT(name) (remote_object_t*)&remote_object_##name + +void add_remote_objects(remote_object_t** remote_objects, uint32_t num_remote_objects); +void reinitialize_serial_link_transport(void); +void transport_recv_frame(uint8_t from, uint8_t* data, uint16_t size); +void update_transport(void); + +#endif diff --git a/quantum/serial_link/protocol/triple_buffered_object.c b/quantum/serial_link/protocol/triple_buffered_object.c new file mode 100644 index 0000000000..e3e8989d30 --- /dev/null +++ b/quantum/serial_link/protocol/triple_buffered_object.c @@ -0,0 +1,78 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Fred Sundvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "serial_link/protocol/triple_buffered_object.h" +#include "serial_link/system/serial_link.h" +#include <stdbool.h> +#include <stddef.h> + +#define GET_READ_INDEX() object->state & 3 +#define GET_WRITE_INDEX() (object->state >> 2) & 3 +#define GET_SHARED_INDEX() (object->state >> 4) & 3 +#define GET_DATA_AVAILABLE() (object->state >> 6) & 1 + +#define SET_READ_INDEX(i) object->state = ((object->state & ~3) | i) +#define SET_WRITE_INDEX(i) object->state = ((object->state & ~(3 << 2)) | (i << 2)) +#define SET_SHARED_INDEX(i) object->state = ((object->state & ~(3 << 4)) | (i << 4)) +#define SET_DATA_AVAILABLE(i) object->state = ((object->state & ~(1 << 6)) | (i << 6)) + +void triple_buffer_init(triple_buffer_object_t* object) { +    object->state = 0; +    SET_WRITE_INDEX(0); +    SET_READ_INDEX(1); +    SET_SHARED_INDEX(2); +    SET_DATA_AVAILABLE(0); +} + +void* triple_buffer_read_internal(uint16_t object_size, triple_buffer_object_t* object) { +    serial_link_lock(); +    if (GET_DATA_AVAILABLE()) { +        uint8_t shared_index = GET_SHARED_INDEX(); +        uint8_t read_index = GET_READ_INDEX(); +        SET_READ_INDEX(shared_index); +        SET_SHARED_INDEX(read_index); +        SET_DATA_AVAILABLE(false); +        serial_link_unlock(); +        return object->buffer + object_size * shared_index; +    } +    else { +        serial_link_unlock(); +        return NULL; +    } +} + +void* triple_buffer_begin_write_internal(uint16_t object_size, triple_buffer_object_t* object) { +    uint8_t write_index = GET_WRITE_INDEX(); +    return object->buffer + object_size * write_index; +} + +void triple_buffer_end_write_internal(triple_buffer_object_t* object) { +    serial_link_lock(); +    uint8_t shared_index = GET_SHARED_INDEX(); +    uint8_t write_index = GET_WRITE_INDEX(); +    SET_SHARED_INDEX(write_index); +    SET_WRITE_INDEX(shared_index); +    SET_DATA_AVAILABLE(true); +    serial_link_unlock(); +} diff --git a/quantum/serial_link/protocol/triple_buffered_object.h b/quantum/serial_link/protocol/triple_buffered_object.h new file mode 100644 index 0000000000..2e57db3f50 --- /dev/null +++ b/quantum/serial_link/protocol/triple_buffered_object.h @@ -0,0 +1,51 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Fred Sundvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef SERIAL_LINK_TRIPLE_BUFFERED_OBJECT_H +#define SERIAL_LINK_TRIPLE_BUFFERED_OBJECT_H + +#include <stdint.h> + +typedef struct { +    uint8_t state; +    uint8_t buffer[] __attribute__((aligned(4))); +}triple_buffer_object_t; + +void triple_buffer_init(triple_buffer_object_t* object); + +#define triple_buffer_begin_write(object) \ +    (typeof(*object.buffer[0])*)triple_buffer_begin_write_internal(sizeof(*object.buffer[0]), (triple_buffer_object_t*)object) + +#define triple_buffer_end_write(object) \ +    triple_buffer_end_write_internal((triple_buffer_object_t*)object) + +#define triple_buffer_read(object) \ +    (typeof(*object.buffer[0])*)triple_buffer_read_internal(sizeof(*object.buffer[0]), (triple_buffer_object_t*)object) + +void* triple_buffer_begin_write_internal(uint16_t object_size, triple_buffer_object_t* object); +void triple_buffer_end_write_internal(triple_buffer_object_t* object); +void* triple_buffer_read_internal(uint16_t object_size, triple_buffer_object_t* object); + + +#endif diff --git a/quantum/serial_link/system/serial_link.c b/quantum/serial_link/system/serial_link.c new file mode 100644 index 0000000000..b3bee62a18 --- /dev/null +++ b/quantum/serial_link/system/serial_link.c @@ -0,0 +1,265 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Fred Sundvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ +#include "report.h" +#include "host_driver.h" +#include "serial_link/system/serial_link.h" +#include "hal.h" +#include "serial_link/protocol/byte_stuffer.h" +#include "serial_link/protocol/transport.h" +#include "serial_link/protocol/frame_router.h" +#include "matrix.h" +#include <stdbool.h> +#include "print.h" +#include "config.h" + +static event_source_t new_data_event; +static bool serial_link_connected; +static bool is_master = false; + +static uint8_t keyboard_leds(void); +static void send_keyboard(report_keyboard_t *report); +static void send_mouse(report_mouse_t *report); +static void send_system(uint16_t data); +static void send_consumer(uint16_t data); + +host_driver_t serial_driver = { +  keyboard_leds, +  send_keyboard, +  send_mouse, +  send_system, +  send_consumer +}; + +// Define these in your Config.h file +#ifndef SERIAL_LINK_BAUD +#error "Serial link baud is not set" +#endif + +#ifndef SERIAL_LINK_THREAD_PRIORITY +#error "Serial link thread priority not set" +#endif + +static SerialConfig config = { +    .sc_speed = SERIAL_LINK_BAUD +}; + +//#define DEBUG_LINK_ERRORS + +static uint32_t read_from_serial(SerialDriver* driver, uint8_t link) { +    const uint32_t buffer_size = 16; +    uint8_t buffer[buffer_size]; +    uint32_t bytes_read = sdAsynchronousRead(driver, buffer, buffer_size); +    uint8_t* current = buffer; +    uint8_t* end = current + bytes_read; +    while(current < end) { +        byte_stuffer_recv_byte(link, *current); +        current++; +    } +    return bytes_read; +} + +static void print_error(char* str, eventflags_t flags, SerialDriver* driver) { +#ifdef DEBUG_LINK_ERRORS +    if (flags & SD_PARITY_ERROR) { +        print(str); +        print(" Parity error\n"); +    } +    if (flags & SD_FRAMING_ERROR) { +        print(str); +        print(" Framing error\n"); +    } +    if (flags & SD_OVERRUN_ERROR) { +        print(str); +        uint32_t size = qSpaceI(&(driver->iqueue)); +        xprintf(" Overrun error, queue size %d\n", size); + +    } +    if (flags & SD_NOISE_ERROR) { +        print(str); +        print(" Noise error\n"); +    } +    if (flags & SD_BREAK_DETECTED) { +        print(str); +        print(" Break detected\n"); +    } +#else +    (void)str; +    (void)flags; +    (void)driver; +#endif +} + +bool is_serial_link_master(void) { +    return is_master; +} + +// TODO: Optimize the stack size, this is probably way too big +static THD_WORKING_AREA(serialThreadStack, 1024); +static THD_FUNCTION(serialThread, arg) { +    (void)arg; +    event_listener_t new_data_listener; +    event_listener_t sd1_listener; +    event_listener_t sd2_listener; +    chEvtRegister(&new_data_event, &new_data_listener, 0); +    eventflags_t events = CHN_INPUT_AVAILABLE +            | SD_PARITY_ERROR | SD_FRAMING_ERROR | SD_OVERRUN_ERROR | SD_NOISE_ERROR | SD_BREAK_DETECTED; +    chEvtRegisterMaskWithFlags(chnGetEventSource(&SD1), +        &sd1_listener, +        EVENT_MASK(1), +        events); +    chEvtRegisterMaskWithFlags(chnGetEventSource(&SD2), +        &sd2_listener, +        EVENT_MASK(2), +        events); +    bool need_wait = false; +    while(true) { +        eventflags_t flags1 = 0; +        eventflags_t flags2 = 0; +        if (need_wait) { +            eventmask_t mask = chEvtWaitAnyTimeout(ALL_EVENTS, MS2ST(1000)); +            if (mask & EVENT_MASK(1)) { +                flags1 = chEvtGetAndClearFlags(&sd1_listener); +                print_error("DOWNLINK", flags1, &SD1); +            } +            if (mask & EVENT_MASK(2)) { +                flags2 = chEvtGetAndClearFlags(&sd2_listener); +                print_error("UPLINK", flags2, &SD2); +            } +        } + +        // Always stay as master, even if the USB goes into sleep mode +        is_master |= usbGetDriverStateI(&USBD1) == USB_ACTIVE; +        router_set_master(is_master); + +        need_wait = true; +        need_wait &= read_from_serial(&SD2, UP_LINK) == 0; +        need_wait &= read_from_serial(&SD1, DOWN_LINK) == 0; +        update_transport(); +    } +} + +void send_data(uint8_t link, const uint8_t* data, uint16_t size) { +    if (link == DOWN_LINK) { +        sdWrite(&SD1, data, size); +    } +    else { +        sdWrite(&SD2, data, size); +    } +} + +static systime_t last_update = 0; + +typedef struct { +    matrix_row_t rows[MATRIX_ROWS]; +} matrix_object_t; + +static matrix_object_t last_matrix = {}; + +SLAVE_TO_MASTER_OBJECT(keyboard_matrix, matrix_object_t); +MASTER_TO_ALL_SLAVES_OBJECT(serial_link_connected, bool); + +static remote_object_t* remote_objects[] = { +    REMOTE_OBJECT(serial_link_connected), +    REMOTE_OBJECT(keyboard_matrix), +}; + +void init_serial_link(void) { +    serial_link_connected = false; +    init_serial_link_hal(); +    add_remote_objects(remote_objects, sizeof(remote_objects)/sizeof(remote_object_t*)); +    init_byte_stuffer(); +    sdStart(&SD1, &config); +    sdStart(&SD2, &config); +    chEvtObjectInit(&new_data_event); +    (void)chThdCreateStatic(serialThreadStack, sizeof(serialThreadStack), +                              SERIAL_LINK_THREAD_PRIORITY, serialThread, NULL); +} + +void matrix_set_remote(matrix_row_t* rows, uint8_t index); + +void serial_link_update(void) { +    if (read_serial_link_connected()) { +        serial_link_connected = true; +    } + +    matrix_object_t matrix; +    bool changed = false; +    for(uint8_t i=0;i<MATRIX_ROWS;i++) { +        matrix.rows[i] = matrix_get_row(i); +        changed |= matrix.rows[i] != last_matrix.rows[i]; +    } + +    systime_t current_time = chVTGetSystemTimeX(); +    systime_t delta = current_time - last_update; +    if (changed || delta > US2ST(5000)) { +        last_update = current_time; +        last_matrix = matrix; +        matrix_object_t* m = begin_write_keyboard_matrix(); +        for(uint8_t i=0;i<MATRIX_ROWS;i++) { +            m->rows[i] = matrix.rows[i]; +        } +        end_write_keyboard_matrix(); +        *begin_write_serial_link_connected() = true; +        end_write_serial_link_connected(); +    } + +    matrix_object_t* m = read_keyboard_matrix(0); +    if (m) { +        matrix_set_remote(m->rows, 0); +    } +} + +void signal_data_written(void) { +    chEvtBroadcast(&new_data_event); +} + +bool is_serial_link_connected(void) { +    return serial_link_connected; +} + +host_driver_t* get_serial_link_driver(void) { +    return &serial_driver; +} + +// NOTE: The driver does nothing, because the master handles everything +uint8_t keyboard_leds(void) { +    return 0; +} + +void send_keyboard(report_keyboard_t *report) { +    (void)report; +} + +void send_mouse(report_mouse_t *report) { +    (void)report; +} + +void send_system(uint16_t data) { +    (void)data; +} + +void send_consumer(uint16_t data) { +    (void)data; +} + diff --git a/quantum/serial_link/system/serial_link.h b/quantum/serial_link/system/serial_link.h new file mode 100644 index 0000000000..351e03877b --- /dev/null +++ b/quantum/serial_link/system/serial_link.h @@ -0,0 +1,63 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Fred Sundvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef SERIAL_LINK_H +#define SERIAL_LINK_H + +#include "host_driver.h" +#include <stdbool.h> + +void init_serial_link(void); +void init_serial_link_hal(void); +bool is_serial_link_connected(void); +bool is_serial_link_master(void); +host_driver_t* get_serial_link_driver(void); +void serial_link_update(void); + +#if defined(PROTOCOL_CHIBIOS) +#include "ch.h" + +static inline void serial_link_lock(void) { +    chSysLock(); +} + +static inline void serial_link_unlock(void) { +    chSysUnlock(); +} + +void signal_data_written(void); + +#else + +inline void serial_link_lock(void) { +} + +inline void serial_link_unlock(void) { +} + +void signal_data_written(void); + +#endif + +#endif diff --git a/quantum/serial_link/tests/Makefile b/quantum/serial_link/tests/Makefile new file mode 100644 index 0000000000..1b072c6f1d --- /dev/null +++ b/quantum/serial_link/tests/Makefile @@ -0,0 +1,61 @@ +# The MIT License (MIT) +#  +# Copyright (c) 2016 Fred Sundvik +#  +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +#  +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +#  +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +CC = gcc +CFLAGS	=  +INCLUDES = -I. -I../../ +LDFLAGS = -L$(BUILDDIR)/cgreen/build-c/src -shared +LDLIBS = -lcgreen +UNITOBJ = $(BUILDDIR)/serialtest/unitobj +DEPDIR = $(BUILDDIR)/serialtest/unit.d +UNITTESTS = $(BUILDDIR)/serialtest/unittests +DEPFLAGS = -MT $@ -MMD -MP -MF $(DEPDIR)/$*.Td +EXT = .so +UNAME := $(shell uname) +ifneq (, $(findstring MINGW, $(UNAME))) +	EXT = .dll +endif +ifneq (, $(findstring CYGWIN, $(UNAME))) +	EXT = .dll +endif +	 +SRC = $(wildcard *.c) +TESTFILES = $(patsubst %.c, $(UNITTESTS)/%$(EXT), $(SRC)) +$(shell mkdir -p $(DEPDIR) >/dev/null) + +test: $(TESTFILES) +	@$(BUILDDIR)/cgreen/build-c/tools/cgreen-runner --color $(TESTFILES) + +$(UNITTESTS)/%$(EXT): $(UNITOBJ)/%.o +	@mkdir -p $(UNITTESTS) +	$(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) + +$(UNITOBJ)/%.o : %.c +$(UNITOBJ)/%.o: %.c $(DEPDIR)/%.d +	@mkdir -p $(UNITOBJ) +	$(CC) $(CFLAGS) $(DEPFLAGS) $(INCLUDES) -c $< -o $@ +	@mv -f $(DEPDIR)/$*.Td $(DEPDIR)/$*.d +	 +$(DEPDIR)/%.d: ; +.PRECIOUS: $(DEPDIR)/%.d + +-include $(patsubst %,$(DEPDIR)/%.d,$(basename $(SRC)))
\ No newline at end of file diff --git a/quantum/serial_link/tests/byte_stuffer_tests.cpp b/quantum/serial_link/tests/byte_stuffer_tests.cpp new file mode 100644 index 0000000000..ff49d727bb --- /dev/null +++ b/quantum/serial_link/tests/byte_stuffer_tests.cpp @@ -0,0 +1,483 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Fred Sundvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "gtest/gtest.h" +#include "gmock/gmock.h" +#include <vector> +#include <algorithm> +extern "C" { +#include "serial_link/protocol/byte_stuffer.h" +#include "serial_link/protocol/frame_validator.h" +#include "serial_link/protocol/physical.h" +} + +using testing::_; +using testing::ElementsAreArray; +using testing::Args; + +class ByteStuffer : public ::testing::Test{ +public: +    ByteStuffer() { +        Instance = this; +        init_byte_stuffer(); +    } + +    ~ByteStuffer() { +        Instance = nullptr; +    } + +    MOCK_METHOD3(validator_recv_frame, void (uint8_t link, uint8_t* data, uint16_t size)); + +    void send_data(uint8_t link, const uint8_t* data, uint16_t size) { +        std::copy(data, data + size, std::back_inserter(sent_data)); +    } +    std::vector<uint8_t> sent_data; + +    static ByteStuffer* Instance; +}; + +ByteStuffer* ByteStuffer::Instance = nullptr; + +extern "C" { +    void validator_recv_frame(uint8_t link, uint8_t* data, uint16_t size) { +        ByteStuffer::Instance->validator_recv_frame(link, data, size); +    } + +    void send_data(uint8_t link, const uint8_t* data, uint16_t size) { +        ByteStuffer::Instance->send_data(link, data, size); +    } +} + +TEST_F(ByteStuffer, receives_no_frame_for_a_single_zero_byte) { +    EXPECT_CALL(*this, validator_recv_frame(_, _, _)) +        .Times(0); +    byte_stuffer_recv_byte(0, 0); +} + +TEST_F(ByteStuffer, receives_no_frame_for_a_single_FF_byte) { +    EXPECT_CALL(*this, validator_recv_frame(_, _, _)) +        .Times(0); +    byte_stuffer_recv_byte(0, 0xFF); +} + +TEST_F(ByteStuffer, receives_no_frame_for_a_single_random_byte) { +    EXPECT_CALL(*this, validator_recv_frame(_, _, _)) +        .Times(0); +    byte_stuffer_recv_byte(0, 0x4A); +} + +TEST_F(ByteStuffer, receives_no_frame_for_a_zero_length_frame) { +    EXPECT_CALL(*this, validator_recv_frame(_, _, _)) +        .Times(0); +    byte_stuffer_recv_byte(0, 1); +    byte_stuffer_recv_byte(0, 0); +} + +TEST_F(ByteStuffer, receives_single_byte_valid_frame) { +    uint8_t expected[] = {0x37}; +    EXPECT_CALL(*this, validator_recv_frame(_, _, _)) +        .With(Args<1, 2>(ElementsAreArray(expected))); +    byte_stuffer_recv_byte(0, 2); +    byte_stuffer_recv_byte(0, 0x37); +    byte_stuffer_recv_byte(0, 0); +} +TEST_F(ByteStuffer, receives_three_bytes_valid_frame) { +    uint8_t expected[] = {0x37, 0x99, 0xFF}; +    EXPECT_CALL(*this, validator_recv_frame(_, _, _)) +        .With(Args<1, 2>(ElementsAreArray(expected))); +    byte_stuffer_recv_byte(0, 4); +    byte_stuffer_recv_byte(0, 0x37); +    byte_stuffer_recv_byte(0, 0x99); +    byte_stuffer_recv_byte(0, 0xFF); +    byte_stuffer_recv_byte(0, 0); +} + +TEST_F(ByteStuffer, receives_single_zero_valid_frame) { +    uint8_t expected[] = {0}; +    EXPECT_CALL(*this, validator_recv_frame(_, _, _)) +        .With(Args<1, 2>(ElementsAreArray(expected))); +    byte_stuffer_recv_byte(0, 1); +    byte_stuffer_recv_byte(0, 1); +    byte_stuffer_recv_byte(0, 0); +} + +TEST_F(ByteStuffer, receives_valid_frame_with_zeroes) { +    uint8_t expected[] = {5, 0, 3, 0}; +    EXPECT_CALL(*this, validator_recv_frame(_, _, _)) +        .With(Args<1, 2>(ElementsAreArray(expected))); +    byte_stuffer_recv_byte(0, 2); +    byte_stuffer_recv_byte(0, 5); +    byte_stuffer_recv_byte(0, 2); +    byte_stuffer_recv_byte(0, 3); +    byte_stuffer_recv_byte(0, 1); +    byte_stuffer_recv_byte(0, 0); +} + + +TEST_F(ByteStuffer, receives_two_valid_frames) { +    uint8_t expected1[] = {5, 0}; +    uint8_t expected2[] = {3}; +    EXPECT_CALL(*this, validator_recv_frame(_, _, _)) +        .With(Args<1, 2>(ElementsAreArray(expected1))); +    EXPECT_CALL(*this, validator_recv_frame(_, _, _)) +        .With(Args<1, 2>(ElementsAreArray(expected2))); +    byte_stuffer_recv_byte(1, 2); +    byte_stuffer_recv_byte(1, 5); +    byte_stuffer_recv_byte(1, 1); +    byte_stuffer_recv_byte(1, 0); +    byte_stuffer_recv_byte(1, 2); +    byte_stuffer_recv_byte(1, 3); +    byte_stuffer_recv_byte(1, 0); +} + +TEST_F(ByteStuffer, receives_valid_frame_after_unexpected_zero) { +    uint8_t expected[] = {5, 7}; +    EXPECT_CALL(*this, validator_recv_frame(_, _, _)) +        .With(Args<1, 2>(ElementsAreArray(expected))); +    byte_stuffer_recv_byte(1, 3); +    byte_stuffer_recv_byte(1, 1); +    byte_stuffer_recv_byte(1, 0); +    byte_stuffer_recv_byte(1, 3); +    byte_stuffer_recv_byte(1, 5); +    byte_stuffer_recv_byte(1, 7); +    byte_stuffer_recv_byte(1, 0); +} + +TEST_F(ByteStuffer, receives_valid_frame_after_unexpected_non_zero) { +    uint8_t expected[] = {5, 7}; +    EXPECT_CALL(*this, validator_recv_frame(_, _, _)) +        .With(Args<1, 2>(ElementsAreArray(expected))); +    byte_stuffer_recv_byte(0, 2); +    byte_stuffer_recv_byte(0, 9); +    byte_stuffer_recv_byte(0, 4); // This should have been zero +    byte_stuffer_recv_byte(0, 0); +    byte_stuffer_recv_byte(0, 3); +    byte_stuffer_recv_byte(0, 5); +    byte_stuffer_recv_byte(0, 7); +    byte_stuffer_recv_byte(0, 0); +} + +TEST_F(ByteStuffer, receives_a_valid_frame_with_over254_non_zeroes_and_then_end_of_frame) { +    uint8_t expected[254]; +    int i; +    for (i=0;i<254;i++) { +        expected[i] = i + 1; +    } +    EXPECT_CALL(*this, validator_recv_frame(_, _, _)) +        .With(Args<1, 2>(ElementsAreArray(expected))); +    byte_stuffer_recv_byte(0, 0xFF); +    for (i=0;i<254;i++) { +        byte_stuffer_recv_byte(0, i+1); +    } +    byte_stuffer_recv_byte(0, 0); +} + +TEST_F(ByteStuffer, receives_a_valid_frame_with_over254_non_zeroes_next_byte_is_non_zero) { +    uint8_t expected[255]; +    int i; +    for (i=0;i<254;i++) { +        expected[i] = i + 1; +    } +    expected[254] = 7; +    EXPECT_CALL(*this, validator_recv_frame(_, _, _)) +        .With(Args<1, 2>(ElementsAreArray(expected))); +    byte_stuffer_recv_byte(0, 0xFF); +    for (i=0;i<254;i++) { +        byte_stuffer_recv_byte(0, i+1); +    } +    byte_stuffer_recv_byte(0, 2); +    byte_stuffer_recv_byte(0, 7); +    byte_stuffer_recv_byte(0, 0); +} + +TEST_F(ByteStuffer, receives_a_valid_frame_with_over254_non_zeroes_next_byte_is_zero) { +    uint8_t expected[255]; +    int i; +    for (i=0;i<254;i++) { +        expected[i] = i + 1; +    } +    expected[254] = 0; +    EXPECT_CALL(*this, validator_recv_frame(_, _, _)) +        .With(Args<1, 2>(ElementsAreArray(expected))); +    byte_stuffer_recv_byte(0, 0xFF); +    for (i=0;i<254;i++) { +        byte_stuffer_recv_byte(0, i+1); +    } +    byte_stuffer_recv_byte(0, 1); +    byte_stuffer_recv_byte(0, 1); +    byte_stuffer_recv_byte(0, 0); +} + +TEST_F(ByteStuffer, receives_two_long_frames_and_some_more) { +    uint8_t expected[515]; +    int i; +    int j; +    for (j=0;j<2;j++) { +        for (i=0;i<254;i++) { +            expected[i+254*j] = i + 1; +        } +    } +    for (i=0;i<7;i++) { +        expected[254*2+i] = i + 1; +    } +    EXPECT_CALL(*this, validator_recv_frame(_, _, _)) +        .With(Args<1, 2>(ElementsAreArray(expected))); +    byte_stuffer_recv_byte(0, 0xFF); +    for (i=0;i<254;i++) { +        byte_stuffer_recv_byte(0, i+1); +    } +    byte_stuffer_recv_byte(0, 0xFF); +    for (i=0;i<254;i++) { +        byte_stuffer_recv_byte(0, i+1); +    } +    byte_stuffer_recv_byte(0, 8); +    byte_stuffer_recv_byte(0, 1); +    byte_stuffer_recv_byte(0, 2); +    byte_stuffer_recv_byte(0, 3); +    byte_stuffer_recv_byte(0, 4); +    byte_stuffer_recv_byte(0, 5); +    byte_stuffer_recv_byte(0, 6); +    byte_stuffer_recv_byte(0, 7); +    byte_stuffer_recv_byte(0, 0); +} + +TEST_F(ByteStuffer, receives_an_all_zeros_frame_that_is_maximum_size) { +    uint8_t expected[MAX_FRAME_SIZE] = {}; +    EXPECT_CALL(*this, validator_recv_frame(_, _, _)) +        .With(Args<1, 2>(ElementsAreArray(expected))); +    int i; +    byte_stuffer_recv_byte(0, 1); +    for(i=0;i<MAX_FRAME_SIZE;i++) { +       byte_stuffer_recv_byte(0, 1); +    } +    byte_stuffer_recv_byte(0, 0); +} + +TEST_F(ByteStuffer, doesnt_recv_a_frame_thats_too_long_all_zeroes) { +    uint8_t expected[1] = {0}; +    EXPECT_CALL(*this, validator_recv_frame(_, _, _)) +        .Times(0); +    int i; +    byte_stuffer_recv_byte(0, 1); +    for(i=0;i<MAX_FRAME_SIZE;i++) { +       byte_stuffer_recv_byte(0, 1); +    } +    byte_stuffer_recv_byte(0, 1); +    byte_stuffer_recv_byte(0, 0); +} + +TEST_F(ByteStuffer, received_frame_is_aborted_when_its_too_long) { +    uint8_t expected[1] = {1}; +    EXPECT_CALL(*this, validator_recv_frame(_, _, _)) +        .With(Args<1, 2>(ElementsAreArray(expected))); +    int i; +    byte_stuffer_recv_byte(0, 1); +    for(i=0;i<MAX_FRAME_SIZE;i++) { +       byte_stuffer_recv_byte(0, 1); +    } +    byte_stuffer_recv_byte(0, 2); +    byte_stuffer_recv_byte(0, 1); +    byte_stuffer_recv_byte(0, 0); +} + +TEST_F(ByteStuffer, does_nothing_when_sending_zero_size_frame) { +    EXPECT_EQ(sent_data.size(), 0); +    byte_stuffer_send_frame(0, NULL, 0); +} + +TEST_F(ByteStuffer, send_one_byte_frame) { +    uint8_t data[] = {5}; +    byte_stuffer_send_frame(1, data, 1); +    uint8_t expected[] = {2, 5, 0}; +    EXPECT_THAT(sent_data, ElementsAreArray(expected)); +} + +TEST_F(ByteStuffer, sends_two_byte_frame) { +    uint8_t data[] = {5, 0x77}; +    byte_stuffer_send_frame(0, data, 2); +    uint8_t expected[] = {3, 5, 0x77, 0}; +    EXPECT_THAT(sent_data, ElementsAreArray(expected)); +} + +TEST_F(ByteStuffer, sends_one_byte_frame_with_zero) { +    uint8_t data[] = {0}; +    byte_stuffer_send_frame(0, data, 1); +    uint8_t expected[] = {1, 1, 0}; +    EXPECT_THAT(sent_data, ElementsAreArray(expected)); +} + +TEST_F(ByteStuffer, sends_two_byte_frame_starting_with_zero) { +    uint8_t data[] = {0, 9}; +    byte_stuffer_send_frame(1, data, 2); +    uint8_t expected[] = {1, 2, 9, 0}; +    EXPECT_THAT(sent_data, ElementsAreArray(expected)); +} + +TEST_F(ByteStuffer, sends_two_byte_frame_starting_with_non_zero) { +    uint8_t data[] = {9, 0}; +    byte_stuffer_send_frame(1, data, 2); +    uint8_t expected[] = {2, 9, 1, 0}; +    EXPECT_THAT(sent_data, ElementsAreArray(expected)); +} + +TEST_F(ByteStuffer, sends_three_byte_frame_zero_in_the_middle) { +    uint8_t data[] = {9, 0, 0x68}; +    byte_stuffer_send_frame(0, data, 3); +    uint8_t expected[] = {2, 9, 2, 0x68, 0}; +    EXPECT_THAT(sent_data, ElementsAreArray(expected)); +} + +TEST_F(ByteStuffer, sends_three_byte_frame_data_in_the_middle) { +    uint8_t data[] = {0, 0x55, 0}; +    byte_stuffer_send_frame(0, data, 3); +    uint8_t expected[] = {1, 2, 0x55, 1, 0}; +    EXPECT_THAT(sent_data, ElementsAreArray(expected)); +} + +TEST_F(ByteStuffer, sends_three_byte_frame_with_all_zeroes) { +    uint8_t data[] = {0, 0, 0}; +    byte_stuffer_send_frame(0, data, 3); +    uint8_t expected[] = {1, 1, 1, 1, 0}; +    EXPECT_THAT(sent_data, ElementsAreArray(expected)); +} + +TEST_F(ByteStuffer, sends_frame_with_254_non_zeroes) { +    uint8_t data[254]; +    int i; +    for(i=0;i<254;i++) { +        data[i] = i + 1; +    } +    byte_stuffer_send_frame(0, data, 254); +    uint8_t expected[256]; +    expected[0] = 0xFF; +    for(i=1;i<255;i++) { +        expected[i] = i; +    } +    expected[255] = 0; +    EXPECT_THAT(sent_data, ElementsAreArray(expected)); +} + +TEST_F(ByteStuffer, sends_frame_with_255_non_zeroes) { +    uint8_t data[255]; +    int i; +    for(i=0;i<255;i++) { +        data[i] = i + 1; +    } +    byte_stuffer_send_frame(0, data, 255); +    uint8_t expected[258]; +    expected[0] = 0xFF; +    for(i=1;i<255;i++) { +        expected[i] = i; +    } +    expected[255] = 2; +    expected[256] = 255; +    expected[257] = 0; +    EXPECT_THAT(sent_data, ElementsAreArray(expected)); +} + +TEST_F(ByteStuffer, sends_frame_with_254_non_zeroes_followed_by_zero) { +    uint8_t data[255]; +    int i; +    for(i=0;i<254;i++) { +        data[i] = i + 1; +    } +    data[254] = 0; +    byte_stuffer_send_frame(0, data, 255); +    uint8_t expected[258]; +    expected[0] = 0xFF; +    for(i=1;i<255;i++) { +        expected[i] = i; +    } +    expected[255] = 1; +    expected[256] = 1; +    expected[257] = 0; +    EXPECT_THAT(sent_data, ElementsAreArray(expected)); +} + +TEST_F(ByteStuffer, sends_and_receives_full_roundtrip_small_packet) { +    uint8_t original_data[] = { 1, 2, 3}; +    byte_stuffer_send_frame(0, original_data, sizeof(original_data)); +    EXPECT_CALL(*this, validator_recv_frame(_, _, _)) +        .With(Args<1, 2>(ElementsAreArray(original_data))); +    int i; +    for(auto& d : sent_data) { +       byte_stuffer_recv_byte(1, d); +    } +} + +TEST_F(ByteStuffer, sends_and_receives_full_roundtrip_small_packet_with_zeros) { +    uint8_t original_data[] = { 1, 0, 3, 0, 0, 9}; +    byte_stuffer_send_frame(1, original_data, sizeof(original_data)); +    EXPECT_CALL(*this, validator_recv_frame(_, _, _)) +        .With(Args<1, 2>(ElementsAreArray(original_data))); +    int i; +    for(auto& d : sent_data) { +       byte_stuffer_recv_byte(1, d); +    } +} + +TEST_F(ByteStuffer, sends_and_receives_full_roundtrip_254_bytes) { +    uint8_t original_data[254]; +    int i; +    for(i=0;i<254;i++) { +        original_data[i] = i + 1; +    } +    byte_stuffer_send_frame(0, original_data, sizeof(original_data)); +    EXPECT_CALL(*this, validator_recv_frame(_, _, _)) +        .With(Args<1, 2>(ElementsAreArray(original_data))); +    for(auto& d : sent_data) { +       byte_stuffer_recv_byte(1, d); +    } +} + +TEST_F(ByteStuffer, sends_and_receives_full_roundtrip_256_bytes) { +    uint8_t original_data[256]; +    int i; +    for(i=0;i<254;i++) { +        original_data[i] = i + 1; +    } +    original_data[254] = 22; +    original_data[255] = 23; +    byte_stuffer_send_frame(0, original_data, sizeof(original_data)); +    EXPECT_CALL(*this, validator_recv_frame(_, _, _)) +        .With(Args<1, 2>(ElementsAreArray(original_data))); +    for(auto& d : sent_data) { +       byte_stuffer_recv_byte(1, d); +    } +} + +TEST_F(ByteStuffer, sends_and_receives_full_roundtrip_254_bytes_and_then_zero) { +    uint8_t original_data[255]; +    int i; +    for(i=0;i<254;i++) { +        original_data[i] = i + 1; +    } +    original_data[254] = 0; +    byte_stuffer_send_frame(0, original_data, sizeof(original_data)); +    EXPECT_CALL(*this, validator_recv_frame(_, _, _)) +        .With(Args<1, 2>(ElementsAreArray(original_data))); +    for(auto& d : sent_data) { +       byte_stuffer_recv_byte(1, d); +    } +} diff --git a/quantum/serial_link/tests/frame_router_tests.cpp b/quantum/serial_link/tests/frame_router_tests.cpp new file mode 100644 index 0000000000..2bd5bf830d --- /dev/null +++ b/quantum/serial_link/tests/frame_router_tests.cpp @@ -0,0 +1,229 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Fred Sundvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "gtest/gtest.h" +#include "gmock/gmock.h" +#include <array> +extern "C" { +    #include "serial_link/protocol/transport.h" +    #include "serial_link/protocol/byte_stuffer.h" +    #include "serial_link/protocol/frame_router.h" +} + +using testing::_; +using testing::ElementsAreArray; +using testing::Args; + +class FrameRouter : public testing::Test { +public: +    FrameRouter() : +        current_router_buffer(nullptr) +    { +        Instance = this; +        init_byte_stuffer(); +    } + +    ~FrameRouter() { +        Instance = nullptr; +    } + +    void send_data(uint8_t link, const uint8_t* data, uint16_t size) { +        auto& buffer = current_router_buffer->send_buffers[link]; +        std::copy(data, data + size, std::back_inserter(buffer)); +    } + +    void receive_data(uint8_t link, uint8_t* data, uint16_t size) { +        int i; +        for(i=0;i<size;i++) { +            byte_stuffer_recv_byte(link, data[i]); +        } +    } + +    void activate_router(uint8_t num) { +        current_router_buffer = router_buffers + num; +        router_set_master(num==0); +    } + +    void simulate_transport(uint8_t from, uint8_t to) { +       activate_router(to); +       if (from > to) { +           receive_data(DOWN_LINK, +                   router_buffers[from].send_buffers[UP_LINK].data(), +                   router_buffers[from].send_buffers[UP_LINK].size()); +       } +       else if(to > from) { +           receive_data(UP_LINK, +                   router_buffers[from].send_buffers[DOWN_LINK].data(), +                   router_buffers[from].send_buffers[DOWN_LINK].size()); +       } +    } + +    MOCK_METHOD3(transport_recv_frame, void (uint8_t from, uint8_t* data, uint16_t size)); + +    std::vector<uint8_t> received_data; + +    struct router_buffer { +        std::vector<uint8_t> send_buffers[2]; +    }; + +    router_buffer router_buffers[8]; +    router_buffer* current_router_buffer; + +    static FrameRouter* Instance; +}; + +FrameRouter* FrameRouter::Instance = nullptr; + + +typedef struct { +    std::array<uint8_t, 4> data; +    uint8_t extra[16]; +} frame_buffer_t; + + +extern "C" { +    void send_data(uint8_t link, const uint8_t* data, uint16_t size) { +        FrameRouter::Instance->send_data(link, data, size); +    } + + +    void transport_recv_frame(uint8_t from, uint8_t* data, uint16_t size) { +        FrameRouter::Instance->transport_recv_frame(from, data, size); +    } +} + +TEST_F(FrameRouter, master_broadcast_is_received_by_everyone) { +    frame_buffer_t data; +    data.data = {0xAB, 0x70, 0x55, 0xBB}; +    activate_router(0); +    router_send_frame(0xFF, (uint8_t*)&data, 4); +    EXPECT_GT(router_buffers[0].send_buffers[DOWN_LINK].size(), 0); +    EXPECT_EQ(router_buffers[0].send_buffers[UP_LINK].size(), 0); +    EXPECT_CALL(*this, transport_recv_frame(0, _, _)) +        .With(Args<1, 2>(ElementsAreArray(data.data))); +    simulate_transport(0, 1); +    EXPECT_GT(router_buffers[1].send_buffers[DOWN_LINK].size(), 0); +    EXPECT_EQ(router_buffers[1].send_buffers[UP_LINK].size(), 0); + +    EXPECT_CALL(*this, transport_recv_frame(0, _, _)) +        .With(Args<1, 2>(ElementsAreArray(data.data))); +    simulate_transport(1, 2); +    EXPECT_GT(router_buffers[2].send_buffers[DOWN_LINK].size(), 0); +    EXPECT_EQ(router_buffers[2].send_buffers[UP_LINK].size(), 0); +} + +TEST_F(FrameRouter, master_send_is_received_by_targets) { +    frame_buffer_t data; +    data.data = {0xAB, 0x70, 0x55, 0xBB}; +    activate_router(0); +    router_send_frame((1 << 1) | (1 << 2), (uint8_t*)&data, 4); +    EXPECT_GT(router_buffers[0].send_buffers[DOWN_LINK].size(), 0); +    EXPECT_EQ(router_buffers[0].send_buffers[UP_LINK].size(), 0); + +    simulate_transport(0, 1); +    EXPECT_GT(router_buffers[1].send_buffers[DOWN_LINK].size(), 0); +    EXPECT_EQ(router_buffers[1].send_buffers[UP_LINK].size(), 0); + +    EXPECT_CALL(*this, transport_recv_frame(0, _, _)) +        .With(Args<1, 2>(ElementsAreArray(data.data))); +    simulate_transport(1, 2); +    EXPECT_GT(router_buffers[2].send_buffers[DOWN_LINK].size(), 0); +    EXPECT_EQ(router_buffers[2].send_buffers[UP_LINK].size(), 0); + +    EXPECT_CALL(*this, transport_recv_frame(0, _, _)) +        .With(Args<1, 2>(ElementsAreArray(data.data))); +    simulate_transport(2, 3); +    EXPECT_GT(router_buffers[3].send_buffers[DOWN_LINK].size(), 0); +    EXPECT_EQ(router_buffers[3].send_buffers[UP_LINK].size(), 0); +} + +TEST_F(FrameRouter, first_link_sends_to_master) { +    frame_buffer_t data; +    data.data = {0xAB, 0x70, 0x55, 0xBB}; +    activate_router(1); +    router_send_frame(0, (uint8_t*)&data, 4); +    EXPECT_GT(router_buffers[1].send_buffers[UP_LINK].size(), 0); +    EXPECT_EQ(router_buffers[1].send_buffers[DOWN_LINK].size(), 0); + +    EXPECT_CALL(*this, transport_recv_frame(1, _, _)) +        .With(Args<1, 2>(ElementsAreArray(data.data))); +    simulate_transport(1, 0); +    EXPECT_EQ(router_buffers[0].send_buffers[DOWN_LINK].size(), 0); +    EXPECT_EQ(router_buffers[0].send_buffers[UP_LINK].size(), 0); +} + +TEST_F(FrameRouter, second_link_sends_to_master) { +    frame_buffer_t data; +    data.data = {0xAB, 0x70, 0x55, 0xBB}; +    activate_router(2); +    router_send_frame(0, (uint8_t*)&data, 4); +    EXPECT_GT(router_buffers[2].send_buffers[UP_LINK].size(), 0); +    EXPECT_EQ(router_buffers[2].send_buffers[DOWN_LINK].size(), 0); + +    simulate_transport(2, 1); +    EXPECT_GT(router_buffers[1].send_buffers[UP_LINK].size(), 0); +    EXPECT_EQ(router_buffers[1].send_buffers[DOWN_LINK].size(), 0); + +    EXPECT_CALL(*this, transport_recv_frame(2, _, _)) +        .With(Args<1, 2>(ElementsAreArray(data.data))); +    simulate_transport(1, 0); +    EXPECT_EQ(router_buffers[0].send_buffers[DOWN_LINK].size(), 0); +    EXPECT_EQ(router_buffers[0].send_buffers[UP_LINK].size(), 0); +} + +TEST_F(FrameRouter, master_sends_to_master_does_nothing) { +    frame_buffer_t data; +    data.data = {0xAB, 0x70, 0x55, 0xBB}; +    activate_router(0); +    router_send_frame(0, (uint8_t*)&data, 4); +    EXPECT_EQ(router_buffers[0].send_buffers[UP_LINK].size(), 0); +    EXPECT_EQ(router_buffers[0].send_buffers[DOWN_LINK].size(), 0); +} + +TEST_F(FrameRouter, link_sends_to_other_link_does_nothing) { +    frame_buffer_t data; +    data.data = {0xAB, 0x70, 0x55, 0xBB}; +    activate_router(1); +    router_send_frame(2, (uint8_t*)&data, 4); +    EXPECT_EQ(router_buffers[1].send_buffers[UP_LINK].size(), 0); +    EXPECT_EQ(router_buffers[1].send_buffers[DOWN_LINK].size(), 0); +} + +TEST_F(FrameRouter, master_receives_on_uplink_does_nothing) { +    frame_buffer_t data; +    data.data = {0xAB, 0x70, 0x55, 0xBB}; +    activate_router(1); +    router_send_frame(0, (uint8_t*)&data, 4); +    EXPECT_GT(router_buffers[1].send_buffers[UP_LINK].size(), 0); +    EXPECT_EQ(router_buffers[1].send_buffers[DOWN_LINK].size(), 0); + +    EXPECT_CALL(*this, transport_recv_frame(_, _, _)) +        .Times(0); +    activate_router(0); +    receive_data(UP_LINK, +        router_buffers[1].send_buffers[UP_LINK].data(), +        router_buffers[1].send_buffers[UP_LINK].size()); +    EXPECT_EQ(router_buffers[0].send_buffers[UP_LINK].size(), 0); +    EXPECT_EQ(router_buffers[0].send_buffers[DOWN_LINK].size(), 0); +} diff --git a/quantum/serial_link/tests/frame_validator_tests.cpp b/quantum/serial_link/tests/frame_validator_tests.cpp new file mode 100644 index 0000000000..9223af83b0 --- /dev/null +++ b/quantum/serial_link/tests/frame_validator_tests.cpp @@ -0,0 +1,115 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Fred Sundvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "gtest/gtest.h" +#include "gmock/gmock.h" +extern "C" { +#include "serial_link/protocol/frame_validator.h" +} + +using testing::_; +using testing::ElementsAreArray; +using testing::Args; + +class FrameValidator : public testing::Test { +public: +    FrameValidator() { +        Instance = this; +    } + +    ~FrameValidator() { +        Instance = nullptr; +    } + +    MOCK_METHOD3(route_incoming_frame, void (uint8_t link, uint8_t* data, uint16_t size)); +    MOCK_METHOD3(byte_stuffer_send_frame, void (uint8_t link, uint8_t* data, uint16_t size)); + +    static FrameValidator* Instance; +}; + +FrameValidator* FrameValidator::Instance = nullptr; + +extern "C" { +void route_incoming_frame(uint8_t link, uint8_t* data, uint16_t size) { +    FrameValidator::Instance->route_incoming_frame(link, data, size); +} + +void byte_stuffer_send_frame(uint8_t link, uint8_t* data, uint16_t size) { +    FrameValidator::Instance->byte_stuffer_send_frame(link, data, size); +} +} + +TEST_F(FrameValidator, doesnt_validate_frames_under_5_bytes) { +    EXPECT_CALL(*this, route_incoming_frame(_, _, _)) +        .Times(0); +    uint8_t data[] = {1, 2}; +    validator_recv_frame(0, 0, 1); +    validator_recv_frame(0, data, 2); +    validator_recv_frame(0, data, 3); +    validator_recv_frame(0, data, 4); +} + +TEST_F(FrameValidator, validates_one_byte_frame_with_correct_crc) { +    uint8_t data[] = {0x44, 0x04, 0x6A, 0xB3, 0xA3}; +    EXPECT_CALL(*this, route_incoming_frame(_, _, _)) +        .With(Args<1, 2>(ElementsAreArray(data, 1))); +    validator_recv_frame(0, data, 5); +} + +TEST_F(FrameValidator, does_not_validate_one_byte_frame_with_incorrect_crc) { +    uint8_t data[] = {0x44, 0, 0, 0, 0}; +    EXPECT_CALL(*this, route_incoming_frame(_, _, _)) +        .Times(0); +    validator_recv_frame(1, data, 5); +} + +TEST_F(FrameValidator, validates_four_byte_frame_with_correct_crc) { +    uint8_t data[] = {0x44, 0x10, 0xFF, 0x00, 0x74, 0x4E, 0x30, 0xBA}; +    EXPECT_CALL(*this, route_incoming_frame(_, _, _)) +        .With(Args<1, 2>(ElementsAreArray(data, 4))); +    validator_recv_frame(1, data, 8); +} + +TEST_F(FrameValidator, validates_five_byte_frame_with_correct_crc) { +    uint8_t data[] = {1, 2, 3, 4, 5, 0xF4, 0x99, 0x0B, 0x47}; +    EXPECT_CALL(*this, route_incoming_frame(_, _, _)) +        .With(Args<1, 2>(ElementsAreArray(data, 5))); +    validator_recv_frame(0, data, 9); +} + +TEST_F(FrameValidator, sends_one_byte_with_correct_crc) { +    uint8_t original[] = {0x44, 0, 0, 0, 0}; +    uint8_t expected[] = {0x44, 0x04, 0x6A, 0xB3, 0xA3}; +    EXPECT_CALL(*this, byte_stuffer_send_frame(_, _, _)) +        .With(Args<1, 2>(ElementsAreArray(expected))); +    validator_send_frame(0, original, 1); +} + +TEST_F(FrameValidator, sends_five_bytes_with_correct_crc) { +    uint8_t original[] = {1, 2, 3, 4, 5, 0, 0, 0, 0}; +    uint8_t expected[] = {1, 2, 3, 4, 5, 0xF4, 0x99, 0x0B, 0x47}; +    EXPECT_CALL(*this, byte_stuffer_send_frame(_, _, _)) +        .With(Args<1, 2>(ElementsAreArray(expected))); +    validator_send_frame(0, original, 5); +} diff --git a/quantum/serial_link/tests/rules.mk b/quantum/serial_link/tests/rules.mk new file mode 100644 index 0000000000..b81515bc55 --- /dev/null +++ b/quantum/serial_link/tests/rules.mk @@ -0,0 +1,22 @@ +serial_link_byte_stuffer_SRC :=\ +	$(SERIAL_PATH)/tests/byte_stuffer_tests.cpp \ +	$(SERIAL_PATH)/protocol/byte_stuffer.c + +serial_link_frame_validator_SRC := \ +	$(SERIAL_PATH)/tests/frame_validator_tests.cpp \ +	$(SERIAL_PATH)/protocol/frame_validator.c  + +serial_link_frame_router_SRC := \ +	$(SERIAL_PATH)/tests/frame_router_tests.cpp \ +	$(SERIAL_PATH)/protocol/byte_stuffer.c \ +	$(SERIAL_PATH)/protocol/frame_validator.c \ +	$(SERIAL_PATH)/protocol/frame_router.c + +serial_link_triple_buffered_object_SRC := \ +	$(SERIAL_PATH)/tests/triple_buffered_object_tests.cpp \ +	$(SERIAL_PATH)/protocol/triple_buffered_object.c  + +serial_link_transport_SRC := \ +	$(SERIAL_PATH)/tests/transport_tests.cpp \ +	$(SERIAL_PATH)/protocol/transport.c \ +	$(SERIAL_PATH)/protocol/triple_buffered_object.c  diff --git a/quantum/serial_link/tests/testlist.mk b/quantum/serial_link/tests/testlist.mk new file mode 100644 index 0000000000..a80e888849 --- /dev/null +++ b/quantum/serial_link/tests/testlist.mk @@ -0,0 +1,6 @@ +TEST_LIST +=\ +	serial_link_byte_stuffer\ +	serial_link_frame_validator\ +	serial_link_frame_router\ +	serial_link_triple_buffered_object\ +	serial_link_transport
\ No newline at end of file diff --git a/quantum/serial_link/tests/transport_tests.cpp b/quantum/serial_link/tests/transport_tests.cpp new file mode 100644 index 0000000000..21b7b165f6 --- /dev/null +++ b/quantum/serial_link/tests/transport_tests.cpp @@ -0,0 +1,188 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Fred Sundvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "gtest/gtest.h" +#include "gmock/gmock.h" + +using testing::_; +using testing::ElementsAreArray; +using testing::Args; + +extern "C" { +#include "serial_link/protocol/transport.h" +} + +struct test_object1 { +    uint32_t test; +}; + +struct test_object2 { +    uint32_t test1; +    uint32_t test2; +}; + +MASTER_TO_ALL_SLAVES_OBJECT(master_to_slave, test_object1); +MASTER_TO_SINGLE_SLAVE_OBJECT(master_to_single_slave, test_object1); +SLAVE_TO_MASTER_OBJECT(slave_to_master, test_object1); + +static remote_object_t* test_remote_objects[] = { +    REMOTE_OBJECT(master_to_slave), +    REMOTE_OBJECT(master_to_single_slave), +    REMOTE_OBJECT(slave_to_master), +}; + +class Transport : public testing::Test { +public: +    Transport() { +        Instance = this; +        add_remote_objects(test_remote_objects, sizeof(test_remote_objects) / sizeof(remote_object_t*)); +    } + +    ~Transport() { +        Instance = nullptr; +        reinitialize_serial_link_transport(); +    } + +    MOCK_METHOD0(signal_data_written, void ()); +    MOCK_METHOD1(router_send_frame, void (uint8_t destination)); + +    void router_send_frame(uint8_t destination, uint8_t* data, uint16_t size) { +        router_send_frame(destination); +        std::copy(data, data + size, std::back_inserter(sent_data)); +    } + +    static Transport* Instance; + +    std::vector<uint8_t> sent_data; +}; + +Transport* Transport::Instance = nullptr; + +extern "C" { +void signal_data_written(void) { +    Transport::Instance->signal_data_written(); +} + +void router_send_frame(uint8_t destination, uint8_t* data, uint16_t size) { +    Transport::Instance->router_send_frame(destination, data, size); +} +} + +TEST_F(Transport, write_to_local_signals_an_event) { +    begin_write_master_to_slave(); +    EXPECT_CALL(*this, signal_data_written()); +    end_write_master_to_slave(); +    begin_write_slave_to_master(); +    EXPECT_CALL(*this, signal_data_written()); +    end_write_slave_to_master(); +    begin_write_master_to_single_slave(1); +    EXPECT_CALL(*this, signal_data_written()); +    end_write_master_to_single_slave(1); +} + +TEST_F(Transport, writes_from_master_to_all_slaves) { +    update_transport(); +    test_object1* obj = begin_write_master_to_slave(); +    obj->test = 5; +    EXPECT_CALL(*this, signal_data_written()); +    end_write_master_to_slave(); +    EXPECT_CALL(*this, router_send_frame(0xFF)); +    update_transport(); +    transport_recv_frame(0, sent_data.data(), sent_data.size()); +    test_object1* obj2 = read_master_to_slave(); +    EXPECT_NE(obj2, nullptr); +    EXPECT_EQ(obj2->test, 5); +} + +TEST_F(Transport, writes_from_slave_to_master) { +    update_transport(); +    test_object1* obj = begin_write_slave_to_master(); +    obj->test = 7; +    EXPECT_CALL(*this, signal_data_written()); +    end_write_slave_to_master(); +    EXPECT_CALL(*this, router_send_frame(0)); +    update_transport(); +    transport_recv_frame(3, sent_data.data(), sent_data.size()); +    test_object1* obj2 = read_slave_to_master(2); +    EXPECT_EQ(read_slave_to_master(0), nullptr); +    EXPECT_NE(obj2, nullptr); +    EXPECT_EQ(obj2->test, 7); +} + +TEST_F(Transport, writes_from_master_to_single_slave) { +    update_transport(); +    test_object1* obj = begin_write_master_to_single_slave(3); +    obj->test = 7; +    EXPECT_CALL(*this, signal_data_written()); +    end_write_master_to_single_slave(3); +    EXPECT_CALL(*this, router_send_frame(4)); +    update_transport(); +    transport_recv_frame(0, sent_data.data(), sent_data.size()); +    test_object1* obj2 = read_master_to_single_slave(); +    EXPECT_NE(obj2, nullptr); +    EXPECT_EQ(obj2->test, 7); +} + +TEST_F(Transport, ignores_object_with_invalid_id) { +    update_transport(); +    test_object1* obj = begin_write_master_to_single_slave(3); +    obj->test = 7; +    EXPECT_CALL(*this, signal_data_written()); +    end_write_master_to_single_slave(3); +    EXPECT_CALL(*this, router_send_frame(4)); +    update_transport(); +    sent_data[sent_data.size() - 1] = 44; +    transport_recv_frame(0, sent_data.data(), sent_data.size()); +    test_object1* obj2 = read_master_to_single_slave(); +    EXPECT_EQ(obj2, nullptr); +} + +TEST_F(Transport, ignores_object_with_size_too_small) { +    update_transport(); +    test_object1* obj = begin_write_master_to_slave(); +    obj->test = 7; +    EXPECT_CALL(*this, signal_data_written()); +    end_write_master_to_slave(); +    EXPECT_CALL(*this, router_send_frame(_)); +    update_transport(); +    sent_data[sent_data.size() - 2] = 0; +    transport_recv_frame(0, sent_data.data(), sent_data.size() - 1); +    test_object1* obj2 = read_master_to_slave(); +    EXPECT_EQ(obj2, nullptr); +} + +TEST_F(Transport, ignores_object_with_size_too_big) { +    update_transport(); +    test_object1* obj = begin_write_master_to_slave(); +    obj->test = 7; +    EXPECT_CALL(*this, signal_data_written()); +    end_write_master_to_slave(); +    EXPECT_CALL(*this, router_send_frame(_)); +    update_transport(); +    sent_data.resize(sent_data.size() + 22); +    sent_data[sent_data.size() - 1] = 0; +    transport_recv_frame(0, sent_data.data(), sent_data.size()); +    test_object1* obj2 = read_master_to_slave(); +    EXPECT_EQ(obj2, nullptr); +} diff --git a/quantum/serial_link/tests/triple_buffered_object_tests.cpp b/quantum/serial_link/tests/triple_buffered_object_tests.cpp new file mode 100644 index 0000000000..7724bbee9c --- /dev/null +++ b/quantum/serial_link/tests/triple_buffered_object_tests.cpp @@ -0,0 +1,84 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Fred Sundvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "gtest/gtest.h" +extern "C" { +#include "serial_link/protocol/triple_buffered_object.h" +} + +struct test_object{ +    uint8_t state; +    uint32_t buffer[3]; +}; + +test_object test_object; + +class TripleBufferedObject : public testing::Test { +public: +    TripleBufferedObject() { +        triple_buffer_init((triple_buffer_object_t*)&test_object); +    } +}; + +TEST_F(TripleBufferedObject, writes_and_reads_object) { +    *triple_buffer_begin_write(&test_object) = 0x3456ABCC; +    triple_buffer_end_write(&test_object); +    EXPECT_EQ(*triple_buffer_read(&test_object), 0x3456ABCC); +} + +TEST_F(TripleBufferedObject, does_not_read_empty) { +    EXPECT_EQ(triple_buffer_read(&test_object), nullptr); +} + +TEST_F(TripleBufferedObject, writes_twice_and_reads_object) { +    *triple_buffer_begin_write(&test_object) = 0x3456ABCC; +    triple_buffer_end_write(&test_object); +    *triple_buffer_begin_write(&test_object) = 0x44778899; +    triple_buffer_end_write(&test_object); +    EXPECT_EQ(*triple_buffer_read(&test_object), 0x44778899); +} + +TEST_F(TripleBufferedObject, performs_another_write_in_the_middle_of_read) { +    *triple_buffer_begin_write(&test_object) = 1; +    triple_buffer_end_write(&test_object); +    uint32_t* read = triple_buffer_read(&test_object); +    *triple_buffer_begin_write(&test_object) = 2; +    triple_buffer_end_write(&test_object); +    EXPECT_EQ(*read, 1); +    EXPECT_EQ(*triple_buffer_read(&test_object), 2); +    EXPECT_EQ(triple_buffer_read(&test_object), nullptr); +} + +TEST_F(TripleBufferedObject, performs_two_writes_in_the_middle_of_read) { +    *triple_buffer_begin_write(&test_object) = 1; +    triple_buffer_end_write(&test_object); +    uint32_t* read = triple_buffer_read(&test_object); +    *triple_buffer_begin_write(&test_object) = 2; +    triple_buffer_end_write(&test_object); +    *triple_buffer_begin_write(&test_object) = 3; +    triple_buffer_end_write(&test_object); +    EXPECT_EQ(*read, 1); +    EXPECT_EQ(*triple_buffer_read(&test_object), 3); +    EXPECT_EQ(triple_buffer_read(&test_object), nullptr); +} diff --git a/quantum/template/Makefile b/quantum/template/Makefile new file mode 100644 index 0000000000..840dc9a286 --- /dev/null +++ b/quantum/template/Makefile @@ -0,0 +1,18 @@ +# Copyright 2013 Jun Wako <wakojun@gmail.com> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program.  If not, see <http://www.gnu.org/licenses/>. + +ifndef MAKEFILE_INCLUDED +	include ../../Makefile +endif diff --git a/quantum/template/config.h b/quantum/template/config.h new file mode 100644 index 0000000000..dbca45765d --- /dev/null +++ b/quantum/template/config.h @@ -0,0 +1,185 @@ +/* +Copyright 2017 REPLACE_WITH_YOUR_NAME + +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 CONFIG_H +#define CONFIG_H + +#include "config_common.h" + +/* USB Device descriptor parameter */ +#define VENDOR_ID       0xFEED +#define PRODUCT_ID      0x6060 +#define DEVICE_VER      0x0001 +#define MANUFACTURER    You +#define PRODUCT         %KEYBOARD% +#define DESCRIPTION     A custom keyboard + +/* key matrix size */ +#define MATRIX_ROWS 2 +#define MATRIX_COLS 3 + +/* + * Keyboard Matrix Assignments + * + * Change this to how you wired your keyboard + * COLS: AVR pins used for columns, left to right + * ROWS: AVR pins used for rows, top to bottom + * DIODE_DIRECTION: COL2ROW = COL = Anode (+), ROW = Cathode (-, marked on diode) + *                  ROW2COL = ROW = Anode (+), COL = Cathode (-, marked on diode) + * +*/ +#define MATRIX_ROW_PINS { D0, D5 } +#define MATRIX_COL_PINS { F1, F0, B0 } +#define UNUSED_PINS + +/* COL2ROW, ROW2COL, or CUSTOM_MATRIX */ +#define DIODE_DIRECTION COL2ROW +  +// #define BACKLIGHT_PIN B7 +// #define BACKLIGHT_BREATHING +// #define BACKLIGHT_LEVELS 3 + + +/* Debounce reduces chatter (unintended double-presses) - set 0 if debouncing is not needed */ +#define DEBOUNCING_DELAY 5 + +/* define if matrix has ghost (lacks anti-ghosting diodes) */ +//#define MATRIX_HAS_GHOST + +/* number of backlight levels */ + +/* 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 + +/* + * Force NKRO + * + * Force NKRO (nKey Rollover) to be enabled by default, regardless of the saved + * state in the bootmagic EEPROM settings. (Note that NKRO must be enabled in the + * makefile for this to work.) + * + * If forced on, NKRO can be disabled via magic key (default = LShift+RShift+N) + * until the next keyboard reset. + * + * NKRO may prevent your keystrokes from being detected in the BIOS, but it is + * fully operational during normal computer usage. + * + * For a less heavy-handed approach, enable NKRO via magic key (LShift+RShift+N) + * or via bootmagic (hold SPACE+N while plugging in the keyboard). Once set by + * bootmagic, NKRO mode will always be enabled until it is toggled again during a + * power-up. + * + */ +//#define FORCE_NKRO + +/* + * Magic Key Options + * + * Magic keys are hotkey commands that allow control over firmware functions of + * the keyboard. They are best used in combination with the HID Listen program, + * found here: https://www.pjrc.com/teensy/hid_listen.html + * + * The options below allow the magic key functionality to be changed. This is + * useful if your keyboard/keypad is missing keys and you want magic key support. + * + */ + +/* key combination for magic key command */ +#define IS_COMMAND() ( \ +    keyboard_report->mods == (MOD_BIT(KC_LSHIFT) | MOD_BIT(KC_RSHIFT)) \ +) + +/* control how magic key switches layers */ +//#define MAGIC_KEY_SWITCH_LAYER_WITH_FKEYS  true +//#define MAGIC_KEY_SWITCH_LAYER_WITH_NKEYS  true +//#define MAGIC_KEY_SWITCH_LAYER_WITH_CUSTOM false + +/* override magic key keymap */ +//#define MAGIC_KEY_SWITCH_LAYER_WITH_FKEYS +//#define MAGIC_KEY_SWITCH_LAYER_WITH_NKEYS +//#define MAGIC_KEY_SWITCH_LAYER_WITH_CUSTOM +//#define MAGIC_KEY_HELP1          H +//#define MAGIC_KEY_HELP2          SLASH +//#define MAGIC_KEY_DEBUG          D +//#define MAGIC_KEY_DEBUG_MATRIX   X +//#define MAGIC_KEY_DEBUG_KBD      K +//#define MAGIC_KEY_DEBUG_MOUSE    M +//#define MAGIC_KEY_VERSION        V +//#define MAGIC_KEY_STATUS         S +//#define MAGIC_KEY_CONSOLE        C +//#define MAGIC_KEY_LAYER0_ALT1    ESC +//#define MAGIC_KEY_LAYER0_ALT2    GRAVE +//#define MAGIC_KEY_LAYER0         0 +//#define MAGIC_KEY_LAYER1         1 +//#define MAGIC_KEY_LAYER2         2 +//#define MAGIC_KEY_LAYER3         3 +//#define MAGIC_KEY_LAYER4         4 +//#define MAGIC_KEY_LAYER5         5 +//#define MAGIC_KEY_LAYER6         6 +//#define MAGIC_KEY_LAYER7         7 +//#define MAGIC_KEY_LAYER8         8 +//#define MAGIC_KEY_LAYER9         9 +//#define MAGIC_KEY_BOOTLOADER     PAUSE +//#define MAGIC_KEY_LOCK           CAPS +//#define MAGIC_KEY_EEPROM         E +//#define MAGIC_KEY_NKRO           N +//#define MAGIC_KEY_SLEEP_LED      Z + +/* + * 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 + +/* + * MIDI options + */ + +/* Prevent use of disabled MIDI features in the keymap */ +//#define MIDI_ENABLE_STRICT 1 + +/* enable basic MIDI features: +   - MIDI notes can be sent when in Music mode is on +*/ +//#define MIDI_BASIC + +/* enable advanced MIDI features: +   - MIDI notes can be added to the keymap +   - Octave shift and transpose +   - Virtual sustain, portamento, and modulation wheel +   - etc. +*/ +//#define MIDI_ADVANCED + +/* override number of MIDI tone keycodes (each octave adds 12 keycodes and allocates 12 bytes) */ +//#define MIDI_TONE_KEYCODE_OCTAVES 1 + +#endif diff --git a/quantum/template/keymaps/default/Makefile b/quantum/template/keymaps/default/Makefile new file mode 100644 index 0000000000..b8879076bd --- /dev/null +++ b/quantum/template/keymaps/default/Makefile @@ -0,0 +1,37 @@ +# Copyright 2013 Jun Wako <wakojun@gmail.com> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program.  If not, see <http://www.gnu.org/licenses/>. + + +# QMK 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 +# +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 +NKRO_ENABLE = yes           # Nkey Rollover - if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work +BACKLIGHT_ENABLE = no       # Enable keyboard backlight functionality +MIDI_ENABLE = no            # MIDI support (+2400 to 4200, depending on config) +AUDIO_ENABLE = no           # Audio output on port C6 +UNICODE_ENABLE = no         # Unicode +BLUETOOTH_ENABLE = no       # Enable Bluetooth with the Adafruit EZ-Key HID +RGBLIGHT_ENABLE = no        # Enable WS2812 RGB underlight.  Do not enable this with audio at the same time. +SLEEP_LED_ENABLE = no       # Breathing sleep LED during USB suspend + +ifndef QUANTUM_DIR +	include ../../../../Makefile +endif diff --git a/quantum/template/keymaps/default/config.h b/quantum/template/keymaps/default/config.h new file mode 100644 index 0000000000..f52a97bbc8 --- /dev/null +++ b/quantum/template/keymaps/default/config.h @@ -0,0 +1,24 @@ +/* Copyright 2017 REPLACE_WITH_YOUR_NAME + * + * 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 CONFIG_USER_H +#define CONFIG_USER_H + +#include "../../config.h" + +// place overrides here + +#endif diff --git a/quantum/template/keymaps/default/keymap.c b/quantum/template/keymaps/default/keymap.c new file mode 100644 index 0000000000..a123cd7bab --- /dev/null +++ b/quantum/template/keymaps/default/keymap.c @@ -0,0 +1,59 @@ +/* Copyright 2017 REPLACE_WITH_YOUR_NAME + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ +#include "%KEYBOARD%.h" + +const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { +[0] = KEYMAP( /* Base */ +  KC_A,  KC_1,  KC_H, \ +    KC_TAB,  KC_SPC   \ +), +}; + +const uint16_t PROGMEM fn_actions[] = { + +}; + +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) { +            register_code(KC_RSFT); +          } else { +            unregister_code(KC_RSFT); +          } +        break; +      } +    return MACRO_NONE; +}; + + +void matrix_init_user(void) { + +} + +void matrix_scan_user(void) { + +} + +bool process_record_user(uint16_t keycode, keyrecord_t *record) { +  return true; +} + +void led_set_user(uint8_t usb_led) { + +} diff --git a/quantum/template/keymaps/default/readme.md b/quantum/template/keymaps/default/readme.md new file mode 100644 index 0000000000..21aa663d55 --- /dev/null +++ b/quantum/template/keymaps/default/readme.md @@ -0,0 +1 @@ +# The default keymap for %KEYBOARD%
\ No newline at end of file diff --git a/quantum/template/readme.md b/quantum/template/readme.md new file mode 100644 index 0000000000..b16f4cd768 --- /dev/null +++ b/quantum/template/readme.md @@ -0,0 +1,28 @@ +%KEYBOARD% keyboard firmware +====================== + +## Quantum MK Firmware + +For the full Quantum feature list, see [the parent readme](/). + +## Building + +Download or clone the whole firmware and navigate to the keyboards/%KEYBOARD% folder. Once your dev env is setup, you'll be able to type `make` to generate your .hex - you can then use the Teensy Loader to program your .hex file.  + +Depending on which keymap you would like to use, you will have to compile slightly differently. + +### Default + +To build with the default keymap, simply run `make default`. + +### Other Keymaps + +Several version of keymap are available in advance but you are recommended to define your favorite layout yourself. To define your own keymap create a folder with the name of your keymap in the keymaps folder, and see keymap documentation (you can find in top readme.md) and existant keymap files. + +To build the firmware binary hex file with a keymap just do `make` with a keymap like this: + +``` +$ make [default|jack|<name>] +``` + +Keymaps follow the format **__\<name\>.c__** and are stored in the `keymaps` folder. diff --git a/quantum/template/rules.mk b/quantum/template/rules.mk new file mode 100644 index 0000000000..a3571e8deb --- /dev/null +++ b/quantum/template/rules.mk @@ -0,0 +1,68 @@ +# MCU name +#MCU = at90usb1286 +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 +#   change yes to no to disable +# +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 ?= yes        # Console for debug(+400) +COMMAND_ENABLE ?= yes        # Commands for debug and configuration +# Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE +SLEEP_LED_ENABLE ?= no       # Breathing sleep LED during USB suspend +# if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work +NKRO_ENABLE ?= no            # USB Nkey Rollover +BACKLIGHT_ENABLE ?= no       # Enable keyboard backlight functionality on B7 by default +MIDI_ENABLE ?= no            # MIDI support (+2400 to 4200, depending on config) +UNICODE_ENABLE ?= no         # Unicode +BLUETOOTH_ENABLE ?= no       # Enable Bluetooth with the Adafruit EZ-Key HID +AUDIO_ENABLE ?= no           # Audio output on port C6 +FAUXCLICKY_ENABLE ?= no      # Use buzzer to emulate clicky switches diff --git a/quantum/template/template.c b/quantum/template/template.c new file mode 100644 index 0000000000..97f788654b --- /dev/null +++ b/quantum/template/template.c @@ -0,0 +1,43 @@ +/* Copyright 2017 REPLACE_WITH_YOUR_NAME + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ +#include "%KEYBOARD%.h" + +void matrix_init_kb(void) { +	// put your keyboard start-up code here +	// runs once when the firmware starts up + +	matrix_init_user(); +} + +void matrix_scan_kb(void) { +	// put your looping keyboard code here +	// runs every cycle (a lot) + +	matrix_scan_user(); +} + +bool process_record_kb(uint16_t keycode, keyrecord_t *record) { +	// put your per-action keyboard code here +	// runs for every action, just before processing by the firmware + +	return process_record_user(keycode, record); +} + +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); +} diff --git a/quantum/template/template.h b/quantum/template/template.h new file mode 100644 index 0000000000..7e2b14f3c1 --- /dev/null +++ b/quantum/template/template.h @@ -0,0 +1,34 @@ +/* Copyright 2017 REPLACE_WITH_YOUR_NAME + * + * 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 %KEYBOARD_UPPERCASE%_H +#define %KEYBOARD_UPPERCASE%_H + +#include "quantum.h" + +// This a shortcut to help you visually see your layout. +// The following is an example using the Planck MIT layout +// The first section contains all of the arguments +// The second converts the arguments into a two-dimensional array +#define KEYMAP( \ +    k00, k01, k02, \ +      k10,  k11   \ +) \ +{ \ +    { k00, k01,   k02 }, \ +    { k10, KC_NO, k11 }, \ +} + +#endif diff --git a/quantum/tools/eeprom_reset.hex b/quantum/tools/eeprom_reset.hex new file mode 100644 index 0000000000..a8a75389fe --- /dev/null +++ b/quantum/tools/eeprom_reset.hex @@ -0,0 +1,9 @@ +:10000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 +:10001000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0 +:10002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 +:10003000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD0 +:10004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0 +:10005000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB0 +:10006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0 +:10007000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF90 +:00000001FF diff --git a/quantum/tools/readme.md b/quantum/tools/readme.md new file mode 100644 index 0000000000..5f355256de --- /dev/null +++ b/quantum/tools/readme.md @@ -0,0 +1,6 @@ +`eeprom_reset.hex` is to reset the eeprom on the Atmega32u4, like this: + +    dfu-programmer atmega32u4 erase +    dfu-programmer atmega32u4 flash --eeprom eeprom_reset.hex + + You'll need to reflash afterwards, because DFU requires the flash to be erased before messing with the eeprom. diff --git a/quantum/variable_trace.c b/quantum/variable_trace.c new file mode 100644 index 0000000000..713747cfc2 --- /dev/null +++ b/quantum/variable_trace.c @@ -0,0 +1,126 @@ +/* Copyright 2016 Fred Sundvik + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include "variable_trace.h" +#include <stddef.h> +#include <string.h> + +#ifdef NO_PRINT +#error "You need undef NO_PRINT to use the variable trace feature" +#endif + +#ifndef CONSOLE_ENABLE +#error "The console needs to be enabled in the makefile to use the variable trace feature" +#endif + + +#define NUM_TRACED_VARIABLES 1 +#ifndef MAX_VARIABLE_TRACE_SIZE +    #define MAX_VARIABLE_TRACE_SIZE 4 +#endif + +typedef struct { +    const char* name; +    void* addr; +    unsigned size; +    const char* func; +    int line; +    uint8_t last_value[MAX_VARIABLE_TRACE_SIZE]; + +} traced_variable_t; + +static traced_variable_t traced_variables[NUM_TRACED_VARIABLES]; + +void add_traced_variable(const char* name, void* addr, unsigned size, const char* func, int line) { +    verify_traced_variables(func, line); +    if (size > MAX_VARIABLE_TRACE_SIZE) { +#if defined(__AVR__) +       xprintf("Traced variable \"%S\" exceeds the maximum size %d\n", name, size); +#else +       xprintf("Traced variable \"%s\" exceeds the maximum size %d\n", name, size); +#endif +       size = MAX_VARIABLE_TRACE_SIZE; +    } +    int index = -1; +    for (int i = 0; i < NUM_TRACED_VARIABLES; i++) { +        if (index == -1 && traced_variables[i].addr == NULL){ +            index = i; +        } +        else if (strcmp_P(name, traced_variables[i].name)==0) { +            index = i; +            break; +        } +    } + +    if (index == -1) { +        xprintf("You can only trace %d variables at the same time\n", NUM_TRACED_VARIABLES); +        return; +    } + +    traced_variable_t* t = &traced_variables[index]; +    t->name = name; +    t->addr = addr; +    t->size = size; +    t->func = func; +    t->line = line; +    memcpy(&t->last_value[0], addr, size); + +} + +void remove_traced_variable(const char* name, const char* func, int line) { +    verify_traced_variables(func, line); +    for (int i = 0; i < NUM_TRACED_VARIABLES; i++) { +        if (strcmp_P(name, traced_variables[i].name)==0) { +            traced_variables[i].name = 0; +            traced_variables[i].addr = NULL; +            break; +        } +    } +} + +void verify_traced_variables(const char* func, int line) { +    for (int i = 0; i < NUM_TRACED_VARIABLES; i++) { +        traced_variable_t* t = &traced_variables[i]; +        if (t->addr != NULL && t->name != NULL) { +            if (memcmp(t->last_value, t->addr, t->size)!=0){ +#if defined(__AVR__) +               xprintf("Traced variable \"%S\" has been modified\n", t->name); +               xprintf("Between %S:%d\n", t->func, t->line); +               xprintf("And %S:%d\n", func, line); + +#else +               xprintf("Traced variable \"%s\" has been modified\n", t->name); +               xprintf("Between %s:%d\n", t->func, t->line); +               xprintf("And %s:%d\n", func, line); +#endif +               xprintf("Previous value "); +               for (int j=0; j<t->size;j++) { +                   print_hex8(t->last_value[j]); +               } +               xprintf("\nNew value "); +               uint8_t* addr = (uint8_t*)(t->addr); +               for (int j=0; j<t->size;j++) { +                   print_hex8(addr[j]); +               } +               xprintf("\n"); +               memcpy(t->last_value, addr, t->size); +           } +        } + +        t->func = func; +        t->line = line; +    } +} diff --git a/quantum/variable_trace.h b/quantum/variable_trace.h new file mode 100644 index 0000000000..dacc13858d --- /dev/null +++ b/quantum/variable_trace.h @@ -0,0 +1,50 @@ +/* Copyright 2016 Fred Sundvik + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef VARIABLE_TRACE_H +#define VARIABLE_TRACE_H + +// For more information about the variable tracing see the readme. + +#include "print.h" + +#ifdef NUM_TRACED_VARIABLES + +// Start tracing a variable at the memory address addr +// The name can be anything and is used only for reporting +// The size should usually be the same size as the variable you are interested in +#define ADD_TRACED_VARIABLE(name, addr, size) \ +    add_traced_variable(PSTR(name), (void*)addr, size, PSTR(__FILE__), __LINE__) + +// Stop tracing the variable with the given name +#define REMOVE_TRACED_VARIABLE(name) remove_traced_variable(PSTR(name), PSTR(__FILE__), __LINE__) + +// Call to get messages when the variable has been changed +#define VERIFY_TRACED_VARIABLES() verify_traced_variables(PSTR(__FILE__), __LINE__) + +#else + +#define ADD_TRACED_VARIABLE(name, addr, size) +#define REMOVE_TRACED_VARIABLE(name) +#define VERIFY_TRACED_VARIABLES() + +#endif + +// Don't call directly, use the macros instead +void add_traced_variable(const char* name, void* addr, unsigned size, const char* func, int line); +void remove_traced_variable(const char* name, const char* func, int line); +void verify_traced_variables(const char* func, int line); +#endif diff --git a/quantum/visualizer/LICENSE.md b/quantum/visualizer/LICENSE.md new file mode 100644 index 0000000000..22d4c3f08b --- /dev/null +++ b/quantum/visualizer/LICENSE.md @@ -0,0 +1,29 @@ +The files in this project are licensed under the MIT license +It uses the following libraries +uGFX - with it's own license, see the license.html file in the uGFX subfolder for more information +tmk_core - is indirectly used and not included in the repository. It's licensed under the GPLv2 license +Chibios - which is used by tmk_core is licensed under GPLv3. + +Therefore the effective license for any project using the library is GPLv3 + +The MIT License (MIT) + +Copyright (c) 2016 Fred Sundvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/quantum/visualizer/lcd_backlight.c b/quantum/visualizer/lcd_backlight.c new file mode 100644 index 0000000000..6cd996f75e --- /dev/null +++ b/quantum/visualizer/lcd_backlight.c @@ -0,0 +1,89 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Fred Sundvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "lcd_backlight.h" +#include <math.h> + +static uint8_t current_hue = 0; +static uint8_t current_saturation = 0; +static uint8_t current_intensity = 0; +static uint8_t current_brightness = 0; + +void lcd_backlight_init(void) { +    lcd_backlight_hal_init(); +    lcd_backlight_color(current_hue, current_saturation, current_intensity); +} + +// This code is based on Brian Neltner's blogpost and example code +// "Why every LED light should be using HSI colorspace". +// http://blog.saikoled.com/post/43693602826/why-every-led-light-should-be-using-hsi +static void hsi_to_rgb(float h, float s, float i, uint16_t* r_out, uint16_t* g_out, uint16_t* b_out) { +    unsigned int r, g, b; +    h = fmodf(h, 360.0f); // cycle h around to 0-360 degrees +    h = 3.14159f * h / 180.0f; // Convert to radians. +    s = s > 0.0f ? (s < 1.0f ? s : 1.0f) : 0.0f; // clamp s and i to interval [0,1] +    i = i > 0.0f ? (i < 1.0f ? i : 1.0f) : 0.0f; + +    // Math! Thanks in part to Kyle Miller. +    if(h < 2.09439f) { +        r = 65535.0f * i/3.0f *(1.0f + s * cos(h) / cosf(1.047196667f - h)); +        g = 65535.0f * i/3.0f *(1.0f + s *(1.0f - cosf(h) / cos(1.047196667f - h))); +        b = 65535.0f * i/3.0f *(1.0f - s); +    } else if(h < 4.188787) { +        h = h - 2.09439; +        g = 65535.0f * i/3.0f *(1.0f + s * cosf(h) / cosf(1.047196667f - h)); +        b = 65535.0f * i/3.0f *(1.0f + s * (1.0f - cosf(h) / cosf(1.047196667f - h))); +        r = 65535.0f * i/3.0f *(1.0f - s); +    } else { +        h = h - 4.188787; +        b = 65535.0f*i/3.0f * (1.0f + s * cosf(h) / cosf(1.047196667f - h)); +        r = 65535.0f*i/3.0f * (1.0f + s * (1.0f - cosf(h) / cosf(1.047196667f - h))); +        g = 65535.0f*i/3.0f * (1.0f - s); +    } +    *r_out = r > 65535 ? 65535 : r; +    *g_out = g > 65535 ? 65535 : g; +    *b_out = b > 65535 ? 65535 : b; +} + +void lcd_backlight_color(uint8_t hue, uint8_t saturation, uint8_t intensity) { +    uint16_t r, g, b; +    float hue_f = 360.0f * (float)hue / 255.0f; +    float saturation_f = (float)saturation / 255.0f; +    float intensity_f = (float)intensity / 255.0f; +    intensity_f *= (float)current_brightness / 255.0f; +    hsi_to_rgb(hue_f, saturation_f, intensity_f, &r, &g, &b); +	current_hue = hue; +	current_saturation = saturation; +	current_intensity = intensity; +	lcd_backlight_hal_color(r, g, b); +} + +void lcd_backlight_brightness(uint8_t b) { +    current_brightness = b; +    lcd_backlight_color(current_hue, current_saturation, current_intensity); +} + +uint8_t lcd_get_backlight_brightness(void) { +	return current_brightness; +} diff --git a/quantum/visualizer/lcd_backlight.h b/quantum/visualizer/lcd_backlight.h new file mode 100644 index 0000000000..95d7a07b46 --- /dev/null +++ b/quantum/visualizer/lcd_backlight.h @@ -0,0 +1,47 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Fred Sundvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef LCD_BACKLIGHT_H_ +#define LCD_BACKLIGHT_H_ +#include "stdint.h" + +// Helper macros for storing hue, staturation and intensity as unsigned integers +#define LCD_COLOR(hue, saturation, intensity) (hue << 16 | saturation << 8 | intensity) +#define LCD_HUE(color) ((color >> 16) & 0xFF) +#define LCD_SAT(color) ((color >> 8) & 0xFF) +#define LCD_INT(color) (color & 0xFF) + +static inline uint32_t change_lcd_color_intensity(uint32_t color, uint8_t new_intensity) { +    return (color & 0xFFFFFF00) | new_intensity; +} + +void lcd_backlight_init(void); +void lcd_backlight_color(uint8_t hue, uint8_t saturation, uint8_t intensity); +void lcd_backlight_brightness(uint8_t b); +uint8_t lcd_get_backlight_brightness(void); + +void lcd_backlight_hal_init(void); +void lcd_backlight_hal_color(uint16_t r, uint16_t g, uint16_t b); + +#endif /* LCD_BACKLIGHT_H_ */ diff --git a/quantum/visualizer/lcd_backlight_keyframes.c b/quantum/visualizer/lcd_backlight_keyframes.c new file mode 100644 index 0000000000..8436d4e3dd --- /dev/null +++ b/quantum/visualizer/lcd_backlight_keyframes.c @@ -0,0 +1,77 @@ +/* Copyright 2017 Fred Sundvik + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include "lcd_backlight_keyframes.h" + +bool backlight_keyframe_animate_color(keyframe_animation_t* animation, visualizer_state_t* state) { +    int frame_length = animation->frame_lengths[animation->current_frame]; +    int current_pos = frame_length - animation->time_left_in_frame; +    uint8_t t_h = LCD_HUE(state->target_lcd_color); +    uint8_t t_s = LCD_SAT(state->target_lcd_color); +    uint8_t t_i = LCD_INT(state->target_lcd_color); +    uint8_t p_h = LCD_HUE(state->prev_lcd_color); +    uint8_t p_s = LCD_SAT(state->prev_lcd_color); +    uint8_t p_i = LCD_INT(state->prev_lcd_color); + +    uint8_t d_h1 = t_h - p_h; //Modulo arithmetic since we want to wrap around +    int d_h2 = t_h - p_h; +    // Chose the shortest way around +    int d_h = abs(d_h2) < d_h1 ? d_h2 : d_h1; +    int d_s = t_s - p_s; +    int d_i = t_i - p_i; + +    int hue = (d_h * current_pos) / frame_length; +    int sat = (d_s * current_pos) / frame_length; +    int intensity = (d_i * current_pos) / frame_length; +    //dprintf("%X -> %X = %X\n", p_h, t_h, hue); +    hue += p_h; +    sat += p_s; +    intensity += p_i; +    state->current_lcd_color = LCD_COLOR(hue, sat, intensity); +    lcd_backlight_color( +            LCD_HUE(state->current_lcd_color), +            LCD_SAT(state->current_lcd_color), +            LCD_INT(state->current_lcd_color)); + +    return true; +} + +bool backlight_keyframe_set_color(keyframe_animation_t* animation, visualizer_state_t* state) { +    (void)animation; +    state->prev_lcd_color = state->target_lcd_color; +    state->current_lcd_color = state->target_lcd_color; +    lcd_backlight_color( +            LCD_HUE(state->current_lcd_color), +            LCD_SAT(state->current_lcd_color), +            LCD_INT(state->current_lcd_color)); +    return false; +} + +bool backlight_keyframe_disable(keyframe_animation_t* animation, visualizer_state_t* state) { +    (void)animation; +    (void)state; +    lcd_backlight_hal_color(0, 0, 0); +    return false; +} + +bool backlight_keyframe_enable(keyframe_animation_t* animation, visualizer_state_t* state) { +    (void)animation; +    (void)state; +    lcd_backlight_color(LCD_HUE(state->current_lcd_color), +        LCD_SAT(state->current_lcd_color), +        LCD_INT(state->current_lcd_color)); +    return false; +} diff --git a/quantum/visualizer/lcd_backlight_keyframes.h b/quantum/visualizer/lcd_backlight_keyframes.h new file mode 100644 index 0000000000..e1c125cf91 --- /dev/null +++ b/quantum/visualizer/lcd_backlight_keyframes.h @@ -0,0 +1,30 @@ +/* Copyright 2017 Fred Sundvik + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef QUANTUM_VISUALIZER_LCD_BACKLIGHT_KEYFRAMES_H_ +#define QUANTUM_VISUALIZER_LCD_BACKLIGHT_KEYFRAMES_H_ + +#include "visualizer.h" + +// Animates the LCD backlight color between the current color and the target color (of the state) +bool backlight_keyframe_animate_color(keyframe_animation_t* animation, visualizer_state_t* state); +// Sets the backlight color to the target color +bool backlight_keyframe_set_color(keyframe_animation_t* animation, visualizer_state_t* state); + +bool backlight_keyframe_disable(keyframe_animation_t* animation, visualizer_state_t* state); +bool backlight_keyframe_enable(keyframe_animation_t* animation, visualizer_state_t* state); + +#endif /* QUANTUM_VISUALIZER_LCD_BACKLIGHT_KEYFRAMES_H_ */ diff --git a/quantum/visualizer/lcd_keyframes.c b/quantum/visualizer/lcd_keyframes.c new file mode 100644 index 0000000000..82e4184d2c --- /dev/null +++ b/quantum/visualizer/lcd_keyframes.c @@ -0,0 +1,188 @@ +/* Copyright 2017 Fred Sundvik + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include "lcd_keyframes.h" +#include <string.h> +#include "action_util.h" +#include "led.h" +#include "resources/resources.h" + +bool lcd_keyframe_display_layer_text(keyframe_animation_t* animation, visualizer_state_t* state) { +    (void)animation; +    gdispClear(White); +    gdispDrawString(0, 10, state->layer_text, state->font_dejavusansbold12, Black); +    return false; +} + +static void format_layer_bitmap_string(uint16_t default_layer, uint16_t layer, char* buffer) { +    for (int i=0; i<16;i++) +    { +        uint32_t mask = (1u << i); +        if (default_layer & mask) { +            if (layer & mask) { +                *buffer = 'B'; +            } else { +                *buffer = 'D'; +            } +        } else if (layer & mask) { +            *buffer = '1'; +        } else { +            *buffer = '0'; +        } +        ++buffer; + +        if (i==3 || i==7 || i==11) { +            *buffer = ' '; +            ++buffer; +        } +    } +    *buffer = 0; +} + +bool lcd_keyframe_display_layer_bitmap(keyframe_animation_t* animation, visualizer_state_t* state) { +    (void)animation; +    const char* layer_help = "1=On D=Default B=Both"; +    char layer_buffer[16 + 4]; // 3 spaces and one null terminator +    gdispClear(White); +    gdispDrawString(0, 0, layer_help, state->font_fixed5x8, Black); +    format_layer_bitmap_string(state->status.default_layer, state->status.layer, layer_buffer); +    gdispDrawString(0, 10, layer_buffer, state->font_fixed5x8, Black); +    format_layer_bitmap_string(state->status.default_layer >> 16, state->status.layer >> 16, layer_buffer); +    gdispDrawString(0, 20, layer_buffer, state->font_fixed5x8, Black); +    return false; +} + +static void format_mods_bitmap_string(uint8_t mods, char* buffer) { +    *buffer = ' '; +    ++buffer; + +    for (int i = 0; i<8; i++) +    { +        uint32_t mask = (1u << i); +        if (mods & mask) { +            *buffer = '1'; +        } else { +            *buffer = '0'; +        } +        ++buffer; + +        if (i==3) { +            *buffer = ' '; +            ++buffer; +        } +    } +    *buffer = 0; +} + +bool lcd_keyframe_display_mods_bitmap(keyframe_animation_t* animation, visualizer_state_t* state) { +    (void)animation; + +    const char* title = "Modifier states"; +    const char* mods_header = " CSAG CSAG "; +    char status_buffer[12]; + +    gdispClear(White); +    gdispDrawString(0, 0, title, state->font_fixed5x8, Black); +    gdispDrawString(0, 10, mods_header, state->font_fixed5x8, Black); +    format_mods_bitmap_string(state->status.mods, status_buffer); +    gdispDrawString(0, 20, status_buffer, state->font_fixed5x8, Black); + +    return false; +} + +#define LED_STATE_STRING_SIZE sizeof("NUM CAPS SCRL COMP KANA") + +static void get_led_state_string(char* output, visualizer_state_t* state) { +    uint8_t pos = 0; + +    if (state->status.leds & (1u << USB_LED_NUM_LOCK)) { +       memcpy(output + pos, "NUM ", 4); +       pos += 4; +    } +    if (state->status.leds & (1u << USB_LED_CAPS_LOCK)) { +       memcpy(output + pos, "CAPS ", 5); +       pos += 5; +    } +    if (state->status.leds & (1u << USB_LED_SCROLL_LOCK)) { +       memcpy(output + pos, "SCRL ", 5); +       pos += 5; +    } +    if (state->status.leds & (1u << USB_LED_COMPOSE)) { +       memcpy(output + pos, "COMP ", 5); +       pos += 5; +    } +    if (state->status.leds & (1u << USB_LED_KANA)) { +       memcpy(output + pos, "KANA", 4); +       pos += 4; +    } +    output[pos] = 0; +} + +bool lcd_keyframe_display_led_states(keyframe_animation_t* animation, visualizer_state_t* state) +{ +    (void)animation; +    char output[LED_STATE_STRING_SIZE]; +    get_led_state_string(output, state); +    gdispClear(White); +    gdispDrawString(0, 10, output, state->font_dejavusansbold12, Black); +    return false; +} + +bool lcd_keyframe_display_layer_and_led_states(keyframe_animation_t* animation, visualizer_state_t* state) { +    (void)animation; +    gdispClear(White); +    uint8_t y = 10; +    if (state->status.leds) { +        char output[LED_STATE_STRING_SIZE]; +        get_led_state_string(output, state); +        gdispDrawString(0, 1, output, state->font_dejavusansbold12, Black); +        y = 17; +    } +    gdispDrawString(0, y, state->layer_text, state->font_dejavusansbold12, Black); +    return false; +} + +bool lcd_keyframe_draw_logo(keyframe_animation_t* animation, visualizer_state_t* state) { +    (void)state; +    (void)animation; +    // Read the uGFX documentation for information how to use the displays +    // http://wiki.ugfx.org/index.php/Main_Page +    gdispClear(White); + +    // You can use static variables for things that can't be found in the animation +    // or state structs, here we use the image + +    //gdispGBlitArea is a tricky function to use since it supports blitting part of the image +    // if you have full screen image, then just use 128 and 32 for both source and target dimensions +    gdispGBlitArea(GDISP, 0, 0, 128, 32, 0, 0, 128, (pixel_t*)resource_lcd_logo); + +    return false; +} + + +bool lcd_keyframe_disable(keyframe_animation_t* animation, visualizer_state_t* state) { +    (void)animation; +    (void)state; +    gdispSetPowerMode(powerOff); +    return false; +} + +bool lcd_keyframe_enable(keyframe_animation_t* animation, visualizer_state_t* state) { +    (void)animation; +    (void)state; +    gdispSetPowerMode(powerOn); +    return false; +} diff --git a/quantum/visualizer/lcd_keyframes.h b/quantum/visualizer/lcd_keyframes.h new file mode 100644 index 0000000000..2e912b4c73 --- /dev/null +++ b/quantum/visualizer/lcd_keyframes.h @@ -0,0 +1,39 @@ +/* Copyright 2017 Fred Sundvik + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef QUANTUM_VISUALIZER_LCD_KEYFRAMES_H_ +#define QUANTUM_VISUALIZER_LCD_KEYFRAMES_H_ + +#include "visualizer.h" + +// Displays the layer text centered vertically on the screen +bool lcd_keyframe_display_layer_text(keyframe_animation_t* animation, visualizer_state_t* state); +// Displays a bitmap (0/1) of all the currently active layers +bool lcd_keyframe_display_layer_bitmap(keyframe_animation_t* animation, visualizer_state_t* state); +// Displays a bitmap (0/1) of all the currently active mods +bool lcd_keyframe_display_mods_bitmap(keyframe_animation_t* animation, visualizer_state_t* state); +// Displays the keyboard led states (CAPS (Caps lock), NUM (Num lock), SCRL (Scroll lock), COMP (Compose), KANA) +bool lcd_keyframe_display_led_states(keyframe_animation_t* animation, visualizer_state_t* state); +// Displays both the layer text and the led states +bool lcd_keyframe_display_layer_and_led_states(keyframe_animation_t* animation, visualizer_state_t* state); +// Displays the QMK logo on the LCD screen +bool lcd_keyframe_draw_logo(keyframe_animation_t* animation, visualizer_state_t* state); + +bool lcd_keyframe_disable(keyframe_animation_t* animation, visualizer_state_t* state); +bool lcd_keyframe_enable(keyframe_animation_t* animation, visualizer_state_t* state); + + +#endif /* QUANTUM_VISUALIZER_LCD_KEYFRAMES_H_ */ diff --git a/quantum/visualizer/led_keyframes.c b/quantum/visualizer/led_keyframes.c new file mode 100644 index 0000000000..7e6e5d1ab9 --- /dev/null +++ b/quantum/visualizer/led_keyframes.c @@ -0,0 +1,143 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Fred Sundvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ +#include "gfx.h" +#include "math.h" +#include "led_keyframes.h" + +static uint8_t fade_led_color(keyframe_animation_t* animation, int from, int to) { +    int frame_length = animation->frame_lengths[animation->current_frame]; +    int current_pos = frame_length - animation->time_left_in_frame; +    int delta = to - from; +    int luma = (delta * current_pos) / frame_length; +    luma += from; +    return luma; +} + +static void keyframe_fade_all_leds_from_to(keyframe_animation_t* animation, uint8_t from, uint8_t to) { +    uint8_t luma = fade_led_color(animation, from, to); +    color_t color = LUMA2COLOR(luma); +    gdispGClear(LED_DISPLAY, color); +} + +// TODO: Should be customizable per keyboard +#define NUM_ROWS LED_NUM_ROWS +#define NUM_COLS LED_NUM_COLS + +static uint8_t crossfade_start_frame[NUM_ROWS][NUM_COLS]; +static uint8_t crossfade_end_frame[NUM_ROWS][NUM_COLS]; + +static uint8_t compute_gradient_color(float t, float index, float num) { +    const float two_pi = M_PI * 2.0f; +    float normalized_index = (1.0f - index / (num - 1.0f)) * two_pi; +    float x = t * two_pi + normalized_index; +    float v = 0.5 * (cosf(x) + 1.0f); +    return (uint8_t)(255.0f * v); +} + +bool led_keyframe_fade_in_all(keyframe_animation_t* animation, visualizer_state_t* state) { +    (void)state; +    keyframe_fade_all_leds_from_to(animation, 0, 255); +    return true; +} + +bool led_keyframe_fade_out_all(keyframe_animation_t* animation, visualizer_state_t* state) { +    (void)state; +    keyframe_fade_all_leds_from_to(animation, 255, 0); +    return true; +} + +bool led_keyframe_left_to_right_gradient(keyframe_animation_t* animation, visualizer_state_t* state) { +    (void)state; +    float frame_length = animation->frame_lengths[animation->current_frame]; +    float current_pos = frame_length - animation->time_left_in_frame; +    float t = current_pos / frame_length; +    for (int i=0; i< NUM_COLS; i++) { +        uint8_t color = compute_gradient_color(t, i, NUM_COLS); +        gdispGDrawLine(LED_DISPLAY, i, 0, i, NUM_ROWS - 1, LUMA2COLOR(color)); +    } +    return true; +} + +bool led_keyframe_top_to_bottom_gradient(keyframe_animation_t* animation, visualizer_state_t* state) { +    (void)state; +    float frame_length = animation->frame_lengths[animation->current_frame]; +    float current_pos = frame_length - animation->time_left_in_frame; +    float t = current_pos / frame_length; +    for (int i=0; i< NUM_ROWS; i++) { +        uint8_t color = compute_gradient_color(t, i, NUM_ROWS); +        gdispGDrawLine(LED_DISPLAY, 0, i, NUM_COLS - 1, i, LUMA2COLOR(color)); +    } +    return true; +} + +static void copy_current_led_state(uint8_t* dest) { +    for (int i=0;i<NUM_ROWS;i++) { +        for (int j=0;j<NUM_COLS;j++) { +            dest[i*NUM_COLS + j] = gdispGGetPixelColor(LED_DISPLAY, j, i); +        } +    } +} +bool led_keyframe_crossfade(keyframe_animation_t* animation, visualizer_state_t* state) { +    (void)state; +    if (animation->first_update_of_frame) { +        copy_current_led_state(&crossfade_start_frame[0][0]); +        run_next_keyframe(animation, state); +        copy_current_led_state(&crossfade_end_frame[0][0]); +    } +    for (int i=0;i<NUM_ROWS;i++) { +        for (int j=0;j<NUM_COLS;j++) { +            color_t color  = LUMA2COLOR(fade_led_color(animation, crossfade_start_frame[i][j], crossfade_end_frame[i][j])); +            gdispGDrawPixel(LED_DISPLAY, j, i, color); +        } +    } +    return true; +} + +bool led_keyframe_mirror_orientation(keyframe_animation_t* animation, visualizer_state_t* state) { +    (void)state; +    (void)animation; +    gdispGSetOrientation(LED_DISPLAY, GDISP_ROTATE_180); +    return false; +} + +bool led_keyframe_normal_orientation(keyframe_animation_t* animation, visualizer_state_t* state) { +    (void)state; +    (void)animation; +    gdispGSetOrientation(LED_DISPLAY, GDISP_ROTATE_0); +    return false; +} + +bool led_keyframe_disable(keyframe_animation_t* animation, visualizer_state_t* state) { +    (void)state; +    (void)animation; +    gdispGSetPowerMode(LED_DISPLAY, powerOff); +    return false; +} + +bool led_keyframe_enable(keyframe_animation_t* animation, visualizer_state_t* state) { +    (void)state; +    (void)animation; +    gdispGSetPowerMode(LED_DISPLAY, powerOn); +    return false; +} diff --git a/quantum/visualizer/led_keyframes.h b/quantum/visualizer/led_keyframes.h new file mode 100644 index 0000000000..a59a4f37d1 --- /dev/null +++ b/quantum/visualizer/led_keyframes.h @@ -0,0 +1,44 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Fred Sundvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef LED_KEYFRAMES_H +#define LED_KEYFRAMES_H + +#include "visualizer.h" + +bool led_keyframe_fade_in_all(keyframe_animation_t* animation, visualizer_state_t* state); +bool led_keyframe_fade_out_all(keyframe_animation_t* animation, visualizer_state_t* state); +bool led_keyframe_left_to_right_gradient(keyframe_animation_t* animation, visualizer_state_t* state); +bool led_keyframe_top_to_bottom_gradient(keyframe_animation_t* animation, visualizer_state_t* state); +bool led_keyframe_crossfade(keyframe_animation_t* animation, visualizer_state_t* state); +bool led_keyframe_mirror_orientation(keyframe_animation_t* animation, visualizer_state_t* state); +bool led_keyframe_normal_orientation(keyframe_animation_t* animation, visualizer_state_t* state); + +bool led_keyframe_disable(keyframe_animation_t* animation, visualizer_state_t* state); +bool led_keyframe_enable(keyframe_animation_t* animation, visualizer_state_t* state); + +extern keyframe_animation_t led_test_animation; + + +#endif /* LED_KEYFRAMES_H */ diff --git a/quantum/visualizer/readme.md b/quantum/visualizer/readme.md new file mode 100644 index 0000000000..545ba22707 --- /dev/null +++ b/quantum/visualizer/readme.md @@ -0,0 +1,18 @@ +# A visualization library for the TMK keyboard firmware + +This library is designed to work together with the [TMK keyboard firmware](https://github.com/tmk/tmk_keyboard). Currently it only works for [Chibios](http://www.chibios.org/) + flavors, but it would be possible to add support for other configurations as well. The LCD display functionality is provided by the [uGFX library](http://www.ugfx.org/).  + +## To use this library as a user +You can and should modify the visualizer\_user.c file. Check the comments in the file for more information. + +## To add this library to custom keyboard projects + +1. Add tmk_visualizer as a submodule to your project +1. Set VISUALIZER_DIR in the main keyboard project makefile to point to the submodule +1. Define LCD\_ENABLE and/or LCD\_BACKLIGHT\_ENABLE, to enable support +1. Include the visualizer.mk make file +1. Copy the files in the example\_integration folder to your keyboard project +1. All other files than the callback.c file are included automatically, so you will need to add callback.c to your makefile manually. If you already have a similar file in your project, you can just copy the functions instead of the whole file. +1. Edit the files to match your hardware. You might might want to read the Chibios and UGfx documentation, for more information. +1. If you enable LCD support you might also have to write a custom uGFX display driver, check the uGFX documentation for that. You probably also want to enable SPI support in your Chibios configuration. diff --git a/quantum/visualizer/resources/lcd_logo.c b/quantum/visualizer/resources/lcd_logo.c new file mode 100644 index 0000000000..d1a0ffa7f9 --- /dev/null +++ b/quantum/visualizer/resources/lcd_logo.c @@ -0,0 +1,61 @@ +/* Copyright 2017 Fred Sundvik + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include "resources.h" + + +// To generate an image array like this +// Ensure the image is 128 x 32 or smaller +// Convert the bitmap to a C array using a program like http://www.riuson.com/lcd-image-converter/ +// Ensure the the conversion process produces a monochrome format array - 1 bit/pixel, left to right, top to bottom +// Update array in the source code with the C array produced by the conversion program + +// The image below is generated from lcd_logo.png +const uint8_t resource_lcd_logo[512] = { +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0xf8, 0xfe, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x38, 0x38, 0x38, 0x06, 0x29, 0x41, 0x24, 0x52, 0x24, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x38, 0x38, 0x38, 0x09, 0x55, 0x42, 0xaa, 0xaa, 0xaa, 0xa8, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x38, 0x38, 0x38, 0x09, 0x55, 0x82, 0x28, 0xaa, 0xae, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x38, 0x38, 0x38, 0x09, 0x55, 0x43, 0x28, 0xaa, 0xaa, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x38, 0x38, 0x38, 0x0a, 0x55, 0x42, 0x28, 0xaa, 0xaa, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x38, 0x38, 0x38, 0x05, 0x45, 0x42, 0x28, 0x89, 0x4a, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x18, 0x38, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x1c, 0x38, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x0e, 0x38, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x03, 0xff, 0x80, 0x04, 0x45, 0x14, 0xa4, 0x92, 0x83, 0x52, 0x22, 0x22, 0x36, 0x00, 0x00, +    0x00, 0x00, 0x38, 0x00, 0x0a, 0xaa, 0xaa, 0xaa, 0xba, 0x84, 0x55, 0x55, 0x57, 0x45, 0x00, 0x00, +    0x00, 0x00, 0x38, 0x00, 0x08, 0xaa, 0xaa, 0xaa, 0x92, 0xb2, 0x55, 0x55, 0x42, 0x65, 0x00, 0x00, +    0x00, 0x00, 0x38, 0x00, 0x08, 0xaa, 0xaa, 0xaa, 0x92, 0x81, 0x56, 0x65, 0x42, 0x45, 0x00, 0x00, +    0x00, 0x00, 0x38, 0x00, 0x0a, 0xaa, 0xaa, 0xaa, 0x92, 0x81, 0x54, 0x45, 0x42, 0x45, 0x00, 0x00, +    0x00, 0x00, 0x38, 0x00, 0x04, 0x48, 0xa2, 0x4a, 0x89, 0x06, 0x24, 0x42, 0x41, 0x36, 0x00, 0x00, +    0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + diff --git a/quantum/visualizer/resources/lcd_logo.png b/quantum/visualizer/resources/lcd_logo.png Binary files differnew file mode 100644 index 0000000000..6cf26fc678 --- /dev/null +++ b/quantum/visualizer/resources/lcd_logo.png diff --git a/quantum/visualizer/resources/resources.h b/quantum/visualizer/resources/resources.h new file mode 100644 index 0000000000..1ea27a5364 --- /dev/null +++ b/quantum/visualizer/resources/resources.h @@ -0,0 +1,27 @@ +/* Copyright 2017 Fred Sundvik + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef QUANTUM_VISUALIZER_RESOURCES_RESOURCES_H_ +#define QUANTUM_VISUALIZER_RESOURCES_RESOURCES_H_ + +#include <stdint.h> + +#ifdef LCD_ENABLE +extern const uint8_t resource_lcd_logo[]; +#endif + + +#endif /* QUANTUM_VISUALIZER_RESOURCES_RESOURCES_H_ */ diff --git a/quantum/visualizer/visualizer.c b/quantum/visualizer/visualizer.c new file mode 100644 index 0000000000..cc99d1e3b6 --- /dev/null +++ b/quantum/visualizer/visualizer.c @@ -0,0 +1,502 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Fred Sundvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "config.h" +#include "visualizer.h" +#include <string.h> +#ifdef PROTOCOL_CHIBIOS +#include "ch.h" +#endif + +#include "gfx.h" + +#ifdef LCD_BACKLIGHT_ENABLE +#include "lcd_backlight.h" +#endif + +//#define DEBUG_VISUALIZER + +#ifdef DEBUG_VISUALIZER +#include "debug.h" +#else +#include "nodebug.h" +#endif + +#ifdef SERIAL_LINK_ENABLE +#include "serial_link/protocol/transport.h" +#include "serial_link/system/serial_link.h" +#endif + +#include "action_util.h" + +// Define this in config.h +#ifndef VISUALIZER_THREAD_PRIORITY +#define "Visualizer thread priority not defined" +#endif + +static visualizer_keyboard_status_t current_status = { +    .layer = 0xFFFFFFFF, +    .default_layer = 0xFFFFFFFF, +    .leds = 0xFFFFFFFF, +#ifdef BACKLIGHT_ENABLE +    .backlight_level = 0, +#endif +    .mods = 0xFF, +    .suspended = false, +#ifdef VISUALIZER_USER_DATA_SIZE +    .user_data = {0} +#endif +}; + +static bool same_status(visualizer_keyboard_status_t* status1, visualizer_keyboard_status_t* status2) { +    return status1->layer == status2->layer && +        status1->default_layer == status2->default_layer && +        status1->mods == status2->mods && +        status1->leds == status2->leds && +        status1->suspended == status2->suspended +#ifdef BACKLIGHT_ENABLE +        && status1->backlight_level == status2->backlight_level +#endif +#ifdef VISUALIZER_USER_DATA_SIZE +        && memcmp(status1->user_data, status2->user_data, VISUALIZER_USER_DATA_SIZE) == 0 +#endif +    ; +} + +static bool visualizer_enabled = false; + +#ifdef VISUALIZER_USER_DATA_SIZE +static uint8_t user_data[VISUALIZER_USER_DATA_SIZE]; +#endif + +#define MAX_SIMULTANEOUS_ANIMATIONS 4 +static keyframe_animation_t* animations[MAX_SIMULTANEOUS_ANIMATIONS] = {}; + +#ifdef SERIAL_LINK_ENABLE +MASTER_TO_ALL_SLAVES_OBJECT(current_status, visualizer_keyboard_status_t); + +static remote_object_t* remote_objects[] = { +    REMOTE_OBJECT(current_status), +}; + +#endif + +GDisplay* LCD_DISPLAY = 0; +GDisplay* LED_DISPLAY = 0; + +#ifdef LCD_DISPLAY_NUMBER +__attribute__((weak)) +GDisplay* get_lcd_display(void) { +    return gdispGetDisplay(LCD_DISPLAY_NUMBER); +} +#endif + +#ifdef LED_DISPLAY_NUMBER +__attribute__((weak)) +GDisplay* get_led_display(void) { +    return gdispGetDisplay(LED_DISPLAY_NUMBER); +} +#endif + +void start_keyframe_animation(keyframe_animation_t* animation) { +    animation->current_frame = -1; +    animation->time_left_in_frame = 0; +    animation->need_update = true; +    int free_index = -1; +    for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) { +        if (animations[i] == animation) { +            return; +        } +        if (free_index == -1 && animations[i] == NULL) { +           free_index=i; +        } +    } +    if (free_index!=-1) { +        animations[free_index] = animation; +    } +} + +void stop_keyframe_animation(keyframe_animation_t* animation) { +    animation->current_frame = animation->num_frames; +    animation->time_left_in_frame = 0; +    animation->need_update = true; +    animation->first_update_of_frame = false; +    animation->last_update_of_frame = false; +    for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) { +        if (animations[i] == animation) { +            animations[i] = NULL; +            return; +        } +    } +} + +void stop_all_keyframe_animations(void) { +    for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) { +        if (animations[i]) { +            animations[i]->current_frame = animations[i]->num_frames; +            animations[i]->time_left_in_frame = 0; +            animations[i]->need_update = true; +            animations[i]->first_update_of_frame = false; +            animations[i]->last_update_of_frame = false; +            animations[i] = NULL; +        } +    } +} + +static uint8_t get_num_running_animations(void) { +    uint8_t count = 0; +    for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) { +        count += animations[i] ? 1 : 0; +    } +    return count; +} + +static bool update_keyframe_animation(keyframe_animation_t* animation, visualizer_state_t* state, systemticks_t delta, systemticks_t* sleep_time) { +    // TODO: Clean up this messy code +    dprintf("Animation frame%d, left %d, delta %d\n", animation->current_frame, +            animation->time_left_in_frame, delta); +    if (animation->current_frame == animation->num_frames) { +        animation->need_update = false; +        return false; +    } +    if (animation->current_frame == -1) { +       animation->current_frame = 0; +       animation->time_left_in_frame = animation->frame_lengths[0]; +       animation->need_update = true; +       animation->first_update_of_frame = true; +    } else { +        animation->time_left_in_frame -= delta; +        while (animation->time_left_in_frame <= 0) { +            int left = animation->time_left_in_frame; +            if (animation->need_update) { +                animation->time_left_in_frame = 0; +                animation->last_update_of_frame = true; +                (*animation->frame_functions[animation->current_frame])(animation, state); +                animation->last_update_of_frame = false; +            } +            animation->current_frame++; +            animation->need_update = true; +            animation->first_update_of_frame = true; +            if (animation->current_frame == animation->num_frames) { +                if (animation->loop) { +                    animation->current_frame = 0; +                } +                else { +                    stop_keyframe_animation(animation); +                    return false; +                } +            } +            delta = -left; +            animation->time_left_in_frame = animation->frame_lengths[animation->current_frame]; +            animation->time_left_in_frame -= delta; +        } +    } +    if (animation->need_update) { +        animation->need_update = (*animation->frame_functions[animation->current_frame])(animation, state); +        animation->first_update_of_frame = false; +    } + +    systemticks_t wanted_sleep = animation->need_update ? gfxMillisecondsToTicks(10) : (unsigned)animation->time_left_in_frame; +    if (wanted_sleep < *sleep_time) { +        *sleep_time = wanted_sleep; +    } + +    return true; +} + +void run_next_keyframe(keyframe_animation_t* animation, visualizer_state_t* state) { +    int next_frame = animation->current_frame + 1; +    if (next_frame == animation->num_frames) { +        next_frame = 0; +    } +    keyframe_animation_t temp_animation = *animation; +    temp_animation.current_frame = next_frame; +    temp_animation.time_left_in_frame = animation->frame_lengths[next_frame]; +    temp_animation.first_update_of_frame = true; +    temp_animation.last_update_of_frame = false; +    temp_animation.need_update  = false; +    visualizer_state_t temp_state = *state; +    (*temp_animation.frame_functions[next_frame])(&temp_animation, &temp_state); +} + +// TODO: Optimize the stack size, this is probably way too big +static DECLARE_THREAD_STACK(visualizerThreadStack, 1024); +static DECLARE_THREAD_FUNCTION(visualizerThread, arg) { +    (void)arg; + +    GListener event_listener; +    geventListenerInit(&event_listener); +    geventAttachSource(&event_listener, (GSourceHandle)¤t_status, 0); + +    visualizer_keyboard_status_t initial_status = { +        .default_layer = 0xFFFFFFFF, +        .layer = 0xFFFFFFFF, +        .mods = 0xFF, +        .leds = 0xFFFFFFFF, +        .suspended = false, +    #ifdef VISUALIZER_USER_DATA_SIZE +        .user_data = {0}, +    #endif +    }; + +    visualizer_state_t state = { +        .status = initial_status, +        .current_lcd_color = 0, +#ifdef LCD_ENABLE +        .font_fixed5x8 = gdispOpenFont("fixed_5x8"), +        .font_dejavusansbold12 = gdispOpenFont("DejaVuSansBold12") +#endif +    }; +    initialize_user_visualizer(&state); +    state.prev_lcd_color = state.current_lcd_color; + +#ifdef LCD_BACKLIGHT_ENABLE +    lcd_backlight_color( +            LCD_HUE(state.current_lcd_color), +            LCD_SAT(state.current_lcd_color), +            LCD_INT(state.current_lcd_color)); +#endif + +    systemticks_t sleep_time = TIME_INFINITE; +    systemticks_t current_time = gfxSystemTicks(); +    bool force_update = true; + +    while(true) { +        systemticks_t new_time = gfxSystemTicks(); +        systemticks_t delta = new_time - current_time; +        current_time = new_time; +        bool enabled = visualizer_enabled; +        if (force_update || !same_status(&state.status, ¤t_status)) { +            force_update = false; +    #if BACKLIGHT_ENABLE +            if(current_status.backlight_level != state.status.backlight_level) { +                if (current_status.backlight_level != 0) { +                    gdispGSetPowerMode(LED_DISPLAY, powerOn); +                    uint16_t percent = (uint16_t)current_status.backlight_level * 100 / BACKLIGHT_LEVELS; +                    gdispGSetBacklight(LED_DISPLAY, percent); +                } +                else { +                    gdispGSetPowerMode(LED_DISPLAY, powerOff); +                } +            } +    #endif +            if (visualizer_enabled) { +                if (current_status.suspended) { +                    stop_all_keyframe_animations(); +                    visualizer_enabled = false; +                    state.status = current_status; +                    user_visualizer_suspend(&state); +                } +                else { +                    visualizer_keyboard_status_t prev_status = state.status; +                    state.status = current_status; +                    update_user_visualizer_state(&state, &prev_status); +                } +                state.prev_lcd_color = state.current_lcd_color; +            } +        } +        if (!enabled && state.status.suspended && current_status.suspended == false) { +            // Setting the status to the initial status will force an update +            // when the visualizer is enabled again +            state.status = initial_status; +            state.status.suspended = false; +            stop_all_keyframe_animations(); +            user_visualizer_resume(&state); +            state.prev_lcd_color = state.current_lcd_color; +        } +        sleep_time = TIME_INFINITE; +        for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) { +            if (animations[i]) { +                update_keyframe_animation(animations[i], &state, delta, &sleep_time); +            } +        } +#ifdef BACKLIGHT_ENABLE +        gdispGFlush(LED_DISPLAY); +#endif + +#ifdef LCD_ENABLE +        gdispGFlush(LCD_DISPLAY); +#endif + +#ifdef EMULATOR +        draw_emulator(); +#endif +        // Enable the visualizer when the startup or the suspend animation has finished +        if (!visualizer_enabled && state.status.suspended == false && get_num_running_animations() == 0) { +            visualizer_enabled = true; +            force_update = true; +            sleep_time = 0; +        } + +        systemticks_t after_update = gfxSystemTicks(); +        unsigned update_delta = after_update - current_time; +        if (sleep_time != TIME_INFINITE) { +            if (sleep_time > update_delta) { +                sleep_time -= update_delta; +            } +            else { +                sleep_time = 0; +            } +        } +        dprintf("Update took %d, last delta %d, sleep_time %d\n", update_delta, delta, sleep_time); +#ifdef PROTOCOL_CHIBIOS +        // The gEventWait function really takes milliseconds, even if the documentation says ticks. +        // Unfortunately there's no generic ugfx conversion from system time to milliseconds, +        // so let's do it in a platform dependent way. + +        // On windows the system ticks is the same as milliseconds anyway +        if (sleep_time != TIME_INFINITE) { +            sleep_time = ST2MS(sleep_time); +        } +#endif +        geventEventWait(&event_listener, sleep_time); +    } +#ifdef LCD_ENABLE +    gdispCloseFont(state.font_fixed5x8); +    gdispCloseFont(state.font_dejavusansbold12); +#endif + +    return 0; +} + +void visualizer_init(void) { +    gfxInit(); + +  #ifdef LCD_BACKLIGHT_ENABLE +    lcd_backlight_init(); +  #endif + +  #ifdef SERIAL_LINK_ENABLE +    add_remote_objects(remote_objects, sizeof(remote_objects) / sizeof(remote_object_t*) ); +  #endif + +  #ifdef LCD_ENABLE +    LCD_DISPLAY = get_lcd_display(); +  #endif + +  #ifdef BACKLIGHT_ENABLE +    LED_DISPLAY = get_led_display(); +  #endif + +    // We are using a low priority thread, the idea is to have it run only +    // when the main thread is sleeping during the matrix scanning +  gfxThreadCreate(visualizerThreadStack, sizeof(visualizerThreadStack), +                  VISUALIZER_THREAD_PRIORITY, visualizerThread, NULL); +} + +void update_status(bool changed) { +    if (changed) { +        GSourceListener* listener = geventGetSourceListener((GSourceHandle)¤t_status, NULL); +        if (listener) { +            geventSendEvent(listener); +        } +    } +#ifdef SERIAL_LINK_ENABLE +    static systime_t last_update = 0; +    systime_t current_update = chVTGetSystemTimeX(); +    systime_t delta = current_update - last_update; +    if (changed || delta > MS2ST(10)) { +        last_update = current_update; +        visualizer_keyboard_status_t* r = begin_write_current_status(); +        *r = current_status; +        end_write_current_status(); +    } +#endif +} + +uint8_t visualizer_get_mods() { +  uint8_t mods = get_mods(); + +#ifndef NO_ACTION_ONESHOT +  if (!has_oneshot_mods_timed_out()) { +    mods |= get_oneshot_mods(); +  } +#endif   +  return mods; +} + +#ifdef VISUALIZER_USER_DATA_SIZE +void visualizer_set_user_data(void* u) { +    memcpy(user_data, u, VISUALIZER_USER_DATA_SIZE); +} +#endif + +void visualizer_update(uint32_t default_state, uint32_t state, uint8_t mods, uint32_t leds) { +    // Note that there's a small race condition here, the thread could read +    // a state where one of these are set but not the other. But this should +    // not really matter as it will be fixed during the next loop step. +    // Alternatively a mutex could be used instead of the volatile variables + +    bool changed = false; +#ifdef SERIAL_LINK_ENABLE +    if (is_serial_link_connected ()) { +        visualizer_keyboard_status_t* new_status = read_current_status(); +        if (new_status) { +            if (!same_status(¤t_status, new_status)) { +                changed = true; +                current_status = *new_status; +            } +        } +    } +    else { +#else +   { +#endif +        visualizer_keyboard_status_t new_status = { +            .layer = state, +            .default_layer = default_state, +            .mods = mods, +            .leds = leds, +#ifdef BACKLIGHT_ENABLE +            .backlight_level = current_status.backlight_level, +#endif +            .suspended = current_status.suspended, +        }; +#ifdef VISUALIZER_USER_DATA_SIZE +       memcpy(new_status.user_data, user_data, VISUALIZER_USER_DATA_SIZE); +#endif +        if (!same_status(¤t_status, &new_status)) { +            changed = true; +            current_status = new_status; +        } +    } +    update_status(changed); +} + +void visualizer_suspend(void) { +    current_status.suspended = true; +    update_status(true); +} + +void visualizer_resume(void) { +    current_status.suspended = false; +    update_status(true); +} + +#ifdef BACKLIGHT_ENABLE +void backlight_set(uint8_t level) { +    current_status.backlight_level = level; +    update_status(true); +} +#endif diff --git a/quantum/visualizer/visualizer.h b/quantum/visualizer/visualizer.h new file mode 100644 index 0000000000..90ecdcbaea --- /dev/null +++ b/quantum/visualizer/visualizer.h @@ -0,0 +1,155 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Fred Sundvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef VISUALIZER_H +#define VISUALIZER_H +#include <stdlib.h> +#include <stdint.h> +#include <stdbool.h> + +#include "config.h" +#include "gfx.h" + +#ifdef LCD_BACKLIGHT_ENABLE +#include "lcd_backlight.h" +#endif + +#ifdef BACKLIGHT_ENABLE +#include "backlight.h" +#endif + +// use this function to merge both real_mods and oneshot_mods in a uint16_t +uint8_t visualizer_get_mods(void); + +// This need to be called once at the start +void visualizer_init(void); +// This should be called at every matrix scan +void visualizer_update(uint32_t default_state, uint32_t state, uint8_t mods, uint32_t leds); + +// This should be called when the keyboard goes to suspend state +void visualizer_suspend(void); +// This should be called when the keyboard wakes up from suspend state +void visualizer_resume(void); + +// These functions are week, so they can be overridden by the keyboard +// if needed +GDisplay* get_lcd_display(void); +GDisplay* get_led_display(void); + +// For emulator builds, this function need to be implemented +#ifdef EMULATOR +void draw_emulator(void); +#endif + +// If you need support for more than 16 keyframes per animation, you can change this +#define MAX_VISUALIZER_KEY_FRAMES 16 + +struct keyframe_animation_t; + +typedef struct { +    uint32_t layer; +    uint32_t default_layer; +    uint32_t leds; // See led.h for available statuses +    uint8_t mods; +    bool suspended; +#ifdef BACKLIGHT_ENABLE +    uint8_t backlight_level; +#endif +#ifdef VISUALIZER_USER_DATA_SIZE +    uint8_t user_data[VISUALIZER_USER_DATA_SIZE]; +#endif +} visualizer_keyboard_status_t; + +// The state struct is used by the various keyframe functions +// It's also used for setting the LCD color and layer text +// from the user customized code +typedef struct visualizer_state_t { +    // The user code should primarily be modifying these +    uint32_t target_lcd_color; +    const char* layer_text; + +    // The user visualizer(and animation functions) can read these +    visualizer_keyboard_status_t status; + +    // These are used by the animation functions +    uint32_t current_lcd_color; +    uint32_t prev_lcd_color; +#ifdef LCD_ENABLE +    font_t font_fixed5x8; +    font_t font_dejavusansbold12; +#endif +} visualizer_state_t; + +// Any custom keyframe function should have this signature +// return true to get continuous updates, otherwise you will only get one +// update per frame +typedef bool (*frame_func)(struct keyframe_animation_t*, visualizer_state_t*); + +// Represents a keyframe animation, so fields are internal to the system +// while others are meant to be initialized by the user code +typedef struct keyframe_animation_t { +    // These should be initialized +    int num_frames; +    bool loop; +    int frame_lengths[MAX_VISUALIZER_KEY_FRAMES]; +    frame_func frame_functions[MAX_VISUALIZER_KEY_FRAMES]; + +    // Used internally by the system, and can also be read by +    // keyframe update functions +    int current_frame; +    int time_left_in_frame; +    bool first_update_of_frame; +    bool last_update_of_frame; +    bool need_update; + +} keyframe_animation_t; + +extern GDisplay* LCD_DISPLAY; +extern GDisplay* LED_DISPLAY; + +void start_keyframe_animation(keyframe_animation_t* animation); +void stop_keyframe_animation(keyframe_animation_t* animation); +// This runs the next keyframe, but does not update the animation state +// Useful for crossfades for example +void run_next_keyframe(keyframe_animation_t* animation, visualizer_state_t* state); + +// The master can set userdata which will be transferred to the slave +#ifdef VISUALIZER_USER_DATA_SIZE +void visualizer_set_user_data(void* user_data); +#endif + +// These functions have to be implemented by the user +// Called regularly each time the state has changed (but not every scan loop) +void update_user_visualizer_state(visualizer_state_t* state, visualizer_keyboard_status_t* prev_status); +// Called when the computer goes to suspend, will also stop calling update_user_visualizer_state +void user_visualizer_suspend(visualizer_state_t* state); +// You have to start at least one animation as a response to the following two functions +// When the animation has finished the visualizer will resume normal operation and start calling the +// update_user_visualizer_state again +// Called when the keyboard boots up +void initialize_user_visualizer(visualizer_state_t* state); +// Called when the computer resumes from a suspend +void user_visualizer_resume(visualizer_state_t* state); + +#endif /* VISUALIZER_H */ diff --git a/quantum/visualizer/visualizer.mk b/quantum/visualizer/visualizer.mk new file mode 100644 index 0000000000..0f7d8636cf --- /dev/null +++ b/quantum/visualizer/visualizer.mk @@ -0,0 +1,73 @@ +# The MIT License (MIT) +#  +# Copyright (c) 2016 Fred Sundvik +#  +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +#  +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +#  +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +SRC += $(VISUALIZER_DIR)/visualizer.c \ +	$(VISUALIZER_DIR)/visualizer_keyframes.c +EXTRAINCDIRS += $(GFXINC) $(VISUALIZER_DIR) +GFXLIB = $(LIB_PATH)/ugfx +VPATH += $(VISUALIZER_PATH) + +OPT_DEFS += -DVISUALIZER_ENABLE + +ifdef LCD_ENABLE +OPT_DEFS += -DLCD_ENABLE +ULIBS += -lm +endif + +ifeq ($(strip $(LCD_ENABLE)), yes) +SRC += $(VISUALIZER_DIR)/lcd_backlight.c +SRC += $(VISUALIZER_DIR)/lcd_keyframes.c +SRC += $(VISUALIZER_DIR)/lcd_backlight_keyframes.c +# Note, that the linker will strip out any resources that are not actually in use +SRC += $(VISUALIZER_DIR)/resources/lcd_logo.c +OPT_DEFS += -DLCD_BACKLIGHT_ENABLE +endif + +ifeq ($(strip $(BACKLIGHT_ENABLE)), yes) +SRC += $(VISUALIZER_DIR)/led_keyframes.c +endif + +include $(GFXLIB)/gfx.mk +GFXSRC := $(patsubst $(TOP_DIR)/%,%,$(GFXSRC)) +GFXDEFS := $(patsubst %,-D%,$(patsubst -D%,%,$(GFXDEFS))) + +ifneq ("$(wildcard $(KEYMAP_PATH)/visualizer.c)","") +    SRC += keyboards/$(KEYBOARD)/keymaps/$(KEYMAP)/visualizer.c +else  +    ifeq ("$(wildcard $(SUBPROJECT_PATH)/keymaps/$(KEYMAP)/visualizer.c)","") +        ifeq ("$(wildcard $(SUBPROJECT_PATH)/visualizer.c)","") +            ifeq ("$(wildcard $(KEYBOARD_PATH)/visualizer.c)","") +$(error "visualizer.c" not found") +            else +               SRC += keyboards/$(KEYBOARD)/visualizer.c +            endif +        else +            SRC += keyboards/$(KEYBOARD)/$(SUBPROJECT)/visualizer.c +        endif +    else +        SRC += keyboards/$(KEYBOARD)/$(SUBPROJECT)/keymaps/$(KEYMAP)/visualizer.c +    endif +endif + +ifdef EMULATOR +UINCDIR += $(TMK_DIR)/common +endif diff --git a/quantum/visualizer/visualizer_keyframes.c b/quantum/visualizer/visualizer_keyframes.c new file mode 100644 index 0000000000..8f6a7e15a4 --- /dev/null +++ b/quantum/visualizer/visualizer_keyframes.c @@ -0,0 +1,23 @@ +/* Copyright 2017 Fred Sundvik + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include "visualizer_keyframes.h" + +bool keyframe_no_operation(keyframe_animation_t* animation, visualizer_state_t* state) { +    (void)animation; +    (void)state; +    return false; +} diff --git a/quantum/visualizer/visualizer_keyframes.h b/quantum/visualizer/visualizer_keyframes.h new file mode 100644 index 0000000000..9ef7653c5e --- /dev/null +++ b/quantum/visualizer/visualizer_keyframes.h @@ -0,0 +1,26 @@ +/* Copyright 2017 Fred Sundvik + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef QUANTUM_VISUALIZER_VISUALIZER_KEYFRAMES_H_ +#define QUANTUM_VISUALIZER_VISUALIZER_KEYFRAMES_H_ + +#include "visualizer.h" + +// Some predefined keyframe functions that can be used by the user code +// Does nothing, useful for adding delays +bool keyframe_no_operation(keyframe_animation_t* animation, visualizer_state_t* state); + +#endif /* QUANTUM_VISUALIZER_VISUALIZER_KEYFRAMES_H_ */  | 
