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 000000000..1ec38df75 --- /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 000000000..8d93de7dc --- /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 000000000..52dfe23e1 --- /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 000000000..efc0ddca1 --- /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 000000000..6a2ee9012 --- /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 000000000..a23f00f57 --- /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 000000000..c924f2bd5 --- /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 000000000..27fdc2ab6 --- /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 000000000..ded86edee --- /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 000000000..57f2d5924 --- /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 000000000..155e34e88 --- /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 000000000..a3aaa2f19 --- /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 000000000..db2d1a94c --- /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 000000000..94147ccb6 --- /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 000000000..9403a6b5e --- /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 000000000..f15615dd1 --- /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 000000000..c88e02d91 --- /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 000000000..045ee95b5 --- /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 000000000..c3341ca33 --- /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 000000000..1a8e188dd --- /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 000000000..eb39c8fe0 --- /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 000000000..022f4bd19 --- /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 000000000..5d64be19c --- /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 000000000..b1460c53c --- /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 000000000..013559e96 --- /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 000000000..b001139dd --- /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 000000000..1d45bee32 --- /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 000000000..2d3f9c06a --- /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 000000000..b1d5604ba --- /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 000000000..50e2d1f46 --- /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 000000000..c0ca832a6 --- /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 000000000..3308dc5f7 --- /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 000000000..ecade3fe9 --- /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 000000000..e007c26ef --- /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 000000000..67350d660 --- /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 000000000..798bb7579 --- /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 000000000..fb74bce8d --- /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 000000000..174f4a6ee --- /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 000000000..6b34db558 --- /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 000000000..b7128973a --- /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 000000000..de6d8c53f --- /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 000000000..224db7be1 --- /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 000000000..dcfad720d --- /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 000000000..9d02efe04 --- /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 000000000..fa9ace929 --- /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 000000000..f5c5c818b --- /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 000000000..c5a38c6a5 --- /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 000000000..b99f26209 --- /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 000000000..af49bf332 --- /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 000000000..2506e3d8e --- /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 000000000..60924a0fb --- /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 000000000..5337e2626 --- /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 000000000..d77977ebe --- /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 000000000..0b6380ed3 --- /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 000000000..7ac15b733 --- /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 000000000..6c6ebe300 --- /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 000000000..8c0f4862a --- /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 000000000..58d45add2 --- /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 000000000..a5dbd788a --- /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 000000000..473906d65 --- /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 000000000..da7a3d2ef --- /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 000000000..9184feaae --- /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 000000000..ccac8981a --- /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 000000000..217dca280 --- /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 000000000..8dfbf041f --- /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 000000000..613af7018 --- /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 000000000..71d3a4b56 --- /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 000000000..3a00f169d --- /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 000000000..4fd45810b --- /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 000000000..f42c154a0 --- /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 000000000..86c0937f5 --- /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 000000000..3f736a709 --- /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 000000000..fd008eca1 --- /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 000000000..c525b74f0 --- /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 000000000..84b5d673d --- /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 000000000..f5be1da5c --- /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 000000000..75f35112b --- /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 000000000..929c88c0b --- /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 000000000..36e586d31 --- /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 000000000..e00fe2346 --- /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 000000000..6038e31c4 --- /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 000000000..4eec2a776 --- /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 000000000..92130192c --- /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 000000000..d13cc4b26 --- /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 000000000..e8490e290 --- /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 000000000..2c87d64c2 --- /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 000000000..97e896856 --- /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 000000000..04b8c2e75 --- /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 000000000..712250ff3 --- /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 000000000..474f80ee8 --- /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 000000000..4a910d510 --- /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 000000000..425e06cdd --- /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 000000000..ff795fe20 --- /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 000000000..2c5d890b2 --- /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 000000000..e3e8989d3 --- /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 000000000..2e57db3f5 --- /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 000000000..b3bee62a1 --- /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 000000000..351e03877 --- /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 000000000..1b072c6f1 --- /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 000000000..ff49d727b --- /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 000000000..2bd5bf830 --- /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 000000000..9223af83b --- /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 000000000..b81515bc5 --- /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 000000000..a80e88884 --- /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 000000000..21b7b165f --- /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 000000000..7724bbee9 --- /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 000000000..840dc9a28 --- /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 000000000..dbca45765 --- /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 000000000..b8879076b --- /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 000000000..f52a97bbc --- /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 000000000..a123cd7ba --- /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 000000000..21aa663d5 --- /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 000000000..b16f4cd76 --- /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 000000000..a3571e8de --- /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 000000000..97f788654 --- /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 000000000..7e2b14f3c --- /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 000000000..a8a75389f --- /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 000000000..5f355256d --- /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 000000000..713747cfc --- /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 000000000..dacc13858 --- /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 000000000..22d4c3f08 --- /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 000000000..6cd996f75 --- /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 000000000..95d7a07b4 --- /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 000000000..8436d4e3d --- /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 000000000..e1c125cf9 --- /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 000000000..82e4184d2 --- /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 000000000..2e912b4c7 --- /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 000000000..7e6e5d1ab --- /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 000000000..a59a4f37d --- /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 000000000..545ba2270 --- /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 000000000..d1a0ffa7f --- /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 000000000..6cf26fc67 --- /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 000000000..1ea27a536 --- /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 000000000..cc99d1e3b --- /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 000000000..90ecdcbae --- /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 000000000..0f7d8636c --- /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 000000000..8f6a7e15a --- /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 000000000..9ef7653c5 --- /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_ */ |