Merge remote-tracking branch 'qmk/master' into vial

This commit is contained in:
Ilya Zhuravlev
2021-03-03 11:12:07 -05:00
8590 changed files with 251950 additions and 200108 deletions
+1 -4
View File
@@ -14,8 +14,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _API_H_
#define _API_H_
#pragma once
#ifdef __AVR__
# include "lufa.h"
@@ -54,5 +53,3 @@ __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
+1 -4
View File
@@ -14,13 +14,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _API_SYSEX_H_
#define _API_SYSEX_H_
#pragma once
#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
+539
View File
@@ -0,0 +1,539 @@
/* Copyright 2016-2020 Jack Humbert
* Copyright 2020 JohSchneider
* 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 "audio.h"
#include "eeconfig.h"
#include "timer.h"
#include "wait.h"
/* audio system:
*
* audio.[ch] takes care of all overall state, tracking the actively playing
* notes/tones; the notes a SONG consists of;
* ...
* = everything audio-related that is platform agnostic
*
* driver_[avr|chibios]_[dac|pwm] take care of the lower hardware dependent parts,
* specific to each platform and the used subsystem/driver to drive
* the output pins/channels with the calculated frequencies for each
* active tone
* as part of this, the driver has to trigger regular state updates by
* calling 'audio_update_state' through some sort of timer - be it a
* dedicated one or piggybacking on for example the timer used to
* generate a pwm signal/clock.
*
*
* A Note on terminology:
* tone, pitch and frequency are used somewhat interchangeably, in a strict Wikipedia-sense:
* "(Musical) tone, a sound characterized by its duration, pitch (=frequency),
* intensity (=volume), and timbre"
* - intensity/volume is currently not handled at all, although the 'dac_additive' driver could do so
* - timbre is handled globally (TODO: only used with the pwm drivers at the moment)
*
* in musical_note.h a 'note' is the combination of a pitch and a duration
* these are used to create SONG arrays; during playback their frequencies
* are handled as single successive tones, while the durations are
* kept track of in 'audio_update_state'
*
* 'voice' as it is used here, equates to a sort of instrument with its own
* characteristics sound and effects
* the audio system as-is deals only with (possibly multiple) tones of one
* instrument/voice at a time (think: chords). since the number of tones that
* can be reproduced depends on the hardware/driver in use: pwm can only
* reproduce one tone per output/speaker; DACs can reproduce/mix multiple
* when doing additive synthesis.
*
* 'duration' can either be in the beats-per-minute related unit found in
* musical_notes.h, OR in ms; keyboards create SONGs with the former, while
* the internal state of the audio system does its calculations with the later - ms
*/
#ifndef AUDIO_TONE_STACKSIZE
# define AUDIO_TONE_STACKSIZE 8
#endif
uint8_t active_tones = 0; // number of tones pushed onto the stack by audio_play_tone - might be more than the hardware is able to reproduce at any single time
musical_tone_t tones[AUDIO_TONE_STACKSIZE]; // stack of currently active tones
bool playing_melody = false; // playing a SONG?
bool playing_note = false; // or (possibly multiple simultaneous) tones
bool state_changed = false; // global flag, which is set if anything changes with the active_tones
// melody/SONG related state variables
float (*notes_pointer)[][2]; // SONG, an array of MUSICAL_NOTEs
uint16_t notes_count; // length of the notes_pointer array
bool notes_repeat; // PLAY_SONG or PLAY_LOOP?
uint16_t melody_current_note_duration = 0; // duration of the currently playing note from the active melody, in ms
uint8_t note_tempo = TEMPO_DEFAULT; // beats-per-minute
uint16_t current_note = 0; // index into the array at notes_pointer
bool note_resting = false; // if a short pause was introduced between two notes with the same frequency while playing a melody
uint16_t last_timestamp = 0;
#ifdef AUDIO_ENABLE_TONE_MULTIPLEXING
# ifndef AUDIO_MAX_SIMULTANEOUS_TONES
# define AUDIO_MAX_SIMULTANEOUS_TONES 3
# endif
uint16_t tone_multiplexing_rate = AUDIO_TONE_MULTIPLEXING_RATE_DEFAULT;
uint8_t tone_multiplexing_index_shift = 0; // offset used on active-tone array access
#endif
// provided and used by voices.c
extern uint8_t note_timbre;
extern bool glissando;
extern bool vibrato;
extern uint16_t voices_timer;
#ifndef STARTUP_SONG
# define STARTUP_SONG SONG(STARTUP_SOUND)
#endif
#ifndef AUDIO_ON_SONG
# define AUDIO_ON_SONG SONG(AUDIO_ON_SOUND)
#endif
#ifndef AUDIO_OFF_SONG
# define AUDIO_OFF_SONG SONG(AUDIO_OFF_SOUND)
#endif
float startup_song[][2] = STARTUP_SONG;
float audio_on_song[][2] = AUDIO_ON_SONG;
float audio_off_song[][2] = AUDIO_OFF_SONG;
static bool audio_initialized = false;
static bool audio_driver_stopped = true;
audio_config_t audio_config;
void audio_init() {
if (audio_initialized) {
return;
}
// Check EEPROM
#ifdef EEPROM_ENABLE
if (!eeconfig_is_enabled()) {
eeconfig_init();
}
audio_config.raw = eeconfig_read_audio();
#else // EEPROM settings
audio_config.enable = true;
# ifdef AUDIO_CLICKY_ON
audio_config.clicky_enable = true;
# endif
#endif // EEPROM settings
for (uint8_t i = 0; i < AUDIO_TONE_STACKSIZE; i++) {
tones[i] = (musical_tone_t){.time_started = 0, .pitch = -1.0f, .duration = 0};
}
if (!audio_initialized) {
audio_driver_initialize();
audio_initialized = true;
}
stop_all_notes();
}
void audio_startup(void) {
if (audio_config.enable) {
PLAY_SONG(startup_song);
}
last_timestamp = timer_read();
}
void audio_toggle(void) {
if (audio_config.enable) {
stop_all_notes();
}
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();
PLAY_SONG(audio_on_song);
}
void audio_off(void) {
PLAY_SONG(audio_off_song);
wait_ms(100);
audio_stop_all();
audio_config.enable = 0;
eeconfig_update_audio(audio_config.raw);
}
bool audio_is_on(void) { return (audio_config.enable != 0); }
void audio_stop_all() {
if (audio_driver_stopped) {
return;
}
active_tones = 0;
audio_driver_stop();
playing_melody = false;
playing_note = false;
melody_current_note_duration = 0;
for (uint8_t i = 0; i < AUDIO_TONE_STACKSIZE; i++) {
tones[i] = (musical_tone_t){.time_started = 0, .pitch = -1.0f, .duration = 0};
}
audio_driver_stopped = true;
}
void audio_stop_tone(float pitch) {
if (pitch < 0.0f) {
pitch = -1 * pitch;
}
if (playing_note) {
if (!audio_initialized) {
audio_init();
}
bool found = false;
for (int i = AUDIO_TONE_STACKSIZE - 1; i >= 0; i--) {
found = (tones[i].pitch == pitch);
if (found) {
tones[i] = (musical_tone_t){.time_started = 0, .pitch = -1.0f, .duration = 0};
for (int j = i; (j < AUDIO_TONE_STACKSIZE - 1); j++) {
tones[j] = tones[j + 1];
tones[j + 1] = (musical_tone_t){.time_started = 0, .pitch = -1.0f, .duration = 0};
}
break;
}
}
if (!found) {
return;
}
state_changed = true;
active_tones--;
if (active_tones < 0) active_tones = 0;
#ifdef AUDIO_ENABLE_TONE_MULTIPLEXING
if (tone_multiplexing_index_shift >= active_tones) {
tone_multiplexing_index_shift = 0;
}
#endif
if (active_tones == 0) {
audio_driver_stop();
audio_driver_stopped = true;
playing_note = false;
}
}
}
void audio_play_note(float pitch, uint16_t duration) {
if (!audio_config.enable) {
return;
}
if (!audio_initialized) {
audio_init();
}
if (pitch < 0.0f) {
pitch = -1 * pitch;
}
// round-robin: shifting out old tones, keeping only unique ones
// if the new frequency is already amongst the active tones, shift it to the top of the stack
bool found = false;
for (int i = active_tones - 1; i >= 0; i--) {
found = (tones[i].pitch == pitch);
if (found) {
for (int j = i; (j < active_tones - 1); j++) {
tones[j] = tones[j + 1];
tones[j + 1] = (musical_tone_t){.time_started = timer_read(), .pitch = pitch, .duration = duration};
}
return; // since this frequency played already, the hardware was already started
}
}
// frequency/tone is actually new, so we put it on the top of the stack
active_tones++;
if (active_tones > AUDIO_TONE_STACKSIZE) {
active_tones = AUDIO_TONE_STACKSIZE;
// shift out the oldest tone to make room
for (int i = 0; i < active_tones - 1; i++) {
tones[i] = tones[i + 1];
}
}
state_changed = true;
playing_note = true;
tones[active_tones - 1] = (musical_tone_t){.time_started = timer_read(), .pitch = pitch, .duration = duration};
// TODO: needs to be handled per note/tone -> use its timestamp instead?
voices_timer = timer_read(); // reset to zero, for the effects added by voices.c
if (audio_driver_stopped) {
audio_driver_start();
audio_driver_stopped = false;
}
}
void audio_play_tone(float pitch) { audio_play_note(pitch, 0xffff); }
void audio_play_melody(float (*np)[][2], uint16_t n_count, bool n_repeat) {
if (!audio_config.enable) {
audio_stop_all();
return;
}
if (!audio_initialized) {
audio_init();
}
// Cancel note if a note is playing
if (playing_note) audio_stop_all();
playing_melody = true;
note_resting = false;
notes_pointer = np;
notes_count = n_count;
notes_repeat = n_repeat;
current_note = 0; // note in the melody-array/list at note_pointer
// start first note manually, which also starts the audio_driver
// all following/remaining notes are played by 'audio_update_state'
audio_play_note((*notes_pointer)[current_note][0], audio_duration_to_ms((*notes_pointer)[current_note][1]));
last_timestamp = timer_read();
melody_current_note_duration = audio_duration_to_ms((*notes_pointer)[current_note][1]);
}
float click[2][2];
void audio_play_click(uint16_t delay, float pitch, uint16_t duration) {
uint16_t duration_tone = audio_ms_to_duration(duration);
uint16_t duration_delay = audio_ms_to_duration(delay);
if (delay <= 0.0f) {
click[0][0] = pitch;
click[0][1] = duration_tone;
click[1][0] = 0.0f;
click[1][1] = 0.0f;
audio_play_melody(&click, 1, false);
} else {
// first note is a rest/pause
click[0][0] = 0.0f;
click[0][1] = duration_delay;
// second note is the actual click
click[1][0] = pitch;
click[1][1] = duration_tone;
audio_play_melody(&click, 2, false);
}
}
bool audio_is_playing_note(void) { return playing_note; }
bool audio_is_playing_melody(void) { return playing_melody; }
uint8_t audio_get_number_of_active_tones(void) { return active_tones; }
float audio_get_frequency(uint8_t tone_index) {
if (tone_index >= active_tones) {
return 0.0f;
}
return tones[active_tones - tone_index - 1].pitch;
}
float audio_get_processed_frequency(uint8_t tone_index) {
if (tone_index >= active_tones) {
return 0.0f;
}
int8_t index = active_tones - tone_index - 1;
// new tones are stacked on top (= appended at the end), so the most recent/current is MAX-1
#ifdef AUDIO_ENABLE_TONE_MULTIPLEXING
index = index - tone_multiplexing_index_shift;
if (index < 0) // wrap around
index += active_tones;
#endif
if (tones[index].pitch <= 0.0f) {
return 0.0f;
}
return voice_envelope(tones[index].pitch);
}
bool audio_update_state(void) {
if (!playing_note && !playing_melody) {
return false;
}
bool goto_next_note = false;
uint16_t current_time = timer_read();
if (playing_melody) {
goto_next_note = timer_elapsed(last_timestamp) >= melody_current_note_duration;
if (goto_next_note) {
uint16_t delta = timer_elapsed(last_timestamp) - melody_current_note_duration;
last_timestamp = current_time;
uint16_t previous_note = current_note;
current_note++;
voices_timer = timer_read(); // reset to zero, for the effects added by voices.c
if (current_note >= notes_count) {
if (notes_repeat) {
current_note = 0;
} else {
audio_stop_all();
return false;
}
}
if (!note_resting && (*notes_pointer)[previous_note][0] == (*notes_pointer)[current_note][0]) {
note_resting = true;
// special handling for successive notes of the same frequency:
// insert a short pause to separate them audibly
audio_play_note(0.0f, audio_duration_to_ms(2));
current_note = previous_note;
melody_current_note_duration = audio_duration_to_ms(2);
} else {
note_resting = false;
// TODO: handle glissando here (or remember previous and current tone)
/* there would need to be a freq(here we are) -> freq(next note)
* and do slide/glissando in between problem here is to know which
* frequency on the stack relates to what other? e.g. a melody starts
* tones in a sequence, and stops expiring one, so the most recently
* stopped is the starting point for a glissando to the most recently started?
* how to detect and preserve this relation?
* and what about user input, chords, ...?
*/
// '- delta': Skip forward in the next note's length if we've over shot
// the last, so the overall length of the song is the same
uint16_t duration = audio_duration_to_ms((*notes_pointer)[current_note][1]);
// Skip forward past any completely missed notes
while (delta > duration && current_note < notes_count - 1) {
delta -= duration;
current_note++;
duration = audio_duration_to_ms((*notes_pointer)[current_note][1]);
}
if (delta < duration) {
duration -= delta;
} else {
// Only way to get here is if it is the last note and
// we have completely missed it. Play it for 1ms...
duration = 1;
}
audio_play_note((*notes_pointer)[current_note][0], duration);
melody_current_note_duration = duration;
}
}
}
if (playing_note) {
#ifdef AUDIO_ENABLE_TONE_MULTIPLEXING
tone_multiplexing_index_shift = (int)(current_time / tone_multiplexing_rate) % MIN(AUDIO_MAX_SIMULTANEOUS_TONES, active_tones);
goto_next_note = true;
#endif
if (vibrato || glissando) {
// force update on each cycle, since vibrato shifts the frequency slightly
goto_next_note = true;
}
// housekeeping: stop notes that have no playtime left
for (int i = 0; i < active_tones; i++) {
if ((tones[i].duration != 0xffff) // indefinitely playing notes, started by 'audio_play_tone'
&& (tones[i].duration != 0) // 'uninitialized'
) {
if (timer_elapsed(tones[i].time_started) >= tones[i].duration) {
audio_stop_tone(tones[i].pitch); // also sets 'state_changed=true'
}
}
}
}
// state-changes have a higher priority, always triggering the hardware to update
if (state_changed) {
state_changed = false;
return true;
}
return goto_next_note;
}
// Tone-multiplexing functions
#ifdef AUDIO_ENABLE_TONE_MULTIPLEXING
void audio_set_tone_multiplexing_rate(uint16_t rate) { tone_multiplexing_rate = rate; }
void audio_enable_tone_multiplexing(void) { tone_multiplexing_rate = AUDIO_TONE_MULTIPLEXING_RATE_DEFAULT; }
void audio_disable_tone_multiplexing(void) { tone_multiplexing_rate = 0; }
void audio_increase_tone_multiplexing_rate(uint16_t change) {
if ((0xffff - change) > tone_multiplexing_rate) {
tone_multiplexing_rate += change;
}
}
void audio_decrease_tone_multiplexing_rate(uint16_t change) {
if (change <= tone_multiplexing_rate) {
tone_multiplexing_rate -= change;
}
}
#endif
// Tempo functions
void audio_set_tempo(uint8_t tempo) {
if (tempo < 10) note_tempo = 10;
// else if (tempo > 250)
// note_tempo = 250;
else
note_tempo = tempo;
}
void audio_increase_tempo(uint8_t tempo_change) {
if (tempo_change > 255 - note_tempo)
note_tempo = 255;
else
note_tempo += tempo_change;
}
void audio_decrease_tempo(uint8_t tempo_change) {
if (tempo_change >= note_tempo - 10)
note_tempo = 10;
else
note_tempo -= tempo_change;
}
// TODO in the int-math version are some bugs; songs sometimes abruptly end - maybe an issue with the timer/system-tick wrapping around?
uint16_t audio_duration_to_ms(uint16_t duration_bpm) {
#if defined(__AVR__)
// doing int-math saves us some bytes in the overall firmware size, but the intermediate result is less accurate before being cast to/returned as uint
return ((uint32_t)duration_bpm * 60 * 1000) / (64 * note_tempo);
// NOTE: beware of uint16_t overflows when note_tempo is low and/or the duration is long
#else
return ((float)duration_bpm * 60) / (64 * note_tempo) * 1000;
#endif
}
uint16_t audio_ms_to_duration(uint16_t duration_ms) {
#if defined(__AVR__)
return ((uint32_t)duration_ms * 64 * note_tempo) / 60 / 1000;
#else
return ((float)duration_ms * 64 * note_tempo) / 60 / 1000;
#endif
}
+237 -59
View File
@@ -1,4 +1,5 @@
/* Copyright 2016 Jack Humbert
/* Copyright 2016-2020 Jack Humbert
* Copyright 2020 JohSchneider
*
* 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
@@ -13,28 +14,30 @@
* 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
#pragma once
#include <stdint.h>
#include <stdbool.h>
#if defined(__AVR__)
# include <avr/io.h>
#endif
#include "wait.h"
#include "musical_notes.h"
#include "song_list.h"
#include "voices.h"
#include "quantum.h"
#include <math.h>
// Largely untested PWM audio mode (doesn't sound as good)
// #define PWM_AUDIO
#if defined(__AVR__)
# include <avr/io.h>
# if defined(AUDIO_DRIVER_PWM)
# include "driver_avr_pwm.h"
# endif
#endif
// #define VIBRATO_ENABLE
// Enable vibrato strength/amplitude - slows down ISR too much
// #define VIBRATO_STRENGTH_ENABLE
#if defined(PROTOCOL_CHIBIOS)
# if defined(AUDIO_DRIVER_PWM)
# include "driver_chibios_pwm.h"
# elif defined(AUDIO_DRIVER_DAC)
# include "driver_chibios_dac.h"
# endif
#endif
typedef union {
uint8_t raw;
@@ -45,63 +48,238 @@ typedef union {
};
} 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
// AVR/LUFA has a MIN, arm/chibios does not
#ifndef MIN
# define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif
// Polyphony functions
/*
* a 'musical note' is represented by pitch and duration; a 'musical tone' adds intensity and timbre
* https://en.wikipedia.org/wiki/Musical_tone
* "A musical tone is characterized by its duration, pitch, intensity (or loudness), and timbre (or quality)"
*/
typedef struct {
uint16_t time_started; // timestamp the tone/note was started, system time runs with 1ms resolution -> 16bit timer overflows every ~64 seconds, long enough under normal circumstances; but might be too soon for long-duration notes when the note_tempo is set to a very low value
float pitch; // aka frequency, in Hz
uint16_t duration; // in ms, converted from the musical_notes.h unit which has 64parts to a beat, factoring in the current tempo in beats-per-minute
// float intensity; // aka volume [0,1] TODO: not used at the moment; pwm drivers can't handle it
// uint8_t timbre; // range: [0,100] TODO: this currently kept track of globally, should we do this per tone instead?
} musical_tone_t;
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);
// public interface
/**
* @brief one-time initialization called by quantum/quantum.c
* @details usually done lazy, when some tones are to be played
*
* @post audio system (and hardware) initialized and ready to play tones
*/
void audio_init(void);
void audio_startup(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);
/**
* @brief en-/disable audio output, save this choice to the eeprom
*/
void audio_toggle(void);
/**
* @brief enable audio output, save this choice to the eeprom
*/
void audio_on(void);
/**
* @brief disable audio output, save this choice to the eeprom
*/
void audio_off(void);
/**
* @brief query the if audio output is enabled
*/
bool audio_is_on(void);
#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), }
/**
* @brief start playback of a tone with the given frequency and duration
*
* @details starts the playback of a given note, which is automatically stopped
* at the the end of its duration = fire&forget
*
* @param[in] pitch frequency of the tone be played
* @param[in] duration in milliseconds, use 'audio_duration_to_ms' to convert
* from the musical_notes.h unit to ms
*/
void audio_play_note(float pitch, uint16_t duration);
// TODO: audio_play_note(float pitch, uint16_t duration, float intensity, float timbre);
// audio_play_note_with_instrument ifdef AUDIO_ENABLE_VOICES
// These macros are used to allow play_notes to play an array of indeterminate
/**
* @brief start playback of a tone with the given frequency
*
* @details the 'frequency' is put on-top the internal stack of active tones,
* as a new tone with indefinite duration. this tone is played by
* the hardware until a call to 'audio_stop_tone'.
* should a tone with that frequency already be active, its entry
* is put on the top of said internal stack - so no duplicate
* entries are kept.
* 'hardware_start' is called upon the first note.
*
* @param[in] pitch frequency of the tone be played
*/
void audio_play_tone(float pitch);
/**
* @brief stop a given tone/frequency
*
* @details removes a tone matching the given frequency from the internal
* playback stack
* the hardware is stopped in case this was the last/only frequency
* being played.
*
* @param[in] pitch tone/frequency to be stopped
*/
void audio_stop_tone(float pitch);
/**
* @brief play a melody
*
* @details starts playback of a melody passed in from a SONG definition - an
* array of {pitch, duration} float-tuples
*
* @param[in] np note-pointer to the SONG array
* @param[in] n_count number of MUSICAL_NOTES of the SONG
* @param[in] n_repeat false for onetime, true for looped playback
*/
void audio_play_melody(float (*np)[][2], uint16_t n_count, bool n_repeat);
/**
* @brief play a short tone of a specific frequency to emulate a 'click'
*
* @details constructs a two-note melody (one pause plus a note) and plays it through
* audio_play_melody. very short durations might not quite work due to
* hardware limitations (DAC: added pulses from zero-crossing feature;...)
*
* @param[in] delay in milliseconds, length for the pause before the pulses, can be zero
* @param[in] pitch
* @param[in] duration in milliseconds, length of the 'click'
*/
void audio_play_click(uint16_t delay, float pitch, uint16_t duration);
/**
* @brief stops all playback
*
* @details stops playback of both a melody as well as single tones, resetting
* the internal state
*/
void audio_stop_all(void);
/**
* @brief query if one/multiple tones are playing
*/
bool audio_is_playing_note(void);
/**
* @brief query if a melody/SONG is playing
*/
bool audio_is_playing_melody(void);
// These macros are used to allow audio_play_melody 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_SONG(note_array) play_notes(&note_array, NOTE_ARRAY_SIZE((note_array)), false)
#define PLAY_LOOP(note_array) play_notes(&note_array, NOTE_ARRAY_SIZE((note_array)), true)
bool is_playing_notes(void);
/**
* @brief convenience macro, to play a melody/SONG once
*/
#define PLAY_SONG(note_array) audio_play_melody(&note_array, NOTE_ARRAY_SIZE((note_array)), false)
// TODO: a 'song' is a melody plus singing/vocals -> PLAY_MELODY
/**
* @brief convenience macro, to play a melody/SONG in a loop, until stopped by 'audio_stop_all'
*/
#define PLAY_LOOP(note_array) audio_play_melody(&note_array, NOTE_ARRAY_SIZE((note_array)), true)
// Tone-Multiplexing functions
// this feature only makes sense for hardware setups which can't do proper
// audio-wave synthesis = have no DAC and need to use PWM for tone generation
#ifdef AUDIO_ENABLE_TONE_MULTIPLEXING
# ifndef AUDIO_TONE_MULTIPLEXING_RATE_DEFAULT
# define AUDIO_TONE_MULTIPLEXING_RATE_DEFAULT 0
// 0=off, good starting value is 4; the lower the value the higher the cpu-load
# endif
void audio_set_tone_multiplexing_rate(uint16_t rate);
void audio_enable_tone_multiplexing(void);
void audio_disable_tone_multiplexing(void);
void audio_increase_tone_multiplexing_rate(uint16_t change);
void audio_decrease_tone_multiplexing_rate(uint16_t change);
#endif
// Tempo functions
void audio_set_tempo(uint8_t tempo);
void audio_increase_tempo(uint8_t tempo_change);
void audio_decrease_tempo(uint8_t tempo_change);
// conversion macros, from 64parts-to-a-beat to milliseconds and back
uint16_t audio_duration_to_ms(uint16_t duration_bpm);
uint16_t audio_ms_to_duration(uint16_t duration_ms);
void audio_startup(void);
// hardware interface
// implementation in the driver_avr/arm_* respective parts
void audio_driver_initialize(void);
void audio_driver_start(void);
void audio_driver_stop(void);
/**
* @brief get the number of currently active tones
* @return number, 0=none active
*/
uint8_t audio_get_number_of_active_tones(void);
/**
* @brief access to the raw/unprocessed frequency for a specific tone
* @details each active tone has a frequency associated with it, which
* the internal state keeps track of, and is usually influenced
* by various effects
* @param[in] tone_index, ranging from 0 to number_of_active_tones-1, with the
* first being the most recent and each increment yielding the next
* older one
* @return a positive frequency, in Hz; or zero if the tone is a pause
*/
float audio_get_frequency(uint8_t tone_index);
/**
* @brief calculate and return the frequency for the requested tone
* @details effects like glissando, vibrato, ... are post-processed onto the
* each active tones 'base'-frequency; this function returns the
* post-processed result.
* @param[in] tone_index, ranging from 0 to number_of_active_tones-1, with the
* first being the most recent and each increment yielding the next
* older one
* @return a positive frequency, in Hz; or zero if the tone is a pause
*/
float audio_get_processed_frequency(uint8_t tone_index);
/**
* @brief update audio internal state: currently playing and active tones,...
* @details This function is intended to be called by the audio-hardware
* specific implementation on a somewhat regular basis while a SONG
* or notes (pitch+duration) are playing to 'advance' the internal
* state (current playing notes, position in the melody, ...)
*
* @return true if something changed in the currently active tones, which the
* hardware might need to react to
*/
bool audio_update_state(void);
// legacy and back-warts compatibility stuff
#define is_audio_on() audio_is_on()
#define is_playing_notes() audio_is_playing_melody()
#define is_playing_note() audio_is_playing_note()
#define stop_all_notes() audio_stop_all()
#define stop_note(f) audio_stop_tone(f)
#define play_note(f, v) audio_play_tone(f)
#define set_timbre(t) voice_set_timbre(t)
#define set_tempo(t) audio_set_tempo(t)
#define increase_tempo(t) audio_increase_tempo(t)
#define decrease_tempo(t) audio_decrease_tempo(t)
// vibrato functions are not used in any keyboards
-810
View File
@@ -1,810 +0,0 @@
/* 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>
#if defined(__AVR__)
# include <avr/pgmspace.h>
# include <avr/interrupt.h>
# include <avr/io.h>
#endif
#include "print.h"
#include "audio.h"
#include "keymap.h"
#include "wait.h"
#include "eeconfig.h"
#define CPU_PRESCALER 8
// -----------------------------------------------------------------------------
// Timer Abstractions
// -----------------------------------------------------------------------------
// Currently we support timers 1 and 3 used at the sime time, channels A-C,
// pins PB5, PB6, PB7, PC4, PC5, and PC6
#if defined(C6_AUDIO)
# define CPIN_AUDIO
# define CPIN_SET_DIRECTION DDRC |= _BV(PORTC6);
# define INIT_AUDIO_COUNTER_3 TCCR3A = (0 << COM3A1) | (0 << COM3A0) | (1 << WGM31) | (0 << WGM30);
# define ENABLE_AUDIO_COUNTER_3_ISR TIMSK3 |= _BV(OCIE3A)
# define DISABLE_AUDIO_COUNTER_3_ISR TIMSK3 &= ~_BV(OCIE3A)
# define ENABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A |= _BV(COM3A1);
# define DISABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A &= ~(_BV(COM3A1) | _BV(COM3A0));
# define TIMER_3_PERIOD ICR3
# define TIMER_3_DUTY_CYCLE OCR3A
# define TIMER3_AUDIO_vect TIMER3_COMPA_vect
#endif
#if defined(C5_AUDIO)
# define CPIN_AUDIO
# define CPIN_SET_DIRECTION DDRC |= _BV(PORTC5);
# define INIT_AUDIO_COUNTER_3 TCCR3A = (0 << COM3B1) | (0 << COM3B0) | (1 << WGM31) | (0 << WGM30);
# define ENABLE_AUDIO_COUNTER_3_ISR TIMSK3 |= _BV(OCIE3B)
# define DISABLE_AUDIO_COUNTER_3_ISR TIMSK3 &= ~_BV(OCIE3B)
# define ENABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A |= _BV(COM3B1);
# define DISABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A &= ~(_BV(COM3B1) | _BV(COM3B0));
# define TIMER_3_PERIOD ICR3
# define TIMER_3_DUTY_CYCLE OCR3B
# define TIMER3_AUDIO_vect TIMER3_COMPB_vect
#endif
#if defined(C4_AUDIO)
# define CPIN_AUDIO
# define CPIN_SET_DIRECTION DDRC |= _BV(PORTC4);
# define INIT_AUDIO_COUNTER_3 TCCR3A = (0 << COM3C1) | (0 << COM3C0) | (1 << WGM31) | (0 << WGM30);
# define ENABLE_AUDIO_COUNTER_3_ISR TIMSK3 |= _BV(OCIE3C)
# define DISABLE_AUDIO_COUNTER_3_ISR TIMSK3 &= ~_BV(OCIE3C)
# define ENABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A |= _BV(COM3C1);
# define DISABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A &= ~(_BV(COM3C1) | _BV(COM3C0));
# define TIMER_3_PERIOD ICR3
# define TIMER_3_DUTY_CYCLE OCR3C
# define TIMER3_AUDIO_vect TIMER3_COMPC_vect
#endif
#if defined(B5_AUDIO)
# define BPIN_AUDIO
# define BPIN_SET_DIRECTION DDRB |= _BV(PORTB5);
# define INIT_AUDIO_COUNTER_1 TCCR1A = (0 << COM1A1) | (0 << COM1A0) | (1 << WGM11) | (0 << WGM10);
# define ENABLE_AUDIO_COUNTER_1_ISR TIMSK1 |= _BV(OCIE1A)
# define DISABLE_AUDIO_COUNTER_1_ISR TIMSK1 &= ~_BV(OCIE1A)
# define ENABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A |= _BV(COM1A1);
# define DISABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A &= ~(_BV(COM1A1) | _BV(COM1A0));
# define TIMER_1_PERIOD ICR1
# define TIMER_1_DUTY_CYCLE OCR1A
# define TIMER1_AUDIO_vect TIMER1_COMPA_vect
#endif
#if defined(B6_AUDIO)
# define BPIN_AUDIO
# define BPIN_SET_DIRECTION DDRB |= _BV(PORTB6);
# define INIT_AUDIO_COUNTER_1 TCCR1A = (0 << COM1B1) | (0 << COM1B0) | (1 << WGM11) | (0 << WGM10);
# define ENABLE_AUDIO_COUNTER_1_ISR TIMSK1 |= _BV(OCIE1B)
# define DISABLE_AUDIO_COUNTER_1_ISR TIMSK1 &= ~_BV(OCIE1B)
# define ENABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A |= _BV(COM1B1);
# define DISABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A &= ~(_BV(COM1B1) | _BV(COM1B0));
# define TIMER_1_PERIOD ICR1
# define TIMER_1_DUTY_CYCLE OCR1B
# define TIMER1_AUDIO_vect TIMER1_COMPB_vect
#endif
#if defined(B7_AUDIO)
# define BPIN_AUDIO
# define BPIN_SET_DIRECTION DDRB |= _BV(PORTB7);
# define INIT_AUDIO_COUNTER_1 TCCR1A = (0 << COM1C1) | (0 << COM1C0) | (1 << WGM11) | (0 << WGM10);
# define ENABLE_AUDIO_COUNTER_1_ISR TIMSK1 |= _BV(OCIE1C)
# define DISABLE_AUDIO_COUNTER_1_ISR TIMSK1 &= ~_BV(OCIE1C)
# define ENABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A |= _BV(COM1C1);
# define DISABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A &= ~(_BV(COM1C1) | _BV(COM1C0));
# define TIMER_1_PERIOD ICR1
# define TIMER_1_DUTY_CYCLE OCR1C
# define TIMER1_AUDIO_vect TIMER1_COMPC_vect
#endif
#if !defined(BPIN_AUDIO) && !defined(CPIN_AUDIO)
# error "Audio feature enabled, but no suitable pin selected - see docs/feature_audio.md under the AVR settings for available options."
#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;
bool note_resting = false;
uint16_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;
#ifndef STARTUP_SONG
# define STARTUP_SONG SONG(STARTUP_SOUND)
#endif
#ifndef AUDIO_ON_SONG
# define AUDIO_ON_SONG SONG(AUDIO_ON_SOUND)
#endif
#ifndef AUDIO_OFF_SONG
# define AUDIO_OFF_SONG SONG(AUDIO_OFF_SOUND)
#endif
float startup_song[][2] = STARTUP_SONG;
float audio_on_song[][2] = AUDIO_ON_SONG;
float audio_off_song[][2] = AUDIO_OFF_SONG;
void audio_init() {
// Check EEPROM
if (!eeconfig_is_enabled()) {
eeconfig_init();
}
audio_config.raw = eeconfig_read_audio();
if (!audio_initialized) {
// Set audio ports as output
#ifdef CPIN_AUDIO
CPIN_SET_DIRECTION
DISABLE_AUDIO_COUNTER_3_ISR;
#endif
#ifdef BPIN_AUDIO
BPIN_SET_DIRECTION
DISABLE_AUDIO_COUNTER_1_ISR;
#endif
// TCCR3A / TCCR3B: Timer/Counter #3 Control Registers TCCR3A/TCCR3B, TCCR1A/TCCR1B
// Compare Output Mode (COM3An and COM1An) = 0b00 = Normal port operation
// OC3A -- PC6
// OC3B -- PC5
// OC3C -- PC4
// OC1A -- PB5
// OC1B -- PB6
// OC1C -- PB7
// Waveform Generation Mode (WGM3n) = 0b1110 = Fast PWM Mode 14. Period = ICR3, Duty Cycle OCR3A)
// OCR3A - PC6
// OCR3B - PC5
// OCR3C - PC4
// OCR1A - PB5
// OCR1B - PB6
// OCR1C - PB7
// Clock Select (CS3n) = 0b010 = Clock / 8
#ifdef CPIN_AUDIO
INIT_AUDIO_COUNTER_3
TCCR3B = (1 << WGM33) | (1 << WGM32) | (0 << CS32) | (1 << CS31) | (0 << CS30);
TIMER_3_PERIOD = (uint16_t)(((float)F_CPU) / (440 * CPU_PRESCALER));
TIMER_3_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (440 * CPU_PRESCALER)) * note_timbre);
#endif
#ifdef BPIN_AUDIO
INIT_AUDIO_COUNTER_1
TCCR1B = (1 << WGM13) | (1 << WGM12) | (0 << CS12) | (1 << CS11) | (0 << CS10);
TIMER_1_PERIOD = (uint16_t)(((float)F_CPU) / (440 * CPU_PRESCALER));
TIMER_1_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (440 * CPU_PRESCALER)) * note_timbre);
#endif
audio_initialized = true;
}
if (audio_config.enable) {
PLAY_SONG(startup_song);
}
}
void stop_all_notes() {
dprintf("audio stop all notes");
if (!audio_initialized) {
audio_init();
}
voices = 0;
#ifdef CPIN_AUDIO
DISABLE_AUDIO_COUNTER_3_ISR;
DISABLE_AUDIO_COUNTER_3_OUTPUT;
#endif
#ifdef BPIN_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 CPIN_AUDIO
DISABLE_AUDIO_COUNTER_3_ISR;
DISABLE_AUDIO_COUNTER_3_OUTPUT;
#endif
#ifdef BPIN_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 CPIN_AUDIO
ISR(TIMER3_AUDIO_vect) {
float freq;
if (playing_note) {
if (voices > 0) {
# ifdef BPIN_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) {
if (!note_resting)
end_of_note = (note_position >= (note_length / TIMER_3_PERIOD * 0xFFFF - 1));
else
end_of_note = (note_position >= (note_length));
} else {
end_of_note = (note_position >= (note_length));
}
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) {
note_resting = true;
current_note--;
if ((*notes_pointer)[current_note][0] == (*notes_pointer)[current_note + 1][0]) {
note_frequency = 0;
note_length = 1;
} else {
note_frequency = (*notes_pointer)[current_note][0];
note_length = 1;
}
} 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 BPIN_AUDIO
ISR(TIMER1_AUDIO_vect) {
# if defined(BPIN_AUDIO) && !defined(CPIN_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) {
if (!note_resting)
end_of_note = (note_position >= (note_length / TIMER_1_PERIOD * 0xFFFF - 1));
else
end_of_note = (note_position >= (note_length));
} else {
end_of_note = (note_position >= (note_length));
}
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) {
note_resting = true;
current_note--;
if ((*notes_pointer)[current_note][0] == (*notes_pointer)[current_note + 1][0]) {
note_frequency = 0;
note_length = 1;
} else {
note_frequency = (*notes_pointer)[current_note][0];
note_length = 1;
}
} 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 CPIN_AUDIO
DISABLE_AUDIO_COUNTER_3_ISR;
#endif
#ifdef BPIN_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 CPIN_AUDIO
ENABLE_AUDIO_COUNTER_3_ISR;
ENABLE_AUDIO_COUNTER_3_OUTPUT;
#endif
#ifdef BPIN_AUDIO
# ifdef CPIN_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) {
if (!audio_initialized) {
audio_init();
}
if (audio_config.enable) {
#ifdef CPIN_AUDIO
DISABLE_AUDIO_COUNTER_3_ISR;
#endif
#ifdef BPIN_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;
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 CPIN_AUDIO
ENABLE_AUDIO_COUNTER_3_ISR;
ENABLE_AUDIO_COUNTER_3_OUTPUT;
#endif
#ifdef BPIN_AUDIO
# ifndef CPIN_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();
PLAY_SONG(audio_on_song);
}
void audio_off(void) {
PLAY_SONG(audio_off_song);
wait_ms(100);
stop_all_notes();
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;
}
}
-702
View File
@@ -1,702 +0,0 @@
/* 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 "audio.h"
#include "ch.h"
#include "hal.h"
#include <string.h>
#include "print.h"
#include "keymap.h"
#include "eeconfig.h"
// -----------------------------------------------------------------------------
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;
bool note_resting = false;
uint16_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;
#ifndef STARTUP_SONG
# define STARTUP_SONG SONG(STARTUP_SOUND)
#endif
float startup_song[][2] = STARTUP_SONG;
static void gpt_cb8(GPTDriver *gptp);
#define DAC_BUFFER_SIZE 100
#ifndef DAC_SAMPLE_MAX
# define DAC_SAMPLE_MAX 65535U
#endif
#define START_CHANNEL_1() \
gptStart(&GPTD6, &gpt6cfg1); \
gptStartContinuous(&GPTD6, 2U)
#define START_CHANNEL_2() \
gptStart(&GPTD7, &gpt7cfg1); \
gptStartContinuous(&GPTD7, 2U)
#define STOP_CHANNEL_1() gptStopTimer(&GPTD6)
#define STOP_CHANNEL_2() gptStopTimer(&GPTD7)
#define RESTART_CHANNEL_1() \
STOP_CHANNEL_1(); \
START_CHANNEL_1()
#define RESTART_CHANNEL_2() \
STOP_CHANNEL_2(); \
START_CHANNEL_2()
#define UPDATE_CHANNEL_1_FREQ(freq) \
gpt6cfg1.frequency = freq * DAC_BUFFER_SIZE; \
RESTART_CHANNEL_1()
#define UPDATE_CHANNEL_2_FREQ(freq) \
gpt7cfg1.frequency = freq * DAC_BUFFER_SIZE; \
RESTART_CHANNEL_2()
#define GET_CHANNEL_1_FREQ (uint16_t)(gpt6cfg1.frequency * DAC_BUFFER_SIZE)
#define GET_CHANNEL_2_FREQ (uint16_t)(gpt7cfg1.frequency * DAC_BUFFER_SIZE)
/*
* GPT6 configuration.
*/
// static const GPTConfig gpt6cfg1 = {
// .frequency = 1000000U,
// .callback = NULL,
// .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */
// .dier = 0U
// };
GPTConfig gpt6cfg1 = {.frequency = 440U * DAC_BUFFER_SIZE,
.callback = NULL,
.cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */
.dier = 0U};
GPTConfig gpt7cfg1 = {.frequency = 440U * DAC_BUFFER_SIZE,
.callback = NULL,
.cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */
.dier = 0U};
GPTConfig gpt8cfg1 = {.frequency = 10,
.callback = gpt_cb8,
.cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */
.dier = 0U};
/*
* DAC test buffer (sine wave).
*/
// static const dacsample_t dac_buffer[DAC_BUFFER_SIZE] = {
// 2047, 2082, 2118, 2154, 2189, 2225, 2260, 2296, 2331, 2367, 2402, 2437,
// 2472, 2507, 2542, 2576, 2611, 2645, 2679, 2713, 2747, 2780, 2813, 2846,
// 2879, 2912, 2944, 2976, 3008, 3039, 3070, 3101, 3131, 3161, 3191, 3221,
// 3250, 3278, 3307, 3335, 3362, 3389, 3416, 3443, 3468, 3494, 3519, 3544,
// 3568, 3591, 3615, 3637, 3660, 3681, 3703, 3723, 3744, 3763, 3782, 3801,
// 3819, 3837, 3854, 3870, 3886, 3902, 3917, 3931, 3944, 3958, 3970, 3982,
// 3993, 4004, 4014, 4024, 4033, 4041, 4049, 4056, 4062, 4068, 4074, 4078,
// 4082, 4086, 4089, 4091, 4092, 4093, 4094, 4093, 4092, 4091, 4089, 4086,
// 4082, 4078, 4074, 4068, 4062, 4056, 4049, 4041, 4033, 4024, 4014, 4004,
// 3993, 3982, 3970, 3958, 3944, 3931, 3917, 3902, 3886, 3870, 3854, 3837,
// 3819, 3801, 3782, 3763, 3744, 3723, 3703, 3681, 3660, 3637, 3615, 3591,
// 3568, 3544, 3519, 3494, 3468, 3443, 3416, 3389, 3362, 3335, 3307, 3278,
// 3250, 3221, 3191, 3161, 3131, 3101, 3070, 3039, 3008, 2976, 2944, 2912,
// 2879, 2846, 2813, 2780, 2747, 2713, 2679, 2645, 2611, 2576, 2542, 2507,
// 2472, 2437, 2402, 2367, 2331, 2296, 2260, 2225, 2189, 2154, 2118, 2082,
// 2047, 2012, 1976, 1940, 1905, 1869, 1834, 1798, 1763, 1727, 1692, 1657,
// 1622, 1587, 1552, 1518, 1483, 1449, 1415, 1381, 1347, 1314, 1281, 1248,
// 1215, 1182, 1150, 1118, 1086, 1055, 1024, 993, 963, 933, 903, 873,
// 844, 816, 787, 759, 732, 705, 678, 651, 626, 600, 575, 550,
// 526, 503, 479, 457, 434, 413, 391, 371, 350, 331, 312, 293,
// 275, 257, 240, 224, 208, 192, 177, 163, 150, 136, 124, 112,
// 101, 90, 80, 70, 61, 53, 45, 38, 32, 26, 20, 16,
// 12, 8, 5, 3, 2, 1, 0, 1, 2, 3, 5, 8,
// 12, 16, 20, 26, 32, 38, 45, 53, 61, 70, 80, 90,
// 101, 112, 124, 136, 150, 163, 177, 192, 208, 224, 240, 257,
// 275, 293, 312, 331, 350, 371, 391, 413, 434, 457, 479, 503,
// 526, 550, 575, 600, 626, 651, 678, 705, 732, 759, 787, 816,
// 844, 873, 903, 933, 963, 993, 1024, 1055, 1086, 1118, 1150, 1182,
// 1215, 1248, 1281, 1314, 1347, 1381, 1415, 1449, 1483, 1518, 1552, 1587,
// 1622, 1657, 1692, 1727, 1763, 1798, 1834, 1869, 1905, 1940, 1976, 2012
// };
// static const dacsample_t dac_buffer_2[DAC_BUFFER_SIZE] = {
// 12, 8, 5, 3, 2, 1, 0, 1, 2, 3, 5, 8,
// 12, 16, 20, 26, 32, 38, 45, 53, 61, 70, 80, 90,
// 101, 112, 124, 136, 150, 163, 177, 192, 208, 224, 240, 257,
// 275, 293, 312, 331, 350, 371, 391, 413, 434, 457, 479, 503,
// 526, 550, 575, 600, 626, 651, 678, 705, 732, 759, 787, 816,
// 844, 873, 903, 933, 963, 993, 1024, 1055, 1086, 1118, 1150, 1182,
// 1215, 1248, 1281, 1314, 1347, 1381, 1415, 1449, 1483, 1518, 1552, 1587,
// 1622, 1657, 1692, 1727, 1763, 1798, 1834, 1869, 1905, 1940, 1976, 2012,
// 2047, 2082, 2118, 2154, 2189, 2225, 2260, 2296, 2331, 2367, 2402, 2437,
// 2472, 2507, 2542, 2576, 2611, 2645, 2679, 2713, 2747, 2780, 2813, 2846,
// 2879, 2912, 2944, 2976, 3008, 3039, 3070, 3101, 3131, 3161, 3191, 3221,
// 3250, 3278, 3307, 3335, 3362, 3389, 3416, 3443, 3468, 3494, 3519, 3544,
// 3568, 3591, 3615, 3637, 3660, 3681, 3703, 3723, 3744, 3763, 3782, 3801,
// 3819, 3837, 3854, 3870, 3886, 3902, 3917, 3931, 3944, 3958, 3970, 3982,
// 3993, 4004, 4014, 4024, 4033, 4041, 4049, 4056, 4062, 4068, 4074, 4078,
// 4082, 4086, 4089, 4091, 4092, 4093, 4094, 4093, 4092, 4091, 4089, 4086,
// 4082, 4078, 4074, 4068, 4062, 4056, 4049, 4041, 4033, 4024, 4014, 4004,
// 3993, 3982, 3970, 3958, 3944, 3931, 3917, 3902, 3886, 3870, 3854, 3837,
// 3819, 3801, 3782, 3763, 3744, 3723, 3703, 3681, 3660, 3637, 3615, 3591,
// 3568, 3544, 3519, 3494, 3468, 3443, 3416, 3389, 3362, 3335, 3307, 3278,
// 3250, 3221, 3191, 3161, 3131, 3101, 3070, 3039, 3008, 2976, 2944, 2912,
// 2879, 2846, 2813, 2780, 2747, 2713, 2679, 2645, 2611, 2576, 2542, 2507,
// 2472, 2437, 2402, 2367, 2331, 2296, 2260, 2225, 2189, 2154, 2118, 2082,
// 2047, 2012, 1976, 1940, 1905, 1869, 1834, 1798, 1763, 1727, 1692, 1657,
// 1622, 1587, 1552, 1518, 1483, 1449, 1415, 1381, 1347, 1314, 1281, 1248,
// 1215, 1182, 1150, 1118, 1086, 1055, 1024, 993, 963, 933, 903, 873,
// 844, 816, 787, 759, 732, 705, 678, 651, 626, 600, 575, 550,
// 526, 503, 479, 457, 434, 413, 391, 371, 350, 331, 312, 293,
// 275, 257, 240, 224, 208, 192, 177, 163, 150, 136, 124, 112,
// 101, 90, 80, 70, 61, 53, 45, 38, 32, 26, 20, 16
// };
// squarewave
static const dacsample_t dac_buffer[DAC_BUFFER_SIZE] = {
// First half is max, second half is 0
[0 ... DAC_BUFFER_SIZE / 2 - 1] = DAC_SAMPLE_MAX,
[DAC_BUFFER_SIZE / 2 ... DAC_BUFFER_SIZE - 1] = 0,
};
// squarewave
static const dacsample_t dac_buffer_2[DAC_BUFFER_SIZE] = {
// opposite of dac_buffer above
[0 ... DAC_BUFFER_SIZE / 2 - 1] = 0,
[DAC_BUFFER_SIZE / 2 ... DAC_BUFFER_SIZE - 1] = DAC_SAMPLE_MAX,
};
/*
* DAC streaming callback.
*/
size_t nz = 0;
static void end_cb1(DACDriver *dacp) {
(void)dacp;
nz++;
if ((nz % 1000) == 0) {
// palTogglePad(GPIOD, GPIOD_LED3);
}
}
/*
* DAC error callback.
*/
static void error_cb1(DACDriver *dacp, dacerror_t err) {
(void)dacp;
(void)err;
chSysHalt("DAC failure");
}
static const DACConfig dac1cfg1 = {.init = DAC_SAMPLE_MAX, .datamode = DAC_DHRM_12BIT_RIGHT};
static const DACConversionGroup dacgrpcfg1 = {.num_channels = 1U, .end_cb = end_cb1, .error_cb = error_cb1, .trigger = DAC_TRG(0)};
static const DACConfig dac1cfg2 = {.init = DAC_SAMPLE_MAX, .datamode = DAC_DHRM_12BIT_RIGHT};
static const DACConversionGroup dacgrpcfg2 = {.num_channels = 1U, .end_cb = end_cb1, .error_cb = error_cb1, .trigger = DAC_TRG(0)};
void audio_init() {
if (audio_initialized) {
return;
}
// Check EEPROM
#ifdef EEPROM_ENABLE
if (!eeconfig_is_enabled()) {
eeconfig_init();
}
audio_config.raw = eeconfig_read_audio();
#else // ARM EEPROM
audio_config.enable = true;
# ifdef AUDIO_CLICKY_ON
audio_config.clicky_enable = true;
# endif
#endif // ARM EEPROM
/*
* Starting DAC1 driver, setting up the output pin as analog as suggested
* by the Reference Manual.
*/
palSetPadMode(GPIOA, 4, PAL_MODE_INPUT_ANALOG);
palSetPadMode(GPIOA, 5, PAL_MODE_INPUT_ANALOG);
dacStart(&DACD1, &dac1cfg1);
dacStart(&DACD2, &dac1cfg2);
/*
* Starting GPT6/7 driver, it is used for triggering the DAC.
*/
START_CHANNEL_1();
START_CHANNEL_2();
/*
* Starting a continuous conversion.
*/
dacStartConversion(&DACD1, &dacgrpcfg1, (dacsample_t *)dac_buffer, DAC_BUFFER_SIZE);
dacStartConversion(&DACD2, &dacgrpcfg2, (dacsample_t *)dac_buffer_2, DAC_BUFFER_SIZE);
audio_initialized = true;
if (audio_config.enable) {
PLAY_SONG(startup_song);
} else {
stop_all_notes();
}
}
void stop_all_notes() {
dprintf("audio stop all notes");
if (!audio_initialized) {
audio_init();
}
voices = 0;
gptStopTimer(&GPTD6);
gptStopTimer(&GPTD7);
gptStopTimer(&GPTD8);
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) {
STOP_CHANNEL_1();
STOP_CHANNEL_2();
gptStopTimer(&GPTD8);
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
static void gpt_cb8(GPTDriver *gptp) {
float freq;
if (playing_note) {
if (voices > 0) {
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;
}
if (GET_CHANNEL_2_FREQ != (uint16_t)freq_alt) {
UPDATE_CHANNEL_2_FREQ(freq_alt);
} else {
RESTART_CHANNEL_2();
}
// note_timbre;
}
if (polyphony_rate > 0) {
if (voices > 1) {
voice_place %= voices;
if (place++ > (frequencies[voice_place] / polyphony_rate)) {
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;
}
if (GET_CHANNEL_1_FREQ != (uint16_t)freq) {
UPDATE_CHANNEL_1_FREQ(freq);
} else {
RESTART_CHANNEL_1();
}
// 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);
if (GET_CHANNEL_1_FREQ != (uint16_t)freq) {
UPDATE_CHANNEL_1_FREQ(freq);
UPDATE_CHANNEL_2_FREQ(freq);
}
// note_timbre;
} else {
// gptStopTimer(&GPTD6);
// gptStopTimer(&GPTD7);
}
note_position++;
bool end_of_note = false;
if (GET_CHANNEL_1_FREQ > 0) {
if (!note_resting)
end_of_note = (note_position >= (note_length * 8 - 1));
else
end_of_note = (note_position >= (note_length * 8));
} else {
end_of_note = (note_position >= (note_length * 8));
}
if (end_of_note) {
current_note++;
if (current_note >= notes_count) {
if (notes_repeat) {
current_note = 0;
} else {
STOP_CHANNEL_1();
STOP_CHANNEL_2();
// gptStopTimer(&GPTD8);
playing_notes = false;
return;
}
}
if (!note_resting) {
note_resting = true;
current_note--;
if ((*notes_pointer)[current_note][0] == (*notes_pointer)[current_note + 1][0]) {
note_frequency = 0;
note_length = 1;
} else {
note_frequency = (*notes_pointer)[current_note][0];
note_length = 1;
}
} 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;
}
}
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) {
// 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++;
}
gptStart(&GPTD8, &gpt8cfg1);
gptStartContinuous(&GPTD8, 2U);
RESTART_CHANNEL_1();
RESTART_CHANNEL_2();
}
}
void play_notes(float (*np)[][2], uint16_t n_count, bool n_repeat) {
if (!audio_initialized) {
audio_init();
}
if (audio_config.enable) {
// 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;
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;
gptStart(&GPTD8, &gpt8cfg1);
gptStartContinuous(&GPTD8, 2U);
RESTART_CHANNEL_1();
RESTART_CHANNEL_2();
}
}
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) {
stop_all_notes();
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;
}
}
-595
View File
@@ -1,595 +0,0 @@
/* 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;
uint16_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() {}
//------------------------------------------------------------------------------
+17
View File
@@ -0,0 +1,17 @@
/* Copyright 2020 Jack Humbert
* Copyright 2020 JohSchneider
*
* 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
+322
View File
@@ -0,0 +1,322 @@
/* Copyright 2016 Jack Humbert
* Copyright 2020 JohSchneider
*
* 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/>.
*/
#if defined(__AVR__)
# include <avr/pgmspace.h>
# include <avr/interrupt.h>
# include <avr/io.h>
#endif
#include "audio.h"
extern bool playing_note;
extern bool playing_melody;
extern uint8_t note_timbre;
#define CPU_PRESCALER 8
/*
Audio Driver: PWM
drive up to two speakers through the AVR PWM hardware-peripheral, using timer1 and/or timer3 on Atmega32U4.
the primary channel_1 can be connected to either pin PC4 PC5 or PC6 (the later being used by most AVR based keyboards) with a PMW signal generated by timer3
and an optional secondary channel_2 on either pin PB5, PB6 or PB7, with a PWM signal from timer1
alternatively, the PWM pins on PORTB can be used as only/primary speaker
*/
#if defined(AUDIO_PIN) && (AUDIO_PIN != C4) && (AUDIO_PIN != C5) && (AUDIO_PIN != C6) && (AUDIO_PIN != B5) && (AUDIO_PIN != B6) && (AUDIO_PIN != B7)
# error "Audio feature enabled, but no suitable pin selected as AUDIO_PIN - see docs/feature_audio under the AVR settings for available options."
#endif
#if (AUDIO_PIN == C4) || (AUDIO_PIN == C5) || (AUDIO_PIN == C6)
# define AUDIO1_PIN_SET
# define AUDIO1_TIMSKx TIMSK3
# define AUDIO1_TCCRxA TCCR3A
# define AUDIO1_TCCRxB TCCR3B
# define AUDIO1_ICRx ICR3
# define AUDIO1_WGMx0 WGM30
# define AUDIO1_WGMx1 WGM31
# define AUDIO1_WGMx2 WGM32
# define AUDIO1_WGMx3 WGM33
# define AUDIO1_CSx0 CS30
# define AUDIO1_CSx1 CS31
# define AUDIO1_CSx2 CS32
# if (AUDIO_PIN == C6)
# define AUDIO1_COMxy0 COM3A0
# define AUDIO1_COMxy1 COM3A1
# define AUDIO1_OCIExy OCIE3A
# define AUDIO1_OCRxy OCR3A
# define AUDIO1_PIN C6
# define AUDIO1_TIMERx_COMPy_vect TIMER3_COMPA_vect
# elif (AUDIO_PIN == C5)
# define AUDIO1_COMxy0 COM3B0
# define AUDIO1_COMxy1 COM3B1
# define AUDIO1_OCIExy OCIE3B
# define AUDIO1_OCRxy OCR3B
# define AUDIO1_PIN C5
# define AUDIO1_TIMERx_COMPy_vect TIMER3_COMPB_vect
# elif (AUDIO_PIN == C4)
# define AUDIO1_COMxy0 COM3C0
# define AUDIO1_COMxy1 COM3C1
# define AUDIO1_OCIExy OCIE3C
# define AUDIO1_OCRxy OCR3C
# define AUDIO1_PIN C4
# define AUDIO1_TIMERx_COMPy_vect TIMER3_COMPC_vect
# endif
#endif
#if defined(AUDIO_PIN) && defined(AUDIO_PIN_ALT) && (AUDIO_PIN == AUDIO_PIN_ALT)
# error "Audio feature: AUDIO_PIN and AUDIO_PIN_ALT on the same pin makes no sense."
#endif
#if ((AUDIO_PIN == B5) && ((AUDIO_PIN_ALT == B6) || (AUDIO_PIN_ALT == B7))) || ((AUDIO_PIN == B6) && ((AUDIO_PIN_ALT == B5) || (AUDIO_PIN_ALT == B7))) || ((AUDIO_PIN == B7) && ((AUDIO_PIN_ALT == B5) || (AUDIO_PIN_ALT == B6)))
# error "Audio feature: PORTB as AUDIO_PIN and AUDIO_PIN_ALT at the same time is not supported."
#endif
#if defined(AUDIO_PIN_ALT) && (AUDIO_PIN_ALT != B5) && (AUDIO_PIN_ALT != B6) && (AUDIO_PIN_ALT != B7)
# error "Audio feature: the pin selected as AUDIO_PIN_ALT is not supported."
#endif
#if (AUDIO_PIN == B5) || (AUDIO_PIN == B6) || (AUDIO_PIN == B7) || (AUDIO_PIN_ALT == B5) || (AUDIO_PIN_ALT == B6) || (AUDIO_PIN_ALT == B7)
# define AUDIO2_PIN_SET
# define AUDIO2_TIMSKx TIMSK1
# define AUDIO2_TCCRxA TCCR1A
# define AUDIO2_TCCRxB TCCR1B
# define AUDIO2_ICRx ICR1
# define AUDIO2_WGMx0 WGM10
# define AUDIO2_WGMx1 WGM11
# define AUDIO2_WGMx2 WGM12
# define AUDIO2_WGMx3 WGM13
# define AUDIO2_CSx0 CS10
# define AUDIO2_CSx1 CS11
# define AUDIO2_CSx2 CS12
# if (AUDIO_PIN == B5) || (AUDIO_PIN_ALT == B5)
# define AUDIO2_COMxy0 COM1A0
# define AUDIO2_COMxy1 COM1A1
# define AUDIO2_OCIExy OCIE1A
# define AUDIO2_OCRxy OCR1A
# define AUDIO2_PIN B5
# define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPA_vect
# elif (AUDIO_PIN == B6) || (AUDIO_PIN_ALT == B6)
# define AUDIO2_COMxy0 COM1B0
# define AUDIO2_COMxy1 COM1B1
# define AUDIO2_OCIExy OCIE1B
# define AUDIO2_OCRxy OCR1B
# define AUDIO2_PIN B6
# define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPB_vect
# elif (AUDIO_PIN == B7) || (AUDIO_PIN_ALT == B7)
# define AUDIO2_COMxy0 COM1C0
# define AUDIO2_COMxy1 COM1C1
# define AUDIO2_OCIExy OCIE1C
# define AUDIO2_OCRxy OCR1C
# define AUDIO2_PIN B7
# define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPC_vect
# endif
#endif
// C6 seems to be the assumed default by many existing keyboard - but sill warn the user
#if !defined(AUDIO1_PIN_SET) && !defined(AUDIO2_PIN_SET)
# pragma message "Audio feature enabled, but no suitable pin selected - see docs/feature_audio under the AVR settings for available options. Don't expect to hear anything... :-)"
// TODO: make this an error - go through the breaking-change-process and change all keyboards to the new define
#endif
// -----------------------------------------------------------------------------
#ifdef AUDIO1_PIN_SET
static float channel_1_frequency = 0.0f;
void channel_1_set_frequency(float freq) {
if (freq == 0.0f) // a pause/rest is a valid "note" with freq=0
{
// disable the output, but keep the pwm-ISR going (with the previous
// frequency) so the audio-state keeps getting updated
// Note: setting the duty-cycle 0 is not possible on non-inverting PWM mode - see the AVR data-sheet
AUDIO1_TCCRxA &= ~(_BV(AUDIO1_COMxy1) | _BV(AUDIO1_COMxy0));
return;
} else {
AUDIO1_TCCRxA |= _BV(AUDIO1_COMxy1); // enable output, PWM mode
}
channel_1_frequency = freq;
// set pwm period
AUDIO1_ICRx = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER));
// and duty cycle
AUDIO1_OCRxy = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre / 100);
}
void channel_1_start(void) {
// enable timer-counter ISR
AUDIO1_TIMSKx |= _BV(AUDIO1_OCIExy);
// enable timer-counter output
AUDIO1_TCCRxA |= _BV(AUDIO1_COMxy1);
}
void channel_1_stop(void) {
// disable timer-counter ISR
AUDIO1_TIMSKx &= ~_BV(AUDIO1_OCIExy);
// disable timer-counter output
AUDIO1_TCCRxA &= ~(_BV(AUDIO1_COMxy1) | _BV(AUDIO1_COMxy0));
}
#endif
#ifdef AUDIO2_PIN_SET
static float channel_2_frequency = 0.0f;
void channel_2_set_frequency(float freq) {
if (freq == 0.0f) {
AUDIO2_TCCRxA &= ~(_BV(AUDIO2_COMxy1) | _BV(AUDIO2_COMxy0));
return;
} else {
AUDIO2_TCCRxA |= _BV(AUDIO2_COMxy1);
}
channel_2_frequency = freq;
AUDIO2_ICRx = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER));
AUDIO2_OCRxy = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre / 100);
}
float channel_2_get_frequency(void) { return channel_2_frequency; }
void channel_2_start(void) {
AUDIO2_TIMSKx |= _BV(AUDIO2_OCIExy);
AUDIO2_TCCRxA |= _BV(AUDIO2_COMxy1);
}
void channel_2_stop(void) {
AUDIO2_TIMSKx &= ~_BV(AUDIO2_OCIExy);
AUDIO2_TCCRxA &= ~(_BV(AUDIO2_COMxy1) | _BV(AUDIO2_COMxy0));
}
#endif
void audio_driver_initialize() {
#ifdef AUDIO1_PIN_SET
channel_1_stop();
setPinOutput(AUDIO1_PIN);
#endif
#ifdef AUDIO2_PIN_SET
channel_2_stop();
setPinOutput(AUDIO2_PIN);
#endif
// TCCR3A / TCCR3B: Timer/Counter #3 Control Registers TCCR3A/TCCR3B, TCCR1A/TCCR1B
// Compare Output Mode (COM3An and COM1An) = 0b00 = Normal port operation
// OC3A -- PC6
// OC3B -- PC5
// OC3C -- PC4
// OC1A -- PB5
// OC1B -- PB6
// OC1C -- PB7
// Waveform Generation Mode (WGM3n) = 0b1110 = Fast PWM Mode 14. Period = ICR3, Duty Cycle OCR3A)
// OCR3A - PC6
// OCR3B - PC5
// OCR3C - PC4
// OCR1A - PB5
// OCR1B - PB6
// OCR1C - PB7
// Clock Select (CS3n) = 0b010 = Clock / 8
#ifdef AUDIO1_PIN_SET
// initialize timer-counter
AUDIO1_TCCRxA = (0 << AUDIO1_COMxy1) | (0 << AUDIO1_COMxy0) | (1 << AUDIO1_WGMx1) | (0 << AUDIO1_WGMx0);
AUDIO1_TCCRxB = (1 << AUDIO1_WGMx3) | (1 << AUDIO1_WGMx2) | (0 << AUDIO1_CSx2) | (1 << AUDIO1_CSx1) | (0 << AUDIO1_CSx0);
#endif
#ifdef AUDIO2_PIN_SET
AUDIO2_TCCRxA = (0 << AUDIO2_COMxy1) | (0 << AUDIO2_COMxy0) | (1 << AUDIO2_WGMx1) | (0 << AUDIO2_WGMx0);
AUDIO2_TCCRxB = (1 << AUDIO2_WGMx3) | (1 << AUDIO2_WGMx2) | (0 << AUDIO2_CSx2) | (1 << AUDIO2_CSx1) | (0 << AUDIO2_CSx0);
#endif
}
void audio_driver_stop() {
#ifdef AUDIO1_PIN_SET
channel_1_stop();
#endif
#ifdef AUDIO2_PIN_SET
channel_2_stop();
#endif
}
void audio_driver_start(void) {
#ifdef AUDIO1_PIN_SET
channel_1_start();
if (playing_note) {
channel_1_set_frequency(audio_get_processed_frequency(0));
}
#endif
#if !defined(AUDIO1_PIN_SET) && defined(AUDIO2_PIN_SET)
channel_2_start();
if (playing_note) {
channel_2_set_frequency(audio_get_processed_frequency(0));
}
#endif
}
static volatile uint32_t isr_counter = 0;
#ifdef AUDIO1_PIN_SET
ISR(AUDIO1_TIMERx_COMPy_vect) {
isr_counter++;
if (isr_counter < channel_1_frequency / (CPU_PRESCALER * 8)) return;
isr_counter = 0;
bool state_changed = audio_update_state();
if (!playing_note && !playing_melody) {
channel_1_stop();
# ifdef AUDIO2_PIN_SET
channel_2_stop();
# endif
return;
}
if (state_changed) {
channel_1_set_frequency(audio_get_processed_frequency(0));
# ifdef AUDIO2_PIN_SET
if (audio_get_number_of_active_tones() > 1) {
channel_2_set_frequency(audio_get_processed_frequency(1));
} else {
channel_2_stop();
}
# endif
}
}
#endif
#if !defined(AUDIO1_PIN_SET) && defined(AUDIO2_PIN_SET)
ISR(AUDIO2_TIMERx_COMPy_vect) {
isr_counter++;
if (isr_counter < channel_2_frequency / (CPU_PRESCALER * 8)) return;
isr_counter = 0;
bool state_changed = audio_update_state();
if (!playing_note && !playing_melody) {
channel_2_stop();
return;
}
if (state_changed) {
channel_2_set_frequency(audio_get_processed_frequency(0));
}
}
#endif
+126
View File
@@ -0,0 +1,126 @@
/* Copyright 2019 Jack Humbert
* Copyright 2020 JohSchneider
*
* 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
#ifndef A4
# define A4 PAL_LINE(GPIOA, 4)
#endif
#ifndef A5
# define A5 PAL_LINE(GPIOA, 5)
#endif
/**
* Size of the dac_buffer arrays. All must be the same size.
*/
#define AUDIO_DAC_BUFFER_SIZE 256U
/**
* Highest value allowed sample value.
* since the DAC is limited to 12 bit, the absolute max is 0xfff = 4095U;
* lower values adjust the peak-voltage aka volume down.
* adjusting this value has only an effect on a sample-buffer whose values are
* are NOT pregenerated - see square-wave
*/
#ifndef AUDIO_DAC_SAMPLE_MAX
# define AUDIO_DAC_SAMPLE_MAX 4095U
#endif
#if !defined(AUDIO_DAC_SAMPLE_RATE) && !defined(AUDIO_MAX_SIMULTANEOUS_TONES) && !defined(AUDIO_DAC_QUALITY_VERY_LOW) && !defined(AUDIO_DAC_QUALITY_LOW) && !defined(AUDIO_DAC_QUALITY_HIGH) && !defined(AUDIO_DAC_QUALITY_VERY_HIGH)
# define AUDIO_DAC_QUALITY_SANE_MINIMUM
#endif
/**
* These presets allow you to quickly switch between quality settings for
* the DAC. The sample rate and maximum number of simultaneous tones roughly
* has an inverse relationship - slightly higher sample rates may be possible.
*
* NOTE: a high sample-rate results in a higher cpu-load, which might lead to
* (audible) discontinuities and/or starve other processes of cpu-time
* (like RGB-led back-lighting, ...)
*/
#ifdef AUDIO_DAC_QUALITY_VERY_LOW
# define AUDIO_DAC_SAMPLE_RATE 11025U
# define AUDIO_MAX_SIMULTANEOUS_TONES 8
#endif
#ifdef AUDIO_DAC_QUALITY_LOW
# define AUDIO_DAC_SAMPLE_RATE 22050U
# define AUDIO_MAX_SIMULTANEOUS_TONES 4
#endif
#ifdef AUDIO_DAC_QUALITY_HIGH
# define AUDIO_DAC_SAMPLE_RATE 44100U
# define AUDIO_MAX_SIMULTANEOUS_TONES 2
#endif
#ifdef AUDIO_DAC_QUALITY_VERY_HIGH
# define AUDIO_DAC_SAMPLE_RATE 88200U
# define AUDIO_MAX_SIMULTANEOUS_TONES 1
#endif
#ifdef AUDIO_DAC_QUALITY_SANE_MINIMUM
/* a sane-minimum config: with a trade-off between cpu-load and tone-range
*
* the (currently) highest defined note is NOTE_B8 with 7902Hz; if we now
* aim for an even even multiple of the buffer-size, we end up with:
* ( roundUptoPow2(highest note / AUDIO_DAC_BUFFER_SIZE) * nyquist-rate * AUDIO_DAC_BUFFER_SIZE)
* 7902/256 = 30.867 * 2 * 256 ~= 16384
* which works out (but the 'scope shows some sampling artifacts with lower harmonics :-P)
*/
# define AUDIO_DAC_SAMPLE_RATE 16384U
# define AUDIO_MAX_SIMULTANEOUS_TONES 8
#endif
/**
* Effective bit-rate of the DAC. 44.1khz is the standard for most audio - any
* lower will sacrifice perceptible audio quality. Any higher will limit the
* number of simultaneous tones. In most situations, a tenth (1/10) of the
* sample rate is where notes become unbearable.
*/
#ifndef AUDIO_DAC_SAMPLE_RATE
# define AUDIO_DAC_SAMPLE_RATE 44100U
#endif
/**
* The number of tones that can be played simultaneously. If too high a value
* is used here, the keyboard will freeze and glitch-out when that many tones
* are being played.
*/
#ifndef AUDIO_MAX_SIMULTANEOUS_TONES
# define AUDIO_MAX_SIMULTANEOUS_TONES 2
#endif
/**
* The default value of the DAC when not playing anything. Certain hardware
* setups may require a high (AUDIO_DAC_SAMPLE_MAX) or low (0) value here.
* Since multiple added sine waves tend to oscillate around the midpoint,
* and possibly never/rarely reach either 0 of MAX, 1/2 MAX can be a
* reasonable default value.
*/
#ifndef AUDIO_DAC_OFF_VALUE
# define AUDIO_DAC_OFF_VALUE AUDIO_DAC_SAMPLE_MAX / 2
#endif
#if AUDIO_DAC_OFF_VALUE > AUDIO_DAC_SAMPLE_MAX
# error "AUDIO_DAC: OFF_VALUE may not be larger than SAMPLE_MAX"
#endif
/**
*user overridable sample generation/processing
*/
uint16_t dac_value_generate(void);
+335
View File
@@ -0,0 +1,335 @@
/* Copyright 2016-2019 Jack Humbert
* Copyright 2020 JohSchneider
*
* 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 "audio.h"
#include <ch.h>
#include <hal.h>
/*
Audio Driver: DAC
which utilizes the dac unit many STM32 are equipped with, to output a modulated waveform from samples stored in the dac_buffer_* array who are passed to the hardware through DMA
it is also possible to have a custom sample-LUT by implementing/overriding 'dac_value_generate'
this driver allows for multiple simultaneous tones to be played through one single channel by doing additive wave-synthesis
*/
#if !defined(AUDIO_PIN)
# error "Audio feature enabled, but no suitable pin selected as AUDIO_PIN - see docs/feature_audio under 'ARM (DAC additive)' for available options."
#endif
#if defined(AUDIO_PIN_ALT) && !defined(AUDIO_PIN_ALT_AS_NEGATIVE)
# pragma message "Audio feature: AUDIO_PIN_ALT set, but not AUDIO_PIN_ALT_AS_NEGATIVE - pin will be left unused; audio might still work though."
#endif
#if !defined(AUDIO_PIN_ALT)
// no ALT pin defined is valid, but the c-ifs below need some value set
# define AUDIO_PIN_ALT PAL_NOLINE
#endif
#if !defined(AUDIO_DAC_SAMPLE_WAVEFORM_SINE) && !defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE) && !defined(AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE) && !defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID)
# define AUDIO_DAC_SAMPLE_WAVEFORM_SINE
#endif
#ifdef AUDIO_DAC_SAMPLE_WAVEFORM_SINE
/* one full sine wave over [0,2*pi], but shifted up one amplitude and left pi/4; for the samples to start at 0
*/
static const dacsample_t dac_buffer_sine[AUDIO_DAC_BUFFER_SIZE] = {
// 256 values, max 4095
0x0, 0x1, 0x2, 0x6, 0xa, 0xf, 0x16, 0x1e, 0x27, 0x32, 0x3d, 0x4a, 0x58, 0x67, 0x78, 0x89, 0x9c, 0xb0, 0xc5, 0xdb, 0xf2, 0x10a, 0x123, 0x13e, 0x159, 0x175, 0x193, 0x1b1, 0x1d1, 0x1f1, 0x212, 0x235, 0x258, 0x27c, 0x2a0, 0x2c6, 0x2ed, 0x314, 0x33c, 0x365, 0x38e, 0x3b8, 0x3e3, 0x40e, 0x43a, 0x467, 0x494, 0x4c2, 0x4f0, 0x51f, 0x54e, 0x57d, 0x5ad, 0x5dd, 0x60e, 0x63f, 0x670, 0x6a1, 0x6d3, 0x705, 0x737, 0x769, 0x79b, 0x7cd, 0x800, 0x832, 0x864, 0x896, 0x8c8, 0x8fa, 0x92c, 0x95e, 0x98f, 0x9c0, 0x9f1, 0xa22, 0xa52, 0xa82, 0xab1, 0xae0, 0xb0f, 0xb3d, 0xb6b, 0xb98, 0xbc5, 0xbf1, 0xc1c, 0xc47, 0xc71, 0xc9a, 0xcc3, 0xceb, 0xd12, 0xd39, 0xd5f, 0xd83, 0xda7, 0xdca, 0xded, 0xe0e, 0xe2e, 0xe4e, 0xe6c, 0xe8a, 0xea6, 0xec1, 0xedc, 0xef5, 0xf0d, 0xf24, 0xf3a, 0xf4f, 0xf63, 0xf76, 0xf87, 0xf98, 0xfa7, 0xfb5, 0xfc2, 0xfcd, 0xfd8, 0xfe1, 0xfe9, 0xff0, 0xff5, 0xff9, 0xffd, 0xffe,
0xfff, 0xffe, 0xffd, 0xff9, 0xff5, 0xff0, 0xfe9, 0xfe1, 0xfd8, 0xfcd, 0xfc2, 0xfb5, 0xfa7, 0xf98, 0xf87, 0xf76, 0xf63, 0xf4f, 0xf3a, 0xf24, 0xf0d, 0xef5, 0xedc, 0xec1, 0xea6, 0xe8a, 0xe6c, 0xe4e, 0xe2e, 0xe0e, 0xded, 0xdca, 0xda7, 0xd83, 0xd5f, 0xd39, 0xd12, 0xceb, 0xcc3, 0xc9a, 0xc71, 0xc47, 0xc1c, 0xbf1, 0xbc5, 0xb98, 0xb6b, 0xb3d, 0xb0f, 0xae0, 0xab1, 0xa82, 0xa52, 0xa22, 0x9f1, 0x9c0, 0x98f, 0x95e, 0x92c, 0x8fa, 0x8c8, 0x896, 0x864, 0x832, 0x800, 0x7cd, 0x79b, 0x769, 0x737, 0x705, 0x6d3, 0x6a1, 0x670, 0x63f, 0x60e, 0x5dd, 0x5ad, 0x57d, 0x54e, 0x51f, 0x4f0, 0x4c2, 0x494, 0x467, 0x43a, 0x40e, 0x3e3, 0x3b8, 0x38e, 0x365, 0x33c, 0x314, 0x2ed, 0x2c6, 0x2a0, 0x27c, 0x258, 0x235, 0x212, 0x1f1, 0x1d1, 0x1b1, 0x193, 0x175, 0x159, 0x13e, 0x123, 0x10a, 0xf2, 0xdb, 0xc5, 0xb0, 0x9c, 0x89, 0x78, 0x67, 0x58, 0x4a, 0x3d, 0x32, 0x27, 0x1e, 0x16, 0xf, 0xa, 0x6, 0x2, 0x1};
#endif // AUDIO_DAC_SAMPLE_WAVEFORM_SINE
#ifdef AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE
static const dacsample_t dac_buffer_triangle[AUDIO_DAC_BUFFER_SIZE] = {
// 256 values, max 4095
0x0, 0x20, 0x40, 0x60, 0x80, 0xa0, 0xc0, 0xe0, 0x100, 0x120, 0x140, 0x160, 0x180, 0x1a0, 0x1c0, 0x1e0, 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0x300, 0x320, 0x340, 0x360, 0x380, 0x3a0, 0x3c0, 0x3e0, 0x400, 0x420, 0x440, 0x460, 0x480, 0x4a0, 0x4c0, 0x4e0, 0x500, 0x520, 0x540, 0x560, 0x580, 0x5a0, 0x5c0, 0x5e0, 0x600, 0x620, 0x640, 0x660, 0x680, 0x6a0, 0x6c0, 0x6e0, 0x700, 0x720, 0x740, 0x760, 0x780, 0x7a0, 0x7c0, 0x7e0, 0x800, 0x81f, 0x83f, 0x85f, 0x87f, 0x89f, 0x8bf, 0x8df, 0x8ff, 0x91f, 0x93f, 0x95f, 0x97f, 0x99f, 0x9bf, 0x9df, 0x9ff, 0xa1f, 0xa3f, 0xa5f, 0xa7f, 0xa9f, 0xabf, 0xadf, 0xaff, 0xb1f, 0xb3f, 0xb5f, 0xb7f, 0xb9f, 0xbbf, 0xbdf, 0xbff, 0xc1f, 0xc3f, 0xc5f, 0xc7f, 0xc9f, 0xcbf, 0xcdf, 0xcff, 0xd1f, 0xd3f, 0xd5f, 0xd7f, 0xd9f, 0xdbf, 0xddf, 0xdff, 0xe1f, 0xe3f, 0xe5f, 0xe7f, 0xe9f, 0xebf, 0xedf, 0xeff, 0xf1f, 0xf3f, 0xf5f, 0xf7f, 0xf9f, 0xfbf, 0xfdf,
0xfff, 0xfdf, 0xfbf, 0xf9f, 0xf7f, 0xf5f, 0xf3f, 0xf1f, 0xeff, 0xedf, 0xebf, 0xe9f, 0xe7f, 0xe5f, 0xe3f, 0xe1f, 0xdff, 0xddf, 0xdbf, 0xd9f, 0xd7f, 0xd5f, 0xd3f, 0xd1f, 0xcff, 0xcdf, 0xcbf, 0xc9f, 0xc7f, 0xc5f, 0xc3f, 0xc1f, 0xbff, 0xbdf, 0xbbf, 0xb9f, 0xb7f, 0xb5f, 0xb3f, 0xb1f, 0xaff, 0xadf, 0xabf, 0xa9f, 0xa7f, 0xa5f, 0xa3f, 0xa1f, 0x9ff, 0x9df, 0x9bf, 0x99f, 0x97f, 0x95f, 0x93f, 0x91f, 0x8ff, 0x8df, 0x8bf, 0x89f, 0x87f, 0x85f, 0x83f, 0x81f, 0x800, 0x7e0, 0x7c0, 0x7a0, 0x780, 0x760, 0x740, 0x720, 0x700, 0x6e0, 0x6c0, 0x6a0, 0x680, 0x660, 0x640, 0x620, 0x600, 0x5e0, 0x5c0, 0x5a0, 0x580, 0x560, 0x540, 0x520, 0x500, 0x4e0, 0x4c0, 0x4a0, 0x480, 0x460, 0x440, 0x420, 0x400, 0x3e0, 0x3c0, 0x3a0, 0x380, 0x360, 0x340, 0x320, 0x300, 0x2e0, 0x2c0, 0x2a0, 0x280, 0x260, 0x240, 0x220, 0x200, 0x1e0, 0x1c0, 0x1a0, 0x180, 0x160, 0x140, 0x120, 0x100, 0xe0, 0xc0, 0xa0, 0x80, 0x60, 0x40, 0x20};
#endif // AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE
#ifdef AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE
static const dacsample_t dac_buffer_square[AUDIO_DAC_BUFFER_SIZE] = {
[0 ... AUDIO_DAC_BUFFER_SIZE / 2 - 1] = 0, // first and
[AUDIO_DAC_BUFFER_SIZE / 2 ... AUDIO_DAC_BUFFER_SIZE - 1] = AUDIO_DAC_SAMPLE_MAX, // second half
};
#endif // AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE
/*
// four steps: 0, 1/3, 2/3 and 1
static const dacsample_t dac_buffer_staircase[AUDIO_DAC_BUFFER_SIZE] = {
[0 ... AUDIO_DAC_BUFFER_SIZE/3 -1 ] = 0,
[AUDIO_DAC_BUFFER_SIZE / 4 ... AUDIO_DAC_BUFFER_SIZE / 2 -1 ] = AUDIO_DAC_SAMPLE_MAX / 3,
[AUDIO_DAC_BUFFER_SIZE / 2 ... 3 * AUDIO_DAC_BUFFER_SIZE / 4 -1 ] = 2 * AUDIO_DAC_SAMPLE_MAX / 3,
[3 * AUDIO_DAC_BUFFER_SIZE / 4 ... AUDIO_DAC_BUFFER_SIZE -1 ] = AUDIO_DAC_SAMPLE_MAX,
}
*/
#ifdef AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID
static const dacsample_t dac_buffer_trapezoid[AUDIO_DAC_BUFFER_SIZE] = {0x0, 0x1f, 0x7f, 0xdf, 0x13f, 0x19f, 0x1ff, 0x25f, 0x2bf, 0x31f, 0x37f, 0x3df, 0x43f, 0x49f, 0x4ff, 0x55f, 0x5bf, 0x61f, 0x67f, 0x6df, 0x73f, 0x79f, 0x7ff, 0x85f, 0x8bf, 0x91f, 0x97f, 0x9df, 0xa3f, 0xa9f, 0xaff, 0xb5f, 0xbbf, 0xc1f, 0xc7f, 0xcdf, 0xd3f, 0xd9f, 0xdff, 0xe5f, 0xebf, 0xf1f, 0xf7f, 0xfdf, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
0xfff, 0xfdf, 0xf7f, 0xf1f, 0xebf, 0xe5f, 0xdff, 0xd9f, 0xd3f, 0xcdf, 0xc7f, 0xc1f, 0xbbf, 0xb5f, 0xaff, 0xa9f, 0xa3f, 0x9df, 0x97f, 0x91f, 0x8bf, 0x85f, 0x7ff, 0x79f, 0x73f, 0x6df, 0x67f, 0x61f, 0x5bf, 0x55f, 0x4ff, 0x49f, 0x43f, 0x3df, 0x37f, 0x31f, 0x2bf, 0x25f, 0x1ff, 0x19f, 0x13f, 0xdf, 0x7f, 0x1f, 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, 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};
#endif // AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID
static dacsample_t dac_buffer_empty[AUDIO_DAC_BUFFER_SIZE] = {AUDIO_DAC_OFF_VALUE};
/* keep track of the sample position for for each frequency */
static float dac_if[AUDIO_MAX_SIMULTANEOUS_TONES] = {0.0};
static float active_tones_snapshot[AUDIO_MAX_SIMULTANEOUS_TONES] = {0, 0};
static uint8_t active_tones_snapshot_length = 0;
typedef enum {
OUTPUT_SHOULD_START,
OUTPUT_RUN_NORMALLY,
// path 1: wait for zero, then change/update active tones
OUTPUT_TONES_CHANGED,
OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE,
// path 2: hardware should stop, wait for zero then turn output off = stop the timer
OUTPUT_SHOULD_STOP,
OUTPUT_REACHED_ZERO_BEFORE_OFF,
OUTPUT_OFF,
OUTPUT_OFF_1,
OUTPUT_OFF_2, // trailing off: giving the DAC two more conversion cycles until the AUDIO_DAC_OFF_VALUE reaches the output, then turn the timer off, which leaves the output at that level
number_of_output_states
} output_states_t;
output_states_t state = OUTPUT_OFF_2;
/**
* Generation of the waveform being passed to the callback. Declared weak so users
* can override it with their own wave-forms/noises.
*/
__attribute__((weak)) uint16_t dac_value_generate(void) {
// DAC is running/asking for values but snapshot length is zero -> must be playing a pause
if (active_tones_snapshot_length == 0) {
return AUDIO_DAC_OFF_VALUE;
}
/* doing additive wave synthesis over all currently playing tones = adding up
* sine-wave-samples for each frequency, scaled by the number of active tones
*/
uint16_t value = 0;
float frequency = 0.0f;
for (uint8_t i = 0; i < active_tones_snapshot_length; i++) {
/* Note: a user implementation does not have to rely on the active_tones_snapshot, but
* could directly query the active frequencies through audio_get_processed_frequency */
frequency = active_tones_snapshot[i];
dac_if[i] = dac_if[i] + ((frequency * AUDIO_DAC_BUFFER_SIZE) / AUDIO_DAC_SAMPLE_RATE) * 2 / 3;
/*Note: the 2/3 are necessary to get the correct frequencies on the
* DAC output (as measured with an oscilloscope), since the gpt
* timer runs with 3*AUDIO_DAC_SAMPLE_RATE; and the DAC callback
* is called twice per conversion.*/
dac_if[i] = fmod(dac_if[i], AUDIO_DAC_BUFFER_SIZE);
// Wavetable generation/lookup
uint16_t dac_i = (uint16_t)dac_if[i];
#if defined(AUDIO_DAC_SAMPLE_WAVEFORM_SINE)
value += dac_buffer_sine[dac_i] / active_tones_snapshot_length;
#elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE)
value += dac_buffer_triangle[dac_i] / active_tones_snapshot_length;
#elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID)
value += dac_buffer_trapezoid[dac_i] / active_tones_snapshot_length;
#elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE)
value += dac_buffer_square[dac_i] / active_tones_snapshot_length;
#endif
/*
// SINE
value += dac_buffer_sine[dac_i] / active_tones_snapshot_length / 3;
// TRIANGLE
value += dac_buffer_triangle[dac_i] / active_tones_snapshot_length / 3;
// SQUARE
value += dac_buffer_square[dac_i] / active_tones_snapshot_length / 3;
//NOTE: combination of these three wave-forms is more exemplary - and doesn't sound particularly good :-P
*/
// STAIRS (mostly usefully as test-pattern)
// value_avg = dac_buffer_staircase[dac_i] / active_tones_snapshot_length;
}
return value;
}
/**
* DAC streaming callback. Does all of the main computing for playing songs.
*
* Note: chibios calls this CB twice: during the 'half buffer event', and the 'full buffer event'.
*/
static void dac_end(DACDriver *dacp) {
dacsample_t *sample_p = (dacp)->samples;
// work on the other half of the buffer
if (dacIsBufferComplete(dacp)) {
sample_p += AUDIO_DAC_BUFFER_SIZE / 2; // 'half_index'
}
for (uint8_t s = 0; s < AUDIO_DAC_BUFFER_SIZE / 2; s++) {
if (OUTPUT_OFF <= state) {
sample_p[s] = AUDIO_DAC_OFF_VALUE;
continue;
} else {
sample_p[s] = dac_value_generate();
}
/* zero crossing (or approach, whereas zero == DAC_OFF_VALUE, which can be configured to anything from 0 to DAC_SAMPLE_MAX)
* ============================*=*========================== AUDIO_DAC_SAMPLE_MAX
* * *
* * *
* ---------------------------------------------------------
* * * } AUDIO_DAC_SAMPLE_MAX/100
* --------------------------------------------------------- AUDIO_DAC_OFF_VALUE
* * * } AUDIO_DAC_SAMPLE_MAX/100
* ---------------------------------------------------------
* *
* * *
* * *
* =====*=*================================================= 0x0
*/
if (((sample_p[s] + (AUDIO_DAC_SAMPLE_MAX / 100)) > AUDIO_DAC_OFF_VALUE) && // value approaches from below
(sample_p[s] < (AUDIO_DAC_OFF_VALUE + (AUDIO_DAC_SAMPLE_MAX / 100))) // or above
) {
if ((OUTPUT_SHOULD_START == state) && (active_tones_snapshot_length > 0)) {
state = OUTPUT_RUN_NORMALLY;
} else if (OUTPUT_TONES_CHANGED == state) {
state = OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE;
} else if (OUTPUT_SHOULD_STOP == state) {
state = OUTPUT_REACHED_ZERO_BEFORE_OFF;
}
}
// still 'ramping up', reset the output to OFF_VALUE until the generated values reach that value, to do a smooth handover
if (OUTPUT_SHOULD_START == state) {
sample_p[s] = AUDIO_DAC_OFF_VALUE;
}
if ((OUTPUT_SHOULD_START == state) || (OUTPUT_REACHED_ZERO_BEFORE_OFF == state) || (OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE == state)) {
uint8_t active_tones = MIN(AUDIO_MAX_SIMULTANEOUS_TONES, audio_get_number_of_active_tones());
active_tones_snapshot_length = 0;
// update the snapshot - once, and only on occasion that something changed;
// -> saves cpu cycles (?)
for (uint8_t i = 0; i < active_tones; i++) {
float freq = audio_get_processed_frequency(i);
if (freq > 0) { // disregard 'rest' notes, with valid frequency 0.0f; which would only lower the resulting waveform volume during the additive synthesis step
active_tones_snapshot[active_tones_snapshot_length++] = freq;
}
}
if ((0 == active_tones_snapshot_length) && (OUTPUT_REACHED_ZERO_BEFORE_OFF == state)) {
state = OUTPUT_OFF;
}
if (OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE == state) {
state = OUTPUT_RUN_NORMALLY;
}
}
}
// update audio internal state (note position, current_note, ...)
if (audio_update_state()) {
if (OUTPUT_SHOULD_STOP != state) {
state = OUTPUT_TONES_CHANGED;
}
}
if (OUTPUT_OFF <= state) {
if (OUTPUT_OFF_2 == state) {
// stopping timer6 = stopping the DAC at whatever value it is currently pushing to the output = AUDIO_DAC_OFF_VALUE
gptStopTimer(&GPTD6);
} else {
state++;
}
}
}
static void dac_error(DACDriver *dacp, dacerror_t err) {
(void)dacp;
(void)err;
chSysHalt("DAC failure. halp");
}
static const GPTConfig gpt6cfg1 = {.frequency = AUDIO_DAC_SAMPLE_RATE * 3,
.callback = NULL,
.cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */
.dier = 0U};
static const DACConfig dac_conf = {.init = AUDIO_DAC_OFF_VALUE, .datamode = DAC_DHRM_12BIT_RIGHT};
/**
* @note The DAC_TRG(0) here selects the Timer 6 TRGO event, which is triggered
* on the rising edge after 3 APB1 clock cycles, causing our gpt6cfg1.frequency
* to be a third of what we expect.
*
* Here are all the values for DAC_TRG (TSEL in the ref manual)
* TIM15_TRGO 0b011
* TIM2_TRGO 0b100
* TIM3_TRGO 0b001
* TIM6_TRGO 0b000
* TIM7_TRGO 0b010
* EXTI9 0b110
* SWTRIG 0b111
*/
static const DACConversionGroup dac_conv_cfg = {.num_channels = 1U, .end_cb = dac_end, .error_cb = dac_error, .trigger = DAC_TRG(0b000)};
void audio_driver_initialize() {
if ((AUDIO_PIN == A4) || (AUDIO_PIN_ALT == A4)) {
palSetLineMode(A4, PAL_MODE_INPUT_ANALOG);
dacStart(&DACD1, &dac_conf);
}
if ((AUDIO_PIN == A5) || (AUDIO_PIN_ALT == A5)) {
palSetLineMode(A5, PAL_MODE_INPUT_ANALOG);
dacStart(&DACD2, &dac_conf);
}
/* enable the output buffer, to directly drive external loads with no additional circuitry
*
* see: AN4566 Application note: Extending the DAC performance of STM32 microcontrollers
* Note: Buffer-Off bit -> has to be set 0 to enable the output buffer
* Note: enabling the output buffer imparts an additional dc-offset of a couple mV
*
* this is done here, reaching directly into the stm32 registers since chibios has not implemented BOFF handling yet
* (see: chibios/os/hal/ports/STM32/todo.txt '- BOFF handling in DACv1.'
*/
DACD1.params->dac->CR &= ~DAC_CR_BOFF1;
DACD2.params->dac->CR &= ~DAC_CR_BOFF2;
if (AUDIO_PIN == A4) {
dacStartConversion(&DACD1, &dac_conv_cfg, dac_buffer_empty, AUDIO_DAC_BUFFER_SIZE);
} else if (AUDIO_PIN == A5) {
dacStartConversion(&DACD2, &dac_conv_cfg, dac_buffer_empty, AUDIO_DAC_BUFFER_SIZE);
}
// no inverted/out-of-phase waveform (yet?), only pulling AUDIO_PIN_ALT to AUDIO_DAC_OFF_VALUE
#if defined(AUDIO_PIN_ALT_AS_NEGATIVE)
if (AUDIO_PIN_ALT == A4) {
dacPutChannelX(&DACD1, 0, AUDIO_DAC_OFF_VALUE);
} else if (AUDIO_PIN_ALT == A5) {
dacPutChannelX(&DACD2, 0, AUDIO_DAC_OFF_VALUE);
}
#endif
gptStart(&GPTD6, &gpt6cfg1);
}
void audio_driver_stop(void) { state = OUTPUT_SHOULD_STOP; }
void audio_driver_start(void) {
gptStartContinuous(&GPTD6, 2U);
for (uint8_t i = 0; i < AUDIO_MAX_SIMULTANEOUS_TONES; i++) {
dac_if[i] = 0.0f;
active_tones_snapshot[i] = 0.0f;
}
active_tones_snapshot_length = 0;
state = OUTPUT_SHOULD_START;
}
+245
View File
@@ -0,0 +1,245 @@
/* Copyright 2016-2020 Jack Humbert
* Copyright 2020 JohSchneider
*
* 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 "audio.h"
#include "ch.h"
#include "hal.h"
/*
Audio Driver: DAC
which utilizes both channels of the DAC unit many STM32 are equipped with to output a modulated square-wave, from precomputed samples stored in a buffer, which is passed to the hardware through DMA
this driver can either be used to drive to separate speakers, wired to A4+Gnd and A5+Gnd, which allows two tones to be played simultaneously
OR
one speaker wired to A4+A5 with the AUDIO_PIN_ALT_AS_NEGATIVE define set - see docs/feature_audio
*/
#if !defined(AUDIO_PIN)
# pragma message "Audio feature enabled, but no suitable pin selected as AUDIO_PIN - see docs/feature_audio under 'ARM (DAC basic)' for available options."
// TODO: make this an 'error' instead; go through a breaking change, and add AUDIO_PIN A5 to all keyboards currently using AUDIO on STM32 based boards? - for now: set the define here
# define AUDIO_PIN A5
#endif
// check configuration for ONE speaker, connected to both DAC pins
#if defined(AUDIO_PIN_ALT_AS_NEGATIVE) && !defined(AUDIO_PIN_ALT)
# error "Audio feature: AUDIO_PIN_ALT_AS_NEGATIVE set, but no pin configured as AUDIO_PIN_ALT"
#endif
#ifndef AUDIO_PIN_ALT
// no ALT pin defined is valid, but the c-ifs below need some value set
# define AUDIO_PIN_ALT -1
#endif
#if !defined(AUDIO_STATE_TIMER)
# define AUDIO_STATE_TIMER GPTD8
#endif
// square-wave
static const dacsample_t dac_buffer_1[AUDIO_DAC_BUFFER_SIZE] = {
// First half is max, second half is 0
[0 ... AUDIO_DAC_BUFFER_SIZE / 2 - 1] = AUDIO_DAC_SAMPLE_MAX,
[AUDIO_DAC_BUFFER_SIZE / 2 ... AUDIO_DAC_BUFFER_SIZE - 1] = 0,
};
// square-wave
static const dacsample_t dac_buffer_2[AUDIO_DAC_BUFFER_SIZE] = {
// opposite of dac_buffer above
[0 ... AUDIO_DAC_BUFFER_SIZE / 2 - 1] = 0,
[AUDIO_DAC_BUFFER_SIZE / 2 ... AUDIO_DAC_BUFFER_SIZE - 1] = AUDIO_DAC_SAMPLE_MAX,
};
GPTConfig gpt6cfg1 = {.frequency = AUDIO_DAC_SAMPLE_RATE,
.callback = NULL,
.cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */
.dier = 0U};
GPTConfig gpt7cfg1 = {.frequency = AUDIO_DAC_SAMPLE_RATE,
.callback = NULL,
.cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */
.dier = 0U};
static void gpt_audio_state_cb(GPTDriver *gptp);
GPTConfig gptStateUpdateCfg = {.frequency = 10,
.callback = gpt_audio_state_cb,
.cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */
.dier = 0U};
static const DACConfig dac_conf_ch1 = {.init = AUDIO_DAC_OFF_VALUE, .datamode = DAC_DHRM_12BIT_RIGHT};
static const DACConfig dac_conf_ch2 = {.init = AUDIO_DAC_OFF_VALUE, .datamode = DAC_DHRM_12BIT_RIGHT};
/**
* @note The DAC_TRG(0) here selects the Timer 6 TRGO event, which is triggered
* on the rising edge after 3 APB1 clock cycles, causing our gpt6cfg1.frequency
* to be a third of what we expect.
*
* Here are all the values for DAC_TRG (TSEL in the ref manual)
* TIM15_TRGO 0b011
* TIM2_TRGO 0b100
* TIM3_TRGO 0b001
* TIM6_TRGO 0b000
* TIM7_TRGO 0b010
* EXTI9 0b110
* SWTRIG 0b111
*/
static const DACConversionGroup dac_conv_grp_ch1 = {.num_channels = 1U, .trigger = DAC_TRG(0b000)};
static const DACConversionGroup dac_conv_grp_ch2 = {.num_channels = 1U, .trigger = DAC_TRG(0b010)};
void channel_1_start(void) {
gptStart(&GPTD6, &gpt6cfg1);
gptStartContinuous(&GPTD6, 2U);
palSetPadMode(GPIOA, 5, PAL_MODE_INPUT_ANALOG);
}
void channel_1_stop(void) {
gptStopTimer(&GPTD6);
palSetPadMode(GPIOA, 4, PAL_MODE_OUTPUT_PUSHPULL);
palSetPad(GPIOA, 4);
}
static float channel_1_frequency = 0.0f;
void channel_1_set_frequency(float freq) {
channel_1_frequency = freq;
channel_1_stop();
if (freq <= 0.0) // a pause/rest has freq=0
return;
gpt6cfg1.frequency = 2 * freq * AUDIO_DAC_BUFFER_SIZE;
channel_1_start();
}
float channel_1_get_frequency(void) { return channel_1_frequency; }
void channel_2_start(void) {
gptStart(&GPTD7, &gpt7cfg1);
gptStartContinuous(&GPTD7, 2U);
palSetPadMode(GPIOA, 5, PAL_MODE_INPUT_ANALOG);
}
void channel_2_stop(void) {
gptStopTimer(&GPTD7);
palSetPadMode(GPIOA, 5, PAL_MODE_OUTPUT_PUSHPULL);
palSetPad(GPIOA, 5);
}
static float channel_2_frequency = 0.0f;
void channel_2_set_frequency(float freq) {
channel_2_frequency = freq;
channel_2_stop();
if (freq <= 0.0) // a pause/rest has freq=0
return;
gpt7cfg1.frequency = 2 * freq * AUDIO_DAC_BUFFER_SIZE;
channel_2_start();
}
float channel_2_get_frequency(void) { return channel_2_frequency; }
static void gpt_audio_state_cb(GPTDriver *gptp) {
if (audio_update_state()) {
#if defined(AUDIO_PIN_ALT_AS_NEGATIVE)
// one piezo/speaker connected to both audio pins, the generated square-waves are inverted
channel_1_set_frequency(audio_get_processed_frequency(0));
channel_2_set_frequency(audio_get_processed_frequency(0));
#else // two separate audio outputs/speakers
// primary speaker on A4, optional secondary on A5
if (AUDIO_PIN == A4) {
channel_1_set_frequency(audio_get_processed_frequency(0));
if (AUDIO_PIN_ALT == A5) {
if (audio_get_number_of_active_tones() > 1) {
channel_2_set_frequency(audio_get_processed_frequency(1));
} else {
channel_2_stop();
}
}
}
// primary speaker on A5, optional secondary on A4
if (AUDIO_PIN == A5) {
channel_2_set_frequency(audio_get_processed_frequency(0));
if (AUDIO_PIN_ALT == A4) {
if (audio_get_number_of_active_tones() > 1) {
channel_1_set_frequency(audio_get_processed_frequency(1));
} else {
channel_1_stop();
}
}
}
#endif
}
}
void audio_driver_initialize() {
if ((AUDIO_PIN == A4) || (AUDIO_PIN_ALT == A4)) {
palSetPadMode(GPIOA, 4, PAL_MODE_INPUT_ANALOG);
dacStart(&DACD1, &dac_conf_ch1);
// initial setup of the dac-triggering timer is still required, even
// though it gets reconfigured and restarted later on
gptStart(&GPTD6, &gpt6cfg1);
}
if ((AUDIO_PIN == A5) || (AUDIO_PIN_ALT == A5)) {
palSetPadMode(GPIOA, 5, PAL_MODE_INPUT_ANALOG);
dacStart(&DACD2, &dac_conf_ch2);
gptStart(&GPTD7, &gpt7cfg1);
}
/* enable the output buffer, to directly drive external loads with no additional circuitry
*
* see: AN4566 Application note: Extending the DAC performance of STM32 microcontrollers
* Note: Buffer-Off bit -> has to be set 0 to enable the output buffer
* Note: enabling the output buffer imparts an additional dc-offset of a couple mV
*
* this is done here, reaching directly into the stm32 registers since chibios has not implemented BOFF handling yet
* (see: chibios/os/hal/ports/STM32/todo.txt '- BOFF handling in DACv1.'
*/
DACD1.params->dac->CR &= ~DAC_CR_BOFF1;
DACD2.params->dac->CR &= ~DAC_CR_BOFF2;
// start state-updater
gptStart(&AUDIO_STATE_TIMER, &gptStateUpdateCfg);
}
void audio_driver_stop(void) {
if ((AUDIO_PIN == A4) || (AUDIO_PIN_ALT == A4)) {
gptStopTimer(&GPTD6);
// stop the ongoing conversion and put the output in a known state
dacStopConversion(&DACD1);
dacPutChannelX(&DACD1, 0, AUDIO_DAC_OFF_VALUE);
}
if ((AUDIO_PIN == A5) || (AUDIO_PIN_ALT == A5)) {
gptStopTimer(&GPTD7);
dacStopConversion(&DACD2);
dacPutChannelX(&DACD2, 0, AUDIO_DAC_OFF_VALUE);
}
gptStopTimer(&AUDIO_STATE_TIMER);
}
void audio_driver_start(void) {
if ((AUDIO_PIN == A4) || (AUDIO_PIN_ALT == A4)) {
dacStartConversion(&DACD1, &dac_conv_grp_ch1, (dacsample_t *)dac_buffer_1, AUDIO_DAC_BUFFER_SIZE);
}
if ((AUDIO_PIN == A5) || (AUDIO_PIN_ALT == A5)) {
dacStartConversion(&DACD2, &dac_conv_grp_ch2, (dacsample_t *)dac_buffer_2, AUDIO_DAC_BUFFER_SIZE);
}
gptStartContinuous(&AUDIO_STATE_TIMER, 2U);
}
+40
View File
@@ -0,0 +1,40 @@
/* Copyright 2020 Jack Humbert
* Copyright 2020 JohSchneider
*
* 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
#if !defined(AUDIO_PWM_DRIVER)
// NOTE: Timer2 seems to be used otherwise in QMK, otherwise we could default to A5 (= TIM2_CH1, with PWMD2 and alternate-function(1))
# define AUDIO_PWM_DRIVER PWMD1
#endif
#if !defined(AUDIO_PWM_CHANNEL)
// NOTE: sticking to the STM data-sheet numbering: TIMxCH1 to TIMxCH4
// default: STM32F303CC PA8+TIM1_CH1 -> 1
# define AUDIO_PWM_CHANNEL 1
#endif
#if !defined(AUDIO_PWM_PAL_MODE)
// pin-alternate function: see the data-sheet for which pin needs what AF to connect to TIMx_CHy
// default: STM32F303CC PA8+TIM1_CH1 -> 6
# define AUDIO_PWM_PAL_MODE 6
#endif
#if !defined(AUDIO_STATE_TIMER)
// timer used to trigger updates in the audio-system, configured/enabled in chibios mcuconf.
// Tim6 is the default for "larger" STMs, smaller ones might not have this one (enabled) and need to switch to a different one (e.g.: STM32F103 has only Tim1-Tim4)
# define AUDIO_STATE_TIMER GPTD6
#endif
+144
View File
@@ -0,0 +1,144 @@
/* Copyright 2020 Jack Humbert
* Copyright 2020 JohSchneider
*
* 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/>.
*/
/*
Audio Driver: PWM
the duty-cycle is always kept at 50%, and the pwm-period is adjusted to match the frequency of a note to be played back.
this driver uses the chibios-PWM system to produce a square-wave on specific output pins that are connected to the PWM hardware.
The hardware directly toggles the pin via its alternate function. see your MCUs data-sheet for which pin can be driven by what timer - looking for TIMx_CHy and the corresponding alternate function.
*/
#include "audio.h"
#include "ch.h"
#include "hal.h"
#if !defined(AUDIO_PIN)
# error "Audio feature enabled, but no pin selected - see docs/feature_audio under the ARM PWM settings"
#endif
extern bool playing_note;
extern bool playing_melody;
extern uint8_t note_timbre;
static PWMConfig pwmCFG = {
.frequency = 100000, /* PWM clock frequency */
// CHIBIOS-BUG? can't set the initial period to <2, or the pwm (hard or software) takes ~130ms with .frequency=500000 for a pwmChangePeriod to take effect; with no output=silence in the meantime
.period = 2, /* initial PWM period (in ticks) 1S (1/10kHz=0.1mS 0.1ms*10000 ticks=1S) */
.callback = NULL, /* no callback, the hardware directly toggles the pin */
.channels =
{
#if AUDIO_PWM_CHANNEL == 4
{PWM_OUTPUT_DISABLED, NULL}, /* channel 0 -> TIMx_CH1 */
{PWM_OUTPUT_DISABLED, NULL}, /* channel 1 -> TIMx_CH2 */
{PWM_OUTPUT_DISABLED, NULL}, /* channel 2 -> TIMx_CH3 */
{PWM_OUTPUT_ACTIVE_HIGH, NULL} /* channel 3 -> TIMx_CH4 */
#elif AUDIO_PWM_CHANNEL == 3
{PWM_OUTPUT_DISABLED, NULL},
{PWM_OUTPUT_DISABLED, NULL},
{PWM_OUTPUT_ACTIVE_HIGH, NULL}, /* TIMx_CH3 */
{PWM_OUTPUT_DISABLED, NULL}
#elif AUDIO_PWM_CHANNEL == 2
{PWM_OUTPUT_DISABLED, NULL},
{PWM_OUTPUT_ACTIVE_HIGH, NULL}, /* TIMx_CH2 */
{PWM_OUTPUT_DISABLED, NULL},
{PWM_OUTPUT_DISABLED, NULL}
#else /*fallback to CH1 */
{PWM_OUTPUT_ACTIVE_HIGH, NULL}, /* TIMx_CH1 */
{PWM_OUTPUT_DISABLED, NULL},
{PWM_OUTPUT_DISABLED, NULL},
{PWM_OUTPUT_DISABLED, NULL}
#endif
},
};
static float channel_1_frequency = 0.0f;
void channel_1_set_frequency(float freq) {
channel_1_frequency = freq;
if (freq <= 0.0) // a pause/rest has freq=0
return;
pwmcnt_t period = (pwmCFG.frequency / freq);
pwmChangePeriod(&AUDIO_PWM_DRIVER, period);
pwmEnableChannel(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1,
// adjust the duty-cycle so that the output is for 'note_timbre' duration HIGH
PWM_PERCENTAGE_TO_WIDTH(&AUDIO_PWM_DRIVER, (100 - note_timbre) * 100));
}
float channel_1_get_frequency(void) { return channel_1_frequency; }
void channel_1_start(void) {
pwmStop(&AUDIO_PWM_DRIVER);
pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG);
}
void channel_1_stop(void) { pwmStop(&AUDIO_PWM_DRIVER); }
static void gpt_callback(GPTDriver *gptp);
GPTConfig gptCFG = {
/* a whole note is one beat, which is - per definition in musical_notes.h - set to 64
the longest note is BREAVE_DOT=128+64=192, the shortest SIXTEENTH=4
the tempo (which might vary!) is in bpm (beats per minute)
therefore: if the timer ticks away at .frequency = (60*64)Hz,
and the .interval counts from 64 downwards - audio_update_state is
called just often enough to not miss any notes
*/
.frequency = 60 * 64,
.callback = gpt_callback,
};
void audio_driver_initialize(void) {
pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG);
// connect the AUDIO_PIN to the PWM hardware
#if defined(USE_GPIOV1) // STM32F103C8
palSetLineMode(AUDIO_PIN, PAL_MODE_STM32_ALTERNATE_PUSHPULL);
#else // GPIOv2 (or GPIOv3 for f4xx, which is the same/compatible at this command)
palSetLineMode(AUDIO_PIN, PAL_STM32_MODE_ALTERNATE | PAL_STM32_ALTERNATE(AUDIO_PWM_PAL_MODE));
#endif
gptStart(&AUDIO_STATE_TIMER, &gptCFG);
}
void audio_driver_start(void) {
channel_1_stop();
channel_1_start();
if (playing_note || playing_melody) {
gptStartContinuous(&AUDIO_STATE_TIMER, 64);
}
}
void audio_driver_stop(void) {
channel_1_stop();
gptStopTimer(&AUDIO_STATE_TIMER);
}
/* a regular timer task, that checks the note to be currently played
* and updates the pwm to output that frequency
*/
static void gpt_callback(GPTDriver *gptp) {
float freq; // TODO: freq_alt
if (audio_update_state()) {
freq = audio_get_processed_frequency(0); // freq_alt would be index=1
channel_1_set_frequency(freq);
}
}
+164
View File
@@ -0,0 +1,164 @@
/* Copyright 2020 Jack Humbert
* Copyright 2020 JohSchneider
*
* 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/>.
*/
/*
Audio Driver: PWM
the duty-cycle is always kept at 50%, and the pwm-period is adjusted to match the frequency of a note to be played back.
this driver uses the chibios-PWM system to produce a square-wave on any given output pin in software
- a pwm callback is used to set/clear the configured pin.
*/
#include "audio.h"
#include "ch.h"
#include "hal.h"
#if !defined(AUDIO_PIN)
# error "Audio feature enabled, but no pin selected - see docs/feature_audio under the ARM PWM settings"
#endif
extern bool playing_note;
extern bool playing_melody;
extern uint8_t note_timbre;
static void pwm_audio_period_callback(PWMDriver *pwmp);
static void pwm_audio_channel_interrupt_callback(PWMDriver *pwmp);
static PWMConfig pwmCFG = {
.frequency = 100000, /* PWM clock frequency */
// CHIBIOS-BUG? can't set the initial period to <2, or the pwm (hard or software) takes ~130ms with .frequency=500000 for a pwmChangePeriod to take effect; with no output=silence in the meantime
.period = 2, /* initial PWM period (in ticks) 1S (1/10kHz=0.1mS 0.1ms*10000 ticks=1S) */
.callback = pwm_audio_period_callback,
.channels =
{
// software-PWM just needs another callback on any channel
{PWM_OUTPUT_ACTIVE_HIGH, pwm_audio_channel_interrupt_callback}, /* channel 0 -> TIMx_CH1 */
{PWM_OUTPUT_DISABLED, NULL}, /* channel 1 -> TIMx_CH2 */
{PWM_OUTPUT_DISABLED, NULL}, /* channel 2 -> TIMx_CH3 */
{PWM_OUTPUT_DISABLED, NULL} /* channel 3 -> TIMx_CH4 */
},
};
static float channel_1_frequency = 0.0f;
void channel_1_set_frequency(float freq) {
channel_1_frequency = freq;
if (freq <= 0.0) // a pause/rest has freq=0
return;
pwmcnt_t period = (pwmCFG.frequency / freq);
pwmChangePeriod(&AUDIO_PWM_DRIVER, period);
pwmEnableChannel(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1,
// adjust the duty-cycle so that the output is for 'note_timbre' duration HIGH
PWM_PERCENTAGE_TO_WIDTH(&AUDIO_PWM_DRIVER, (100 - note_timbre) * 100));
}
float channel_1_get_frequency(void) { return channel_1_frequency; }
void channel_1_start(void) {
pwmStop(&AUDIO_PWM_DRIVER);
pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG);
pwmEnablePeriodicNotification(&AUDIO_PWM_DRIVER);
pwmEnableChannelNotification(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1);
}
void channel_1_stop(void) {
pwmStop(&AUDIO_PWM_DRIVER);
palClearLine(AUDIO_PIN); // leave the line low, after last note was played
#if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE)
palClearLine(AUDIO_PIN_ALT); // leave the line low, after last note was played
#endif
}
// generate a PWM signal on any pin, not necessarily the one connected to the timer
static void pwm_audio_period_callback(PWMDriver *pwmp) {
(void)pwmp;
palClearLine(AUDIO_PIN);
#if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE)
palSetLine(AUDIO_PIN_ALT);
#endif
}
static void pwm_audio_channel_interrupt_callback(PWMDriver *pwmp) {
(void)pwmp;
if (channel_1_frequency > 0) {
palSetLine(AUDIO_PIN); // generate a PWM signal on any pin, not necessarily the one connected to the timer
#if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE)
palClearLine(AUDIO_PIN_ALT);
#endif
}
}
static void gpt_callback(GPTDriver *gptp);
GPTConfig gptCFG = {
/* a whole note is one beat, which is - per definition in musical_notes.h - set to 64
the longest note is BREAVE_DOT=128+64=192, the shortest SIXTEENTH=4
the tempo (which might vary!) is in bpm (beats per minute)
therefore: if the timer ticks away at .frequency = (60*64)Hz,
and the .interval counts from 64 downwards - audio_update_state is
called just often enough to not miss anything
*/
.frequency = 60 * 64,
.callback = gpt_callback,
};
void audio_driver_initialize(void) {
pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG);
palSetLineMode(AUDIO_PIN, PAL_MODE_OUTPUT_PUSHPULL);
palClearLine(AUDIO_PIN);
#if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE)
palSetLineMode(AUDIO_PIN_ALT, PAL_MODE_OUTPUT_PUSHPULL);
palClearLine(AUDIO_PIN_ALT);
#endif
pwmEnablePeriodicNotification(&AUDIO_PWM_DRIVER); // enable pwm callbacks
pwmEnableChannelNotification(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1);
gptStart(&AUDIO_STATE_TIMER, &gptCFG);
}
void audio_driver_start(void) {
channel_1_stop();
channel_1_start();
if (playing_note || playing_melody) {
gptStartContinuous(&AUDIO_STATE_TIMER, 64);
}
}
void audio_driver_stop(void) {
channel_1_stop();
gptStopTimer(&AUDIO_STATE_TIMER);
}
/* a regular timer task, that checks the note to be currently played
* and updates the pwm to output that frequency
*/
static void gpt_callback(GPTDriver *gptp) {
float freq; // TODO: freq_alt
if (audio_update_state()) {
freq = audio_get_processed_frequency(0); // freq_alt would be index=1
channel_1_set_frequency(freq);
}
}
+6 -9
View File
@@ -14,23 +14,20 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#if defined(__AVR__)
# include <avr/io.h>
# include <avr/interrupt.h>
# include <avr/pgmspace.h>
#else
# include "ch.h"
# include "hal.h"
# include <ch.h>
# include <hal.h>
#endif
#ifndef LUTS_H
# define LUTS_H
#define VIBRATO_LUT_LENGTH 20
# define VIBRATO_LUT_LENGTH 20
# define FREQUENCY_LUT_LENGTH 349
#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 */
+1 -4
View File
@@ -1,9 +1,6 @@
#ifndef MUSE_H
#define MUSE_H
#pragma once
#include "quantum.h"
#include "process_audio.h"
uint8_t muse_clock_pulse(void);
#endif
+44 -43
View File
@@ -1,4 +1,5 @@
/* Copyright 2016 Jack Humbert
* Copyright 2020 JohSchneider
*
* 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
@@ -13,12 +14,12 @@
* 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
#ifndef MUSICAL_NOTES_H
#define MUSICAL_NOTES_H
// Tempo Placeholder
#define TEMPO_DEFAULT 100
#ifndef TEMPO_DEFAULT
# define TEMPO_DEFAULT 120
// in beats-per-minute
#endif
#define SONG(notes...) \
{ notes }
@@ -26,12 +27,14 @@
// Note Types
#define MUSICAL_NOTE(note, duration) \
{ (NOTE##note), duration }
#define BREVE_NOTE(note) MUSICAL_NOTE(note, 128)
#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 THIRTYSECOND_NOTE(note) MUSICAL_NOTE(note, 2)
#define BREVE_DOT_NOTE(note) MUSICAL_NOTE(note, 128 + 64)
#define WHOLE_DOT_NOTE(note) MUSICAL_NOTE(note, 64 + 32)
@@ -39,6 +42,9 @@
#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)
#define THIRTYSECOND_DOT_NOTE(note) MUSICAL_NOTE(note, 2 + 1)
// duration of 64 units == one beat == one whole note
// with a tempo of 60bpm this comes to a length of one second
// Note Type Shortcuts
#define M__NOTE(note, duration) MUSICAL_NOTE(note, duration)
@@ -48,55 +54,52 @@
#define Q__NOTE(n) QUARTER_NOTE(n)
#define E__NOTE(n) EIGHTH_NOTE(n)
#define S__NOTE(n) SIXTEENTH_NOTE(n)
#define T__NOTE(n) THIRTYSECOND_NOTE(n)
#define BD_NOTE(n) BREVE_DOT_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)
#define TD_NOTE(n) THIRTYSECOND_DOT_NOTE(n)
// Note Timbre
// Changes how the notes sound
#define TIMBRE_12 0.125f
#define TIMBRE_25 0.250f
#define TIMBRE_50 0.500f
#define TIMBRE_75 0.750f
#define TIMBRE_DEFAULT TIMBRE_50
#define TIMBRE_12 12
#define TIMBRE_25 25
#define TIMBRE_50 50
#define TIMBRE_75 75
#ifndef TIMBRE_DEFAULT
# define TIMBRE_DEFAULT TIMBRE_50
#endif
// Notes - # = Octave
#ifdef __arm__
# define NOTE_REST 1.00f
#else
# define NOTE_REST 0.00f
#endif
/* These notes are currently bugged
#define NOTE_C0 16.35f
#define NOTE_CS0 17.32f
#define NOTE_D0 18.35f
#define NOTE_DS0 19.45f
#define NOTE_E0 20.60f
#define NOTE_F0 21.83f
#define NOTE_FS0 23.12f
#define NOTE_G0 24.50f
#define NOTE_GS0 25.96f
#define NOTE_A0 27.50f
#define NOTE_AS0 29.14f
#define NOTE_B0 30.87f
#define NOTE_C1 32.70f
#define NOTE_CS1 34.65f
#define NOTE_D1 36.71f
#define NOTE_DS1 38.89f
#define NOTE_E1 41.20f
#define NOTE_F1 43.65f
#define NOTE_FS1 46.25f
#define NOTE_G1 49.00f
#define NOTE_GS1 51.91f
#define NOTE_A1 55.00f
#define NOTE_AS1 58.27f
*/
#define NOTE_REST 0.00f
#define NOTE_C0 16.35f
#define NOTE_CS0 17.32f
#define NOTE_D0 18.35f
#define NOTE_DS0 19.45f
#define NOTE_E0 20.60f
#define NOTE_F0 21.83f
#define NOTE_FS0 23.12f
#define NOTE_G0 24.50f
#define NOTE_GS0 25.96f
#define NOTE_A0 27.50f
#define NOTE_AS0 29.14f
#define NOTE_B0 30.87f
#define NOTE_C1 32.70f
#define NOTE_CS1 34.65f
#define NOTE_D1 36.71f
#define NOTE_DS1 38.89f
#define NOTE_E1 41.20f
#define NOTE_F1 43.65f
#define NOTE_FS1 46.25f
#define NOTE_G1 49.00f
#define NOTE_GS1 51.91f
#define NOTE_A1 55.00f
#define NOTE_AS1 58.27f
#define NOTE_B1 61.74f
#define NOTE_C2 65.41f
#define NOTE_CS2 69.30f
@@ -229,5 +232,3 @@
#define NOTE_GF8 NOTE_FS8
#define NOTE_AF8 NOTE_GS8
#define NOTE_BF8 NOTE_AS8
#endif
+117 -55
View File
@@ -1,4 +1,5 @@
/* Copyright 2016 Jack Humbert
* Copyright 2020 JohSchneider
*
* 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
@@ -15,15 +16,21 @@
*/
#include "voices.h"
#include "audio.h"
#include "stdlib.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;
uint8_t note_timbre = TIMBRE_DEFAULT;
bool glissando = false;
bool vibrato = false;
float vibrato_strength = 0.5;
float vibrato_rate = 0.125;
uint16_t voices_timer = 0;
#ifdef AUDIO_VOICE_DEFAULT
voice_type voice = AUDIO_VOICE_DEFAULT;
#else
voice_type voice = default_voice;
#endif
void set_voice(voice_type v) { voice = v; }
@@ -31,22 +38,54 @@ void voice_iterate() { voice = (voice + 1) % number_of_voices; }
void voice_deiterate() { voice = (voice - 1 + number_of_voices) % number_of_voices; }
#ifdef AUDIO_VOICES
float mod(float a, int b) {
float r = fmod(a, b);
return r < 0 ? r + b : r;
}
// Effect: 'vibrate' a given target frequency slightly above/below its initial value
float voice_add_vibrato(float average_freq) {
float vibrato_counter = mod(timer_read() / (100 * vibrato_rate), VIBRATO_LUT_LENGTH);
return average_freq * pow(vibrato_lut[(int)vibrato_counter], vibrato_strength);
}
// Effect: 'slides' the 'frequency' from the starting-point, to the target frequency
float voice_add_glissando(float from_freq, float to_freq) {
if (to_freq != 0 && from_freq < to_freq && from_freq < to_freq * pow(2, -440 / to_freq / 12 / 2)) {
return from_freq * pow(2, 440 / from_freq / 12 / 2);
} else if (to_freq != 0 && from_freq > to_freq && from_freq > to_freq * pow(2, 440 / to_freq / 12 / 2)) {
return from_freq * pow(2, -440 / from_freq / 12 / 2);
} else {
return to_freq;
}
}
#endif
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));
// __attribute__((unused)) uint16_t compensated_index = (uint16_t)((float)envelope_index * (880.0 / frequency));
#ifdef AUDIO_VOICES
uint16_t envelope_index = timer_elapsed(voices_timer); // TODO: multiply in some factor?
uint16_t compensated_index = envelope_index / 100; // TODO: correct factor would be?
#endif
switch (voice) {
case default_voice:
glissando = false;
note_timbre = TIMBRE_50;
polyphony_rate = 0;
glissando = false;
// note_timbre = TIMBRE_50; //Note: leave the user the possibility to adjust the timbre with 'audio_set_timbre'
break;
#ifdef AUDIO_VOICES
case vibrating:
glissando = false;
vibrato = true;
break;
case something:
glissando = false;
polyphony_rate = 0;
glissando = false;
switch (compensated_index) {
case 0 ... 9:
note_timbre = TIMBRE_12;
@@ -57,24 +96,23 @@ float voice_envelope(float frequency) {
break;
case 20 ... 200:
note_timbre = .125 + .125;
note_timbre = 12 + 12;
break;
default:
note_timbre = .125;
note_timbre = 12;
break;
}
break;
case drums:
glissando = false;
polyphony_rate = 0;
glissando = false;
// switch (compensated_index) {
// case 0 ... 10:
// note_timbre = 0.5;
// note_timbre = 50;
// break;
// case 11 ... 20:
// note_timbre = 0.5 * (21 - compensated_index) / 10;
// note_timbre = 50 * (21 - compensated_index) / 10;
// break;
// default:
// note_timbre = 0;
@@ -88,10 +126,10 @@ float voice_envelope(float frequency) {
frequency = (rand() % (int)(40)) + 60;
switch (envelope_index) {
case 0 ... 10:
note_timbre = 0.5;
note_timbre = 50;
break;
case 11 ... 20:
note_timbre = 0.5 * (21 - envelope_index) / 10;
note_timbre = 50 * (21 - envelope_index) / 10;
break;
default:
note_timbre = 0;
@@ -103,10 +141,10 @@ float voice_envelope(float frequency) {
frequency = (rand() % (int)(1000)) + 1000;
switch (envelope_index) {
case 0 ... 5:
note_timbre = 0.5;
note_timbre = 50;
break;
case 6 ... 20:
note_timbre = 0.5 * (21 - envelope_index) / 15;
note_timbre = 50 * (21 - envelope_index) / 15;
break;
default:
note_timbre = 0;
@@ -118,10 +156,10 @@ float voice_envelope(float frequency) {
frequency = (rand() % (int)(2000)) + 3000;
switch (envelope_index) {
case 0 ... 15:
note_timbre = 0.5;
note_timbre = 50;
break;
case 16 ... 20:
note_timbre = 0.5 * (21 - envelope_index) / 5;
note_timbre = 50 * (21 - envelope_index) / 5;
break;
default:
note_timbre = 0;
@@ -133,10 +171,10 @@ float voice_envelope(float frequency) {
frequency = (rand() % (int)(2000)) + 3000;
switch (envelope_index) {
case 0 ... 35:
note_timbre = 0.5;
note_timbre = 50;
break;
case 36 ... 50:
note_timbre = 0.5 * (51 - envelope_index) / 15;
note_timbre = 50 * (51 - envelope_index) / 15;
break;
default:
note_timbre = 0;
@@ -145,8 +183,7 @@ float voice_envelope(float frequency) {
}
break;
case butts_fader:
glissando = true;
polyphony_rate = 0;
glissando = true;
switch (compensated_index) {
case 0 ... 9:
frequency = frequency / 4;
@@ -159,7 +196,7 @@ float voice_envelope(float frequency) {
break;
case 20 ... 200:
note_timbre = .125 - pow(((float)compensated_index - 20) / (200 - 20), 2) * .125;
note_timbre = 12 - (uint8_t)(pow(((float)compensated_index - 20) / (200 - 20), 2) * 12.5);
break;
default:
@@ -169,7 +206,6 @@ float voice_envelope(float frequency) {
break;
// case octave_crunch:
// polyphony_rate = 0;
// switch (compensated_index) {
// case 0 ... 9:
// case 20 ... 24:
@@ -187,14 +223,13 @@ float voice_envelope(float frequency) {
// default:
// note_timbre = TIMBRE_12;
// break;
// break;
// }
// break;
case duty_osc:
// This slows the loop down a substantial amount, so higher notes may freeze
glissando = true;
polyphony_rate = 0;
glissando = true;
switch (compensated_index) {
default:
# define OCS_SPEED 10
@@ -202,38 +237,36 @@ float voice_envelope(float frequency) {
// 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;
note_timbre = (uint8_t)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;
glissando = true;
note_timbre = (uint8_t)(100 * (envelope_index % 2) * .125 + .375 * 2);
if ((envelope_index % 4) == 0) note_timbre = 50;
if ((envelope_index % 8) == 0) note_timbre = 0;
break;
case delayed_vibrato:
glissando = true;
polyphony_rate = 0;
note_timbre = TIMBRE_50;
glissando = true;
note_timbre = TIMBRE_50;
# define VOICE_VIBRATO_DELAY 150
# define VOICE_VIBRATO_SPEED 50
switch (compensated_index) {
case 0 ... VOICE_VIBRATO_DELAY:
break;
default:
// TODO: merge/replace with voice_add_vibrato above
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;
// note_timbre = 55;
// } else {
// note_timbre = 0.45;
// note_timbre = 45;
// }
// #define VOICE_VIBRATO_DELAY 150
// #define VOICE_VIBRATO_SPEED 50
@@ -246,35 +279,64 @@ float voice_envelope(float frequency) {
// }
// break;
// case duty_fifth_down:
// note_timbre = 0.5;
// note_timbre = TIMBRE_50;
// if ((envelope_index % 3) == 0)
// note_timbre = 0.75;
// note_timbre = TIMBRE_75;
// break;
// case duty_fourth_down:
// note_timbre = 0.0;
// note_timbre = 0;
// if ((envelope_index % 12) == 0)
// note_timbre = 0.75;
// note_timbre = TIMBRE_75;
// if (((envelope_index % 12) % 4) != 1)
// note_timbre = 0.75;
// note_timbre = TIMBRE_75;
// break;
// case duty_third_down:
// note_timbre = 0.5;
// note_timbre = TIMBRE_50;
// if ((envelope_index % 5) == 0)
// note_timbre = 0.75;
// note_timbre = TIMBRE_75;
// break;
// case duty_fifth_third_down:
// note_timbre = 0.5;
// note_timbre = TIMBRE_50;
// if ((envelope_index % 5) == 0)
// note_timbre = 0.75;
// note_timbre = TIMBRE_75;
// if ((envelope_index % 3) == 0)
// note_timbre = 0.25;
// note_timbre = TIMBRE_25;
// break;
#endif
#endif // AUDIO_VOICES
default:
break;
}
#ifdef AUDIO_VOICES
if (vibrato && (vibrato_strength > 0)) {
frequency = voice_add_vibrato(frequency);
}
if (glissando) {
// TODO: where to keep track of the start-frequency?
// frequency = voice_add_glissando(??, frequency);
}
#endif // AUDIO_VOICES
return frequency;
}
// Vibrato functions
void voice_set_vibrato_rate(float rate) { vibrato_rate = rate; }
void voice_increase_vibrato_rate(float change) { vibrato_rate *= change; }
void voice_decrease_vibrato_rate(float change) { vibrato_rate /= change; }
void voice_set_vibrato_strength(float strength) { vibrato_strength = strength; }
void voice_increase_vibrato_strength(float change) { vibrato_strength *= change; }
void voice_decrease_vibrato_strength(float change) { vibrato_strength /= change; }
// Timbre functions
void voice_set_timbre(uint8_t timbre) {
if ((timbre > 0) && (timbre < 100)) {
note_timbre = timbre;
}
}
uint8_t voice_get_timbre(void) { return note_timbre; }
+23 -6
View File
@@ -1,4 +1,5 @@
/* Copyright 2016 Jack Humbert
* Copyright 2020 JohSchneider
*
* 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
@@ -13,6 +14,8 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#if defined(__AVR__)
@@ -21,14 +24,12 @@
#include "wait.h"
#include "luts.h"
#ifndef VOICES_H
# define VOICES_H
float voice_envelope(float frequency);
typedef enum {
default_voice,
# ifdef AUDIO_VOICES
#ifdef AUDIO_VOICES
vibrating,
something,
drums,
butts_fader,
@@ -41,7 +42,7 @@ typedef enum {
// duty_fourth_down,
// duty_third_down,
// duty_fifth_third_down,
# endif
#endif
number_of_voices // important that this is last
} voice_type;
@@ -49,4 +50,20 @@ void set_voice(voice_type v);
void voice_iterate(void);
void voice_deiterate(void);
#endif
// Vibrato functions
void voice_set_vibrato_rate(float rate);
void voice_increase_vibrato_rate(float change);
void voice_decrease_vibrato_rate(float change);
void voice_set_vibrato_strength(float strength);
void voice_increase_vibrato_strength(float change);
void voice_decrease_vibrato_strength(float change);
// Timbre functions
/**
* @brief set the global timbre for tones to be played
* @note: only applies to pwm implementations - where it adjusts the duty-cycle
* @note: using any instrument from voices.[ch] other than 'default' may override the set value
* @param[in]: timbre: valid range is (0,100)
*/
void voice_set_timbre(uint8_t timbre);
uint8_t voice_get_timbre(void);
-36
View File
@@ -1,36 +0,0 @@
/* 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};
+13 -5
View File
@@ -3,6 +3,11 @@
#include "backlight_driver_common.h"
#include "debug.h"
// Maximum duty cycle limit
#ifndef BACKLIGHT_LIMIT_VAL
# define BACKLIGHT_LIMIT_VAL 255
#endif
// This logic is a bit complex, we support 3 setups:
//
// 1. Hardware PWM when backlight is wired to a PWM pin.
@@ -63,7 +68,7 @@
# define COMxx1 COM3A1
# define OCRxx OCR3A
# endif
#elif (defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega32U2__)) && (BACKLIGHT_PIN == B7 || BACKLIGHT_PIN == C5 || BACKLIGHT_PIN == C6)
#elif (defined(__AVR_AT90USB162__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega32U2__)) && (BACKLIGHT_PIN == B7 || BACKLIGHT_PIN == C5 || BACKLIGHT_PIN == C6)
# define HARDWARE_PWM
# define ICRx ICR1
# define TCCRxA TCCR1A
@@ -121,7 +126,7 @@
# define COMxx1 COM1B1
# define OCRxx OCR1B
# endif
#elif !defined(B5_AUDIO) && !defined(B6_AUDIO) && !defined(B7_AUDIO)
#elif (AUDIO_PIN != B5) && (AUDIO_PIN != B6) && (AUDIO_PIN != B7) && (AUDIO_PIN_ALT != B5) && (AUDIO_PIN_ALT != B6) && (AUDIO_PIN_ALT != B7)
// Timer 1 is not in use by Audio feature, Backlight can use it
# pragma message "Using hardware timer 1 with software PWM"
# define HARDWARE_PWM
@@ -140,7 +145,7 @@
# define OCIExA OCIE1A
# define OCRxx OCR1A
#elif !defined(C6_AUDIO) && !defined(C5_AUDIO) && !defined(C4_AUDIO)
#elif (AUDIO_PIN != C4) && (AUDIO_PIN != C5) && (AUDIO_PIN != C6)
# pragma message "Using hardware timer 3 with software PWM"
// Timer 3 is not in use by Audio feature, Backlight can use it
# define HARDWARE_PWM
@@ -240,6 +245,9 @@ static uint16_t cie_lightness(uint16_t v) {
}
}
// rescale the supplied backlight value to be in terms of the value limit
static uint32_t rescale_limit_val(uint32_t val) { return (val * (BACKLIGHT_LIMIT_VAL + 1)) / 256; }
// range for val is [0..TIMER_TOP]. PWM pin is high while the timer count is below val.
static inline void set_pwm(uint16_t val) { OCRxx = val; }
@@ -269,7 +277,7 @@ void backlight_set(uint8_t level) {
#endif
}
// Set the brightness
set_pwm(cie_lightness(TIMER_TOP * (uint32_t)level / BACKLIGHT_LEVELS));
set_pwm(cie_lightness(rescale_limit_val(TIMER_TOP * (uint32_t)level / BACKLIGHT_LEVELS)));
}
void backlight_task(void) {}
@@ -375,7 +383,7 @@ ISR(TIMERx_OVF_vect)
breathing_interrupt_disable();
}
set_pwm(cie_lightness(scale_backlight((uint16_t)pgm_read_byte(&breathing_table[index]) * 0x0101U)));
set_pwm(cie_lightness(rescale_limit_val(scale_backlight((uint16_t)pgm_read_byte(&breathing_table[index]) * 0x0101U))));
}
#endif // BACKLIGHT_BREATHING
+12 -2
View File
@@ -3,6 +3,11 @@
#include <hal.h>
#include "debug.h"
// Maximum duty cycle limit
#ifndef BACKLIGHT_LIMIT_VAL
# define BACKLIGHT_LIMIT_VAL 255
#endif
// GPIOV2 && GPIOV3
#ifndef BACKLIGHT_PAL_MODE
# define BACKLIGHT_PAL_MODE 2
@@ -58,6 +63,11 @@ static uint16_t cie_lightness(uint16_t v) {
}
}
static uint32_t rescale_limit_val(uint32_t val) {
// rescale the supplied backlight value to be in terms of the value limit
return (val * (BACKLIGHT_LIMIT_VAL + 1)) / 256;
}
void backlight_init_ports(void) {
#ifdef USE_GPIOV1
palSetPadMode(PAL_PORT(BACKLIGHT_PIN), PAL_PAD(BACKLIGHT_PIN), PAL_MODE_STM32_ALTERNATE_PUSHPULL);
@@ -85,7 +95,7 @@ void backlight_set(uint8_t level) {
pwmDisableChannel(&BACKLIGHT_PWM_DRIVER, BACKLIGHT_PWM_CHANNEL - 1);
} else {
// Turn backlight on
uint32_t duty = (uint32_t)(cie_lightness(0xFFFF * (uint32_t)level / BACKLIGHT_LEVELS));
uint32_t duty = (uint32_t)(cie_lightness(rescale_limit_val(0xFFFF * (uint32_t)level / BACKLIGHT_LEVELS)));
pwmEnableChannel(&BACKLIGHT_PWM_DRIVER, BACKLIGHT_PWM_CHANNEL - 1, PWM_FRACTION_TO_WIDTH(&BACKLIGHT_PWM_DRIVER, 0xFFFF, duty));
}
}
@@ -129,7 +139,7 @@ void breathing_callback(PWMDriver *pwmp) {
static uint16_t breathing_counter = 0;
breathing_counter = (breathing_counter + 1) % (breathing_period * 256);
uint8_t index = breathing_counter / interval % BREATHING_STEPS;
uint32_t duty = cie_lightness(scale_backlight(breathing_table[index] * 256));
uint32_t duty = cie_lightness(rescale_limit_val(scale_backlight(breathing_table[index] * 256)));
chSysLockFromISR();
pwmEnableChannelI(pwmp, BACKLIGHT_PWM_CHANNEL - 1, PWM_FRACTION_TO_WIDTH(&BACKLIGHT_PWM_DRIVER, 0xFFFF, duty));
+123
View File
@@ -0,0 +1,123 @@
/*
Copyright 2011 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 "util.h"
// bit population - return number of on-bit
__attribute__((noinline)) uint8_t bitpop(uint8_t bits) {
uint8_t c;
for (c = 0; bits; c++) bits &= bits - 1;
return c;
/*
const uint8_t bit_count[] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 };
return bit_count[bits>>4] + bit_count[bits&0x0F]
*/
}
uint8_t bitpop16(uint16_t bits) {
uint8_t c;
for (c = 0; bits; c++) bits &= bits - 1;
return c;
}
uint8_t bitpop32(uint32_t bits) {
uint8_t c;
for (c = 0; bits; c++) bits &= bits - 1;
return c;
}
// most significant on-bit - return highest location of on-bit
// NOTE: return 0 when bit0 is on or all bits are off
__attribute__((noinline)) uint8_t biton(uint8_t bits) {
uint8_t n = 0;
if (bits >> 4) {
bits >>= 4;
n += 4;
}
if (bits >> 2) {
bits >>= 2;
n += 2;
}
if (bits >> 1) {
bits >>= 1;
n += 1;
}
return n;
}
uint8_t biton16(uint16_t bits) {
uint8_t n = 0;
if (bits >> 8) {
bits >>= 8;
n += 8;
}
if (bits >> 4) {
bits >>= 4;
n += 4;
}
if (bits >> 2) {
bits >>= 2;
n += 2;
}
if (bits >> 1) {
bits >>= 1;
n += 1;
}
return n;
}
uint8_t biton32(uint32_t bits) {
uint8_t n = 0;
if (bits >> 16) {
bits >>= 16;
n += 16;
}
if (bits >> 8) {
bits >>= 8;
n += 8;
}
if (bits >> 4) {
bits >>= 4;
n += 4;
}
if (bits >> 2) {
bits >>= 2;
n += 2;
}
if (bits >> 1) {
bits >>= 1;
n += 1;
}
return n;
}
__attribute__((noinline)) uint8_t bitrev(uint8_t bits) {
bits = (bits & 0x0f) << 4 | (bits & 0xf0) >> 4;
bits = (bits & 0b00110011) << 2 | (bits & 0b11001100) >> 2;
bits = (bits & 0b01010101) << 1 | (bits & 0b10101010) >> 1;
return bits;
}
uint16_t bitrev16(uint16_t bits) {
bits = bitrev(bits & 0x00ff) << 8 | bitrev((bits & 0xff00) >> 8);
return bits;
}
uint32_t bitrev32(uint32_t bits) {
bits = (uint32_t)bitrev16(bits & 0x0000ffff) << 16 | bitrev16((bits & 0xffff0000) >> 16);
return bits;
}
+40
View File
@@ -0,0 +1,40 @@
/*
Copyright 2011 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/>.
*/
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
uint8_t bitpop(uint8_t bits);
uint8_t bitpop16(uint16_t bits);
uint8_t bitpop32(uint32_t bits);
uint8_t biton(uint8_t bits);
uint8_t biton16(uint16_t bits);
uint8_t biton32(uint32_t bits);
uint8_t bitrev(uint8_t bits);
uint16_t bitrev16(uint16_t bits);
uint32_t bitrev32(uint32_t bits);
#ifdef __cplusplus
}
#endif
+29 -4
View File
@@ -14,8 +14,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef COLOR_H
#define COLOR_H
#pragma once
#include <stdint.h>
#include <stdbool.h>
@@ -36,20 +35,47 @@
# define LED_TYPE RGB
#endif
// WS2812 specific layout
#define WS2812_BYTE_ORDER_RGB 0
#define WS2812_BYTE_ORDER_GRB 1
#define WS2812_BYTE_ORDER_BGR 2
#ifndef WS2812_BYTE_ORDER
# define WS2812_BYTE_ORDER WS2812_BYTE_ORDER_GRB
#endif
typedef struct PACKED {
#if (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_GRB)
uint8_t g;
uint8_t r;
uint8_t b;
#elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_RGB)
uint8_t r;
uint8_t g;
uint8_t b;
#elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_BGR)
uint8_t b;
uint8_t g;
uint8_t r;
#endif
} cRGB;
typedef cRGB RGB;
// WS2812 specific layout
typedef struct PACKED {
#if (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_GRB)
uint8_t g;
uint8_t r;
uint8_t b;
#elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_RGB)
uint8_t r;
uint8_t g;
uint8_t b;
#elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_BGR)
uint8_t b;
uint8_t g;
uint8_t r;
#endif
uint8_t w;
} cRGBW;
@@ -68,4 +94,3 @@ RGB hsv_to_rgb_nocie(HSV hsv);
#ifdef RGBW
void convert_rgb_to_rgbw(LED_TYPE *led);
#endif
#endif // COLOR_H
+793
View File
@@ -0,0 +1,793 @@
/*
Copyright 2011 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 <stdint.h>
#include <stdbool.h>
#include "wait.h"
#include "keycode.h"
#include "host.h"
#include "keymap.h"
#include "print.h"
#include "debug.h"
#include "util.h"
#include "timer.h"
#include "keyboard.h"
#include "bootloader.h"
#include "action_layer.h"
#include "action_util.h"
#include "eeconfig.h"
#include "sleep_led.h"
#include "led.h"
#include "command.h"
#include "quantum.h"
#include "version.h"
#ifdef BACKLIGHT_ENABLE
# include "backlight.h"
#endif
#if defined(MOUSEKEY_ENABLE) && !defined(MK_3_SPEED)
# include "mousekey.h"
#endif
#ifdef AUDIO_ENABLE
# include "audio.h"
#endif /* AUDIO_ENABLE */
#ifdef VIAL_ENABLE
# include "vial.h"
#endif
static bool command_common(uint8_t code);
static void command_common_help(void);
static void print_version(void);
static void print_status(void);
static bool command_console(uint8_t code);
static void command_console_help(void);
#if defined(MOUSEKEY_ENABLE) && !defined(MK_3_SPEED)
static bool mousekey_console(uint8_t code);
static void mousekey_console_help(void);
#endif
static void switch_default_layer(uint8_t layer);
command_state_t command_state = ONESHOT;
bool command_proc(uint8_t code) {
switch (command_state) {
case ONESHOT:
if (!IS_COMMAND()) return false;
return (command_extra(code) || command_common(code));
break;
case CONSOLE:
if (IS_COMMAND())
return (command_extra(code) || command_common(code));
else
return (command_console_extra(code) || command_console(code));
break;
#if defined(MOUSEKEY_ENABLE) && !defined(MK_3_SPEED)
case MOUSEKEY:
mousekey_console(code);
break;
#endif
default:
command_state = ONESHOT;
return false;
}
return true;
}
/* TODO: Refactoring is needed. */
/* This allows to define extra commands. return false when not processed. */
bool command_extra(uint8_t code) __attribute__((weak));
bool command_extra(uint8_t code) {
(void)code;
return false;
}
bool command_console_extra(uint8_t code) __attribute__((weak));
bool command_console_extra(uint8_t code) {
(void)code;
return false;
}
/***********************************************************
* Command common
***********************************************************/
static void command_common_help(void) {
print("\n\t- Magic -\n" STR(MAGIC_KEY_DEBUG) ": Debug Message Toggle\n" STR(MAGIC_KEY_DEBUG_MATRIX) ": Matrix Debug Mode Toggle - Show keypresses in matrix grid\n" STR(MAGIC_KEY_DEBUG_KBD) ": Keyboard Debug Toggle - Show keypress report\n" STR(MAGIC_KEY_DEBUG_MOUSE) ": Debug Mouse Toggle\n" STR(MAGIC_KEY_VERSION) ": Version\n" STR(MAGIC_KEY_STATUS) ": Status\n" STR(MAGIC_KEY_CONSOLE) ": Activate Console Mode\n"
#if MAGIC_KEY_SWITCH_LAYER_WITH_CUSTOM
STR(MAGIC_KEY_LAYER0) ": Switch to Layer 0\n" STR(MAGIC_KEY_LAYER1) ": Switch to Layer 1\n" STR(MAGIC_KEY_LAYER2) ": Switch to Layer 2\n" STR(MAGIC_KEY_LAYER3) ": Switch to Layer 3\n" STR(MAGIC_KEY_LAYER4) ": Switch to Layer 4\n" STR(MAGIC_KEY_LAYER5) ": Switch to Layer 5\n" STR(MAGIC_KEY_LAYER6) ": Switch to Layer 6\n" STR(MAGIC_KEY_LAYER7) ": Switch to Layer 7\n" STR(MAGIC_KEY_LAYER8) ": Switch to Layer 8\n" STR(MAGIC_KEY_LAYER9) ": Switch to Layer 9\n"
#endif
#if MAGIC_KEY_SWITCH_LAYER_WITH_FKEYS
"F1-F10: Switch to Layer 0-9 (F10 = L0)\n"
#endif
#if MAGIC_KEY_SWITCH_LAYER_WITH_NKEYS
"0-9: Switch to Layer 0-9\n"
#endif
STR(MAGIC_KEY_LAYER0_ALT) ": Switch to Layer 0 (alternate)\n"
STR(MAGIC_KEY_BOOTLOADER) ": Jump to Bootloader\n" STR(MAGIC_KEY_BOOTLOADER_ALT) ": Jump to Bootloader (alternate)\n"
#ifdef KEYBOARD_LOCK_ENABLE
STR(MAGIC_KEY_LOCK) ": Lock Keyboard\n"
#endif
STR(MAGIC_KEY_EEPROM) ": Print EEPROM Settings\n" STR(MAGIC_KEY_EEPROM_CLEAR) ": Clear EEPROM\n"
#ifdef NKRO_ENABLE
STR(MAGIC_KEY_NKRO) ": NKRO Toggle\n"
#endif
#ifdef SLEEP_LED_ENABLE
STR(MAGIC_KEY_SLEEP_LED) ": Sleep LED Test\n"
#endif
);
}
static void print_version(void) {
// print version & information
print("\n\t- Version -\n");
print("VID: " STR(VENDOR_ID) "(" STR(MANUFACTURER) ") "
"PID: " STR(PRODUCT_ID) "(" STR(PRODUCT) ") "
"VER: " STR(DEVICE_VER) "\n");
print("BUILD: (" __DATE__ ")\n");
#ifndef SKIP_VERSION
# ifdef PROTOCOL_CHIBIOS
print("CHIBIOS: " STR(CHIBIOS_VERSION) ", CONTRIB: " STR(CHIBIOS_CONTRIB_VERSION) "\n");
# endif
#endif
/* build options */
print("OPTIONS:"
#ifdef PROTOCOL_LUFA
" LUFA"
#endif
#ifdef PROTOCOL_VUSB
" VUSB"
#endif
#ifdef BOOTMAGIC_ENABLE
" BOOTMAGIC"
#endif
#ifdef MOUSEKEY_ENABLE
" MOUSEKEY"
#endif
#ifdef EXTRAKEY_ENABLE
" EXTRAKEY"
#endif
#ifdef CONSOLE_ENABLE
" CONSOLE"
#endif
#ifdef COMMAND_ENABLE
" COMMAND"
#endif
#ifdef NKRO_ENABLE
" NKRO"
#endif
#ifdef LTO_ENABLE
" LTO"
#endif
" " STR(BOOTLOADER_SIZE) "\n");
print("GCC: " STR(__GNUC__) "." STR(__GNUC_MINOR__) "." STR(__GNUC_PATCHLEVEL__)
#if defined(__AVR__)
" AVR-LIBC: " __AVR_LIBC_VERSION_STRING__ " AVR_ARCH: avr" STR(__AVR_ARCH__)
#endif
"\n");
return;
}
static void print_status(void) {
print("\n\t- Status -\n");
print_val_hex8(host_keyboard_leds());
#ifndef PROTOCOL_VUSB
// these aren't set on the V-USB protocol, so we just ignore them for now
print_val_hex8(keyboard_protocol);
print_val_hex8(keyboard_idle);
#endif
#ifdef NKRO_ENABLE
print_val_hex8(keymap_config.nkro);
#endif
print_val_hex32(timer_read32());
return;
}
static void print_eeconfig(void) {
// Print these variables if NO_PRINT or USER_PRINT are not defined.
#if !defined(NO_PRINT) && !defined(USER_PRINT)
print("default_layer: ");
print_dec(eeconfig_read_default_layer());
print("\n");
debug_config_t dc;
dc.raw = eeconfig_read_debug();
print("debug_config.raw: ");
print_hex8(dc.raw);
print("\n");
print(".enable: ");
print_dec(dc.enable);
print("\n");
print(".matrix: ");
print_dec(dc.matrix);
print("\n");
print(".keyboard: ");
print_dec(dc.keyboard);
print("\n");
print(".mouse: ");
print_dec(dc.mouse);
print("\n");
keymap_config_t kc;
kc.raw = eeconfig_read_keymap();
print("keymap_config.raw: ");
print_hex8(kc.raw);
print("\n");
print(".swap_control_capslock: ");
print_dec(kc.swap_control_capslock);
print("\n");
print(".capslock_to_control: ");
print_dec(kc.capslock_to_control);
print("\n");
print(".swap_lctl_lgui: ");
print_dec(kc.swap_lctl_lgui);
print("\n");
print(".swap_rctl_rgui: ");
print_dec(kc.swap_rctl_rgui);
print("\n");
print(".swap_lalt_lgui: ");
print_dec(kc.swap_lalt_lgui);
print("\n");
print(".swap_ralt_rgui: ");
print_dec(kc.swap_ralt_rgui);
print("\n");
print(".no_gui: ");
print_dec(kc.no_gui);
print("\n");
print(".swap_grave_esc: ");
print_dec(kc.swap_grave_esc);
print("\n");
print(".swap_backslash_backspace: ");
print_dec(kc.swap_backslash_backspace);
print("\n");
print(".nkro: ");
print_dec(kc.nkro);
print("\n");
# ifdef BACKLIGHT_ENABLE
backlight_config_t bc;
bc.raw = eeconfig_read_backlight();
print("backlight_config.raw: ");
print_hex8(bc.raw);
print("\n");
print(".enable: ");
print_dec(bc.enable);
print("\n");
print(".level: ");
print_dec(bc.level);
print("\n");
# endif /* BACKLIGHT_ENABLE */
#endif /* !NO_PRINT */
}
static bool command_common(uint8_t code) {
#ifdef KEYBOARD_LOCK_ENABLE
static host_driver_t *host_driver = 0;
#endif
switch (code) {
#ifdef SLEEP_LED_ENABLE
// test breathing sleep LED
case MAGIC_KC(MAGIC_KEY_SLEEP_LED):
print("Sleep LED Test\n");
sleep_led_toggle();
led_set(host_keyboard_leds());
break;
#endif
// print stored eeprom config
case MAGIC_KC(MAGIC_KEY_EEPROM):
print("eeconfig:\n");
print_eeconfig();
break;
// clear eeprom
case MAGIC_KC(MAGIC_KEY_EEPROM_CLEAR):
print("Clearing EEPROM\n");
eeconfig_init();
break;
#ifdef KEYBOARD_LOCK_ENABLE
// lock/unlock keyboard
case MAGIC_KC(MAGIC_KEY_LOCK):
if (host_get_driver()) {
host_driver = host_get_driver();
clear_keyboard();
host_set_driver(0);
print("Locked.\n");
} else {
host_set_driver(host_driver);
print("Unlocked.\n");
}
break;
#endif
// print help
case MAGIC_KC(MAGIC_KEY_HELP):
case MAGIC_KC(MAGIC_KEY_HELP_ALT):
command_common_help();
break;
// activate console
case MAGIC_KC(MAGIC_KEY_CONSOLE):
debug_matrix = false;
debug_keyboard = false;
debug_mouse = false;
debug_enable = false;
command_console_help();
print("C> ");
command_state = CONSOLE;
break;
// jump to bootloader
case MAGIC_KC(MAGIC_KEY_BOOTLOADER):
case MAGIC_KC(MAGIC_KEY_BOOTLOADER_ALT):
print("\n\nJumping to bootloader... ");
#ifdef VIAL_ENABLE
if (vial_unlocked)
#endif
reset_keyboard();
break;
// debug toggle
case MAGIC_KC(MAGIC_KEY_DEBUG):
debug_enable = !debug_enable;
if (debug_enable) {
print("\ndebug: on\n");
} else {
print("\ndebug: off\n");
debug_matrix = false;
debug_keyboard = false;
debug_mouse = false;
}
break;
// debug matrix toggle
case MAGIC_KC(MAGIC_KEY_DEBUG_MATRIX):
debug_matrix = !debug_matrix;
if (debug_matrix) {
print("\nmatrix: on\n");
debug_enable = true;
} else {
print("\nmatrix: off\n");
}
break;
// debug keyboard toggle
case MAGIC_KC(MAGIC_KEY_DEBUG_KBD):
debug_keyboard = !debug_keyboard;
if (debug_keyboard) {
print("\nkeyboard: on\n");
debug_enable = true;
} else {
print("\nkeyboard: off\n");
}
break;
// debug mouse toggle
case MAGIC_KC(MAGIC_KEY_DEBUG_MOUSE):
debug_mouse = !debug_mouse;
if (debug_mouse) {
print("\nmouse: on\n");
debug_enable = true;
} else {
print("\nmouse: off\n");
}
break;
// print version
case MAGIC_KC(MAGIC_KEY_VERSION):
print_version();
break;
// print status
case MAGIC_KC(MAGIC_KEY_STATUS):
print_status();
break;
#ifdef NKRO_ENABLE
// NKRO toggle
case MAGIC_KC(MAGIC_KEY_NKRO):
clear_keyboard(); // clear to prevent stuck keys
keymap_config.nkro = !keymap_config.nkro;
if (keymap_config.nkro) {
print("NKRO: on\n");
} else {
print("NKRO: off\n");
}
break;
#endif
// switch layers
case MAGIC_KC(MAGIC_KEY_LAYER0_ALT):
switch_default_layer(0);
break;
#if MAGIC_KEY_SWITCH_LAYER_WITH_CUSTOM
case MAGIC_KC(MAGIC_KEY_LAYER0):
switch_default_layer(0);
break;
case MAGIC_KC(MAGIC_KEY_LAYER1):
switch_default_layer(1);
break;
case MAGIC_KC(MAGIC_KEY_LAYER2):
switch_default_layer(2);
break;
case MAGIC_KC(MAGIC_KEY_LAYER3):
switch_default_layer(3);
break;
case MAGIC_KC(MAGIC_KEY_LAYER4):
switch_default_layer(4);
break;
case MAGIC_KC(MAGIC_KEY_LAYER5):
switch_default_layer(5);
break;
case MAGIC_KC(MAGIC_KEY_LAYER6):
switch_default_layer(6);
break;
case MAGIC_KC(MAGIC_KEY_LAYER7):
switch_default_layer(7);
break;
case MAGIC_KC(MAGIC_KEY_LAYER8):
switch_default_layer(8);
break;
case MAGIC_KC(MAGIC_KEY_LAYER9):
switch_default_layer(9);
break;
#endif
#if MAGIC_KEY_SWITCH_LAYER_WITH_FKEYS
case KC_F1 ... KC_F9:
switch_default_layer((code - KC_F1) + 1);
break;
case KC_F10:
switch_default_layer(0);
break;
#endif
#if MAGIC_KEY_SWITCH_LAYER_WITH_NKEYS
case KC_1 ... KC_9:
switch_default_layer((code - KC_1) + 1);
break;
case KC_0:
switch_default_layer(0);
break;
#endif
default:
print("?");
return false;
}
return true;
}
/***********************************************************
* Command console
***********************************************************/
static void command_console_help(void) {
print("\n\t- Console -\n"
"ESC/q: quit\n"
#ifdef MOUSEKEY_ENABLE
"m: mousekey\n"
#endif
);
}
static bool command_console(uint8_t code) {
switch (code) {
case KC_H:
case KC_SLASH: /* ? */
command_console_help();
break;
case KC_Q:
case KC_ESC:
command_state = ONESHOT;
return false;
#if defined(MOUSEKEY_ENABLE) && !defined(MK_3_SPEED)
case KC_M:
mousekey_console_help();
print("M> ");
command_state = MOUSEKEY;
return true;
#endif
default:
print("?");
return false;
}
print("C> ");
return true;
}
#if defined(MOUSEKEY_ENABLE) && !defined(MK_3_SPEED)
/***********************************************************
* Mousekey console
***********************************************************/
static uint8_t mousekey_param = 0;
static void mousekey_param_print(void) {
// Print these variables if NO_PRINT or USER_PRINT are not defined.
# if !defined(NO_PRINT) && !defined(USER_PRINT)
print("\n\t- Values -\n");
print("1: delay(*10ms): ");
print_dec(mk_delay);
print("\n");
print("2: interval(ms): ");
print_dec(mk_interval);
print("\n");
print("3: max_speed: ");
print_dec(mk_max_speed);
print("\n");
print("4: time_to_max: ");
print_dec(mk_time_to_max);
print("\n");
print("5: wheel_max_speed: ");
print_dec(mk_wheel_max_speed);
print("\n");
print("6: wheel_time_to_max: ");
print_dec(mk_wheel_time_to_max);
print("\n");
# endif /* !NO_PRINT */
}
//#define PRINT_SET_VAL(v) print(#v " = "); print_dec(v); print("\n");
# define PRINT_SET_VAL(v) xprintf(# v " = %d\n", (v))
static void mousekey_param_inc(uint8_t param, uint8_t inc) {
switch (param) {
case 1:
if (mk_delay + inc < UINT8_MAX)
mk_delay += inc;
else
mk_delay = UINT8_MAX;
PRINT_SET_VAL(mk_delay);
break;
case 2:
if (mk_interval + inc < UINT8_MAX)
mk_interval += inc;
else
mk_interval = UINT8_MAX;
PRINT_SET_VAL(mk_interval);
break;
case 3:
if (mk_max_speed + inc < UINT8_MAX)
mk_max_speed += inc;
else
mk_max_speed = UINT8_MAX;
PRINT_SET_VAL(mk_max_speed);
break;
case 4:
if (mk_time_to_max + inc < UINT8_MAX)
mk_time_to_max += inc;
else
mk_time_to_max = UINT8_MAX;
PRINT_SET_VAL(mk_time_to_max);
break;
case 5:
if (mk_wheel_max_speed + inc < UINT8_MAX)
mk_wheel_max_speed += inc;
else
mk_wheel_max_speed = UINT8_MAX;
PRINT_SET_VAL(mk_wheel_max_speed);
break;
case 6:
if (mk_wheel_time_to_max + inc < UINT8_MAX)
mk_wheel_time_to_max += inc;
else
mk_wheel_time_to_max = UINT8_MAX;
PRINT_SET_VAL(mk_wheel_time_to_max);
break;
}
}
static void mousekey_param_dec(uint8_t param, uint8_t dec) {
switch (param) {
case 1:
if (mk_delay > dec)
mk_delay -= dec;
else
mk_delay = 0;
PRINT_SET_VAL(mk_delay);
break;
case 2:
if (mk_interval > dec)
mk_interval -= dec;
else
mk_interval = 0;
PRINT_SET_VAL(mk_interval);
break;
case 3:
if (mk_max_speed > dec)
mk_max_speed -= dec;
else
mk_max_speed = 0;
PRINT_SET_VAL(mk_max_speed);
break;
case 4:
if (mk_time_to_max > dec)
mk_time_to_max -= dec;
else
mk_time_to_max = 0;
PRINT_SET_VAL(mk_time_to_max);
break;
case 5:
if (mk_wheel_max_speed > dec)
mk_wheel_max_speed -= dec;
else
mk_wheel_max_speed = 0;
PRINT_SET_VAL(mk_wheel_max_speed);
break;
case 6:
if (mk_wheel_time_to_max > dec)
mk_wheel_time_to_max -= dec;
else
mk_wheel_time_to_max = 0;
PRINT_SET_VAL(mk_wheel_time_to_max);
break;
}
}
static void mousekey_console_help(void) {
print("\n\t- Mousekey -\n"
"ESC/q: quit\n"
"1: delay(*10ms)\n"
"2: interval(ms)\n"
"3: max_speed\n"
"4: time_to_max\n"
"5: wheel_max_speed\n"
"6: wheel_time_to_max\n"
"\n"
"p: print values\n"
"d: set defaults\n"
"up: +1\n"
"down: -1\n"
"pgup: +10\n"
"pgdown: -10\n"
"\n"
"speed = delta * max_speed * (repeat / time_to_max)\n");
xprintf("where delta: cursor=%d, wheel=%d\n"
"See http://en.wikipedia.org/wiki/Mouse_keys\n",
MOUSEKEY_MOVE_DELTA, MOUSEKEY_WHEEL_DELTA);
}
static bool mousekey_console(uint8_t code) {
switch (code) {
case KC_H:
case KC_SLASH: /* ? */
mousekey_console_help();
break;
case KC_Q:
case KC_ESC:
if (mousekey_param) {
mousekey_param = 0;
} else {
print("C> ");
command_state = CONSOLE;
return false;
}
break;
case KC_P:
mousekey_param_print();
break;
case KC_1:
case KC_2:
case KC_3:
case KC_4:
case KC_5:
case KC_6:
mousekey_param = numkey2num(code);
break;
case KC_UP:
mousekey_param_inc(mousekey_param, 1);
break;
case KC_DOWN:
mousekey_param_dec(mousekey_param, 1);
break;
case KC_PGUP:
mousekey_param_inc(mousekey_param, 10);
break;
case KC_PGDN:
mousekey_param_dec(mousekey_param, 10);
break;
case KC_D:
mk_delay = MOUSEKEY_DELAY / 10;
mk_interval = MOUSEKEY_INTERVAL;
mk_max_speed = MOUSEKEY_MAX_SPEED;
mk_time_to_max = MOUSEKEY_TIME_TO_MAX;
mk_wheel_max_speed = MOUSEKEY_WHEEL_MAX_SPEED;
mk_wheel_time_to_max = MOUSEKEY_WHEEL_TIME_TO_MAX;
print("set default\n");
break;
default:
print("?");
return false;
}
if (mousekey_param) {
xprintf("M%d> ", mousekey_param);
} else {
print("M>");
}
return true;
}
#endif
/***********************************************************
* Utilities
***********************************************************/
uint8_t numkey2num(uint8_t code) {
switch (code) {
case KC_1:
return 1;
case KC_2:
return 2;
case KC_3:
return 3;
case KC_4:
return 4;
case KC_5:
return 5;
case KC_6:
return 6;
case KC_7:
return 7;
case KC_8:
return 8;
case KC_9:
return 9;
case KC_0:
return 0;
}
return 0;
}
static void switch_default_layer(uint8_t layer) {
xprintf("L%d\n", layer);
default_layer_set(1UL << layer);
clear_keyboard();
}
+163
View File
@@ -0,0 +1,163 @@
/*
Copyright 2011 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/>.
*/
#pragma once
/* FIXME: Add doxygen comments for the behavioral defines in here. */
/* TODO: Refactoring */
typedef enum { ONESHOT, CONSOLE, MOUSEKEY } command_state_t;
extern command_state_t command_state;
/* This allows to extend commands. Return false when command is not processed. */
bool command_extra(uint8_t code);
bool command_console_extra(uint8_t code);
#ifdef COMMAND_ENABLE
uint8_t numkey2num(uint8_t code);
bool command_proc(uint8_t code);
#else
# define command_proc(code) false
#endif
#ifndef IS_COMMAND
# define IS_COMMAND() (get_mods() == MOD_MASK_SHIFT)
#endif
#ifndef MAGIC_KEY_SWITCH_LAYER_WITH_FKEYS
# define MAGIC_KEY_SWITCH_LAYER_WITH_FKEYS true
#endif
#ifndef MAGIC_KEY_SWITCH_LAYER_WITH_NKEYS
# define MAGIC_KEY_SWITCH_LAYER_WITH_NKEYS true
#endif
#ifndef MAGIC_KEY_SWITCH_LAYER_WITH_CUSTOM
# define MAGIC_KEY_SWITCH_LAYER_WITH_CUSTOM false
#endif
#ifndef MAGIC_KEY_HELP
# define MAGIC_KEY_HELP H
#endif
#ifndef MAGIC_KEY_HELP_ALT
# define MAGIC_KEY_HELP_ALT SLASH
#endif
#ifndef MAGIC_KEY_DEBUG
# define MAGIC_KEY_DEBUG D
#endif
#ifndef MAGIC_KEY_DEBUG_MATRIX
# define MAGIC_KEY_DEBUG_MATRIX X
#endif
#ifndef MAGIC_KEY_DEBUG_KBD
# define MAGIC_KEY_DEBUG_KBD K
#endif
#ifndef MAGIC_KEY_DEBUG_MOUSE
# define MAGIC_KEY_DEBUG_MOUSE M
#endif
#ifndef MAGIC_KEY_VERSION
# define MAGIC_KEY_VERSION V
#endif
#ifndef MAGIC_KEY_STATUS
# define MAGIC_KEY_STATUS S
#endif
#ifndef MAGIC_KEY_CONSOLE
# define MAGIC_KEY_CONSOLE C
#endif
#ifndef MAGIC_KEY_LAYER0
# define MAGIC_KEY_LAYER0 0
#endif
#ifndef MAGIC_KEY_LAYER0_ALT
# define MAGIC_KEY_LAYER0_ALT GRAVE
#endif
#ifndef MAGIC_KEY_LAYER1
# define MAGIC_KEY_LAYER1 1
#endif
#ifndef MAGIC_KEY_LAYER2
# define MAGIC_KEY_LAYER2 2
#endif
#ifndef MAGIC_KEY_LAYER3
# define MAGIC_KEY_LAYER3 3
#endif
#ifndef MAGIC_KEY_LAYER4
# define MAGIC_KEY_LAYER4 4
#endif
#ifndef MAGIC_KEY_LAYER5
# define MAGIC_KEY_LAYER5 5
#endif
#ifndef MAGIC_KEY_LAYER6
# define MAGIC_KEY_LAYER6 6
#endif
#ifndef MAGIC_KEY_LAYER7
# define MAGIC_KEY_LAYER7 7
#endif
#ifndef MAGIC_KEY_LAYER8
# define MAGIC_KEY_LAYER8 8
#endif
#ifndef MAGIC_KEY_LAYER9
# define MAGIC_KEY_LAYER9 9
#endif
#ifndef MAGIC_KEY_BOOTLOADER
# define MAGIC_KEY_BOOTLOADER B
#endif
#ifndef MAGIC_KEY_BOOTLOADER_ALT
# define MAGIC_KEY_BOOTLOADER_ALT ESC
#endif
#ifndef MAGIC_KEY_LOCK
# define MAGIC_KEY_LOCK CAPS
#endif
#ifndef MAGIC_KEY_EEPROM
# define MAGIC_KEY_EEPROM E
#endif
#ifndef MAGIC_KEY_EEPROM_CLEAR
# define MAGIC_KEY_EEPROM_CLEAR BSPACE
#endif
#ifndef MAGIC_KEY_NKRO
# define MAGIC_KEY_NKRO N
#endif
#ifndef MAGIC_KEY_SLEEP_LED
# define MAGIC_KEY_SLEEP_LED Z
#endif
#define XMAGIC_KC(key) KC_##key
#define MAGIC_KC(key) XMAGIC_KC(key)
+4 -312
View File
@@ -16,322 +16,14 @@
#pragma once
#ifndef __ASSEMBLER__
# include "pin_defs.h"
#endif
/* diode directions */
#define COL2ROW 0
#define ROW2COL 1
// useful for direct pin mapping
#define NO_PIN (pin_t)(~0)
#ifdef __AVR__
# ifndef __ASSEMBLER__
# include <avr/io.h>
# endif
# define PORT_SHIFTER 4 // this may be 4 for all AVR chips
// If you want to add more to this list, reference the PINx definitions in these header
// files: https://github.com/vancegroup-mirrors/avr-libc/tree/master/avr-libc/include/avr
# if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__)
# define ADDRESS_BASE 0x00
# define PINB_ADDRESS 0x3
# define PINC_ADDRESS 0x6
# define PIND_ADDRESS 0x9
# define PINE_ADDRESS 0xC
# define PINF_ADDRESS 0xF
# elif defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U2__)
# define ADDRESS_BASE 0x00
# define PINB_ADDRESS 0x3
# define PINC_ADDRESS 0x6
# define PIND_ADDRESS 0x9
# elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)
# define ADDRESS_BASE 0x00
# define PINA_ADDRESS 0x0
# define PINB_ADDRESS 0x3
# define PINC_ADDRESS 0x6
# define PIND_ADDRESS 0x9
# define PINE_ADDRESS 0xC
# define PINF_ADDRESS 0xF
# elif defined(__AVR_ATmega32A__)
# define ADDRESS_BASE 0x10
# define PIND_ADDRESS 0x0
# define PINC_ADDRESS 0x3
# define PINB_ADDRESS 0x6
# define PINA_ADDRESS 0x9
# elif defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__)
# define ADDRESS_BASE 0x00
# define PINB_ADDRESS 0x3
# define PINC_ADDRESS 0x6
# define PIND_ADDRESS 0x9
# elif defined(__AVR_ATtiny85__)
# define ADDRESS_BASE 0x10
# define PINB_ADDRESS 0x6
# else
# error "Pins are not defined"
# endif
/* I/O pins */
# define PINDEF(port, pin) ((PIN##port##_ADDRESS << PORT_SHIFTER) | pin)
# ifdef PORTA
# define A0 PINDEF(A, 0)
# define A1 PINDEF(A, 1)
# define A2 PINDEF(A, 2)
# define A3 PINDEF(A, 3)
# define A4 PINDEF(A, 4)
# define A5 PINDEF(A, 5)
# define A6 PINDEF(A, 6)
# define A7 PINDEF(A, 7)
# endif
# ifdef PORTB
# define B0 PINDEF(B, 0)
# define B1 PINDEF(B, 1)
# define B2 PINDEF(B, 2)
# define B3 PINDEF(B, 3)
# define B4 PINDEF(B, 4)
# define B5 PINDEF(B, 5)
# define B6 PINDEF(B, 6)
# define B7 PINDEF(B, 7)
# endif
# ifdef PORTC
# define C0 PINDEF(C, 0)
# define C1 PINDEF(C, 1)
# define C2 PINDEF(C, 2)
# define C3 PINDEF(C, 3)
# define C4 PINDEF(C, 4)
# define C5 PINDEF(C, 5)
# define C6 PINDEF(C, 6)
# define C7 PINDEF(C, 7)
# endif
# ifdef PORTD
# define D0 PINDEF(D, 0)
# define D1 PINDEF(D, 1)
# define D2 PINDEF(D, 2)
# define D3 PINDEF(D, 3)
# define D4 PINDEF(D, 4)
# define D5 PINDEF(D, 5)
# define D6 PINDEF(D, 6)
# define D7 PINDEF(D, 7)
# endif
# ifdef PORTE
# define E0 PINDEF(E, 0)
# define E1 PINDEF(E, 1)
# define E2 PINDEF(E, 2)
# define E3 PINDEF(E, 3)
# define E4 PINDEF(E, 4)
# define E5 PINDEF(E, 5)
# define E6 PINDEF(E, 6)
# define E7 PINDEF(E, 7)
# endif
# ifdef PORTF
# define F0 PINDEF(F, 0)
# define F1 PINDEF(F, 1)
# define F2 PINDEF(F, 2)
# define F3 PINDEF(F, 3)
# define F4 PINDEF(F, 4)
# define F5 PINDEF(F, 5)
# define F6 PINDEF(F, 6)
# define F7 PINDEF(F, 7)
# endif
# ifndef __ASSEMBLER__
# define _PIN_ADDRESS(p, offset) _SFR_IO8(ADDRESS_BASE + ((p) >> PORT_SHIFTER) + (offset))
// Port X Input Pins Address
# define PINx_ADDRESS(p) _PIN_ADDRESS(p, 0)
// Port X Data Direction Register, 0:input 1:output
# define DDRx_ADDRESS(p) _PIN_ADDRESS(p, 1)
// Port X Data Register
# define PORTx_ADDRESS(p) _PIN_ADDRESS(p, 2)
# endif
#elif defined(PROTOCOL_CHIBIOS)
// Defines mapping for Proton C replacement
# ifdef CONVERT_TO_PROTON_C
// Left side (front)
# define D3 PAL_LINE(GPIOA, 9)
# define D2 PAL_LINE(GPIOA, 10)
// GND
// GND
# define D1 PAL_LINE(GPIOB, 7)
# define D0 PAL_LINE(GPIOB, 6)
# define D4 PAL_LINE(GPIOB, 5)
# define C6 PAL_LINE(GPIOB, 4)
# define D7 PAL_LINE(GPIOB, 3)
# define E6 PAL_LINE(GPIOB, 2)
# define B4 PAL_LINE(GPIOB, 1)
# define B5 PAL_LINE(GPIOB, 0)
// Right side (front)
// RAW
// GND
// RESET
// VCC
# define F4 PAL_LINE(GPIOA, 2)
# define F5 PAL_LINE(GPIOA, 1)
# define F6 PAL_LINE(GPIOA, 0)
# define F7 PAL_LINE(GPIOB, 8)
# define B1 PAL_LINE(GPIOB, 13)
# define B3 PAL_LINE(GPIOB, 14)
# define B2 PAL_LINE(GPIOB, 15)
# define B6 PAL_LINE(GPIOB, 9)
// LEDs (only D5/C13 uses an actual LED)
# ifdef CONVERT_TO_PROTON_C_RXLED
# define D5 PAL_LINE(GPIOC, 14)
# define B0 PAL_LINE(GPIOC, 13)
# else
# define D5 PAL_LINE(GPIOC, 13)
# define B0 PAL_LINE(GPIOC, 14)
# endif
# else
# define A0 PAL_LINE(GPIOA, 0)
# define A1 PAL_LINE(GPIOA, 1)
# define A2 PAL_LINE(GPIOA, 2)
# define A3 PAL_LINE(GPIOA, 3)
# define A4 PAL_LINE(GPIOA, 4)
# define A5 PAL_LINE(GPIOA, 5)
# define A6 PAL_LINE(GPIOA, 6)
# define A7 PAL_LINE(GPIOA, 7)
# define A8 PAL_LINE(GPIOA, 8)
# define A9 PAL_LINE(GPIOA, 9)
# define A10 PAL_LINE(GPIOA, 10)
# define A11 PAL_LINE(GPIOA, 11)
# define A12 PAL_LINE(GPIOA, 12)
# define A13 PAL_LINE(GPIOA, 13)
# define A14 PAL_LINE(GPIOA, 14)
# define A15 PAL_LINE(GPIOA, 15)
# define B0 PAL_LINE(GPIOB, 0)
# define B1 PAL_LINE(GPIOB, 1)
# define B2 PAL_LINE(GPIOB, 2)
# define B3 PAL_LINE(GPIOB, 3)
# define B4 PAL_LINE(GPIOB, 4)
# define B5 PAL_LINE(GPIOB, 5)
# define B6 PAL_LINE(GPIOB, 6)
# define B7 PAL_LINE(GPIOB, 7)
# define B8 PAL_LINE(GPIOB, 8)
# define B9 PAL_LINE(GPIOB, 9)
# define B10 PAL_LINE(GPIOB, 10)
# define B11 PAL_LINE(GPIOB, 11)
# define B12 PAL_LINE(GPIOB, 12)
# define B13 PAL_LINE(GPIOB, 13)
# define B14 PAL_LINE(GPIOB, 14)
# define B15 PAL_LINE(GPIOB, 15)
# define B16 PAL_LINE(GPIOB, 16)
# define B17 PAL_LINE(GPIOB, 17)
# define B18 PAL_LINE(GPIOB, 18)
# define B19 PAL_LINE(GPIOB, 19)
# define C0 PAL_LINE(GPIOC, 0)
# define C1 PAL_LINE(GPIOC, 1)
# define C2 PAL_LINE(GPIOC, 2)
# define C3 PAL_LINE(GPIOC, 3)
# define C4 PAL_LINE(GPIOC, 4)
# define C5 PAL_LINE(GPIOC, 5)
# define C6 PAL_LINE(GPIOC, 6)
# define C7 PAL_LINE(GPIOC, 7)
# define C8 PAL_LINE(GPIOC, 8)
# define C9 PAL_LINE(GPIOC, 9)
# define C10 PAL_LINE(GPIOC, 10)
# define C11 PAL_LINE(GPIOC, 11)
# define C12 PAL_LINE(GPIOC, 12)
# define C13 PAL_LINE(GPIOC, 13)
# define C14 PAL_LINE(GPIOC, 14)
# define C15 PAL_LINE(GPIOC, 15)
# define D0 PAL_LINE(GPIOD, 0)
# define D1 PAL_LINE(GPIOD, 1)
# define D2 PAL_LINE(GPIOD, 2)
# define D3 PAL_LINE(GPIOD, 3)
# define D4 PAL_LINE(GPIOD, 4)
# define D5 PAL_LINE(GPIOD, 5)
# define D6 PAL_LINE(GPIOD, 6)
# define D7 PAL_LINE(GPIOD, 7)
# define D8 PAL_LINE(GPIOD, 8)
# define D9 PAL_LINE(GPIOD, 9)
# define D10 PAL_LINE(GPIOD, 10)
# define D11 PAL_LINE(GPIOD, 11)
# define D12 PAL_LINE(GPIOD, 12)
# define D13 PAL_LINE(GPIOD, 13)
# define D14 PAL_LINE(GPIOD, 14)
# define D15 PAL_LINE(GPIOD, 15)
# define E0 PAL_LINE(GPIOE, 0)
# define E1 PAL_LINE(GPIOE, 1)
# define E2 PAL_LINE(GPIOE, 2)
# define E3 PAL_LINE(GPIOE, 3)
# define E4 PAL_LINE(GPIOE, 4)
# define E5 PAL_LINE(GPIOE, 5)
# define E6 PAL_LINE(GPIOE, 6)
# define E7 PAL_LINE(GPIOE, 7)
# define E8 PAL_LINE(GPIOE, 8)
# define E9 PAL_LINE(GPIOE, 9)
# define E10 PAL_LINE(GPIOE, 10)
# define E11 PAL_LINE(GPIOE, 11)
# define E12 PAL_LINE(GPIOE, 12)
# define E13 PAL_LINE(GPIOE, 13)
# define E14 PAL_LINE(GPIOE, 14)
# define E15 PAL_LINE(GPIOE, 15)
# define F0 PAL_LINE(GPIOF, 0)
# define F1 PAL_LINE(GPIOF, 1)
# define F2 PAL_LINE(GPIOF, 2)
# define F3 PAL_LINE(GPIOF, 3)
# define F4 PAL_LINE(GPIOF, 4)
# define F5 PAL_LINE(GPIOF, 5)
# define F6 PAL_LINE(GPIOF, 6)
# define F7 PAL_LINE(GPIOF, 7)
# define F8 PAL_LINE(GPIOF, 8)
# define F9 PAL_LINE(GPIOF, 9)
# define F10 PAL_LINE(GPIOF, 10)
# define F11 PAL_LINE(GPIOF, 11)
# define F12 PAL_LINE(GPIOF, 12)
# define F13 PAL_LINE(GPIOF, 13)
# define F14 PAL_LINE(GPIOF, 14)
# define F15 PAL_LINE(GPIOF, 15)
# endif
#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)
# elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)
# define SERIAL_UART_BAUD 115200
# define SERIAL_UART_DATA UDR1
/* UBRR should result in ~16 and set UCSR1A = _BV(U2X1) as per rn42 documentation. HC05 needs baudrate configured accordingly */
# define SERIAL_UART_UBRR (F_CPU / (8UL * 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 { \
UCSR1A = _BV(U2X1); \
/* 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
#include "song_list.h"
+11 -5
View File
@@ -23,6 +23,12 @@ When no state changes have occured for DEBOUNCE milliseconds, we push the state.
#include "quantum.h"
#include <stdlib.h>
#ifdef PROTOCOL_CHIBIOS
# if CH_CFG_USE_MEMCORE == FALSE
# error ChibiOS is configured without a memory allocator. Your keyboard may have set `#define CH_CFG_USE_MEMCORE FALSE`, which is incompatible with this debounce algorithm.
# endif
#endif
#ifndef DEBOUNCE
# define DEBOUNCE 5
#endif
@@ -38,12 +44,12 @@ static bool counters_need_update;
#define MAX_DEBOUNCE (DEBOUNCE_ELAPSED - 1)
static uint8_t wrapping_timer_read(void) {
static uint16_t time = 0;
static uint16_t time = 0;
static uint8_t last_result = 0;
uint16_t new_time = timer_read();
uint16_t diff = new_time - time;
time = new_time;
last_result = (last_result + diff) % (MAX_DEBOUNCE + 1);
uint16_t new_time = timer_read();
uint16_t diff = new_time - time;
time = new_time;
last_result = (last_result + diff) % (MAX_DEBOUNCE + 1);
return last_result;
}
+6
View File
@@ -23,6 +23,12 @@ No further inputs are accepted until DEBOUNCE milliseconds have occurred.
#include "quantum.h"
#include <stdlib.h>
#ifdef PROTOCOL_CHIBIOS
# if CH_CFG_USE_MEMCORE == FALSE
# error ChibiOS is configured without a memory allocator. Your keyboard may have set `#define CH_CFG_USE_MEMCORE FALSE`, which is incompatible with this debounce algorithm.
# endif
#endif
#ifndef DEBOUNCE
# define DEBOUNCE 5
#endif
+6
View File
@@ -23,6 +23,12 @@ No further inputs are accepted until DEBOUNCE milliseconds have occurred.
#include "quantum.h"
#include <stdlib.h>
#ifdef PROTOCOL_CHIBIOS
# if CH_CFG_USE_MEMCORE == FALSE
# error ChibiOS is configured without a memory allocator. Your keyboard may have set `#define CH_CFG_USE_MEMCORE FALSE`, which is incompatible with this debounce algorithm.
# endif
#endif
#ifndef DEBOUNCE
# define DEBOUNCE 5
#endif
+2
View File
@@ -37,6 +37,8 @@
#ifndef DYNAMIC_KEYMAP_EEPROM_MAX_ADDR
# if defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)
# define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR 2047
# elif defined(__AVR_AT90USB162__)
# define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR 511
# else
# define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR 1023
# endif
+38 -8
View File
@@ -23,7 +23,7 @@
// for memcpy
#include <string.h>
#ifndef ENCODER_RESOLUTION
#if !defined(ENCODER_RESOLUTIONS) && !defined(ENCODER_RESOLUTION)
# define ENCODER_RESOLUTION 4
#endif
@@ -34,6 +34,9 @@
#define NUMBER_OF_ENCODERS (sizeof(encoders_pad_a) / sizeof(pin_t))
static pin_t encoders_pad_a[] = ENCODERS_PAD_A;
static pin_t encoders_pad_b[] = ENCODERS_PAD_B;
#ifdef ENCODER_RESOLUTIONS
static uint8_t encoder_resolutions[] = ENCODER_RESOLUTIONS;
#endif
#ifndef ENCODER_DIRECTION_FLIP
# define ENCODER_CLOCKWISE true
@@ -70,9 +73,15 @@ void encoder_init(void) {
if (!isLeftHand) {
const pin_t encoders_pad_a_right[] = ENCODERS_PAD_A_RIGHT;
const pin_t encoders_pad_b_right[] = ENCODERS_PAD_B_RIGHT;
# if defined(ENCODER_RESOLUTIONS_RIGHT)
const uint8_t encoder_resolutions_right[] = ENCODER_RESOLUTIONS_RIGHT;
# endif
for (uint8_t i = 0; i < NUMBER_OF_ENCODERS; i++) {
encoders_pad_a[i] = encoders_pad_a_right[i];
encoders_pad_b[i] = encoders_pad_b_right[i];
# if defined(ENCODER_RESOLUTIONS_RIGHT)
encoder_resolutions[i] = encoder_resolutions_right[i];
# endif
}
}
#endif
@@ -90,48 +99,69 @@ void encoder_init(void) {
#endif
}
static void encoder_update(int8_t index, uint8_t state) {
uint8_t i = index;
static bool encoder_update(int8_t index, uint8_t state) {
bool changed = false;
uint8_t i = index;
#ifdef ENCODER_RESOLUTIONS
int8_t resolution = encoder_resolutions[i];
#else
int8_t resolution = ENCODER_RESOLUTION;
#endif
#ifdef SPLIT_KEYBOARD
index += thisHand;
#endif
encoder_pulses[i] += encoder_LUT[state & 0xF];
if (encoder_pulses[i] >= ENCODER_RESOLUTION) {
if (encoder_pulses[i] >= resolution) {
encoder_value[index]++;
changed = true;
encoder_update_kb(index, ENCODER_COUNTER_CLOCKWISE);
}
if (encoder_pulses[i] <= -ENCODER_RESOLUTION) { // direction is arbitrary here, but this clockwise
if (encoder_pulses[i] <= -resolution) { // direction is arbitrary here, but this clockwise
encoder_value[index]--;
changed = true;
encoder_update_kb(index, ENCODER_CLOCKWISE);
}
encoder_pulses[i] %= ENCODER_RESOLUTION;
encoder_pulses[i] %= resolution;
return changed;
}
void encoder_read(void) {
bool encoder_read(void) {
bool changed = false;
for (uint8_t i = 0; i < NUMBER_OF_ENCODERS; i++) {
encoder_state[i] <<= 2;
encoder_state[i] |= (readPin(encoders_pad_a[i]) << 0) | (readPin(encoders_pad_b[i]) << 1);
encoder_update(i, encoder_state[i]);
changed |= encoder_update(i, encoder_state[i]);
}
return changed;
}
#ifdef SPLIT_KEYBOARD
void last_encoder_activity_trigger(void);
void encoder_state_raw(uint8_t* slave_state) { memcpy(slave_state, &encoder_value[thisHand], sizeof(uint8_t) * NUMBER_OF_ENCODERS); }
void encoder_update_raw(uint8_t* slave_state) {
bool changed = false;
for (uint8_t i = 0; i < NUMBER_OF_ENCODERS; i++) {
uint8_t index = i + thatHand;
int8_t delta = slave_state[i] - encoder_value[index];
while (delta > 0) {
delta--;
encoder_value[index]++;
changed = true;
encoder_update_kb(index, ENCODER_COUNTER_CLOCKWISE);
}
while (delta < 0) {
delta++;
encoder_value[index]--;
changed = true;
encoder_update_kb(index, ENCODER_CLOCKWISE);
}
}
// Update the last encoder input time -- handled external to encoder_read() when we're running a split
if (changed) last_encoder_activity_trigger();
}
#endif
+1 -1
View File
@@ -20,7 +20,7 @@
#include "quantum.h"
void encoder_init(void);
void encoder_read(void);
bool encoder_read(void);
void encoder_update_kb(int8_t index, bool clockwise);
void encoder_update_user(int8_t index, bool clockwise);
-59
View File
@@ -1,59 +0,0 @@
/*
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();
}
}
-97
View File
@@ -1,97 +0,0 @@
/*
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);
+10 -2
View File
@@ -1,5 +1,9 @@
#pragma once
#include "quantum.h"
#include <stdint.h>
#ifndef JOYSTICK_BUTTON_COUNT
# define JOYSTICK_BUTTON_COUNT 8
#endif
@@ -8,9 +12,13 @@
# define JOYSTICK_AXES_COUNT 4
#endif
#include "quantum.h"
#ifndef JOYSTICK_AXES_RESOLUTION
# define JOYSTICK_AXES_RESOLUTION 8
#elif JOYSTICK_AXES_RESOLUTION < 8 || JOYSTICK_AXES_RESOLUTION > 16
# error JOYSTICK_AXES_RESOLUTION must be between 8 and 16
#endif
#include <stdint.h>
#define JOYSTICK_RESOLUTION ((1L << (JOYSTICK_AXES_RESOLUTION - 1)) - 1)
// configure on input_pin of the joystick_axes array entry to JS_VIRTUAL_AXIS
// to prevent it from being read from the ADC. This allows outputing forged axis value.
+2 -5
View File
@@ -14,13 +14,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#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);
@@ -42,5 +41,3 @@ typedef union {
} keymap_config_t;
extern keymap_config_t keymap_config;
#endif /* KEYCODE_CONFIG_H */
+2 -5
View File
@@ -15,8 +15,7 @@ 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
#pragma once
#include <stdint.h>
#include <stdbool.h>
@@ -25,7 +24,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
# include <avr/pgmspace.h>
#elif defined PROTOCOL_CHIBIOS
// We need to ensure that chibios is include before redefining reset
# include "ch.h"
# include <ch.h>
#endif
#include "keycode.h"
#include "action_macro.h"
@@ -55,5 +54,3 @@ 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
@@ -149,8 +149,8 @@
// Row 3
#define CA_DEG ALGR(CA_SCLN) // °
// Row 4
#define CA_LDAQ ALGR(CA_X) // «
#define CA_RDAQ ALGR(CA_C) // »
#define CA_LDAQ ALGR(CA_Z) // «
#define CA_RDAQ ALGR(CA_X) // »
#define CA_LABK ALGR(CA_COMM) // <
#define CA_RABK ALGR(CA_DOT) // >
+5
View File
@@ -20,6 +20,11 @@
// clang-format off
#ifdef CH_H
// The ChibiOS ch.h file defines this...
# undef CH_H
#endif
/*
* ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐
* │ § │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ ' │ ^ │       │
+1 -1
View File
@@ -237,7 +237,7 @@
#define FR_PERM S(A(FR_LUGR)) // ‰
// Row 4
#define FR_GTEQ S(A(FR_LABK)) // ≥
#define FR_LSAQ S(A(FR_W)) //
#define FR_RSAQ S(A(FR_W)) //
#define FR_FRSL S(A(FR_X)) //
#define FR_CENT S(A(FR_C)) // ¢
#define FR_SQRT S(A(FR_V)) // √
+2 -4
View File
@@ -13,8 +13,8 @@
* 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
#pragma once
#include "keymap.h"
@@ -66,5 +66,3 @@
#define NO_BSLS ALGR(KC_MINS)
#define NO_MU ALGR(KC_M)
#endif
+2 -4
View File
@@ -13,8 +13,8 @@
* 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_DVORAK_H
#define KEYMAP_PLOVER_DVORAK_H
#pragma once
#include "keymap_dvorak.h"
@@ -43,5 +43,3 @@
#define PD_O DV_V
#define PD_E DV_N
#define PD_U DV_M
#endif
@@ -0,0 +1,234 @@
/* Copyright 2020
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "keymap.h"
// clang-format off
/*
* ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐
* │ § │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ ' │ + │     │
* ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤
* │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ º │ ´ │   │
* ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐  │
* │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ Ç │ ~ │ \ │  │
* ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴──┤
* │    │ < │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ - │        │
* ├────┴┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤
* │     │    │     │                       │     │    │     │
* └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘
*/
// Row 1
#define PT_SECT KC_GRV // §
#define PT_1 KC_1 // 1
#define PT_2 KC_2 // 2
#define PT_3 KC_3 // 3
#define PT_4 KC_4 // 4
#define PT_5 KC_5 // 5
#define PT_6 KC_6 // 6
#define PT_7 KC_7 // 7
#define PT_8 KC_8 // 8
#define PT_9 KC_9 // 9
#define PT_0 KC_0 // 0
#define PT_QUOT KC_MINS // '
#define PT_PLUS KC_EQL // +
// Row 2
#define PT_Q KC_Q // Q
#define PT_W KC_W // W
#define PT_E KC_E // E
#define PT_R KC_R // R
#define PT_T KC_T // T
#define PT_Y KC_Y // Y
#define PT_U KC_U // U
#define PT_I KC_I // I
#define PT_O KC_O // O
#define PT_P KC_P // P
#define PT_MORD KC_LBRC // º
#define PT_ACUT KC_RBRC // ´ (dead)
// Row 3
#define PT_A KC_A // A
#define PT_S KC_S // S
#define PT_D KC_D // D
#define PT_F KC_F // F
#define PT_G KC_G // G
#define PT_H KC_H // H
#define PT_J KC_J // J
#define PT_K KC_K // K
#define PT_L KC_L // L
#define PT_CCED KC_SCLN // Ç
#define PT_TILD KC_QUOT // ~ (dead)
#define PT_BSLS KC_NUHS // (backslash)
// Row 4
#define PT_LABK KC_NUBS // <
#define PT_Z KC_Z // Z
#define PT_X KC_X // X
#define PT_C KC_C // C
#define PT_V KC_V // V
#define PT_B KC_B // B
#define PT_N KC_N // N
#define PT_M KC_M // M
#define PT_COMM KC_COMM // ,
#define PT_DOT KC_DOT // .
#define PT_MINS KC_SLSH // -
/* Shifted symbols
* ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐
* │ ± │ ! │ " │ # │ $ │ % │ & │ / │ ( │ ) │ = │ ? │ * │     │
* ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤
* │     │   │   │   │   │   │   │   │   │   │   │ ª │ ` │   │
* ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐  │
* │      │   │   │   │   │   │   │   │   │   │   │ ^ │ | │  │
* ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴──┤
* │    │ > │   │   │   │   │   │   │   │ ; │ : │ _ │        │
* ├────┴┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤
* │     │    │     │                       │     │    │     │
* └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘
*/
// Row 1
#define PT_PLMN S(PT_SECT) // ±
#define PT_EXLM S(PT_1) // !
#define PT_DQUO S(PT_2) // "
#define PT_HASH S(PT_3) // #
#define PT_DLR S(PT_4) // $
#define PT_PERC S(PT_5) // %
#define PT_AMPR S(PT_6) // &
#define PT_SLSH S(PT_7) // /
#define PT_LPRN S(PT_8) // (
#define PT_RPRN S(PT_9) // )
#define PT_EQL S(PT_0) // =
#define PT_QUES S(PT_QUOT) // ?
#define PT_ASTR S(PT_PLUS) // *
// Row 2
#define PT_FORD S(PT_MORD) // ª
#define PT_GRV S(PT_ACUT) // ` (dead)
// Row 3
#define PT_CIRC S(PT_TILD) // ^ (dead)
#define PT_PIPE S(PT_BSLS) // |
// Row 4
#define PT_RABK S(PT_LABK) // >
#define PT_SCLN S(PT_COMM) // ;
#define PT_COLN S(PT_DOT) // :
#define PT_UNDS S(PT_MINS) // _
/* Alted symbols
* ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐
* │   │  │ @ │ € │ £ │ ‰ │ ¶ │ ÷ │ [ │ ] │ ≠ │   │   │     │
* ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤
* │     │ Œ │ ∑ │ Æ │ ® │ ™ │ ¥ │ † │ ı │ Ø │ π │ ° │ ¨ │   │
* ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐  │
* │      │ Å │ ß │ ∂ │ ƒ │ ˙ │ ˇ │ ¯ │ „ │ ‘ │ ¸ │ ˜ │ ‹ │  │
* ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴──┤
* │    │ ≤ │ Ω │ « │ © │ √ │ ∫ │ ¬ │ µ │ “ │ … │ — │        │
* ├────┴┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤
* │     │    │     │                       │     │    │     │
* └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘
*/
// Row 1
#define PT_APPL A(PT_1) //  (Apple logo)
#define PT_AT A(PT_2) // @
#define PT_EURO A(PT_3) // €
#define PT_PND A(PT_4) // £
#define PT_PERM A(PT_5) // ‰
#define PT_PILC A(PT_6) // ¶
#define PT_DIV A(PT_7) // ÷
#define PT_LBRC A(PT_8) // [
#define PT_RBRC A(PT_9) // ]
#define PT_NEQL A(PT_0) // ≠
// Row 2
#define PT_OE A(PT_Q) // Œ
#define PT_NARS A(PT_W) // ∑
#define PT_AE A(PT_E) // Æ
#define PT_REGD A(PT_R) // ®
#define PT_TM A(PT_T) // ™
#define PT_YEN A(PT_Y) // ¥
#define PT_DAGG A(PT_U) // †
#define PT_DLSI A(PT_I) // ı
#define PT_OSTR A(PT_O) // Ø
#define PT_PI A(PT_P) // π
#define PT_DEG A(PT_MORD) // °
#define PT_DIAE A(PT_ACUT) // ¨ (dead)
// Row 3
#define PT_ARNG A(PT_A) // å
#define PT_SS A(PT_S) // ß
#define PT_PDIF A(PT_D) // ∂
#define PT_FHK A(PT_F) // ƒ
#define PT_DOTA A(PT_G) // ˙
#define PT_CARN A(PT_H) // ˇ
#define PT_MACR A(PT_J) // ¯
#define PT_DLQU A(PT_K) // „
#define PT_LSQU A(PT_L) //
#define PT_CEDL A(PT_CCED) // ¸
#define PT_STIL A(PT_TILD) // ˜ (dead)
#define PT_LSAQ A(PT_BSLS) //
// Row 4
#define PT_LTEQ A(PT_LABK) // ≤
#define PT_OMEG A(PT_Z) // Ω
#define PT_LDAQ A(PT_X) // «
#define PT_COPY A(PT_C) // ©
#define PT_SQRT A(PT_V) // √
#define PT_INTG A(PT_B) // ∫
#define PT_NOT A(PT_N) // ¬
#define PT_MICR A(PT_M) // µ
#define PT_LDQU A(PT_COMM) // “
#define PT_ELLP A(PT_DOT) // …
#define PT_MDSH A(PT_MINS) // —
/* Shift+Alted symbols
* ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐
* │   │ ¡ │ fi │ fl │ ¢ │ ∞ │ • │ ⁄ │ { │ } │ ≈ │ ¿ │ ◊ │     │
* ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤
* │     │   │   │   │   │   │   │ ‡ │ ˚ │   │ ∏ │   │ ˝ │   │
* ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐  │
* │      │   │   │ ∆ │   │   │   │   │ ‚ │ ’ │ ˛ │ ˆ │ › │  │
* ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴──┤
* │    │ ≥ │   │ » │   │   │   │   │   │ ” │ · │ – │        │
* ├────┴┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤
* │     │    │     │                       │     │    │     │
* └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘
*/
// Row 1
#define PT_IEXL S(A(PT_1)) // ¡
#define PT_FI S(A(PT_2)) // fi
#define PT_FL S(A(PT_3)) // fl
#define PT_CENT S(A(PT_4)) // ¢
#define PT_INFN S(A(PT_5)) // ∞
#define PT_BULT S(A(PT_6)) // •
#define PT_FRSL S(A(PT_7)) //
#define PT_LCBR S(A(PT_8)) // {
#define PT_RCBR S(A(PT_9)) // }
#define PT_AEQL S(A(PT_0)) // ≈
#define PT_IQUE S(A(PT_QUOT)) // ¿
#define PT_LOZN S(A(PT_PLUS)) // ◊
// Row 2
#define PT_DDAG S(A(PT_U)) // ‡
#define PT_RNGA S(A(PT_I)) // ˚
#define PT_NARP S(A(PT_P)) // ∏
#define PT_DACU S(A(PT_ACUT)) // ˝
// Row 3
#define PT_INCR S(A(PT_D)) // ∆
#define PT_SLQU S(A(PT_K)) //
#define PT_RSQU S(A(PT_L)) //
#define PT_OGON S(A(PT_CCED)) // ˛
#define PT_DCIR S(A(PT_TILD)) // ˆ (dead)
#define PT_RSAQ S(A(PT_BSLS)) //
// Row 4
#define PT_GTEQ S(A(PT_LABK)) // ≥
#define PT_RDAQ S(A(PT_X)) // »
#define PT_RDQU S(A(PT_COMM)) // ”
#define PT_MDDT S(A(PT_DOT)) // ·
#define PT_NDSH S(A(PT_MINS)) //
@@ -26,7 +26,7 @@
* ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
* │     │ . │ , │ Ñ │ P │ Y │ F │ G │ C │ H │ L │ ` │ + │     │
* ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │
* │      │ A │ O │ E │ U │ I │ D │ R │ T │ N │ S │ ' │ Ç │    │
* │      │ A │ O │ E │ U │ I │ D │ R │ T │ N │ S │ ´ │ Ç │    │
* ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤
* │    │ < │ - │ Q │ J │ K │ X │ B │ M │ W │ V │ Z │          │
* ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤
@@ -119,7 +119,7 @@
#define DV_CIRC S(DV_GRV) // ^ (dead)
#define DV_ASTR S(DV_PLUS) // *
// Row 3
#define DV_DIAE S(DV_GRV) // ¨ (dead)
#define DV_DIAE S(DV_ACUT) // ¨ (dead)
// Row 4
#define DV_RABK S(DV_LABK) // >
#define DV_UNDS S(DV_MINS) // _
+2 -4
View File
@@ -13,8 +13,8 @@
* 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_STENO_H
#define KEYMAP_STENO_H
#pragma once
#include "keymap.h"
@@ -72,5 +72,3 @@ enum steno_keycodes {
STN_ZR,
STN__MAX = STN_ZR, // must be less than QK_STENO_BOLT
};
#endif
+227
View File
@@ -0,0 +1,227 @@
/* Copyright 2020
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "keymap.h"
// clang-format off
/*
* ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐
* │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │       │
* ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
* │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \  │
* ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤
* │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │        │
* ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤
* │        │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │          │
* ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤
* │    │    │    │                        │    │    │    │    │
* └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘
*/
// Row 1
#define US_GRV KC_GRV // `
#define US_1 KC_1 // 1
#define US_2 KC_2 // 2
#define US_3 KC_3 // 3
#define US_4 KC_4 // 4
#define US_5 KC_5 // 5
#define US_6 KC_6 // 6
#define US_7 KC_7 // 7
#define US_8 KC_8 // 8
#define US_9 KC_9 // 9
#define US_0 KC_0 // 0
#define US_MINS KC_MINS // -
#define US_EQL KC_EQL // =
// Row 2
#define US_Q KC_Q // Q
#define US_W KC_W // W
#define US_E KC_E // E
#define US_R KC_R // R
#define US_T KC_T // T
#define US_Y KC_Y // Y
#define US_U KC_U // U
#define US_I KC_I // I
#define US_O KC_O // O
#define US_P KC_P // P
#define US_LBRC KC_LBRC // [
#define US_RBRC KC_RBRC // ]
#define US_BSLS KC_BSLS // (backslash)
// Row 3
#define US_A KC_A // A
#define US_S KC_S // S
#define US_D KC_D // D
#define US_F KC_F // F
#define US_G KC_G // G
#define US_H KC_H // H
#define US_J KC_J // J
#define US_K KC_K // K
#define US_L KC_L // L
#define US_SCLN KC_SCLN // ;
#define US_QUOT KC_QUOT // '
// Row 4
#define US_Z KC_Z // Z
#define US_X KC_X // X
#define US_C KC_C // C
#define US_V KC_V // V
#define US_B KC_B // B
#define US_N KC_N // N
#define US_M KC_M // M
#define US_COMM KC_COMM // ,
#define US_DOT KC_DOT // .
#define US_SLSH KC_SLSH // /
/* Shifted symbols
* ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐
* │ ~ │ ! │ @ │ # │ $ │ % │ ^ │ & │ * │ ( │ ) │ _ │ + │       │
* ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
* │     │   │   │   │   │   │   │   │   │   │   │ { │ } │  |  │
* ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤
* │      │   │   │   │   │   │   │   │   │   │ : │ " │        │
* ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤
* │        │   │   │   │   │   │   │   │ < │ > │ ? │          │
* ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤
* │    │    │    │                        │    │    │    │    │
* └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘
*/
// Row 1
#define US_TILD S(US_GRV) // ~
#define US_EXLM S(US_1) // !
#define US_AT S(US_2) // @
#define US_HASH S(US_3) // #
#define US_DLR S(US_4) // $
#define US_PERC S(US_5) // %
#define US_CIRC S(US_6) // ^
#define US_AMPR S(US_7) // &
#define US_ASTR S(US_8) // *
#define US_LPRN S(US_9) // (
#define US_RPRN S(US_0) // )
#define US_UNDS S(US_MINS) // _
#define US_PLUS S(US_EQL) // +
// Row 2
#define US_LCBR S(US_LBRC) // {
#define US_RCBR S(US_RBRC) // }
#define US_PIPE S(US_BSLS) // |
// Row 3
#define US_COLN S(US_SCLN) // :
#define US_DQUO S(US_QUOT) // "
// Row 4
#define US_LABK S(US_COMM) // <
#define US_RABK S(US_DOT) // >
#define US_QUES S(US_SLSH) // ?
/* AltGr symbols
* ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐
* │ ` │ ¹ │ ² │ ³ │ ¤ │ € │ ^ │ ̛  │ ¾ │ ‘ │ ’ │ ¥ │ × │       │
* ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
* │     │ Ä │ Å │ É │ ® │ Þ │ Ü │ Ú │ Í │ Ó │ Ö │ « │ » │  ¬  │
* ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤
* │      │ Á │ ß │ Ð │   │   │   │ Ï │ Œ │ Ø │ ¶ │ ' │        │
* ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤
* │        │ Æ │   │ © │   │   │ Ñ │ µ │ Ç │ ˙ │ ¿ │          │
* ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤
* │    │    │    │                        │    │    │    │    │
* └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘
*/
// Row 1
#define US_DGRV ALGR(US_GRV) // ` (dead)
#define US_SUP1 ALGR(US_1) // ¹
#define US_SUP2 ALGR(US_2) // ²
#define US_SUP3 ALGR(US_3) // ³
#define US_CURR ALGR(US_4) // ¤
#define US_EURO ALGR(US_5) // €
#define US_DCIR ALGR(US_6) // ^ (dead)
#define US_HORN ALGR(US_7) // ̛̛ (dead)
#define US_OGON ALGR(US_8) // ˛ (dead)
#define US_LSQU ALGR(US_9) //
#define US_RSQU ALGR(US_0) //
#define US_YEN ALGR(US_MINS) // ¥
#define US_MUL ALGR(US_EQL) // ×
// Row 2
#define US_ADIA ALGR(US_Q) // Ä
#define US_ARNG ALGR(US_W) // Å
#define US_EACU ALGR(US_E) // É
#define US_EDIA ALGR(US_R) // Ë
#define US_THRN ALGR(US_T) // Þ
#define US_UDIA ALGR(US_Y) // Ü
#define US_UACU ALGR(US_U) // Ú
#define US_IACU ALGR(US_I) // Í
#define US_OACU ALGR(US_O) // Ó
#define US_ODIA ALGR(US_P) // Ö
#define US_LDAQ ALGR(US_LBRC) // «
#define US_RDAQ ALGR(US_RBRC) // »
#define US_NOT ALGR(US_BSLS) // ¬
// Row 3
#define US_AACU ALGR(US_A) // Á
#define US_SS ALGR(US_S) // ß
#define US_ETH ALGR(US_D) // Ð
#define US_IDIA ALGR(US_J) // Ï
#define US_OE ALGR(US_K) // Œ
#define US_OSTR ALGR(US_L) // Ø
#define US_PILC ALGR(US_SCLN) // ¶
#define US_ACUT ALGR(US_QUOT) // ´ (dead)
// Row 4
#define US_AE ALGR(US_Z) // Æ
#define US_OE_2 ALGR(US_X) // Œ
#define US_COPY ALGR(US_C) // ©
#define US_REGD ALGR(US_V) // ®
#define US_NTIL ALGR(US_N) // Ñ
#define US_MICR ALGR(US_M) // µ
#define US_CCED ALGR(US_COMM) // Ç
#define US_DOTA ALGR(US_DOT) // ˙ (dead)
#define US_IQUE ALGR(US_SLSH) // ¿
/* Shift+AltGr symbols
* ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐
* │ ~ │ ¡ │ ˝ │ ¯ │ £ │ ¸ │ ¼ │ ½ │ ¾ │ ˘ │ ° │  ̣ │ ÷ │       │
* ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
* │     │   │   │   │   │   │   │   │   │   │   │ “ │ ” │  ¦  │
* ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤
* │      │   │ § │   │   │   │   │   │   │   │ ° │ " │        │
* ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤
* │        │   │   │ ¢ │   │   │   │   │   │ ˇ │  ̉ │          │
* ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤
* │    │    │    │                        │    │    │    │    │
* └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘
*/
// Row 1
#define US_DTIL S(ALGR(US_GRV)) // ~ (dead)
#define US_IEXL S(ALGR(US_1)) // ¡
#define US_DACU S(ALGR(US_2)) // ˝ (dead)
#define US_MACR S(ALGR(US_3)) // ¯ (dead)
#define US_PND S(ALGR(US_4)) // £
#define US_CEDL S(ALGR(US_5)) // ¸ (dead)
#define US_QRTR S(ALGR(US_6)) // ¼
#define US_HALF S(ALGR(US_7)) // ½
#define US_TQTR S(ALGR(US_8)) // ¾
#define US_BREV S(ALGR(US_9)) // ˘ (dead)
#define US_RNGA S(ALGR(US_0)) // ° (dead)
#define US_DOTB S(ALGR(US_MINS)) // ̣ (dead)
#define US_DIV S(ALGR(US_EQL)) // ÷
// Row 2
#define US_LDQU S(ALGR(US_LBRC)) // “
#define US_RDQU S(ALGR(US_LBRC)) // ”
#define US_BRKP S(ALGR(US_BSLS)) // ¦
// Row 3
#define US_SECT S(ALGR(US_S)) // §
#define US_DEG S(ALGR(US_SCLN)) // °
#define US_DIAE S(ALGR(US_QUOT)) // ¨ (dead)
// Row 4
#define US_CENT S(ALGR(US_C)) // ¢
#define US_CARN S(ALGR(US_DOT)) // ˇ (dead)
#define US_HOKA S(ALGR(US_SLSH)) // ̉ (dead)
+10 -10
View File
@@ -26,7 +26,7 @@
* ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
* │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \  │
* ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤
* │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │        │
* │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ´ │        │
* ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤
* │        │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │          │
* ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤
@@ -34,7 +34,7 @@
* └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘
*/
// Row 1
#define US_GRV KC_GRV // ` (dead)
#define US_DGRV KC_GRV // ` (dead)
#define US_1 KC_1 // 1
#define US_2 KC_2 // 2
#define US_3 KC_3 // 3
@@ -72,7 +72,7 @@
#define US_K KC_K // K
#define US_L KC_L // L
#define US_SCLN KC_SCLN // ;
#define US_QUOT KC_QUOT // ' (dead)
#define US_ACUT KC_QUOT // ´ (dead)
// Row 4
#define US_Z KC_Z // Z
#define US_X KC_X // X
@@ -91,7 +91,7 @@
* ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
* │     │   │   │   │   │   │   │   │   │   │   │ { │ } │  |  │
* ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤
* │      │   │   │   │   │   │   │   │   │   │ : │ " │        │
* │      │   │   │   │   │   │   │   │   │   │ : │ ¨ │        │
* ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤
* │        │   │   │   │   │   │   │   │ < │ > │ ? │          │
* ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤
@@ -99,13 +99,13 @@
* └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘
*/
// Row 1
#define US_TILD S(US_GRV) // ~ (dead)
#define US_DTIL S(US_DGRV) // ~ (dead)
#define US_EXLM S(US_1) // !
#define US_AT S(US_2) // "
#define US_AT S(US_2) // @
#define US_HASH S(US_3) // #
#define US_DLR S(US_4) // $
#define US_PERC S(US_5) // %
#define US_CIRC S(US_6) // ^
#define US_DCIR S(US_6) // ^ (dead)
#define US_AMPR S(US_7) // &
#define US_ASTR S(US_8) // *
#define US_LPRN S(US_9) // (
@@ -118,7 +118,7 @@
#define US_PIPE S(US_BSLS) // |
// Row 3
#define US_COLN S(US_SCLN) // :
#define US_DQUO S(US_QUOT) // " (dead)
#define US_DIAE S(US_ACUT) // ¨ (dead)
// Row 4
#define US_LABK S(US_COMM) // <
#define US_RABK S(US_DOT) // >
@@ -170,7 +170,7 @@
#define US_ETH ALGR(US_D) // Ð
#define US_OSTR ALGR(US_L) // Ø
#define US_PILC ALGR(US_SCLN) // ¶
#define US_ACUT ALGR(US_QUOT) // ´
#define US_NDAC ALGR(US_ACUT) // ´
// Row 4
#define US_AE ALGR(US_Z) // Æ
#define US_COPY ALGR(US_C) // ©
@@ -201,6 +201,6 @@
// Row 3
#define US_SECT S(ALGR(US_S)) // §
#define US_DEG S(ALGR(US_SCLN)) // °
#define US_DIAE S(ALGR(US_QUOT)) // ¨
#define US_NDDR S(ALGR(US_ACUT)) // ¨
// Row 4
#define US_CENT S(ALGR(US_C)) // ¢
@@ -0,0 +1,224 @@
/* Copyright 2020
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "keymap.h"
// clang-format off
/*
* ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐
* │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │       │
* ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
* │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \  │
* ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤
* │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ´ │        │
* ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤
* │        │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │          │
* ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤
* │    │    │    │                        │    │    │    │    │
* └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘
*/
// Row 1
#define US_DGRV KC_GRV // ` (dead)
#define US_1 KC_1 // 1
#define US_2 KC_2 // 2
#define US_3 KC_3 // 3
#define US_4 KC_4 // 4
#define US_5 KC_5 // 5
#define US_6 KC_6 // 6
#define US_7 KC_7 // 7
#define US_8 KC_8 // 8
#define US_9 KC_9 // 9
#define US_0 KC_0 // 0
#define US_MINS KC_MINS // -
#define US_EQL KC_EQL // =
// Row 2
#define US_Q KC_Q // Q
#define US_W KC_W // W
#define US_E KC_E // E
#define US_R KC_R // R
#define US_T KC_T // T
#define US_Y KC_Y // Y
#define US_U KC_U // U
#define US_I KC_I // I
#define US_O KC_O // O
#define US_P KC_P // P
#define US_LBRC KC_LBRC // [
#define US_RBRC KC_RBRC // ]
#define US_BSLS KC_BSLS // (backslash)
// Row 3
#define US_A KC_A // A
#define US_S KC_S // S
#define US_D KC_D // D
#define US_F KC_F // F
#define US_G KC_G // G
#define US_H KC_H // H
#define US_J KC_J // J
#define US_K KC_K // K
#define US_L KC_L // L
#define US_SCLN KC_SCLN // ;
#define US_ACUT KC_QUOT // ´ (dead)
// Row 4
#define US_Z KC_Z // Z
#define US_X KC_X // X
#define US_C KC_C // C
#define US_V KC_V // V
#define US_B KC_B // B
#define US_N KC_N // N
#define US_M KC_M // M
#define US_COMM KC_COMM // ,
#define US_DOT KC_DOT // .
#define US_SLSH KC_SLSH // /
/* Shifted symbols
* ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐
* │ ~ │ ! │ @ │ # │ $ │ % │ ^ │ & │ * │ ( │ ) │ _ │ + │       │
* ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
* │     │   │   │   │   │   │   │   │   │   │   │ { │ } │  |  │
* ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤
* │      │   │   │   │   │   │   │   │   │   │ : │ ¨ │        │
* ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤
* │        │   │   │   │   │   │   │   │ < │ > │ ? │          │
* ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤
* │    │    │    │                        │    │    │    │    │
* └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘
*/
// Row 1
#define US_DTIL S(US_DGRV) // ~ (dead)
#define US_EXLM S(US_1) // !
#define US_AT S(US_2) // @
#define US_HASH S(US_3) // #
#define US_DLR S(US_4) // $
#define US_PERC S(US_5) // %
#define US_DCIR S(US_6) // ^ (dead)
#define US_AMPR S(US_7) // &
#define US_ASTR S(US_8) // *
#define US_LPRN S(US_9) // (
#define US_RPRN S(US_0) // )
#define US_UNDS S(US_MINS) // _
#define US_PLUS S(US_EQL) // +
// Row 2
#define US_LCBR S(US_LBRC) // {
#define US_RCBR S(US_RBRC) // }
#define US_PIPE S(US_BSLS) // |
// Row 3
#define US_COLN S(US_SCLN) // :
#define US_DIAE S(US_ACUT) // ¨ (dead)
// Row 4
#define US_LABK S(US_COMM) // <
#define US_RABK S(US_DOT) // >
#define US_QUES S(US_SLSH) // ?
/* AltGr symbols
* ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐
* │ ` │ ¡ │ ² │ ³ │ ¤ │ € │ ¼ │ ½ │ ¾ │ ‘ │ ’ │ ¥ │ × │       │
* ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
* │     │ Ä │ Å │ É │ ® │ Þ │ Ü │ Ú │ Í │ Ó │ Ö │ « │ » │  ¬  │
* ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤
* │      │ Á │ ß │ Ð │   │   │   │   │ Œ │ Ø │ ¶ │ ' │        │
* ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤
* │        │ Æ │   │ © │   │   │ Ñ │ µ │ Ç │ ˙ │ ¿ │          │
* ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤
* │    │    │    │                        │    │    │    │    │
* └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘
*/
// Row 1
#define US_GRV ALGR(US_DGRV) // `
#define US_IEXL ALGR(US_1) // ¡
#define US_SUP2 ALGR(US_2) // ²
#define US_SUP3 ALGR(US_3) // ³
#define US_CURR ALGR(US_4) // ¤
#define US_EURO ALGR(US_5) // €
#define US_QRTR ALGR(US_6) // ¼
#define US_HALF ALGR(US_7) // ½
#define US_TQTR ALGR(US_8) // ¾
#define US_LSQU ALGR(US_9) //
#define US_RSQU ALGR(US_0) //
#define US_YEN ALGR(US_MINS) // ¥
#define US_MUL ALGR(US_EQL) // ×
// Row 2
#define US_ADIA ALGR(US_Q) // Ä
#define US_ARNG ALGR(US_W) // Å
#define US_EACU ALGR(US_E) // É
#define US_REGD ALGR(US_R) // ®
#define US_THRN ALGR(US_T) // Þ
#define US_UDIA ALGR(US_Y) // Ü
#define US_UACU ALGR(US_U) // Ú
#define US_IACU ALGR(US_I) // Í
#define US_OACU ALGR(US_O) // Ó
#define US_ODIA ALGR(US_P) // Ö
#define US_LDAQ ALGR(US_LBRC) // «
#define US_RDAQ ALGR(US_RBRC) // »
#define US_NOT ALGR(US_BSLS) // ¬
// Row 3
#define US_AACU ALGR(US_A) // Á
#define US_SS ALGR(US_S) // ß
#define US_ETH ALGR(US_D) // Ð
#define US_OE ALGR(US_K) // Œ
#define US_OSTR ALGR(US_L) // Ø
#define US_PILC ALGR(US_SCLN) // ¶
#define US_QUOT ALGR(US_ACUT) // '
// Row 4
#define US_AE ALGR(US_Z) // Æ
#define US_COPY ALGR(US_C) // ©
#define US_NTIL ALGR(US_N) // Ñ
#define US_MICR ALGR(US_M) // µ
#define US_CCED ALGR(US_COMM) // Ç
#define US_DOTA ALGR(US_DOT) // ˙ (dead)
#define US_IQUE ALGR(US_SLSH) // ¿
/* Shift+AltGr symbols
* ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐
* │ ~ │ ¹ │ ˝ │ ¯ │ £ │ ¸ │ ^ │ ̛  │ ˛ │ ˘ │ ° │  ̣ │ ÷ │       │
* ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
* │     │   │   │   │   │   │   │   │   │   │   │ “ │ ” │  ¦  │
* ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤
* │      │   │ § │   │   │   │   │   │   │   │ ° │ " │        │
* ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤
* │        │   │   │ ¢ │   │   │   │   │   │ ˇ │  ̉ │          │
* ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤
* │    │    │    │                        │    │    │    │    │
* └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘
*/
// Row 1
#define US_TILD S(ALGR(US_DGRV)) // ~
#define US_SUP1 S(ALGR(US_1)) // ¹
#define US_DACU S(ALGR(US_2)) // ˝ (dead)
#define US_MACR S(ALGR(US_3)) // ¯ (dead)
#define US_PND S(ALGR(US_4)) // £
#define US_CEDL S(ALGR(US_5)) // ¸ (dead)
#define US_CIRC S(ALGR(US_6)) // ^
#define US_HORN S(ALGR(US_7)) // ̛ (dead)
#define US_OGON S(ALGR(US_8)) // ˛ (dead)
#define US_BREV S(ALGR(US_9)) // ˘ (dead)
#define US_RNGA S(ALGR(US_0)) // ° (dead)
#define US_DOTB S(ALGR(US_MINS)) // ̣ (dead)
#define US_DIV S(ALGR(US_EQL)) // ÷
// Row 2
#define US_LDQU S(ALGR(US_LBRC)) // “
#define US_RDQU S(ALGR(US_LBRC)) // ”
#define US_BRKP S(ALGR(US_BSLS)) // ¦
// Row 3
#define US_SECT S(ALGR(US_S)) // §
#define US_DEG S(ALGR(US_SCLN)) // °
#define US_DQUO S(ALGR(US_ACUT)) // "
// Row 4
#define US_CENT S(ALGR(US_C)) // ¢
#define US_CARN S(ALGR(US_DOT)) // ˇ (dead)
#define US_HOKA S(ALGR(US_SLSH)) // ̉ (dead)
@@ -0,0 +1,100 @@
/* Copyright 2020
*
* 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 lookup tables for Portuguese layouts
#pragma once
#include "keymap_portuguese_osx_iso.h"
#include "quantum.h"
// clang-format off
const uint8_t ascii_to_shift_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 0),
KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 1, 1, 0, 1, 1, 1),
KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),
KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),
KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),
KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 1, 1),
KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 0, 0)
};
const uint8_t ascii_to_altgr_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 1, 0, 1, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 1, 0, 1, 0, 0)
};
const uint8_t ascii_to_keycode_lut[128] PROGMEM = {
// NUL SOH STX ETX EOT ENQ ACK BEL
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,
// BS TAB LF VT FF CR SO SI
KC_BSPC, KC_TAB, KC_ENT, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,
// DLE DC1 DC2 DC3 DC4 NAK SYN ETB
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,
// CAN EM SUB ESC FS GS RS US
XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,
// ! " # $ % & '
KC_SPC, PT_1, PT_2, PT_3, PT_4, PT_5, PT_6, PT_QUOT,
// ( ) * + , - . /
PT_8, PT_9, PT_PLUS, PT_PLUS, PT_COMM, PT_MINS, PT_DOT, PT_7,
// 0 1 2 3 4 5 6 7
PT_0, PT_1, PT_2, PT_3, PT_4, PT_5, PT_6, PT_7,
// 8 9 : ; < = > ?
PT_8, PT_9, PT_DOT, PT_COMM, PT_LABK, PT_0, PT_LABK, PT_QUOT,
// @ A B C D E F G
PT_2, PT_A, PT_B, PT_C, PT_D, PT_E, PT_F, PT_G,
// H I J K L M N O
PT_H, PT_I, PT_J, PT_K, PT_L, PT_M, PT_N, PT_O,
// P Q R S T U V W
PT_P, PT_Q, PT_R, PT_S, PT_T, PT_U, PT_V, PT_W,
// X Y Z [ \ ] ^ _
PT_X, PT_Y, PT_Z, PT_8, PT_BSLS, PT_9, PT_TILD, PT_MINS,
// ` a b c d e f g
PT_ACUT, PT_A, PT_B, PT_C, PT_D, PT_E, PT_F, PT_G,
// h i j k l m n o
PT_H, PT_I, PT_J, PT_K, PT_L, PT_M, PT_N, PT_O,
// p q r s t u v w
PT_P, PT_Q, PT_R, PT_S, PT_T, PT_U, PT_V, PT_W,
// x y z { | } ~ DEL
PT_X, PT_Y, PT_Z, PT_8, PT_BSLS, PT_9, PT_TILD, KC_DEL
};
@@ -0,0 +1,100 @@
/* Copyright 2019 Rys Sommefeldt
*
* 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 lookup tables for UK layouts
#pragma once
#include "keymap_us_international.h"
#include "quantum.h"
// clang-format off
const uint8_t ascii_to_shift_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 0),
KCLUT_ENTRY(1, 1, 1, 1, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 1, 0, 1, 0, 1, 1),
KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),
KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),
KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),
KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 1, 1),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0),
};
__attribute__((weak)) const uint8_t ascii_to_dead_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 1, 0, 0, 0, 0, 1),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),
KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),
};
const uint8_t ascii_to_keycode_lut[128] PROGMEM = {
// NUL SOH STX ETX EOT ENQ ACK BEL
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,
// BS TAB LF VT FF CR SO SI
KC_BSPC, KC_TAB, KC_ENT, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,
// DLE DC1 DC2 DC3 DC4 NAK SYN ETB
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,
// CAN EM SUB ESC FS GS RS US
XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,
// ! " # $ % & '
KC_SPC, US_1, US_ACUT, US_3, US_4, US_5, US_7, US_ACUT,
// ( ) * + , - . /
US_9, US_0, US_8, US_EQL, US_COMM, US_MINS, US_DOT, US_SLSH,
// 0 1 2 3 4 5 6 7
US_0, US_1, US_2, US_3, US_4, US_5, US_6, US_7,
// 8 9 : ; < = > ?
US_8, US_9, US_SCLN, US_SCLN, US_COMM, US_EQL, US_DOT, US_SLSH,
// @ A B C D E F G
US_2, US_A, US_B, US_C, US_D, US_E, US_F, US_G,
// H I J K L M N O
US_H, US_I, US_J, US_K, US_L, US_M, US_N, US_O,
// P Q R S T U V W
US_P, US_Q, US_R, US_S, US_T, US_U, US_V, US_W,
// X Y Z [ \ ] ^ _
US_X, US_Y, US_Z, US_LBRC, US_BSLS, US_RBRC, US_6, US_MINS,
// ` a b c d e f g
US_DGRV, US_A, US_B, US_C, US_D, US_E, US_F, US_G,
// h i j k l m n o
US_H, US_I, US_J, US_K, US_L, US_M, US_N, US_O,
// p q r s t u v w
US_P, US_Q, US_R, US_S, US_T, US_U, US_V, US_W,
// x y z { | } ~ DEL
US_X, US_Y, US_Z, US_LBRC, US_BSLS, US_RBRC, US_DGRV, KC_DEL
};
+137
View File
@@ -0,0 +1,137 @@
/* Copyright 2020 zvecr<git@zvecr.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 "quantum.h"
#ifdef BACKLIGHT_ENABLE
# include "backlight.h"
extern backlight_config_t backlight_config;
#else
// Cannot use BACKLIGHT_CAPS_LOCK without backlight being enabled
# undef BACKLIGHT_CAPS_LOCK
#endif
#ifndef LED_PIN_ON_STATE
# define LED_PIN_ON_STATE 1
#endif
#if defined(BACKLIGHT_CAPS_LOCK)
/** \brief Caps Lock indicator using backlight (for keyboards without dedicated LED)
*/
static void handle_backlight_caps_lock(led_t led_state) {
// Use backlight as Caps Lock indicator
uint8_t bl_toggle_lvl = 0;
if (led_state.caps_lock && !backlight_config.enable) {
// Turning Caps Lock ON and backlight is disabled in config
// Toggling backlight to the brightest level
bl_toggle_lvl = BACKLIGHT_LEVELS;
} else if (!led_state.caps_lock && backlight_config.enable) {
// Turning Caps Lock OFF and backlight is enabled in config
// Toggling backlight and restoring config level
bl_toggle_lvl = backlight_config.level;
}
// Set level without modify backlight_config to keep ability to restore state
backlight_set(bl_toggle_lvl);
}
#endif
/** \brief Lock LED set callback - keymap/user level
*
* \deprecated Use led_update_user() instead.
*/
__attribute__((weak)) void led_set_user(uint8_t usb_led) {}
/** \brief Lock LED set callback - keyboard level
*
* \deprecated Use led_update_kb() instead.
*/
__attribute__((weak)) void led_set_kb(uint8_t usb_led) { led_set_user(usb_led); }
/** \brief Lock LED update callback - keymap/user level
*
* \return True if led_update_kb() should run its own code, false otherwise.
*/
__attribute__((weak)) bool led_update_user(led_t led_state) { return true; }
/** \brief Lock LED update callback - keyboard level
*
* \return Ignored for now.
*/
__attribute__((weak)) bool led_update_kb(led_t led_state) {
bool res = led_update_user(led_state);
if (res) {
#if defined(LED_NUM_LOCK_PIN) || defined(LED_CAPS_LOCK_PIN) || defined(LED_SCROLL_LOCK_PIN) || defined(LED_COMPOSE_PIN) || defined(LED_KANA_PIN)
# if LED_PIN_ON_STATE == 0
// invert the whole thing to avoid having to conditionally !led_state.x later
led_state.raw = ~led_state.raw;
# endif
# ifdef LED_NUM_LOCK_PIN
writePin(LED_NUM_LOCK_PIN, led_state.num_lock);
# endif
# ifdef LED_CAPS_LOCK_PIN
writePin(LED_CAPS_LOCK_PIN, led_state.caps_lock);
# endif
# ifdef LED_SCROLL_LOCK_PIN
writePin(LED_SCROLL_LOCK_PIN, led_state.scroll_lock);
# endif
# ifdef LED_COMPOSE_PIN
writePin(LED_COMPOSE_PIN, led_state.compose);
# endif
# ifdef LED_KANA_PIN
writePin(LED_KANA_PIN, led_state.kana);
# endif
#endif
}
return res;
}
/** \brief Initialise any LED related hardware and/or state
*/
__attribute__((weak)) void led_init_ports(void) {
#ifdef LED_NUM_LOCK_PIN
setPinOutput(LED_NUM_LOCK_PIN);
writePin(LED_NUM_LOCK_PIN, !LED_PIN_ON_STATE);
#endif
#ifdef LED_CAPS_LOCK_PIN
setPinOutput(LED_CAPS_LOCK_PIN);
writePin(LED_CAPS_LOCK_PIN, !LED_PIN_ON_STATE);
#endif
#ifdef LED_SCROLL_LOCK_PIN
setPinOutput(LED_SCROLL_LOCK_PIN);
writePin(LED_SCROLL_LOCK_PIN, !LED_PIN_ON_STATE);
#endif
#ifdef LED_COMPOSE_PIN
setPinOutput(LED_COMPOSE_PIN);
writePin(LED_COMPOSE_PIN, !LED_PIN_ON_STATE);
#endif
#ifdef LED_KANA_PIN
setPinOutput(LED_KANA_PIN);
writePin(LED_KANA_PIN, !LED_PIN_ON_STATE);
#endif
}
/** \brief Entrypoint for protocol to LED binding
*/
__attribute__((weak)) void led_set(uint8_t usb_led) {
#ifdef BACKLIGHT_CAPS_LOCK
handle_backlight_caps_lock((led_t)usb_led);
#endif
led_set_kb(usb_led);
led_update_kb((led_t)usb_led);
}
+54
View File
@@ -0,0 +1,54 @@
/*
Copyright 2011 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/>.
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
/* FIXME: Add doxygen comments here. */
/* keyboard LEDs */
#define USB_LED_NUM_LOCK 0
#define USB_LED_CAPS_LOCK 1
#define USB_LED_SCROLL_LOCK 2
#define USB_LED_COMPOSE 3
#define USB_LED_KANA 4
#ifdef __cplusplus
extern "C" {
#endif
typedef union {
uint8_t raw;
struct {
bool num_lock : 1;
bool caps_lock : 1;
bool scroll_lock : 1;
bool compose : 1;
bool kana : 1;
uint8_t reserved : 3;
};
} led_t;
void led_set(uint8_t usb_led);
void led_init_ports(void);
#ifdef __cplusplus
}
#endif
+67 -75
View File
@@ -20,14 +20,14 @@
#include <stdint.h>
#include <stdbool.h>
#include "quantum.h"
#include "ledmatrix.h"
#include "led_matrix.h"
#include "progmem.h"
#include "config.h"
#include "eeprom.h"
#include <string.h>
#include <math.h>
led_config_t led_matrix_config;
led_eeconfig_t led_matrix_eeconfig;
#ifndef MAX
# define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
@@ -59,7 +59,7 @@ bool g_suspend_state = false;
uint32_t g_tick = 0;
// Ticks since this key was last hit.
uint8_t g_key_hit[LED_DRIVER_LED_COUNT];
uint8_t g_key_hit[DRIVER_LED_TOTAL];
// Ticks since any key was last hit.
uint32_t g_any_key_hit = 0;
@@ -70,40 +70,32 @@ void eeconfig_update_led_matrix(uint32_t config_value) { eeprom_update_dword(EEC
void eeconfig_update_led_matrix_default(void) {
dprintf("eeconfig_update_led_matrix_default\n");
led_matrix_config.enable = 1;
led_matrix_config.mode = LED_MATRIX_UNIFORM_BRIGHTNESS;
led_matrix_config.val = 128;
led_matrix_config.speed = 0;
eeconfig_update_led_matrix(led_matrix_config.raw);
led_matrix_eeconfig.enable = 1;
led_matrix_eeconfig.mode = LED_MATRIX_UNIFORM_BRIGHTNESS;
led_matrix_eeconfig.val = 128;
led_matrix_eeconfig.speed = 0;
eeconfig_update_led_matrix(led_matrix_eeconfig.raw);
}
void eeconfig_debug_led_matrix(void) {
dprintf("led_matrix_config eeprom\n");
dprintf("led_matrix_config.enable = %d\n", led_matrix_config.enable);
dprintf("led_matrix_config.mode = %d\n", led_matrix_config.mode);
dprintf("led_matrix_config.val = %d\n", led_matrix_config.val);
dprintf("led_matrix_config.speed = %d\n", led_matrix_config.speed);
dprintf("led_matrix_eeconfig eeprom\n");
dprintf("led_matrix_eeconfig.enable = %d\n", led_matrix_eeconfig.enable);
dprintf("led_matrix_eeconfig.mode = %d\n", led_matrix_eeconfig.mode);
dprintf("led_matrix_eeconfig.val = %d\n", led_matrix_eeconfig.val);
dprintf("led_matrix_eeconfig.speed = %d\n", led_matrix_eeconfig.speed);
}
// Last led hit
#ifndef LED_HITS_TO_REMEMBER
# define LED_HITS_TO_REMEMBER 8
#endif
uint8_t g_last_led_hit[LED_HITS_TO_REMEMBER] = {255};
uint8_t g_last_led_count = 0;
void map_row_column_to_led(uint8_t row, uint8_t column, uint8_t *led_i, uint8_t *led_count) {
led_matrix led;
*led_count = 0;
for (uint8_t i = 0; i < LED_DRIVER_LED_COUNT; i++) {
// map_index_to_led(i, &led);
led = g_leds[i];
if (row == led.matrix_co.row && column == led.matrix_co.col) {
led_i[*led_count] = i;
(*led_count)++;
}
uint8_t map_row_column_to_led(uint8_t row, uint8_t column, uint8_t *led_i) {
uint8_t led_count = 0;
uint8_t led_index = g_led_config.matrix_co[row][column];
if (led_index != NO_LED) {
led_i[led_count] = led_index;
led_count++;
}
return led_count;
}
void led_matrix_update_pwm_buffers(void) { led_matrix_driver.flush(); }
@@ -114,8 +106,8 @@ void led_matrix_set_index_value_all(uint8_t value) { led_matrix_driver.set_value
bool process_led_matrix(uint16_t keycode, keyrecord_t *record) {
if (record->event.pressed) {
uint8_t led[8], led_count;
map_row_column_to_led(record->event.key.row, record->event.key.col, led, &led_count);
uint8_t led[8];
uint8_t led_count = map_row_column_to_led(record->event.key.row, record->event.key.col, led);
if (led_count > 0) {
for (uint8_t i = LED_HITS_TO_REMEMBER; i > 1; i--) {
g_last_led_hit[i - 1] = g_last_led_hit[i - 2];
@@ -127,8 +119,8 @@ bool process_led_matrix(uint16_t keycode, keyrecord_t *record) {
g_any_key_hit = 0;
} else {
#ifdef LED_MATRIX_KEYRELEASES
uint8_t led[8], led_count;
map_row_column_to_led(record->event.key.row, record->event.key.col, led, &led_count);
uint8_t led[8];
uint8_t led_count = map_row_column_to_led(record->event.key.row, record->event.key.col, led);
for (uint8_t i = 0; i < led_count; i++) g_key_hit[led[i]] = 255;
g_any_key_hit = 255;
@@ -143,12 +135,12 @@ void led_matrix_set_suspend_state(bool state) { g_suspend_state = state; }
void led_matrix_all_off(void) { led_matrix_set_index_value_all(0); }
// Uniform brightness
void led_matrix_uniform_brightness(void) { led_matrix_set_index_value_all(LED_MATRIX_MAXIMUM_BRIGHTNESS / BACKLIGHT_LEVELS * led_matrix_config.val); }
void led_matrix_uniform_brightness(void) { led_matrix_set_index_value_all(LED_MATRIX_MAXIMUM_BRIGHTNESS / BACKLIGHT_LEVELS * led_matrix_eeconfig.val); }
void led_matrix_custom(void) {}
void led_matrix_task(void) {
if (!led_matrix_config.enable) {
if (!led_matrix_eeconfig.enable) {
led_matrix_all_off();
led_matrix_indicators();
return;
@@ -160,7 +152,7 @@ void led_matrix_task(void) {
g_any_key_hit++;
}
for (int led = 0; led < LED_DRIVER_LED_COUNT; led++) {
for (int led = 0; led < DRIVER_LED_TOTAL; led++) {
if (g_key_hit[led] < 255) {
if (g_key_hit[led] == 254) g_last_led_count = MAX(g_last_led_count - 1, 0);
g_key_hit[led]++;
@@ -170,7 +162,7 @@ void led_matrix_task(void) {
// Ideally we would also stop sending zeros to the LED driver PWM buffers
// while suspended and just do a software shutdown. This is a cheap hack for now.
bool suspend_backlight = ((g_suspend_state && LED_DISABLE_WHEN_USB_SUSPENDED) || (LED_DISABLE_AFTER_TIMEOUT > 0 && g_any_key_hit > LED_DISABLE_AFTER_TIMEOUT * 60 * 20));
uint8_t effect = suspend_backlight ? 0 : led_matrix_config.mode;
uint8_t effect = suspend_backlight ? 0 : led_matrix_eeconfig.mode;
// this gets ticked at 20 Hz.
// each effect can opt to do calculations
@@ -211,8 +203,8 @@ __attribute__((weak)) void led_matrix_indicators_user(void) {}
// else
// {
// // This needs updated to something like
// // uint8_t led[8], led_count;
// // map_row_column_to_led(row,column,led,&led_count);
// // uint8_t led[8];
// // uint8_t led_count = map_row_column_to_led(row, column, led);
// // for(uint8_t i = 0; i < led_count; i++)
// map_row_column_to_led(row, column, index);
// }
@@ -225,7 +217,7 @@ void led_matrix_init(void) {
wait_ms(500);
// clear the key hits
for (int led = 0; led < LED_DRIVER_LED_COUNT; led++) {
for (int led = 0; led < DRIVER_LED_TOTAL; led++) {
g_key_hit[led] = 255;
}
@@ -235,12 +227,12 @@ void led_matrix_init(void) {
eeconfig_update_led_matrix_default();
}
led_matrix_config.raw = eeconfig_read_led_matrix();
led_matrix_eeconfig.raw = eeconfig_read_led_matrix();
if (!led_matrix_config.mode) {
dprintf("led_matrix_init_drivers led_matrix_config.mode = 0. Write default values to EEPROM.\n");
if (!led_matrix_eeconfig.mode) {
dprintf("led_matrix_init_drivers led_matrix_eeconfig.mode = 0. Write default values to EEPROM.\n");
eeconfig_update_led_matrix_default();
led_matrix_config.raw = eeconfig_read_led_matrix();
led_matrix_eeconfig.raw = eeconfig_read_led_matrix();
}
eeconfig_debug_led_matrix(); // display current eeprom values
@@ -270,10 +262,10 @@ static uint8_t decrement(uint8_t value, uint8_t step, uint8_t min, uint8_t max)
// }
// void backlight_set_key_value(uint8_t row, uint8_t column, uint8_t value) {
// uint8_t led[8], led_count;
// map_row_column_to_led(row,column,led,&led_count);
// uint8_t led[8];
// uint8_t led_count = map_row_column_to_led(row, column, led);
// for(uint8_t i = 0; i < led_count; i++) {
// if (led[i] < LED_DRIVER_LED_COUNT) {
// if (led[i] < DRIVER_LED_TOTAL) {
// void *address = backlight_get_custom_key_value_eeprom_address(led[i]);
// eeprom_update_byte(address, value);
// }
@@ -283,74 +275,74 @@ static uint8_t decrement(uint8_t value, uint8_t step, uint8_t min, uint8_t max)
uint32_t led_matrix_get_tick(void) { return g_tick; }
void led_matrix_toggle(void) {
led_matrix_config.enable ^= 1;
eeconfig_update_led_matrix(led_matrix_config.raw);
led_matrix_eeconfig.enable ^= 1;
eeconfig_update_led_matrix(led_matrix_eeconfig.raw);
}
void led_matrix_enable(void) {
led_matrix_config.enable = 1;
eeconfig_update_led_matrix(led_matrix_config.raw);
led_matrix_eeconfig.enable = 1;
eeconfig_update_led_matrix(led_matrix_eeconfig.raw);
}
void led_matrix_enable_noeeprom(void) { led_matrix_config.enable = 1; }
void led_matrix_enable_noeeprom(void) { led_matrix_eeconfig.enable = 1; }
void led_matrix_disable(void) {
led_matrix_config.enable = 0;
eeconfig_update_led_matrix(led_matrix_config.raw);
led_matrix_eeconfig.enable = 0;
eeconfig_update_led_matrix(led_matrix_eeconfig.raw);
}
void led_matrix_disable_noeeprom(void) { led_matrix_config.enable = 0; }
void led_matrix_disable_noeeprom(void) { led_matrix_eeconfig.enable = 0; }
void led_matrix_step(void) {
led_matrix_config.mode++;
if (led_matrix_config.mode >= LED_MATRIX_EFFECT_MAX) {
led_matrix_config.mode = 1;
led_matrix_eeconfig.mode++;
if (led_matrix_eeconfig.mode >= LED_MATRIX_EFFECT_MAX) {
led_matrix_eeconfig.mode = 1;
}
eeconfig_update_led_matrix(led_matrix_config.raw);
eeconfig_update_led_matrix(led_matrix_eeconfig.raw);
}
void led_matrix_step_reverse(void) {
led_matrix_config.mode--;
if (led_matrix_config.mode < 1) {
led_matrix_config.mode = LED_MATRIX_EFFECT_MAX - 1;
led_matrix_eeconfig.mode--;
if (led_matrix_eeconfig.mode < 1) {
led_matrix_eeconfig.mode = LED_MATRIX_EFFECT_MAX - 1;
}
eeconfig_update_led_matrix(led_matrix_config.raw);
eeconfig_update_led_matrix(led_matrix_eeconfig.raw);
}
void led_matrix_increase_val(void) {
led_matrix_config.val = increment(led_matrix_config.val, 8, 0, LED_MATRIX_MAXIMUM_BRIGHTNESS);
eeconfig_update_led_matrix(led_matrix_config.raw);
led_matrix_eeconfig.val = increment(led_matrix_eeconfig.val, 8, 0, LED_MATRIX_MAXIMUM_BRIGHTNESS);
eeconfig_update_led_matrix(led_matrix_eeconfig.raw);
}
void led_matrix_decrease_val(void) {
led_matrix_config.val = decrement(led_matrix_config.val, 8, 0, LED_MATRIX_MAXIMUM_BRIGHTNESS);
eeconfig_update_led_matrix(led_matrix_config.raw);
led_matrix_eeconfig.val = decrement(led_matrix_eeconfig.val, 8, 0, LED_MATRIX_MAXIMUM_BRIGHTNESS);
eeconfig_update_led_matrix(led_matrix_eeconfig.raw);
}
void led_matrix_increase_speed(void) {
led_matrix_config.speed = increment(led_matrix_config.speed, 1, 0, 3);
eeconfig_update_led_matrix(led_matrix_config.raw); // EECONFIG needs to be increased to support this
led_matrix_eeconfig.speed = increment(led_matrix_eeconfig.speed, 1, 0, 3);
eeconfig_update_led_matrix(led_matrix_eeconfig.raw); // EECONFIG needs to be increased to support this
}
void led_matrix_decrease_speed(void) {
led_matrix_config.speed = decrement(led_matrix_config.speed, 1, 0, 3);
eeconfig_update_led_matrix(led_matrix_config.raw); // EECONFIG needs to be increased to support this
led_matrix_eeconfig.speed = decrement(led_matrix_eeconfig.speed, 1, 0, 3);
eeconfig_update_led_matrix(led_matrix_eeconfig.raw); // EECONFIG needs to be increased to support this
}
void led_matrix_mode(uint8_t mode, bool eeprom_write) {
led_matrix_config.mode = mode;
led_matrix_eeconfig.mode = mode;
if (eeprom_write) {
eeconfig_update_led_matrix(led_matrix_config.raw);
eeconfig_update_led_matrix(led_matrix_eeconfig.raw);
}
}
uint8_t led_matrix_get_mode(void) { return led_matrix_config.mode; }
uint8_t led_matrix_get_mode(void) { return led_matrix_eeconfig.mode; }
void led_matrix_set_value_noeeprom(uint8_t val) { led_matrix_config.val = val; }
void led_matrix_set_value_noeeprom(uint8_t val) { led_matrix_eeconfig.val = val; }
void led_matrix_set_value(uint8_t val) {
led_matrix_set_value_noeeprom(val);
eeconfig_update_led_matrix(led_matrix_config.raw);
eeconfig_update_led_matrix(led_matrix_eeconfig.raw);
}
void backlight_set(uint8_t val) { led_matrix_set_value(val); }
+6 -39
View File
@@ -17,49 +17,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LED_MATRIX_H
#define LED_MATRIX_H
#pragma once
#include "led_matrix_types.h"
#ifndef BACKLIGHT_ENABLE
# error You must define BACKLIGHT_ENABLE with LED_MATRIX_ENABLE
#endif
typedef struct Point {
uint8_t x;
uint8_t y;
} __attribute__((packed)) Point;
typedef struct led_matrix {
union {
uint8_t raw;
struct {
uint8_t row : 4; // 16 max
uint8_t col : 4; // 16 max
};
} matrix_co;
Point point;
uint8_t modifier : 1;
} __attribute__((packed)) led_matrix;
extern const led_matrix g_leds[LED_DRIVER_LED_COUNT];
typedef struct {
uint8_t index;
uint8_t value;
} led_indicator;
typedef union {
uint32_t raw;
struct {
bool enable : 1;
uint8_t mode : 6;
uint8_t hue : 8; // Unused by led_matrix
uint8_t sat : 8; // Unused by led_matrix
uint8_t val : 8;
uint8_t speed : 8; // EECONFIG needs to be increased to support this
};
} led_config_t;
enum led_matrix_effects {
LED_MATRIX_UNIFORM_BRIGHTNESS = 1,
// All new effects go above this line
@@ -124,4 +89,6 @@ typedef struct {
extern const led_matrix_driver_t led_matrix_driver;
#endif
extern led_eeconfig_t led_matrix_eeconfig;
extern led_config_t g_led_config;
+2 -2
View File
@@ -18,7 +18,7 @@
#include <stdint.h>
#include <stdbool.h>
#include "quantum.h"
#include "ledmatrix.h"
#include "led_matrix.h"
/* Each driver needs to define a struct:
*
@@ -66,7 +66,7 @@ static void init(void) {
# endif
# endif
for (int index = 0; index < LED_DRIVER_LED_COUNT; index++) {
for (int index = 0; index < DRIVER_LED_TOTAL; index++) {
# ifdef IS31FL3731
IS31FL3731_set_led_control_register(index, true);
# else
+69
View File
@@ -0,0 +1,69 @@
/* Copyright 2021
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#if defined(__GNUC__)
# define PACKED __attribute__((__packed__))
#else
# define PACKED
#endif
#if defined(_MSC_VER)
# pragma pack(push, 1)
#endif
// Last led hit
#ifndef LED_HITS_TO_REMEMBER
# define LED_HITS_TO_REMEMBER 8
#endif // LED_HITS_TO_REMEMBER
typedef struct PACKED {
uint8_t x;
uint8_t y;
} point_t;
#define LED_FLAG_ALL 0xFF
#define LED_FLAG_NONE 0x00
#define LED_FLAG_MODIFIER 0x01
#define LED_FLAG_KEYLIGHT 0x04
#define LED_FLAG_INDICATOR 0x08
#define NO_LED 255
typedef struct PACKED {
uint8_t matrix_co[MATRIX_ROWS][MATRIX_COLS];
point_t point[DRIVER_LED_TOTAL];
uint8_t flags[DRIVER_LED_TOTAL];
} led_config_t;
typedef union {
uint32_t raw;
struct PACKED {
uint8_t enable : 2;
uint8_t mode : 6;
uint16_t reserved;
uint8_t val;
uint8_t speed; // EECONFIG needs to be increased to support this
};
} led_eeconfig_t;
#if defined(_MSC_VER)
# pragma pack(pop)
#endif
+1 -4
View File
@@ -13,8 +13,7 @@ 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
#pragma once
#include "progmem.h"
#include <stdint.h>
@@ -22,5 +21,3 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifdef USE_CIE1931_CURVE
extern const uint8_t CIE1931_CURVE[] PROGMEM;
#endif
#endif
+29 -18
View File
@@ -32,6 +32,17 @@ static const pin_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS;
extern matrix_row_t raw_matrix[MATRIX_ROWS]; // raw values
extern matrix_row_t matrix[MATRIX_ROWS]; // debounced values
static inline void setPinOutput_writeLow(pin_t pin) {
ATOMIC_BLOCK_FORCEON {
setPinOutput(pin);
writePinLow(pin);
}
}
static inline void setPinInputHigh_atomic(pin_t pin) {
ATOMIC_BLOCK_FORCEON { setPinInputHigh(pin); }
}
// matrix code
#ifdef DIRECT_PINS
@@ -69,23 +80,20 @@ static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row)
#elif defined(DIODE_DIRECTION)
# if (DIODE_DIRECTION == COL2ROW)
static void select_row(uint8_t row) {
setPinOutput(row_pins[row]);
writePinLow(row_pins[row]);
}
static void select_row(uint8_t row) { setPinOutput_writeLow(row_pins[row]); }
static void unselect_row(uint8_t row) { setPinInputHigh(row_pins[row]); }
static void unselect_row(uint8_t row) { setPinInputHigh_atomic(row_pins[row]); }
static void unselect_rows(void) {
for (uint8_t x = 0; x < MATRIX_ROWS; x++) {
setPinInputHigh(row_pins[x]);
setPinInputHigh_atomic(row_pins[x]);
}
}
static void init_pins(void) {
unselect_rows();
for (uint8_t x = 0; x < MATRIX_COLS; x++) {
setPinInputHigh(col_pins[x]);
setPinInputHigh_atomic(col_pins[x]);
}
}
@@ -93,9 +101,9 @@ static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row)
// Start with a clear matrix row
matrix_row_t current_row_value = 0;
// Select row and wait for row selecton to stabilize
// Select row
select_row(current_row);
matrix_io_delay();
matrix_output_select_delay();
// For each col...
for (uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++) {
@@ -108,6 +116,9 @@ static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row)
// Unselect row
unselect_row(current_row);
if (current_row + 1 < MATRIX_ROWS) {
matrix_output_unselect_delay(); // wait for row signal to go HIGH
}
// If the row has changed, store the row and return the changed flag.
if (current_matrix[current_row] != current_row_value) {
@@ -119,32 +130,29 @@ static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row)
# elif (DIODE_DIRECTION == ROW2COL)
static void select_col(uint8_t col) {
setPinOutput(col_pins[col]);
writePinLow(col_pins[col]);
}
static void select_col(uint8_t col) { setPinOutput_writeLow(col_pins[col]); }
static void unselect_col(uint8_t col) { setPinInputHigh(col_pins[col]); }
static void unselect_col(uint8_t col) { setPinInputHigh_atomic(col_pins[col]); }
static void unselect_cols(void) {
for (uint8_t x = 0; x < MATRIX_COLS; x++) {
setPinInputHigh(col_pins[x]);
setPinInputHigh_atomic(col_pins[x]);
}
}
static void init_pins(void) {
unselect_cols();
for (uint8_t x = 0; x < MATRIX_ROWS; x++) {
setPinInputHigh(row_pins[x]);
setPinInputHigh_atomic(row_pins[x]);
}
}
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
select_col(current_col);
matrix_io_delay();
matrix_output_select_delay();
// For each row...
for (uint8_t row_index = 0; row_index < MATRIX_ROWS; row_index++) {
@@ -170,6 +178,9 @@ static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col)
// Unselect col
unselect_col(current_col);
if (current_col + 1 < MATRIX_COLS) {
matrix_output_unselect_delay(); // wait for col signal to go HIGH
}
return matrix_changed;
}
+79
View File
@@ -0,0 +1,79 @@
/*
Copyright 2011 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/>.
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#if (MATRIX_COLS <= 8)
typedef uint8_t matrix_row_t;
#elif (MATRIX_COLS <= 16)
typedef uint16_t matrix_row_t;
#elif (MATRIX_COLS <= 32)
typedef uint32_t matrix_row_t;
#else
# error "MATRIX_COLS: invalid value"
#endif
#define MATRIX_ROW_SHIFTER ((matrix_row_t)1)
#ifdef __cplusplus
extern "C" {
#endif
/* number of matrix rows */
uint8_t matrix_rows(void);
/* number of matrix columns */
uint8_t matrix_cols(void);
/* should be called at early stage of startup before matrix_init.(optional) */
void matrix_setup(void);
/* intialize matrix for scaning. */
void matrix_init(void);
/* scan all key states on matrix */
uint8_t matrix_scan(void);
/* whether modified from previous scan. used after matrix_scan. */
bool matrix_is_modified(void) __attribute__((deprecated));
/* whether a switch is on */
bool matrix_is_on(uint8_t row, uint8_t col);
/* matrix state on row */
matrix_row_t matrix_get_row(uint8_t row);
/* print matrix for debug */
void matrix_print(void);
/* delay between changing matrix pin state and reading values */
void matrix_output_select_delay(void);
void matrix_output_unselect_delay(void);
/* only for backwards compatibility. delay between changing matrix pin state and reading values */
void matrix_io_delay(void);
/* power control */
void matrix_power_up(void);
void matrix_power_down(void);
/* executes code for Quantum */
void matrix_init_quantum(void);
void matrix_scan_quantum(void);
void matrix_init_kb(void);
void matrix_scan_kb(void);
void matrix_init_user(void);
void matrix_scan_user(void);
#ifdef __cplusplus
}
#endif
+6 -1
View File
@@ -1,3 +1,4 @@
#include "quantum.h"
#include "matrix.h"
#include "debounce.h"
#include "wait.h"
@@ -68,7 +69,7 @@ void matrix_print(void) {
print_matrix_header();
for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
phex(row);
print_hex8(row);
print(": ");
print_matrix_row(row);
print("\n");
@@ -83,8 +84,12 @@ uint8_t matrix_key_count(void) {
return count;
}
/* `matrix_io_delay ()` exists for backwards compatibility. From now on, use matrix_output_unselect_delay(). */
__attribute__((weak)) void matrix_io_delay(void) { wait_us(MATRIX_IO_DELAY); }
__attribute__((weak)) void matrix_output_select_delay(void) { waitInputPinDelay(); }
__attribute__((weak)) void matrix_output_unselect_delay(void) { matrix_io_delay(); }
// CUSTOM MATRIX 'LITE'
__attribute__((weak)) void matrix_init_custom(void) {}
+70 -15
View File
@@ -16,7 +16,6 @@ ifneq ($(findstring MKL26Z64, $(MCU)),)
# Linker script to use
# - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
# or <keyboard_dir>/ld/
# - NOTE: a custom ld script is needed for EEPROM on Teensy LC
MCU_LDSCRIPT ?= MKL26Z64
# Startup code to use
@@ -280,7 +279,73 @@ ifneq ($(findstring STM32F411, $(MCU)),)
DFU_SUFFIX_ARGS ?= -v 0483 -p DF11
endif
ifneq (,$(filter $(MCU),atmega16u2 atmega32u2 atmega16u4 atmega32u4 at90usb646 at90usb647 at90usb1286 at90usb1287))
ifneq ($(findstring STM32G431, $(MCU)),)
# Cortex version
MCU = cortex-m4
# ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
ARMV = 7
## chip/board settings
# - the next two should match the directories in
# <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
MCU_FAMILY = STM32
MCU_SERIES = STM32G4xx
# Linker script to use
# - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
# or <keyboard_dir>/ld/
MCU_LDSCRIPT ?= STM32G431xB
# Startup code to use
# - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/
MCU_STARTUP ?= stm32g4xx
# Board: it should exist either in <chibios>/os/hal/boards/,
# <keyboard_dir>/boards/, or drivers/boards/
BOARD ?= GENERIC_STM32_G431XB
USE_FPU ?= yes
# Options to pass to dfu-util when flashing
DFU_ARGS ?= -d 0483:DF11 -a 0 -s 0x08000000:leave
DFU_SUFFIX_ARGS ?= -v 0483 -p DF11
endif
ifneq ($(findstring STM32G474, $(MCU)),)
# Cortex version
MCU = cortex-m4
# ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
ARMV = 7
## chip/board settings
# - the next two should match the directories in
# <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
MCU_FAMILY = STM32
MCU_SERIES = STM32G4xx
# Linker script to use
# - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
# or <keyboard_dir>/ld/
MCU_LDSCRIPT ?= STM32G474xE
# Startup code to use
# - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/
MCU_STARTUP ?= stm32g4xx
# Board: it should exist either in <chibios>/os/hal/boards/,
# <keyboard_dir>/boards/, or drivers/boards/
BOARD ?= GENERIC_STM32_G474XE
USE_FPU ?= yes
# Options to pass to dfu-util when flashing
DFU_ARGS ?= -d 0483:DF11 -a 0 -s 0x08000000:leave
DFU_SUFFIX_ARGS ?= -v 0483 -p DF11
endif
ifneq (,$(filter $(MCU),at90usb162 atmega16u2 atmega32u2 atmega16u4 atmega32u4 at90usb646 at90usb647 at90usb1286 at90usb1287))
PROTOCOL = LUFA
# Processor frequency.
@@ -318,6 +383,9 @@ ifneq (,$(filter $(MCU),atmega16u2 atmega32u2 atmega16u4 atmega32u4 at90usb646 a
ifeq (,$(filter $(NO_INTERRUPT_CONTROL_ENDPOINT),yes))
OPT_DEFS += -DINTERRUPT_CONTROL_ENDPOINT
endif
ifneq (,$(filter $(MCU),at90usb162 atmega16u2 atmega32u2))
NO_I2C = yes
endif
endif
ifneq (,$(filter $(MCU),atmega32a))
@@ -332,9 +400,6 @@ ifneq (,$(filter $(MCU),atmega32a))
# 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.
F_CPU ?= 12000000
# unsupported features for now
NO_SUSPEND_POWER_DOWN ?= yes
endif
ifneq (,$(filter $(MCU),atmega328p))
@@ -349,9 +414,6 @@ ifneq (,$(filter $(MCU),atmega328p))
# 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.
F_CPU ?= 16000000
# unsupported features for now
NO_SUSPEND_POWER_DOWN ?= yes
endif
ifneq (,$(filter $(MCU),atmega328))
@@ -366,10 +428,6 @@ ifneq (,$(filter $(MCU),atmega328))
# 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.
F_CPU ?= 16000000
# unsupported features for now
NO_UART ?= yes
NO_SUSPEND_POWER_DOWN ?= yes
endif
ifneq (,$(filter $(MCU),attiny85))
@@ -381,7 +439,4 @@ ifneq (,$(filter $(MCU),attiny85))
# 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.
F_CPU ?= 16500000
# unsupported features for now
NO_SUSPEND_POWER_DOWN ?= yes
endif
+488
View File
@@ -0,0 +1,488 @@
/*
* Copyright 2011 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 <stdint.h>
#include "keycode.h"
#include "host.h"
#include "timer.h"
#include "print.h"
#include "debug.h"
#include "mousekey.h"
inline int8_t times_inv_sqrt2(int8_t x) {
// 181/256 is pretty close to 1/sqrt(2)
// 0.70703125 0.707106781
// 1 too small for x=99 and x=198
// This ends up being a mult and discard lower 8 bits
return (x * 181) >> 8;
}
static report_mouse_t mouse_report = {0};
static void mousekey_debug(void);
static uint8_t mousekey_accel = 0;
static uint8_t mousekey_repeat = 0;
static uint8_t mousekey_wheel_repeat = 0;
#ifdef MK_KINETIC_SPEED
static uint16_t mouse_timer = 0;
#endif
#ifndef MK_3_SPEED
static uint16_t last_timer_c = 0;
static uint16_t last_timer_w = 0;
/*
* Mouse keys acceleration algorithm
* http://en.wikipedia.org/wiki/Mouse_keys
*
* speed = delta * max_speed * (repeat / time_to_max)**((1000+curve)/1000)
*/
/* milliseconds between the initial key press and first repeated motion event (0-2550) */
uint8_t mk_delay = MOUSEKEY_DELAY / 10;
/* milliseconds between repeated motion events (0-255) */
uint8_t mk_interval = MOUSEKEY_INTERVAL;
/* steady speed (in action_delta units) applied each event (0-255) */
uint8_t mk_max_speed = MOUSEKEY_MAX_SPEED;
/* number of events (count) accelerating to steady speed (0-255) */
uint8_t mk_time_to_max = MOUSEKEY_TIME_TO_MAX;
/* ramp used to reach maximum pointer speed (NOT SUPPORTED) */
// int8_t mk_curve = 0;
/* wheel params */
/* milliseconds between the initial key press and first repeated motion event (0-2550) */
uint8_t mk_wheel_delay = MOUSEKEY_WHEEL_DELAY / 10;
/* milliseconds between repeated motion events (0-255) */
uint8_t mk_wheel_interval = MOUSEKEY_WHEEL_INTERVAL;
uint8_t mk_wheel_max_speed = MOUSEKEY_WHEEL_MAX_SPEED;
uint8_t mk_wheel_time_to_max = MOUSEKEY_WHEEL_TIME_TO_MAX;
# ifndef MK_COMBINED
static uint8_t move_unit(void) {
uint16_t unit;
if (mousekey_accel & (1 << 0)) {
unit = (MOUSEKEY_MOVE_DELTA * mk_max_speed) / 4;
} else if (mousekey_accel & (1 << 1)) {
unit = (MOUSEKEY_MOVE_DELTA * mk_max_speed) / 2;
} else if (mousekey_accel & (1 << 2)) {
unit = (MOUSEKEY_MOVE_DELTA * mk_max_speed);
} else if (mousekey_repeat == 0) {
unit = MOUSEKEY_MOVE_DELTA;
} else if (mousekey_repeat >= mk_time_to_max) {
unit = MOUSEKEY_MOVE_DELTA * mk_max_speed;
} else {
unit = (MOUSEKEY_MOVE_DELTA * mk_max_speed * mousekey_repeat) / mk_time_to_max;
}
return (unit > MOUSEKEY_MOVE_MAX ? MOUSEKEY_MOVE_MAX : (unit == 0 ? 1 : unit));
}
static uint8_t wheel_unit(void) {
uint16_t unit;
if (mousekey_accel & (1 << 0)) {
unit = (MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed) / 4;
} else if (mousekey_accel & (1 << 1)) {
unit = (MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed) / 2;
} else if (mousekey_accel & (1 << 2)) {
unit = (MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed);
} else if (mousekey_wheel_repeat == 0) {
unit = MOUSEKEY_WHEEL_DELTA;
} else if (mousekey_wheel_repeat >= mk_wheel_time_to_max) {
unit = MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed;
} else {
unit = (MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed * mousekey_wheel_repeat) / mk_wheel_time_to_max;
}
return (unit > MOUSEKEY_WHEEL_MAX ? MOUSEKEY_WHEEL_MAX : (unit == 0 ? 1 : unit));
}
# else /* #ifndef MK_COMBINED */
# ifndef MK_KINETIC_SPEED
/*
* Kinetic movement acceleration algorithm
*
* current speed = I + A * T/50 + A * 0.5 * T^2 | maximum B
*
* T: time since the mouse movement started
* E: mouse events per second (set through MOUSEKEY_INTERVAL, UHK sends 250, the
* pro micro on my Signum 3.0 sends only 125!)
* I: initial speed at time 0
* A: acceleration
* B: base mouse travel speed
*/
const uint16_t mk_accelerated_speed = MOUSEKEY_ACCELERATED_SPEED;
const uint16_t mk_base_speed = MOUSEKEY_BASE_SPEED;
const uint16_t mk_decelerated_speed = MOUSEKEY_DECELERATED_SPEED;
const uint16_t mk_initial_speed = MOUSEKEY_INITIAL_SPEED;
static uint8_t move_unit(void) {
float speed = mk_initial_speed;
if (mousekey_accel & ((1 << 0) | (1 << 2))) {
speed = mousekey_accel & (1 << 2) ? mk_accelerated_speed : mk_decelerated_speed;
} else if (mousekey_repeat && mouse_timer) {
const float time_elapsed = timer_elapsed(mouse_timer) / 50;
speed = mk_initial_speed + MOUSEKEY_MOVE_DELTA * time_elapsed + MOUSEKEY_MOVE_DELTA * 0.5 * time_elapsed * time_elapsed;
speed = speed > mk_base_speed ? mk_base_speed : speed;
}
/* convert speed to USB mouse speed 1 to 127 */
speed = (uint8_t)(speed / (1000.0f / mk_interval));
speed = speed < 1 ? 1 : speed;
return speed > MOUSEKEY_MOVE_MAX ? MOUSEKEY_MOVE_MAX : speed;
}
float mk_wheel_interval = 1000.0f / MOUSEKEY_WHEEL_INITIAL_MOVEMENTS;
static uint8_t wheel_unit(void) {
float speed = MOUSEKEY_WHEEL_INITIAL_MOVEMENTS;
if (mousekey_accel & ((1 << 0) | (1 << 2))) {
speed = mousekey_accel & (1 << 2) ? MOUSEKEY_WHEEL_ACCELERATED_MOVEMENTS : MOUSEKEY_WHEEL_DECELERATED_MOVEMENTS;
} else if (mousekey_repeat && mouse_timer) {
if (mk_wheel_interval != MOUSEKEY_WHEEL_BASE_MOVEMENTS) {
const float time_elapsed = timer_elapsed(mouse_timer) / 50;
speed = MOUSEKEY_WHEEL_INITIAL_MOVEMENTS + 1 * time_elapsed + 1 * 0.5 * time_elapsed * time_elapsed;
}
speed = speed > MOUSEKEY_WHEEL_BASE_MOVEMENTS ? MOUSEKEY_WHEEL_BASE_MOVEMENTS : speed;
}
mk_wheel_interval = 1000.0f / speed;
return 1;
}
# else /* #ifndef MK_KINETIC_SPEED */
static uint8_t move_unit(void) {
uint16_t unit;
if (mousekey_accel & (1 << 0)) {
unit = 1;
} else if (mousekey_accel & (1 << 1)) {
unit = (MOUSEKEY_MOVE_DELTA * mk_max_speed) / 2;
} else if (mousekey_accel & (1 << 2)) {
unit = MOUSEKEY_MOVE_MAX;
} else if (mousekey_repeat == 0) {
unit = MOUSEKEY_MOVE_DELTA;
} else if (mousekey_repeat >= mk_time_to_max) {
unit = MOUSEKEY_MOVE_DELTA * mk_max_speed;
} else {
unit = (MOUSEKEY_MOVE_DELTA * mk_max_speed * mousekey_repeat) / mk_time_to_max;
}
return (unit > MOUSEKEY_MOVE_MAX ? MOUSEKEY_MOVE_MAX : (unit == 0 ? 1 : unit));
}
static uint8_t wheel_unit(void) {
uint16_t unit;
if (mousekey_accel & (1 << 0)) {
unit = 1;
} else if (mousekey_accel & (1 << 1)) {
unit = (MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed) / 2;
} else if (mousekey_accel & (1 << 2)) {
unit = MOUSEKEY_WHEEL_MAX;
} else if (mousekey_repeat == 0) {
unit = MOUSEKEY_WHEEL_DELTA;
} else if (mousekey_repeat >= mk_wheel_time_to_max) {
unit = MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed;
} else {
unit = (MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed * mousekey_repeat) / mk_wheel_time_to_max;
}
return (unit > MOUSEKEY_WHEEL_MAX ? MOUSEKEY_WHEEL_MAX : (unit == 0 ? 1 : unit));
}
# endif /* #ifndef MK_KINETIC_SPEED */
# endif /* #ifndef MK_COMBINED */
void mousekey_task(void) {
// report cursor and scroll movement independently
report_mouse_t const tmpmr = mouse_report;
mouse_report.x = 0;
mouse_report.y = 0;
mouse_report.v = 0;
mouse_report.h = 0;
if ((tmpmr.x || tmpmr.y) && timer_elapsed(last_timer_c) > (mousekey_repeat ? mk_interval : mk_delay * 10)) {
if (mousekey_repeat != UINT8_MAX) mousekey_repeat++;
if (tmpmr.x != 0) mouse_report.x = move_unit() * ((tmpmr.x > 0) ? 1 : -1);
if (tmpmr.y != 0) mouse_report.y = move_unit() * ((tmpmr.y > 0) ? 1 : -1);
/* diagonal move [1/sqrt(2)] */
if (mouse_report.x && mouse_report.y) {
mouse_report.x = times_inv_sqrt2(mouse_report.x);
if (mouse_report.x == 0) {
mouse_report.x = 1;
}
mouse_report.y = times_inv_sqrt2(mouse_report.y);
if (mouse_report.y == 0) {
mouse_report.y = 1;
}
}
}
if ((tmpmr.v || tmpmr.h) && timer_elapsed(last_timer_w) > (mousekey_wheel_repeat ? mk_wheel_interval : mk_wheel_delay * 10)) {
if (mousekey_wheel_repeat != UINT8_MAX) mousekey_wheel_repeat++;
if (tmpmr.v != 0) mouse_report.v = wheel_unit() * ((tmpmr.v > 0) ? 1 : -1);
if (tmpmr.h != 0) mouse_report.h = wheel_unit() * ((tmpmr.h > 0) ? 1 : -1);
/* diagonal move [1/sqrt(2)] */
if (mouse_report.v && mouse_report.h) {
mouse_report.v = times_inv_sqrt2(mouse_report.v);
if (mouse_report.v == 0) {
mouse_report.v = 1;
}
mouse_report.h = times_inv_sqrt2(mouse_report.h);
if (mouse_report.h == 0) {
mouse_report.h = 1;
}
}
}
if (mouse_report.x || mouse_report.y || mouse_report.v || mouse_report.h) mousekey_send();
mouse_report = tmpmr;
}
void mousekey_on(uint8_t code) {
# ifdef MK_KINETIC_SPEED
if (mouse_timer == 0) {
mouse_timer = timer_read();
}
# endif /* #ifdef MK_KINETIC_SPEED */
if (code == KC_MS_UP)
mouse_report.y = move_unit() * -1;
else if (code == KC_MS_DOWN)
mouse_report.y = move_unit();
else if (code == KC_MS_LEFT)
mouse_report.x = move_unit() * -1;
else if (code == KC_MS_RIGHT)
mouse_report.x = move_unit();
else if (code == KC_MS_WH_UP)
mouse_report.v = wheel_unit();
else if (code == KC_MS_WH_DOWN)
mouse_report.v = wheel_unit() * -1;
else if (code == KC_MS_WH_LEFT)
mouse_report.h = wheel_unit() * -1;
else if (code == KC_MS_WH_RIGHT)
mouse_report.h = wheel_unit();
else if (IS_MOUSEKEY_BUTTON(code))
mouse_report.buttons |= 1 << (code - KC_MS_BTN1);
else if (code == KC_MS_ACCEL0)
mousekey_accel |= (1 << 0);
else if (code == KC_MS_ACCEL1)
mousekey_accel |= (1 << 1);
else if (code == KC_MS_ACCEL2)
mousekey_accel |= (1 << 2);
}
void mousekey_off(uint8_t code) {
if (code == KC_MS_UP && mouse_report.y < 0)
mouse_report.y = 0;
else if (code == KC_MS_DOWN && mouse_report.y > 0)
mouse_report.y = 0;
else if (code == KC_MS_LEFT && mouse_report.x < 0)
mouse_report.x = 0;
else if (code == KC_MS_RIGHT && mouse_report.x > 0)
mouse_report.x = 0;
else if (code == KC_MS_WH_UP && mouse_report.v > 0)
mouse_report.v = 0;
else if (code == KC_MS_WH_DOWN && mouse_report.v < 0)
mouse_report.v = 0;
else if (code == KC_MS_WH_LEFT && mouse_report.h < 0)
mouse_report.h = 0;
else if (code == KC_MS_WH_RIGHT && mouse_report.h > 0)
mouse_report.h = 0;
else if (IS_MOUSEKEY_BUTTON(code))
mouse_report.buttons &= ~(1 << (code - KC_MS_BTN1));
else if (code == KC_MS_ACCEL0)
mousekey_accel &= ~(1 << 0);
else if (code == KC_MS_ACCEL1)
mousekey_accel &= ~(1 << 1);
else if (code == KC_MS_ACCEL2)
mousekey_accel &= ~(1 << 2);
if (mouse_report.x == 0 && mouse_report.y == 0) {
mousekey_repeat = 0;
# ifdef MK_KINETIC_SPEED
mouse_timer = 0;
# endif /* #ifdef MK_KINETIC_SPEED */
}
if (mouse_report.v == 0 && mouse_report.h == 0) mousekey_wheel_repeat = 0;
}
#else /* #ifndef MK_3_SPEED */
enum { mkspd_unmod, mkspd_0, mkspd_1, mkspd_2, mkspd_COUNT };
# ifndef MK_MOMENTARY_ACCEL
static uint8_t mk_speed = mkspd_1;
# else
static uint8_t mk_speed = mkspd_unmod;
static uint8_t mkspd_DEFAULT = mkspd_unmod;
# endif
static uint16_t last_timer_c = 0;
static uint16_t last_timer_w = 0;
uint16_t c_offsets[mkspd_COUNT] = {MK_C_OFFSET_UNMOD, MK_C_OFFSET_0, MK_C_OFFSET_1, MK_C_OFFSET_2};
uint16_t c_intervals[mkspd_COUNT] = {MK_C_INTERVAL_UNMOD, MK_C_INTERVAL_0, MK_C_INTERVAL_1, MK_C_INTERVAL_2};
uint16_t w_offsets[mkspd_COUNT] = {MK_W_OFFSET_UNMOD, MK_W_OFFSET_0, MK_W_OFFSET_1, MK_W_OFFSET_2};
uint16_t w_intervals[mkspd_COUNT] = {MK_W_INTERVAL_UNMOD, MK_W_INTERVAL_0, MK_W_INTERVAL_1, MK_W_INTERVAL_2};
void mousekey_task(void) {
// report cursor and scroll movement independently
report_mouse_t const tmpmr = mouse_report;
mouse_report.x = 0;
mouse_report.y = 0;
mouse_report.v = 0;
mouse_report.h = 0;
if ((tmpmr.x || tmpmr.y) && timer_elapsed(last_timer_c) > c_intervals[mk_speed]) {
mouse_report.x = tmpmr.x;
mouse_report.y = tmpmr.y;
}
if ((tmpmr.h || tmpmr.v) && timer_elapsed(last_timer_w) > w_intervals[mk_speed]) {
mouse_report.v = tmpmr.v;
mouse_report.h = tmpmr.h;
}
if (mouse_report.x || mouse_report.y || mouse_report.v || mouse_report.h) mousekey_send();
mouse_report = tmpmr;
}
void adjust_speed(void) {
uint16_t const c_offset = c_offsets[mk_speed];
uint16_t const w_offset = w_offsets[mk_speed];
if (mouse_report.x > 0) mouse_report.x = c_offset;
if (mouse_report.x < 0) mouse_report.x = c_offset * -1;
if (mouse_report.y > 0) mouse_report.y = c_offset;
if (mouse_report.y < 0) mouse_report.y = c_offset * -1;
if (mouse_report.h > 0) mouse_report.h = w_offset;
if (mouse_report.h < 0) mouse_report.h = w_offset * -1;
if (mouse_report.v > 0) mouse_report.v = w_offset;
if (mouse_report.v < 0) mouse_report.v = w_offset * -1;
// adjust for diagonals
if (mouse_report.x && mouse_report.y) {
mouse_report.x = times_inv_sqrt2(mouse_report.x);
if (mouse_report.x == 0) {
mouse_report.x = 1;
}
mouse_report.y = times_inv_sqrt2(mouse_report.y);
if (mouse_report.y == 0) {
mouse_report.y = 1;
}
}
if (mouse_report.h && mouse_report.v) {
mouse_report.h = times_inv_sqrt2(mouse_report.h);
mouse_report.v = times_inv_sqrt2(mouse_report.v);
}
}
void mousekey_on(uint8_t code) {
uint16_t const c_offset = c_offsets[mk_speed];
uint16_t const w_offset = w_offsets[mk_speed];
uint8_t const old_speed = mk_speed;
if (code == KC_MS_UP)
mouse_report.y = c_offset * -1;
else if (code == KC_MS_DOWN)
mouse_report.y = c_offset;
else if (code == KC_MS_LEFT)
mouse_report.x = c_offset * -1;
else if (code == KC_MS_RIGHT)
mouse_report.x = c_offset;
else if (code == KC_MS_WH_UP)
mouse_report.v = w_offset;
else if (code == KC_MS_WH_DOWN)
mouse_report.v = w_offset * -1;
else if (code == KC_MS_WH_LEFT)
mouse_report.h = w_offset * -1;
else if (code == KC_MS_WH_RIGHT)
mouse_report.h = w_offset;
else if (IS_MOUSEKEY_BUTTON(code))
mouse_report.buttons |= 1 << (code - KC_MS_BTN1);
else if (code == KC_MS_ACCEL0)
mk_speed = mkspd_0;
else if (code == KC_MS_ACCEL1)
mk_speed = mkspd_1;
else if (code == KC_MS_ACCEL2)
mk_speed = mkspd_2;
if (mk_speed != old_speed) adjust_speed();
}
void mousekey_off(uint8_t code) {
# ifdef MK_MOMENTARY_ACCEL
uint8_t const old_speed = mk_speed;
# endif
if (code == KC_MS_UP && mouse_report.y < 0)
mouse_report.y = 0;
else if (code == KC_MS_DOWN && mouse_report.y > 0)
mouse_report.y = 0;
else if (code == KC_MS_LEFT && mouse_report.x < 0)
mouse_report.x = 0;
else if (code == KC_MS_RIGHT && mouse_report.x > 0)
mouse_report.x = 0;
else if (code == KC_MS_WH_UP && mouse_report.v > 0)
mouse_report.v = 0;
else if (code == KC_MS_WH_DOWN && mouse_report.v < 0)
mouse_report.v = 0;
else if (code == KC_MS_WH_LEFT && mouse_report.h < 0)
mouse_report.h = 0;
else if (code == KC_MS_WH_RIGHT && mouse_report.h > 0)
mouse_report.h = 0;
else if (IS_MOUSEKEY_BUTTON(code))
mouse_report.buttons &= ~(1 << (code - KC_MS_BTN1));
# ifdef MK_MOMENTARY_ACCEL
else if (code == KC_MS_ACCEL0)
mk_speed = mkspd_DEFAULT;
else if (code == KC_MS_ACCEL1)
mk_speed = mkspd_DEFAULT;
else if (code == KC_MS_ACCEL2)
mk_speed = mkspd_DEFAULT;
if (mk_speed != old_speed) adjust_speed();
# endif
}
#endif /* #ifndef MK_3_SPEED */
void mousekey_send(void) {
mousekey_debug();
uint16_t time = timer_read();
if (mouse_report.x || mouse_report.y) last_timer_c = time;
if (mouse_report.v || mouse_report.h) last_timer_w = time;
host_mouse_send(&mouse_report);
}
void mousekey_clear(void) {
mouse_report = (report_mouse_t){};
mousekey_repeat = 0;
mousekey_wheel_repeat = 0;
mousekey_accel = 0;
}
static void mousekey_debug(void) {
if (!debug_mouse) return;
print("mousekey [btn|x y v h](rep/acl): [");
print_hex8(mouse_report.buttons);
print("|");
print_decs(mouse_report.x);
print(" ");
print_decs(mouse_report.y);
print(" ");
print_decs(mouse_report.v);
print(" ");
print_decs(mouse_report.h);
print("](");
print_dec(mousekey_repeat);
print("/");
print_dec(mousekey_accel);
print(")\n");
}
+179
View File
@@ -0,0 +1,179 @@
/*
Copyright 2011 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/>.
*/
#pragma once
#include <stdint.h>
#include "host.h"
#ifndef MK_3_SPEED
/* max value on report descriptor */
# ifndef MOUSEKEY_MOVE_MAX
# define MOUSEKEY_MOVE_MAX 127
# elif MOUSEKEY_MOVE_MAX > 127
# error MOUSEKEY_MOVE_MAX needs to be smaller than 127
# endif
# ifndef MOUSEKEY_WHEEL_MAX
# define MOUSEKEY_WHEEL_MAX 127
# elif MOUSEKEY_WHEEL_MAX > 127
# error MOUSEKEY_WHEEL_MAX needs to be smaller than 127
# endif
# ifndef MOUSEKEY_MOVE_DELTA
# ifndef MK_KINETIC_SPEED
# define MOUSEKEY_MOVE_DELTA 5
# else
# define MOUSEKEY_MOVE_DELTA 25
# endif
# endif
# ifndef MOUSEKEY_WHEEL_DELTA
# define MOUSEKEY_WHEEL_DELTA 1
# endif
# ifndef MOUSEKEY_DELAY
# ifndef MK_KINETIC_SPEED
# define MOUSEKEY_DELAY 300
# else
# define MOUSEKEY_DELAY 8
# endif
# endif
# ifndef MOUSEKEY_INTERVAL
# ifndef MK_KINETIC_SPEED
# define MOUSEKEY_INTERVAL 50
# else
# define MOUSEKEY_INTERVAL 8
# endif
# endif
# ifndef MOUSEKEY_MAX_SPEED
# define MOUSEKEY_MAX_SPEED 10
# endif
# ifndef MOUSEKEY_TIME_TO_MAX
# define MOUSEKEY_TIME_TO_MAX 20
# endif
# ifndef MOUSEKEY_WHEEL_DELAY
# define MOUSEKEY_WHEEL_DELAY 300
# endif
# ifndef MOUSEKEY_WHEEL_INTERVAL
# define MOUSEKEY_WHEEL_INTERVAL 100
# endif
# ifndef MOUSEKEY_WHEEL_MAX_SPEED
# define MOUSEKEY_WHEEL_MAX_SPEED 8
# endif
# ifndef MOUSEKEY_WHEEL_TIME_TO_MAX
# define MOUSEKEY_WHEEL_TIME_TO_MAX 40
# endif
# ifndef MOUSEKEY_INITIAL_SPEED
# define MOUSEKEY_INITIAL_SPEED 100
# endif
# ifndef MOUSEKEY_BASE_SPEED
# define MOUSEKEY_BASE_SPEED 1000
# endif
# ifndef MOUSEKEY_DECELERATED_SPEED
# define MOUSEKEY_DECELERATED_SPEED 400
# endif
# ifndef MOUSEKEY_ACCELERATED_SPEED
# define MOUSEKEY_ACCELERATED_SPEED 3000
# endif
# ifndef MOUSEKEY_WHEEL_INITIAL_MOVEMENTS
# define MOUSEKEY_WHEEL_INITIAL_MOVEMENTS 16
# endif
# ifndef MOUSEKEY_WHEEL_BASE_MOVEMENTS
# define MOUSEKEY_WHEEL_BASE_MOVEMENTS 32
# endif
# ifndef MOUSEKEY_WHEEL_ACCELERATED_MOVEMENTS
# define MOUSEKEY_WHEEL_ACCELERATED_MOVEMENTS 48
# endif
# ifndef MOUSEKEY_WHEEL_DECELERATED_MOVEMENTS
# define MOUSEKEY_WHEEL_DECELERATED_MOVEMENTS 8
# endif
#else /* #ifndef MK_3_SPEED */
# ifndef MK_C_OFFSET_UNMOD
# define MK_C_OFFSET_UNMOD 16
# endif
# ifndef MK_C_INTERVAL_UNMOD
# define MK_C_INTERVAL_UNMOD 16
# endif
# ifndef MK_C_OFFSET_0
# define MK_C_OFFSET_0 1
# endif
# ifndef MK_C_INTERVAL_0
# define MK_C_INTERVAL_0 32
# endif
# ifndef MK_C_OFFSET_1
# define MK_C_OFFSET_1 4
# endif
# ifndef MK_C_INTERVAL_1
# define MK_C_INTERVAL_1 16
# endif
# ifndef MK_C_OFFSET_2
# define MK_C_OFFSET_2 32
# endif
# ifndef MK_C_INTERVAL_2
# define MK_C_INTERVAL_2 16
# endif
# ifndef MK_W_OFFSET_UNMOD
# define MK_W_OFFSET_UNMOD 1
# endif
# ifndef MK_W_INTERVAL_UNMOD
# define MK_W_INTERVAL_UNMOD 40
# endif
# ifndef MK_W_OFFSET_0
# define MK_W_OFFSET_0 1
# endif
# ifndef MK_W_INTERVAL_0
# define MK_W_INTERVAL_0 360
# endif
# ifndef MK_W_OFFSET_1
# define MK_W_OFFSET_1 1
# endif
# ifndef MK_W_INTERVAL_1
# define MK_W_INTERVAL_1 120
# endif
# ifndef MK_W_OFFSET_2
# define MK_W_OFFSET_2 1
# endif
# ifndef MK_W_INTERVAL_2
# define MK_W_INTERVAL_2 20
# endif
#endif /* #ifndef MK_3_SPEED */
#ifdef __cplusplus
extern "C" {
#endif
extern uint8_t mk_delay;
extern uint8_t mk_interval;
extern uint8_t mk_max_speed;
extern uint8_t mk_time_to_max;
extern uint8_t mk_wheel_max_speed;
extern uint8_t mk_wheel_time_to_max;
void mousekey_task(void);
void mousekey_on(uint8_t code);
void mousekey_off(uint8_t code);
void mousekey_clear(void);
void mousekey_send(void);
#ifdef __cplusplus
}
#endif
+9 -2
View File
@@ -25,18 +25,25 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
static report_mouse_t mouseReport = {};
__attribute__((weak)) bool has_mouse_report_changed(report_mouse_t new, report_mouse_t old) { return (new.buttons != old.buttons) || (new.x&& new.x != old.x) || (new.y&& new.y != old.y) || (new.h&& new.h != old.h) || (new.v&& new.v != old.v); }
__attribute__((weak)) void pointing_device_init(void) {
// initialize device, if that needs to be done.
}
__attribute__((weak)) void pointing_device_send(void) {
static report_mouse_t old_report = {};
// If you need to do other things, like debugging, this is the place to do it.
host_mouse_send(&mouseReport);
if (has_mouse_report_changed(mouseReport, old_report)) {
host_mouse_send(&mouseReport);
}
// send it and 0 it out except for buttons, so those stay until they are explicity over-ridden using update_pointing_device
mouseReport.x = 0;
mouseReport.y = 0;
mouseReport.v = 0;
mouseReport.h = 0;
old_report = mouseReport;
}
__attribute__((weak)) void pointing_device_task(void) {
@@ -52,4 +59,4 @@ __attribute__((weak)) void pointing_device_task(void) {
report_mouse_t pointing_device_get_report(void) { return mouseReport; }
void pointing_device_set_report(report_mouse_t newMouseReport) { mouseReport = newMouseReport; }
void pointing_device_set_report(report_mouse_t newMouseReport) { mouseReport = newMouseReport; }
+2 -4
View File
@@ -15,8 +15,7 @@ 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 POINTING_DEVICE_H
#define POINTING_DEVICE_H
#pragma once
#include <stdint.h>
#include "host.h"
@@ -27,5 +26,4 @@ void pointing_device_task(void);
void pointing_device_send(void);
report_mouse_t pointing_device_get_report(void);
void pointing_device_set_report(report_mouse_t newMouseReport);
#endif
bool has_mouse_report_changed(report_mouse_t new, report_mouse_t old);
+1 -4
View File
@@ -1,5 +1,4 @@
#ifndef PROCESS_AUDIO_H
#define PROCESS_AUDIO_H
#pragma once
float compute_freq_for_midi_note(uint8_t note);
@@ -9,5 +8,3 @@ void process_audio_noteoff(uint8_t note);
void process_audio_all_notes_off(void);
void audio_on_user(void);
#endif
+158 -59
View File
@@ -16,48 +16,149 @@
#ifdef AUTO_SHIFT_ENABLE
# include <stdbool.h>
# include <stdio.h>
# include "process_auto_shift.h"
static bool autoshift_enabled = true;
static uint16_t autoshift_time = 0;
static uint16_t autoshift_timeout = AUTO_SHIFT_TIMEOUT;
static uint16_t autoshift_lastkey = KC_NO;
static struct {
// Whether autoshift is enabled.
bool enabled : 1;
// Whether the last auto-shifted key was released after the timeout. This
// is used to replicate the last key for a tap-then-hold.
bool lastshifted : 1;
// Whether an auto-shiftable key has been pressed but not processed.
bool in_progress : 1;
// Whether the auto-shifted keypress has been registered.
bool holding_shift : 1;
} autoshift_flags = {true, false, false, false};
void autoshift_flush(void) {
if (autoshift_lastkey != KC_NO) {
uint16_t elapsed = timer_elapsed(autoshift_time);
if (elapsed > autoshift_timeout) {
tap_code16(LSFT(autoshift_lastkey));
} else {
tap_code(autoshift_lastkey);
}
autoshift_time = 0;
autoshift_lastkey = KC_NO;
/** \brief Record the press of an autoshiftable key
*
* \return Whether the record should be further processed.
*/
static bool autoshift_press(uint16_t keycode, uint16_t now, keyrecord_t *record) {
if (!autoshift_flags.enabled) {
return true;
}
# ifndef AUTO_SHIFT_MODIFIERS
if (get_mods() & (~MOD_BIT(KC_LSFT))) {
return true;
}
# endif
# ifdef AUTO_SHIFT_REPEAT
const uint16_t elapsed = TIMER_DIFF_16(now, autoshift_time);
# ifndef AUTO_SHIFT_NO_AUTO_REPEAT
if (!autoshift_flags.lastshifted) {
# endif
if (elapsed < TAPPING_TERM && keycode == autoshift_lastkey) {
// Allow a tap-then-hold for keyrepeat.
if (!autoshift_flags.lastshifted) {
register_code(autoshift_lastkey);
} else {
// Simulate pressing the shift key.
add_weak_mods(MOD_BIT(KC_LSFT));
register_code(autoshift_lastkey);
}
return false;
}
# ifndef AUTO_SHIFT_NO_AUTO_REPEAT
}
# endif
# endif
// Record the keycode so we can simulate it later.
autoshift_lastkey = keycode;
autoshift_time = now;
autoshift_flags.in_progress = true;
# if !defined(NO_ACTION_ONESHOT) && !defined(NO_ACTION_TAPPING)
clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
# endif
return false;
}
void autoshift_on(uint16_t keycode) {
autoshift_time = timer_read();
autoshift_lastkey = keycode;
/** \brief Registers an autoshiftable key under the right conditions
*
* If the autoshift delay has elapsed, register a shift and the key.
*
* If the autoshift key is released before the delay has elapsed, register the
* key without a shift.
*/
static void autoshift_end(uint16_t keycode, uint16_t now, bool matrix_trigger) {
// Called on key down with KC_NO, auto-shifted key up, and timeout.
if (autoshift_flags.in_progress) {
// Process the auto-shiftable key.
autoshift_flags.in_progress = false;
// Time since the initial press was recorded.
const uint16_t elapsed = TIMER_DIFF_16(now, autoshift_time);
if (elapsed < autoshift_timeout) {
register_code(autoshift_lastkey);
autoshift_flags.lastshifted = false;
} else {
// Simulate pressing the shift key.
add_weak_mods(MOD_BIT(KC_LSFT));
register_code(autoshift_lastkey);
autoshift_flags.lastshifted = true;
# if defined(AUTO_SHIFT_REPEAT) && !defined(AUTO_SHIFT_NO_AUTO_REPEAT)
if (matrix_trigger) {
// Prevents release.
return;
}
# endif
}
# if TAP_CODE_DELAY > 0
wait_ms(TAP_CODE_DELAY);
# endif
unregister_code(autoshift_lastkey);
del_weak_mods(MOD_BIT(KC_LSFT));
} else {
// Release after keyrepeat.
unregister_code(keycode);
if (keycode == autoshift_lastkey) {
// This will only fire when the key was the last auto-shiftable
// pressed. That prevents aaaaBBBB then releasing a from unshifting
// later Bs (if B wasn't auto-shiftable).
del_weak_mods(MOD_BIT(KC_LSFT));
}
}
send_keyboard_report(); // del_weak_mods doesn't send one.
// Roll the autoshift_time forward for detecting tap-and-hold.
autoshift_time = now;
}
/** \brief Simulates auto-shifted key releases when timeout is hit
*
* Can be called from \c matrix_scan_user so that auto-shifted keys are sent
* immediately after the timeout has expired, rather than waiting for the key
* to be released.
*/
void autoshift_matrix_scan(void) {
if (autoshift_flags.in_progress) {
const uint16_t now = timer_read();
const uint16_t elapsed = TIMER_DIFF_16(now, autoshift_time);
if (elapsed >= autoshift_timeout) {
autoshift_end(autoshift_lastkey, now, true);
}
}
}
void autoshift_toggle(void) {
if (autoshift_enabled) {
autoshift_enabled = false;
autoshift_flush();
} else {
autoshift_enabled = true;
}
autoshift_flags.enabled = !autoshift_flags.enabled;
del_weak_mods(MOD_BIT(KC_LSFT));
}
void autoshift_enable(void) { autoshift_enabled = true; }
void autoshift_enable(void) { autoshift_flags.enabled = true; }
void autoshift_disable(void) {
autoshift_enabled = false;
autoshift_flush();
autoshift_flags.enabled = false;
del_weak_mods(MOD_BIT(KC_LSFT));
}
# ifndef AUTO_SHIFT_NO_SETUP
@@ -70,19 +171,30 @@ void autoshift_timer_report(void) {
}
# endif
bool get_autoshift_state(void) { return autoshift_enabled; }
bool get_autoshift_state(void) { return autoshift_flags.enabled; }
uint16_t get_autoshift_timeout(void) { return autoshift_timeout; }
void set_autoshift_timeout(uint16_t timeout) { autoshift_timeout = timeout; }
bool process_auto_shift(uint16_t keycode, keyrecord_t *record) {
// Note that record->event.time isn't reliable, see:
// https://github.com/qmk/qmk_firmware/pull/9826#issuecomment-733559550
const uint16_t now = timer_read();
if (record->event.pressed) {
if (autoshift_flags.in_progress) {
// Evaluate previous key if there is one. Doing this elsewhere is
// more complicated and easier to break.
autoshift_end(KC_NO, now, false);
}
// For pressing another key while keyrepeating shifted autoshift.
del_weak_mods(MOD_BIT(KC_LSFT));
switch (keycode) {
case KC_ASTG:
autoshift_toggle();
return true;
case KC_ASON:
autoshift_enable();
return true;
@@ -102,41 +214,28 @@ bool process_auto_shift(uint16_t keycode, keyrecord_t *record) {
autoshift_timer_report();
return true;
# endif
# ifndef NO_AUTO_SHIFT_ALPHA
case KC_A ... KC_Z:
# endif
# ifndef NO_AUTO_SHIFT_NUMERIC
case KC_1 ... KC_0:
# endif
# ifndef NO_AUTO_SHIFT_SPECIAL
case KC_TAB:
case KC_MINUS ... KC_SLASH:
case KC_NONUS_BSLASH:
# endif
autoshift_flush();
if (!autoshift_enabled) return true;
# ifndef AUTO_SHIFT_MODIFIERS
if (get_mods()) {
return true;
}
# endif
autoshift_on(keycode);
// We need some extra handling here for OSL edge cases
# if !defined(NO_ACTION_ONESHOT) && !defined(NO_ACTION_TAPPING)
clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
# endif
return false;
default:
autoshift_flush();
return true;
}
} else {
autoshift_flush();
}
switch (keycode) {
# ifndef NO_AUTO_SHIFT_ALPHA
case KC_A ... KC_Z:
# endif
# ifndef NO_AUTO_SHIFT_NUMERIC
case KC_1 ... KC_0:
# endif
# ifndef NO_AUTO_SHIFT_SPECIAL
case KC_TAB:
case KC_MINUS ... KC_SLASH:
case KC_NONUS_BSLASH:
# endif
if (record->event.pressed) {
return autoshift_press(keycode, now, record);
} else {
autoshift_end(keycode, now, false);
return false;
}
}
return true;
}
@@ -30,3 +30,4 @@ void autoshift_toggle(void);
bool get_autoshift_state(void);
uint16_t get_autoshift_timeout(void);
void set_autoshift_timeout(uint16_t timeout);
void autoshift_matrix_scan(void);
+1 -4
View File
@@ -1,5 +1,4 @@
#ifndef PROCESS_CLICKY_H
#define PROCESS_CLICKY_H
#pragma once
void clicky_play(void);
bool process_clicky(uint16_t keycode, keyrecord_t *record);
@@ -13,5 +12,3 @@ void clicky_on(void);
void clicky_off(void);
bool is_clicky_on(void);
#endif
+1 -4
View File
@@ -14,8 +14,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PROCESS_COMBO_H
#define PROCESS_COMBO_H
#pragma once
#include "progmem.h"
#include "quantum.h"
@@ -62,5 +61,3 @@ void combo_enable(void);
void combo_disable(void);
void combo_toggle(void);
bool is_combo_enabled(void);
#endif
+4 -4
View File
@@ -129,17 +129,17 @@ bool process_joystick_analogread_quantum() {
// test the converted value against the lower range
int32_t ref = joystick_axes[axis_index].mid_digit;
int32_t range = joystick_axes[axis_index].min_digit;
int32_t ranged_val = ((axis_val - ref) * -127) / (range - ref);
int32_t ranged_val = ((axis_val - ref) * -JOYSTICK_RESOLUTION) / (range - ref);
if (ranged_val > 0) {
// the value is in the higher range
range = joystick_axes[axis_index].max_digit;
ranged_val = ((axis_val - ref) * 127) / (range - ref);
ranged_val = ((axis_val - ref) * JOYSTICK_RESOLUTION) / (range - ref);
}
// clamp the result in the valid range
ranged_val = ranged_val < -127 ? -127 : ranged_val;
ranged_val = ranged_val > 127 ? 127 : ranged_val;
ranged_val = ranged_val < -JOYSTICK_RESOLUTION ? -JOYSTICK_RESOLUTION : ranged_val;
ranged_val = ranged_val > JOYSTICK_RESOLUTION ? JOYSTICK_RESOLUTION : ranged_val;
if (ranged_val != joystick_status.axes[axis_index]) {
joystick_status.axes[axis_index] = ranged_val;
+2 -2
View File
@@ -14,8 +14,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "inttypes.h"
#include "stdint.h"
#include <inttypes.h>
#include <stdint.h>
#include "process_key_lock.h"
#define BV_64(shift) (((uint64_t)1) << (shift))
+1 -4
View File
@@ -14,11 +14,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PROCESS_KEY_LOCK_H
#define PROCESS_KEY_LOCK_H
#pragma once
#include "quantum.h"
bool process_key_lock(uint16_t *keycode, keyrecord_t *record);
#endif // PROCESS_KEY_LOCK_H
+1 -4
View File
@@ -14,8 +14,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PROCESS_LEADER_H
#define PROCESS_LEADER_H
#pragma once
#include "quantum.h"
@@ -37,5 +36,3 @@ void qk_leader_start(void);
extern uint16_t leader_sequence[5]; \
extern uint8_t leader_sequence_size
#define LEADER_DICTIONARY() if (leading && timer_elapsed(leader_time) > LEADER_TIMEOUT)
#endif
+18 -7
View File
@@ -41,12 +41,12 @@ static int8_t midi_modulation_step;
static uint16_t midi_modulation_timer;
midi_config_t midi_config;
inline uint8_t compute_velocity(uint8_t setting) { return (setting + 1) * (128 / (MIDI_VELOCITY_MAX - MIDI_VELOCITY_MIN + 1)); }
inline uint8_t compute_velocity(uint8_t setting) { return setting * (128 / (MIDI_VELOCITY_MAX - MIDI_VELOCITY_MIN)); }
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.velocity = 127;
midi_config.channel = 0;
midi_config.modulation_interval = 8;
@@ -66,7 +66,7 @@ bool process_midi(uint16_t keycode, keyrecord_t *record) {
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);
uint8_t velocity = midi_config.velocity;
if (record->event.pressed) {
if (tone_status[tone] == MIDI_INVALID_NOTE) {
uint8_t note = midi_compute_note(keycode);
@@ -124,19 +124,30 @@ bool process_midi(uint16_t keycode, keyrecord_t *record) {
return false;
case MIDI_VELOCITY_MIN ... MIDI_VELOCITY_MAX:
if (record->event.pressed) {
midi_config.velocity = keycode - MIDI_VELOCITY_MIN;
midi_config.velocity = compute_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--;
if (midi_config.velocity == 127) {
midi_config.velocity -= 10;
} else if (midi_config.velocity > 12) {
midi_config.velocity -= 13;
} else {
midi_config.velocity = 0;
}
dprintf("midi velocity %d\n", midi_config.velocity);
}
return false;
case MI_VELU:
if (record->event.pressed) {
midi_config.velocity++;
if (record->event.pressed && midi_config.velocity < 127) {
if (midi_config.velocity < 115) {
midi_config.velocity += 13;
} else {
midi_config.velocity = 127;
}
dprintf("midi velocity %d\n", midi_config.velocity);
}
return false;
+2 -5
View File
@@ -14,8 +14,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PROCESS_MIDI_H
#define PROCESS_MIDI_H
#pragma once
#include "quantum.h"
@@ -35,7 +34,7 @@ typedef union {
struct {
uint8_t octave : 4;
int8_t transpose : 4;
uint8_t velocity : 4;
uint8_t velocity : 7;
uint8_t channel : 4;
uint8_t modulation_interval : 4;
};
@@ -53,5 +52,3 @@ uint8_t midi_compute_note(uint16_t keycode);
# endif // MIDI_ADVANCED
#endif // MIDI_ENABLE
#endif
+1 -1
View File
@@ -191,7 +191,7 @@ bool process_music(uint16_t keycode, keyrecord_t *record) {
note = music_starting_note + music_offset + 36 + music_map[record->event.key.row][record->event.key.col];
} else {
uint8_t position = music_map[record->event.key.row][record->event.key.col];
note = music_starting_note + music_offset + 36 + SCALE[position % 12] + (position / 12) * 12;
note = music_starting_note + music_offset + 36 + SCALE[position % 7] + (position / 7) * 12;
}
# else
if (music_mode == MUSIC_MODE_CHROMATIC)
+1 -4
View File
@@ -14,8 +14,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PROCESS_MUSIC_H
#define PROCESS_MUSIC_H
#pragma once
#include "quantum.h"
@@ -57,5 +56,3 @@ bool music_mask_user(uint16_t keycode);
# endif
#endif // defined(AUDIO_ENABLE) || (defined(MIDI_ENABLE) && defined(MIDI_BASIC))
#endif
+1 -4
View File
@@ -14,13 +14,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PROCESS_PRINTER_H
#define PROCESS_PRINTER_H
#pragma once
#include "quantum.h"
#include "protocol/serial.h"
bool process_printer(uint16_t keycode, keyrecord_t *record);
#endif
@@ -0,0 +1,62 @@
/* Copyright 2020 Rodolphe Belouin
*
* 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_sequencer.h"
bool process_sequencer(uint16_t keycode, keyrecord_t *record) {
if (record->event.pressed) {
switch (keycode) {
case SQ_ON:
sequencer_on();
return false;
case SQ_OFF:
sequencer_off();
return false;
case SQ_TOG:
sequencer_toggle();
return false;
case SQ_TMPD:
sequencer_decrease_tempo();
return false;
case SQ_TMPU:
sequencer_increase_tempo();
return false;
case SEQUENCER_RESOLUTION_MIN ... SEQUENCER_RESOLUTION_MAX:
sequencer_set_resolution(keycode - SEQUENCER_RESOLUTION_MIN);
return false;
case SQ_RESD:
sequencer_decrease_resolution();
return false;
case SQ_RESU:
sequencer_increase_resolution();
return false;
case SQ_SALL:
sequencer_set_all_steps_on();
return false;
case SQ_SCLR:
sequencer_set_all_steps_off();
return false;
case SEQUENCER_STEP_MIN ... SEQUENCER_STEP_MAX:
sequencer_toggle_step(keycode - SEQUENCER_STEP_MIN);
return false;
case SEQUENCER_TRACK_MIN ... SEQUENCER_TRACK_MAX:
sequencer_toggle_single_active_track(keycode - SEQUENCER_TRACK_MIN);
return false;
}
}
return true;
}
@@ -0,0 +1,21 @@
/* Copyright 2020 Rodolphe Belouin
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "quantum.h"
bool process_sequencer(uint16_t keycode, keyrecord_t *record);
@@ -16,10 +16,6 @@
#include "process_space_cadet.h"
#include "action_tapping.h"
#ifdef NO_ACTION_TAPPING
__attribute__((weak)) uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) { return TAPPING_TERM; };
#endif
// ********** OBSOLETE DEFINES, STOP USING! (pls?) **********
// Shift / paren setup
#ifndef LSPO_KEY
@@ -97,7 +93,12 @@ void perform_space_cadet(keyrecord_t *record, uint16_t sc_keycode, uint8_t holdM
register_mods(MOD_BIT(holdMod));
}
} else {
if (sc_last == holdMod && timer_elapsed(sc_timer) < get_tapping_term(sc_keycode, record)) {
#ifdef TAPPING_TERM_PER_KEY
if (sc_last == holdMod && timer_elapsed(sc_timer) < get_tapping_term(sc_keycode, record))
#else
if (sc_last == holdMod && timer_elapsed(sc_timer) < TAPPING_TERM)
#endif
{
if (holdMod != tapMod) {
if (IS_MOD(holdMod)) {
unregister_mods(MOD_BIT(holdMod));
@@ -19,6 +19,3 @@
void perform_space_cadet(keyrecord_t *record, uint16_t sc_keycode, uint8_t holdMod, uint8_t tapMod, uint8_t keycode);
bool process_space_cadet(uint16_t keycode, keyrecord_t *record);
#ifdef NO_ACTION_TAPPING
uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record);
#endif
+2 -4
View File
@@ -13,8 +13,8 @@
* 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_STENO_H
#define PROCESS_STENO_H
#pragma once
#include "quantum.h"
@@ -25,5 +25,3 @@ void steno_init(void);
void steno_set_mode(steno_mode_t mode);
uint8_t *steno_get_state(void);
uint8_t *steno_get_chord(void);
#endif
+4 -1
View File
@@ -14,7 +14,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "quantum.h"
#include "action_tapping.h"
#ifndef NO_ACTION_ONESHOT
uint8_t get_oneshot_mods(void);
@@ -167,7 +166,11 @@ void matrix_scan_tap_dance() {
if (action->custom_tapping_term > 0) {
tap_user_defined = action->custom_tapping_term;
} else {
#ifdef TAPPING_TERM_PER_KEY
tap_user_defined = get_tapping_term(action->state.keycode, NULL);
#else
tap_user_defined = TAPPING_TERM;
#endif
}
if (action->state.count && timer_elapsed(action->state.timer) > tap_user_defined) {
process_tap_dance_action_on_dance_finished(action);
+2 -4
View File
@@ -13,8 +13,8 @@
* 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
#pragma once
#ifdef TAP_DANCE_ENABLE
@@ -101,5 +101,3 @@ void qk_tap_dance_dual_role_reset(qk_tap_dance_state_t *state, void *user_data);
# define TD(n) KC_NO
#endif
#endif
+1 -4
View File
@@ -14,8 +14,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PROCESS_TERMINAL_H
#define PROCESS_TERMINAL_H
#pragma once
#include "quantum.h"
@@ -23,5 +22,3 @@ extern const char keycode_to_ascii_lut[58];
extern const char shifted_keycode_to_ascii_lut[58];
extern const char terminal_prompt[8];
bool process_terminal(uint16_t keycode, keyrecord_t *record);
#endif
@@ -14,12 +14,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PROCESS_TERMINAL_H
#define PROCESS_TERMINAL_H
#pragma once
#include "quantum.h"
#define TERM_ON KC_NO
#define TERM_OFF KC_NO
#endif
@@ -21,6 +21,7 @@
unicode_config_t unicode_config;
uint8_t unicode_saved_mods;
bool unicode_saved_caps_lock;
#if UNICODE_SELECTED_MODES != -1
static uint8_t selected[] = {UNICODE_SELECTED_MODES};
@@ -77,6 +78,16 @@ void cycle_unicode_input_mode(int8_t offset) {
void persist_unicode_input_mode(void) { eeprom_update_byte(EECONFIG_UNICODEMODE, unicode_config.input_mode); }
__attribute__((weak)) void unicode_input_start(void) {
unicode_saved_caps_lock = host_keyboard_led_state().caps_lock;
// Note the order matters here!
// Need to do this before we mess around with the mods, or else
// UNICODE_KEY_LNX (which is usually Ctrl-Shift-U) might not work
// correctly in the shifted case.
if (unicode_config.input_mode == UC_LNX && unicode_saved_caps_lock) {
tap_code(KC_CAPS);
}
unicode_saved_mods = get_mods(); // Save current mods
clear_mods(); // Unregister mods to start from a clean state
@@ -107,6 +118,9 @@ __attribute__((weak)) void unicode_input_finish(void) {
break;
case UC_LNX:
tap_code(KC_SPC);
if (unicode_saved_caps_lock) {
tap_code(KC_CAPS);
}
break;
case UC_WIN:
unregister_code(KC_LALT);
@@ -125,6 +139,11 @@ __attribute__((weak)) void unicode_input_cancel(void) {
unregister_code(UNICODE_KEY_MAC);
break;
case UC_LNX:
tap_code(KC_ESC);
if (unicode_saved_caps_lock) {
tap_code(KC_CAPS);
}
break;
case UC_WINC:
tap_code(KC_ESC);
break;
@@ -136,20 +155,10 @@ __attribute__((weak)) void unicode_input_cancel(void) {
set_mods(unicode_saved_mods); // Reregister previously set mods
}
__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);
tap_code(hex_to_keycode(digit));
tap_code16(hex_to_keycode(digit));
}
}
@@ -162,10 +171,10 @@ void register_hex32(uint32_t hex) {
uint8_t digit = ((hex >> (i * 4)) & 0xF);
if (digit == 0) {
if (!onzerostart) {
tap_code(hex_to_keycode(digit));
tap_code16(hex_to_keycode(digit));
}
} else {
tap_code(hex_to_keycode(digit));
tap_code16(hex_to_keycode(digit));
onzerostart = false;
}
}
@@ -75,7 +75,6 @@ typedef union {
} unicode_config_t;
extern unicode_config_t unicode_config;
extern uint8_t unicode_saved_mods;
void unicode_input_mode_init(void);
uint8_t get_unicode_input_mode(void);
+7 -2
View File
@@ -21,8 +21,13 @@ __attribute__((weak)) uint16_t unicodemap_index(uint16_t keycode) {
// Keycode is a pair: extract index based on Shift / Caps Lock state
uint16_t index = keycode - QK_UNICODEMAP_PAIR;
bool shift = unicode_saved_mods & MOD_MASK_SHIFT;
bool caps = IS_HOST_LED_ON(USB_LED_CAPS_LOCK);
uint8_t mods = get_mods() | get_weak_mods();
#ifndef NO_ACTION_ONESHOT
mods |= get_oneshot_mods();
#endif
bool shift = mods & MOD_MASK_SHIFT;
bool caps = host_keyboard_led_state().caps_lock;
if (shift ^ caps) {
index >>= 7;
}
+46 -311
View File
@@ -14,7 +14,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <ctype.h>
#include "quantum.h"
#ifdef BLUETOOTH_ENABLE
@@ -23,11 +22,6 @@
#ifdef BACKLIGHT_ENABLE
# include "backlight.h"
extern backlight_config_t backlight_config;
#endif
#ifdef FAUXCLICKY_ENABLE
# include "fauxclicky.h"
#endif
#ifdef API_ENABLE
@@ -63,6 +57,10 @@ float bell_song[][2] = SONG(TERMINAL_SOUND);
# endif
#endif
#ifdef AUTO_SHIFT_ENABLE
# include "process_auto_shift.h"
#endif
static void do_code16(uint16_t code, void (*f)(uint8_t)) {
switch (code) {
case QK_MODS ... QK_MODS_MAX:
@@ -226,13 +224,13 @@ bool process_record_quantum(keyrecord_t *record) {
#ifdef HAPTIC_ENABLE
process_haptic(keycode, record) &&
#endif // HAPTIC_ENABLE
#if defined(RGB_MATRIX_ENABLE)
process_rgb_matrix(keycode, record) &&
#endif
#if defined(VIA_ENABLE)
process_record_via(keycode, record) &&
#endif
process_record_kb(keycode, record) &&
#if defined(SEQUENCER_ENABLE)
process_sequencer(keycode, record) &&
#endif
#if defined(MIDI_ENABLE) && defined(MIDI_ADVANCED)
process_midi(keycode, record) &&
#endif
@@ -308,17 +306,6 @@ bool process_record_quantum(keyrecord_t *record) {
case EEPROM_RESET:
eeconfig_init();
return false;
#ifdef FAUXCLICKY_ENABLE
case FC_TOG:
FAUXCLICKY_TOGGLE;
return false;
case FC_ON:
FAUXCLICKY_ON;
return false;
case FC_OFF:
FAUXCLICKY_OFF;
return false;
#endif
#ifdef VELOCIKEY_ENABLE
case VLK_TOG:
velocikey_toggle();
@@ -341,210 +328,6 @@ bool process_record_quantum(keyrecord_t *record) {
return process_action_kb(record);
}
// clang-format off
/* Bit-Packed look-up table to convert an ASCII character to whether
* [Shift] needs to be sent with the keycode.
*/
__attribute__((weak)) const uint8_t ascii_to_shift_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 0),
KCLUT_ENTRY(1, 1, 1, 1, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 1, 0, 1, 0, 1, 1),
KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),
KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),
KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),
KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 1, 1),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0),
};
/* Bit-Packed look-up table to convert an ASCII character to whether
* [AltGr] needs to be sent with the keycode.
*/
__attribute__((weak)) const uint8_t ascii_to_altgr_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
};
/* Look-up table to convert an ASCII character to a keycode.
*/
__attribute__((weak)) const uint8_t ascii_to_keycode_lut[128] PROGMEM = {
// NUL SOH STX ETX EOT ENQ ACK BEL
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,
// BS TAB LF VT FF CR SO SI
KC_BSPC, KC_TAB, KC_ENT, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,
// DLE DC1 DC2 DC3 DC4 NAK SYN ETB
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,
// CAN EM SUB ESC FS GS RS US
XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,
// ! " # $ % & '
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,
// 0 1 2 3 4 5 6 7
KC_0, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7,
// 8 9 : ; < = > ?
KC_8, KC_9, KC_SCLN, KC_SCLN, KC_COMM, KC_EQL, KC_DOT, KC_SLSH,
// @ A B C D E F G
KC_2, KC_A, KC_B, KC_C, KC_D, KC_E, KC_F, KC_G,
// H I J K L M N O
KC_H, KC_I, KC_J, KC_K, KC_L, KC_M, KC_N, KC_O,
// P Q R S T U V W
KC_P, KC_Q, KC_R, KC_S, KC_T, KC_U, KC_V, KC_W,
// X Y Z [ \ ] ^ _
KC_X, KC_Y, KC_Z, KC_LBRC, KC_BSLS, KC_RBRC, KC_6, KC_MINS,
// ` a b c d e f g
KC_GRV, KC_A, KC_B, KC_C, KC_D, KC_E, KC_F, KC_G,
// h i j k l m n o
KC_H, KC_I, KC_J, KC_K, KC_L, KC_M, KC_N, KC_O,
// p q r s t u v w
KC_P, KC_Q, KC_R, KC_S, KC_T, KC_U, KC_V, KC_W,
// x y z { | } ~ DEL
KC_X, KC_Y, KC_Z, KC_LBRC, KC_BSLS, KC_RBRC, KC_GRV, KC_DEL
};
// clang-format on
// Note: we bit-pack in "reverse" order to optimize loading
#define PGM_LOADBIT(mem, pos) ((pgm_read_byte(&((mem)[(pos) / 8])) >> ((pos) % 8)) & 0x01)
void send_string(const char *str) { send_string_with_delay(str, 0); }
void send_string_P(const char *str) { send_string_with_delay_P(str, 0); }
void send_string_with_delay(const char *str, uint8_t interval) {
while (1) {
char ascii_code = *str;
if (!ascii_code) break;
if (ascii_code == SS_QMK_PREFIX) {
ascii_code = *(++str);
if (ascii_code == SS_TAP_CODE) {
// tap
uint8_t keycode = *(++str);
tap_code(keycode);
} else if (ascii_code == SS_DOWN_CODE) {
// down
uint8_t keycode = *(++str);
register_code(keycode);
} else if (ascii_code == SS_UP_CODE) {
// up
uint8_t keycode = *(++str);
unregister_code(keycode);
} else if (ascii_code == SS_DELAY_CODE) {
// delay
int ms = 0;
uint8_t keycode = *(++str);
while (isdigit(keycode)) {
ms *= 10;
ms += keycode - '0';
keycode = *(++str);
}
while (ms--) wait_ms(1);
}
} else {
send_char(ascii_code);
}
++str;
// interval
{
uint8_t ms = interval;
while (ms--) wait_ms(1);
}
}
}
void send_string_with_delay_P(const char *str, uint8_t interval) {
while (1) {
char ascii_code = pgm_read_byte(str);
if (!ascii_code) break;
if (ascii_code == SS_QMK_PREFIX) {
ascii_code = pgm_read_byte(++str);
if (ascii_code == SS_TAP_CODE) {
// tap
uint8_t keycode = pgm_read_byte(++str);
tap_code(keycode);
} else if (ascii_code == SS_DOWN_CODE) {
// down
uint8_t keycode = pgm_read_byte(++str);
register_code(keycode);
} else if (ascii_code == SS_UP_CODE) {
// up
uint8_t keycode = pgm_read_byte(++str);
unregister_code(keycode);
} else if (ascii_code == SS_DELAY_CODE) {
// delay
int ms = 0;
uint8_t keycode = pgm_read_byte(++str);
while (isdigit(keycode)) {
ms *= 10;
ms += keycode - '0';
keycode = pgm_read_byte(++str);
}
while (ms--) wait_ms(1);
}
} else {
send_char(ascii_code);
}
++str;
// interval
{
uint8_t ms = interval;
while (ms--) wait_ms(1);
}
}
}
void send_char(char ascii_code) {
#if defined(AUDIO_ENABLE) && defined(SENDSTRING_BELL)
if (ascii_code == '\a') { // BEL
PLAY_SONG(bell_song);
return;
}
#endif
uint8_t keycode = pgm_read_byte(&ascii_to_keycode_lut[(uint8_t)ascii_code]);
bool is_shifted = PGM_LOADBIT(ascii_to_shift_lut, (uint8_t)ascii_code);
bool is_altgred = PGM_LOADBIT(ascii_to_altgr_lut, (uint8_t)ascii_code);
if (is_shifted) {
register_code(KC_LSFT);
}
if (is_altgred) {
register_code(KC_RALT);
}
tap_code(keycode);
if (is_altgred) {
unregister_code(KC_RALT);
}
if (is_shifted) {
unregister_code(KC_LSFT);
}
}
void set_single_persistent_default_layer(uint8_t default_layer) {
#if defined(AUDIO_ENABLE) && defined(DEFAULT_LAYER_SONGS)
PLAY_SONG(default_layer_songs[default_layer]);
@@ -569,32 +352,22 @@ void tap_random_base64(void) {
#endif
switch (key) {
case 0 ... 25:
register_code(KC_LSFT);
register_code(key + KC_A);
unregister_code(key + KC_A);
unregister_code(KC_LSFT);
send_char(key + 'A');
break;
case 26 ... 51:
register_code(key - 26 + KC_A);
unregister_code(key - 26 + KC_A);
send_char(key - 26 + 'a');
break;
case 52:
register_code(KC_0);
unregister_code(KC_0);
send_char('0');
break;
case 53 ... 61:
register_code(key - 53 + KC_1);
unregister_code(key - 53 + KC_1);
send_char(key - 53 + '1');
break;
case 62:
register_code(KC_LSFT);
register_code(KC_EQL);
unregister_code(KC_EQL);
unregister_code(KC_LSFT);
send_char('+');
break;
case 63:
register_code(KC_SLSH);
unregister_code(KC_SLSH);
send_char('/');
break;
}
}
@@ -606,6 +379,10 @@ void matrix_init_quantum() {
if (!eeconfig_is_enabled()) {
eeconfig_init();
}
#if defined(LED_NUM_LOCK_PIN) || defined(LED_CAPS_LOCK_PIN) || defined(LED_SCROLL_LOCK_PIN) || defined(LED_COMPOSE_PIN) || defined(LED_KANA_PIN)
// TODO: remove calls to led_init_ports from keyboards and remove ifdef
led_init_ports();
#endif
#ifdef BACKLIGHT_ENABLE
# ifdef LED_MATRIX_ENABLE
led_matrix_init();
@@ -633,10 +410,34 @@ void matrix_init_quantum() {
}
void matrix_scan_quantum() {
#if defined(AUDIO_ENABLE)
// There are some tasks that need to be run a little bit
// after keyboard startup, or else they will not work correctly
// because of interaction with the USB device state, which
// may still be in flux...
//
// At the moment the only feature that needs this is the
// startup song.
static bool delayed_tasks_run = false;
static uint16_t delayed_task_timer = 0;
if (!delayed_tasks_run) {
if (!delayed_task_timer) {
delayed_task_timer = timer_read();
} else if (timer_elapsed(delayed_task_timer) > 300) {
audio_startup();
delayed_tasks_run = true;
}
}
#endif
#if defined(AUDIO_ENABLE) && !defined(NO_MUSIC_MODE)
matrix_scan_music();
#endif
#ifdef SEQUENCER_ENABLE
matrix_scan_sequencer();
#endif
#ifdef TAP_DANCE_ENABLE
matrix_scan_tap_dance();
#endif
@@ -649,10 +450,6 @@ void matrix_scan_quantum() {
led_matrix_task();
#endif
#ifdef RGB_MATRIX_ENABLE
rgb_matrix_task();
#endif
#ifdef WPM_ENABLE
decay_wpm();
#endif
@@ -665,6 +462,10 @@ void matrix_scan_quantum() {
dip_switch_read(false);
#endif
#ifdef AUTO_SHIFT_ENABLE
autoshift_matrix_scan();
#endif
matrix_scan_kb();
}
@@ -693,22 +494,7 @@ void send_byte(uint8_t number) {
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;
}
}
void send_nibble(uint8_t number) { tap_code16(hex_to_keycode(number)); }
__attribute__((weak)) uint16_t hex_to_keycode(uint8_t hex) {
hex = hex & 0xF;
@@ -729,55 +515,6 @@ void api_send_unicode(uint32_t unicode) {
#endif
}
/** \brief Lock LED set callback - keymap/user level
*
* \deprecated Use led_update_user() instead.
*/
__attribute__((weak)) void led_set_user(uint8_t usb_led) {}
/** \brief Lock LED set callback - keyboard level
*
* \deprecated Use led_update_kb() instead.
*/
__attribute__((weak)) void led_set_kb(uint8_t usb_led) { led_set_user(usb_led); }
/** \brief Lock LED update callback - keymap/user level
*
* \return True if led_update_kb() should run its own code, false otherwise.
*/
__attribute__((weak)) bool led_update_user(led_t led_state) { return true; }
/** \brief Lock LED update callback - keyboard level
*
* \return Ignored for now.
*/
__attribute__((weak)) bool led_update_kb(led_t led_state) { return led_update_user(led_state); }
__attribute__((weak)) void led_init_ports(void) {}
__attribute__((weak)) void led_set(uint8_t usb_led) {
#if defined(BACKLIGHT_CAPS_LOCK) && defined(BACKLIGHT_ENABLE)
// Use backlight as Caps Lock indicator
uint8_t bl_toggle_lvl = 0;
if (IS_LED_ON(usb_led, USB_LED_CAPS_LOCK) && !backlight_config.enable) {
// Turning Caps Lock ON and backlight is disabled in config
// Toggling backlight to the brightest level
bl_toggle_lvl = BACKLIGHT_LEVELS;
} else if (IS_LED_OFF(usb_led, USB_LED_CAPS_LOCK) && backlight_config.enable) {
// Turning Caps Lock OFF and backlight is enabled in config
// Toggling backlight and restoring config level
bl_toggle_lvl = backlight_config.level;
}
// Set level without modify backlight_config to keep ability to restore state
backlight_set(bl_toggle_lvl);
#endif
led_set_kb(usb_led);
led_update_kb((led_t)usb_led);
}
//------------------------------------------------------------------------------
// Override these functions in your keymap file to play different tunes on
// different events such as startup and bootloader jump
@@ -785,5 +522,3 @@ __attribute__((weak)) void led_set(uint8_t usb_led) {
__attribute__((weak)) void startup_user() {}
__attribute__((weak)) void shutdown_user() {}
//------------------------------------------------------------------------------
+44 -57
View File
@@ -21,7 +21,7 @@
# include <avr/interrupt.h>
#endif
#if defined(PROTOCOL_CHIBIOS)
# include "hal.h"
# include <hal.h>
# include "chibios_config.h"
#endif
@@ -31,7 +31,7 @@
#ifdef BACKLIGHT_ENABLE
# ifdef LED_MATRIX_ENABLE
# include "ledmatrix.h"
# include "led_matrix.h"
# else
# include "backlight.h"
# endif
@@ -53,11 +53,15 @@
#include "eeconfig.h"
#include "bootloader.h"
#include "timer.h"
#include "sync_timer.h"
#include "config_common.h"
#include "gpio.h"
#include "atomic_util.h"
#include "led.h"
#include "action_util.h"
#include "action_tapping.h"
#include "print.h"
#include "send_string_keycodes.h"
#include "send_string.h"
#include "suspend.h"
#include <stddef.h>
#include <stdlib.h>
@@ -68,6 +72,11 @@ extern layer_state_t default_layer_state;
extern layer_state_t layer_state;
#endif
#if defined(SEQUENCER_ENABLE)
# include "sequencer.h"
# include "process_sequencer.h"
#endif
#if defined(MIDI_ENABLE) && defined(MIDI_ADVANCED)
# include "process_midi.h"
#endif
@@ -186,64 +195,42 @@ extern layer_state_t layer_state;
# include "wpm.h"
#endif
// Function substitutions to ease GPIO manipulation
#if defined(__AVR__)
typedef uint8_t pin_t;
# define setPinInput(pin) (DDRx_ADDRESS(pin) &= ~_BV((pin)&0xF), PORTx_ADDRESS(pin) &= ~_BV((pin)&0xF))
# define setPinInputHigh(pin) (DDRx_ADDRESS(pin) &= ~_BV((pin)&0xF), PORTx_ADDRESS(pin) |= _BV((pin)&0xF))
# define setPinInputLow(pin) _Static_assert(0, "AVR processors cannot implement an input as pull low")
# define setPinOutput(pin) (DDRx_ADDRESS(pin) |= _BV((pin)&0xF))
# define writePinHigh(pin) (PORTx_ADDRESS(pin) |= _BV((pin)&0xF))
# define writePinLow(pin) (PORTx_ADDRESS(pin) &= ~_BV((pin)&0xF))
# define writePin(pin, level) ((level) ? writePinHigh(pin) : writePinLow(pin))
# define readPin(pin) ((bool)(PINx_ADDRESS(pin) & _BV((pin)&0xF)))
# define togglePin(pin) (PORTx_ADDRESS(pin) ^= _BV((pin)&0xF))
#elif defined(PROTOCOL_CHIBIOS)
typedef ioline_t pin_t;
# define setPinInput(pin) palSetLineMode(pin, PAL_MODE_INPUT)
# define setPinInputHigh(pin) palSetLineMode(pin, PAL_MODE_INPUT_PULLUP)
# define setPinInputLow(pin) palSetLineMode(pin, PAL_MODE_INPUT_PULLDOWN)
# define setPinOutput(pin) palSetLineMode(pin, PAL_MODE_OUTPUT_PUSHPULL)
# define writePinHigh(pin) palSetLine(pin)
# define writePinLow(pin) palClearLine(pin)
# define writePin(pin, level) ((level) ? writePinHigh(pin) : writePinLow(pin))
# define readPin(pin) palReadLine(pin)
# define togglePin(pin) palToggleLine(pin)
#ifdef USBPD_ENABLE
# include "usbpd.h"
#endif
#define SEND_STRING(string) send_string_P(PSTR(string))
#define SEND_STRING_DELAY(string, interval) send_string_with_delay_P(PSTR(string), interval)
// Function substitutions to ease GPIO manipulation
#if defined(__AVR__)
// Look-Up Tables (LUTs) to convert ASCII character to keycode sequence.
extern const uint8_t ascii_to_keycode_lut[128];
extern const uint8_t ascii_to_shift_lut[16];
extern const uint8_t ascii_to_altgr_lut[16];
// clang-format off
#define KCLUT_ENTRY(a, b, c, d, e, f, g, h) \
( ((a) ? 1 : 0) << 0 \
| ((b) ? 1 : 0) << 1 \
| ((c) ? 1 : 0) << 2 \
| ((d) ? 1 : 0) << 3 \
| ((e) ? 1 : 0) << 4 \
| ((f) ? 1 : 0) << 5 \
| ((g) ? 1 : 0) << 6 \
| ((h) ? 1 : 0) << 7 )
// clang-format on
/* The AVR series GPIOs have a one clock read delay for changes in the digital input signal.
* But here's more margin to make it two clocks. */
# if !defined(GPIO_INPUT_PIN_DELAY)
# define GPIO_INPUT_PIN_DELAY 2
# endif
# define waitInputPinDelay() wait_cpuclock(GPIO_INPUT_PIN_DELAY)
void send_string(const char *str);
void send_string_with_delay(const char *str, uint8_t interval);
void send_string_P(const char *str);
void send_string_with_delay_P(const char *str, uint8_t interval);
void send_char(char ascii_code);
#elif defined(__ARMEL__) || defined(__ARMEB__)
/* For GPIOs on ARM-based MCUs, the input pins are sampled by the clock of the bus
* to which the GPIO is connected.
* The connected buses differ depending on the various series of MCUs.
* And since the instruction execution clock of the CPU and the bus clock of GPIO are different,
* there is a delay of several clocks to read the change of the input signal.
*
* Define this delay with the GPIO_INPUT_PIN_DELAY macro.
* If the GPIO_INPUT_PIN_DELAY macro is not defined, the following default values will be used.
* (A fairly large value of 0.25 microseconds is set.)
*/
# if !defined(GPIO_INPUT_PIN_DELAY)
# if defined(STM32_SYSCLK)
# define GPIO_INPUT_PIN_DELAY (STM32_SYSCLK / 1000000L / 4)
# elif defined(KINETIS_SYSCLK_FREQUENCY)
# define GPIO_INPUT_PIN_DELAY (KINETIS_SYSCLK_FREQUENCY / 1000000L / 4)
# endif
# endif
# define waitInputPinDelay() wait_cpuclock(GPIO_INPUT_PIN_DELAY)
#endif
// For tri-layer
void update_tri_layer(uint8_t layer1, uint8_t layer2, uint8_t layer3);
+42 -12
View File
@@ -13,8 +13,12 @@
* 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
#pragma once
#if defined(SEQUENCER_ENABLE)
# include "sequencer.h"
#endif
#ifndef MIDI_ENABLE_STRICT
# define MIDI_ENABLE_STRICT 0
@@ -146,13 +150,6 @@ enum quantum_keycodes {
CLICKY_DOWN,
CLICKY_RESET,
#ifdef FAUXCLICKY_ENABLE
// Faux clicky
FC_ON,
FC_OFF,
FC_TOG,
#endif
// Music mode on/off/toggle
MU_ON,
MU_OFF,
@@ -343,7 +340,8 @@ enum quantum_keycodes {
MI_TRNSU, // transpose up
MIDI_VELOCITY_MIN,
MI_VEL_1 = MIDI_VELOCITY_MIN,
MI_VEL_0 = MIDI_VELOCITY_MIN,
MI_VEL_1,
MI_VEL_2,
MI_VEL_3,
MI_VEL_4,
@@ -549,6 +547,37 @@ enum quantum_keycodes {
JS_BUTTON31,
JS_BUTTON_MAX = JS_BUTTON31,
#if defined(SEQUENCER_ENABLE)
SQ_ON,
SQ_OFF,
SQ_TOG,
SQ_TMPD, // Decrease tempo
SQ_TMPU, // Increase tempo
SEQUENCER_RESOLUTION_MIN,
SEQUENCER_RESOLUTION_MAX = SEQUENCER_RESOLUTION_MIN + SEQUENCER_RESOLUTIONS,
SQ_RESD, // Decrease resolution
SQ_RESU, // Increase resolution
SQ_SALL, // All steps on
SQ_SCLR, // All steps off
SEQUENCER_STEP_MIN,
SEQUENCER_STEP_MAX = SEQUENCER_STEP_MIN + SEQUENCER_STEPS,
SEQUENCER_TRACK_MIN,
SEQUENCER_TRACK_MAX = SEQUENCER_TRACK_MIN + SEQUENCER_TRACKS,
/**
* Helpers to assign a keycode to a step, a resolution, or a track.
* Falls back to NOOP if n is out of range.
*/
# define SQ_S(n) (n < SEQUENCER_STEPS ? SEQUENCER_STEP_MIN + n : XXXXXXX)
# define SQ_R(n) (n < SEQUENCER_RESOLUTIONS ? SEQUENCER_RESOLUTION_MIN + n : XXXXXXX)
# define SQ_T(n) (n < SEQUENCER_TRACKS ? SEQUENCER_TRACK_MIN + n : XXXXXXX)
#endif
// always leave at the end
SAFE_RANGE
};
@@ -681,6 +710,9 @@ enum quantum_keycodes {
#define CK_DOWN CLICKY_DOWN
#define CK_ON CLICKY_ENABLE
#define CK_OFF CLICKY_DISABLE
#define FC_ON CLICKY_ENABLE
#define FC_OFF CLICKY_DISABLE
#define FC_TOGG CLICKY_TOGGLE
#define RGB_MOD RGB_MODE_FORWARD
#define RGB_RMOD RGB_MODE_REVERSE
@@ -853,5 +885,3 @@ enum quantum_keycodes {
#define DM_RSTP DYN_REC_STOP
#define DM_PLY1 DYN_MACRO_PLAY1
#define DM_PLY2 DYN_MACRO_PLAY2
#endif // QUANTUM_KEYCODES_H
+1 -4
View File
@@ -14,8 +14,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef RGB_H
#define RGB_H
#pragma once
__attribute__((weak)) void rgblight_toggle(void){};
@@ -38,5 +37,3 @@ __attribute__((weak)) void rgblight_decrease_val(void){};
__attribute__((weak)) void rgblight_increase_speed(void){};
__attribute__((weak)) void rgblight_decrease_speed(void){};
#endif
+41 -18
View File
@@ -23,7 +23,7 @@
#include <string.h>
#include <math.h>
#include "lib/lib8tion/lib8tion.h"
#include <lib/lib8tion/lib8tion.h>
#ifndef RGB_MATRIX_CENTER
const point_t k_rgb_matrix_center = {112, 32};
@@ -31,6 +31,8 @@ const point_t k_rgb_matrix_center = {112, 32};
const point_t k_rgb_matrix_center = RGB_MATRIX_CENTER;
#endif
__attribute__((weak)) RGB rgb_matrix_hsv_to_rgb(HSV hsv) { return hsv_to_rgb(hsv); }
// Generic effect runners
#include "rgb_matrix_runners/effect_runner_dx_dy_dist.h"
#include "rgb_matrix_runners/effect_runner_dx_dy.h"
@@ -182,11 +184,12 @@ void rgb_matrix_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
void rgb_matrix_set_color_all(uint8_t red, uint8_t green, uint8_t blue) { rgb_matrix_driver.set_color_all(red, green, blue); }
bool process_rgb_matrix(uint16_t keycode, keyrecord_t *record) {
void process_rgb_matrix(uint8_t row, uint8_t col, bool pressed) {
#ifndef RGB_MATRIX_SPLIT
if (!is_keyboard_master()) return;
#endif
#if RGB_DISABLE_TIMEOUT > 0
if (record->event.pressed) {
rgb_anykey_timer = 0;
}
rgb_anykey_timer = 0;
#endif // RGB_DISABLE_TIMEOUT > 0
#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED
@@ -194,12 +197,12 @@ bool process_rgb_matrix(uint16_t keycode, keyrecord_t *record) {
uint8_t led_count = 0;
# if defined(RGB_MATRIX_KEYRELEASES)
if (!record->event.pressed)
if (!pressed)
# elif defined(RGB_MATRIX_KEYPRESSES)
if (record->event.pressed)
if (pressed)
# endif // defined(RGB_MATRIX_KEYRELEASES)
{
led_count = rgb_matrix_map_row_column_to_led(record->event.key.row, record->event.key.col, led);
led_count = rgb_matrix_map_row_column_to_led(row, col, led);
}
if (last_hit_buffer.count + led_count > LED_HITS_TO_REMEMBER) {
@@ -222,11 +225,9 @@ bool process_rgb_matrix(uint16_t keycode, keyrecord_t *record) {
#if defined(RGB_MATRIX_FRAMEBUFFER_EFFECTS) && !defined(DISABLE_RGB_MATRIX_TYPING_HEATMAP)
if (rgb_matrix_config.mode == RGB_MATRIX_TYPING_HEATMAP) {
process_rgb_matrix_typing_heatmap(record);
process_rgb_matrix_typing_heatmap(row, col);
}
#endif // defined(RGB_MATRIX_FRAMEBUFFER_EFFECTS) && !defined(DISABLE_RGB_MATRIX_TYPING_HEATMAP)
return true;
}
void rgb_matrix_test(void) {
@@ -264,9 +265,9 @@ static bool rgb_matrix_none(effect_params_t *params) {
static void rgb_task_timers(void) {
#if defined(RGB_MATRIX_KEYREACTIVE_ENABLED) || RGB_DISABLE_TIMEOUT > 0
uint32_t deltaTime = timer_elapsed32(rgb_timer_buffer);
uint32_t deltaTime = sync_timer_elapsed32(rgb_timer_buffer);
#endif // defined(RGB_MATRIX_KEYREACTIVE_ENABLED) || RGB_DISABLE_TIMEOUT > 0
rgb_timer_buffer = timer_read32();
rgb_timer_buffer = sync_timer_read32();
// Update double buffer timers
#if RGB_DISABLE_TIMEOUT > 0
@@ -294,7 +295,7 @@ static void rgb_task_timers(void) {
static void rgb_task_sync(void) {
// next task
if (timer_elapsed32(g_rgb_timer) >= RGB_MATRIX_LED_FLUSH_LIMIT) rgb_task_state = STARTING;
if (sync_timer_elapsed32(g_rgb_timer) >= RGB_MATRIX_LED_FLUSH_LIMIT) rgb_task_state = STARTING;
}
static void rgb_task_start(void) {
@@ -401,6 +402,10 @@ void rgb_matrix_task(void) {
break;
case RENDERING:
rgb_task_render(effect);
if (effect) {
rgb_matrix_indicators();
rgb_matrix_indicators_advanced(&rgb_effect_params);
}
break;
case FLUSHING:
rgb_task_flush(effect);
@@ -409,10 +414,6 @@ void rgb_matrix_task(void) {
rgb_task_sync();
break;
}
if (!suspend_backlight) {
rgb_matrix_indicators();
}
}
void rgb_matrix_indicators(void) {
@@ -424,6 +425,28 @@ __attribute__((weak)) void rgb_matrix_indicators_kb(void) {}
__attribute__((weak)) void rgb_matrix_indicators_user(void) {}
void rgb_matrix_indicators_advanced(effect_params_t *params) {
/* special handling is needed for "params->iter", since it's already been incremented.
* Could move the invocations to rgb_task_render, but then it's missing a few checks
* and not sure which would be better. Otherwise, this should be called from
* rgb_task_render, right before the iter++ line.
*/
#if defined(RGB_MATRIX_LED_PROCESS_LIMIT) && RGB_MATRIX_LED_PROCESS_LIMIT > 0 && RGB_MATRIX_LED_PROCESS_LIMIT < DRIVER_LED_TOTAL
uint8_t min = RGB_MATRIX_LED_PROCESS_LIMIT * (params->iter - 1);
uint8_t max = min + RGB_MATRIX_LED_PROCESS_LIMIT;
if (max > DRIVER_LED_TOTAL) max = DRIVER_LED_TOTAL;
#else
uint8_t min = 0;
uint8_t max = DRIVER_LED_TOTAL;
#endif
rgb_matrix_indicators_advanced_kb(min, max);
rgb_matrix_indicators_advanced_user(min, max);
}
__attribute__((weak)) void rgb_matrix_indicators_advanced_kb(uint8_t led_min, uint8_t led_max) {}
__attribute__((weak)) void rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) {}
void rgb_matrix_init(void) {
rgb_matrix_driver.init();
+13 -5
View File
@@ -16,8 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef RGB_MATRIX_H
#define RGB_MATRIX_H
#pragma once
#include <stdint.h>
#include <stdbool.h>
@@ -57,6 +56,11 @@
uint8_t max = DRIVER_LED_TOTAL;
#endif
#define RGB_MATRIX_INDICATOR_SET_COLOR(i, r, g, b) \
if (i >= led_min && i <= led_max) { \
rgb_matrix_set_color(i, r, g, b); \
}
#define RGB_MATRIX_TEST_LED_FLAGS() \
if (!HAS_ANY_FLAGS(g_led_config.flags[i], params->flags)) continue
@@ -86,6 +90,7 @@ enum rgb_matrix_effects {
};
void eeconfig_update_rgb_matrix_default(void);
void eeconfig_update_rgb_matrix(void);
uint8_t rgb_matrix_map_row_column_to_led_kb(uint8_t row, uint8_t column, uint8_t *led_i);
uint8_t rgb_matrix_map_row_column_to_led(uint8_t row, uint8_t column, uint8_t *led_i);
@@ -93,7 +98,7 @@ uint8_t rgb_matrix_map_row_column_to_led(uint8_t row, uint8_t column, uint8_t *l
void rgb_matrix_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
void rgb_matrix_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
bool process_rgb_matrix(uint16_t keycode, keyrecord_t *record);
void process_rgb_matrix(uint8_t row, uint8_t col, bool pressed);
void rgb_matrix_task(void);
@@ -103,6 +108,10 @@ void rgb_matrix_indicators(void);
void rgb_matrix_indicators_kb(void);
void rgb_matrix_indicators_user(void);
void rgb_matrix_indicators_advanced(effect_params_t *params);
void rgb_matrix_indicators_advanced_kb(uint8_t led_min, uint8_t led_max);
void rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max);
void rgb_matrix_init(void);
void rgb_matrix_set_suspend_state(bool state);
@@ -150,6 +159,7 @@ led_flags_t rgb_matrix_get_flags(void);
void rgb_matrix_set_flags(led_flags_t flags);
#ifndef RGBLIGHT_ENABLE
# define eeconfig_update_rgblight_current eeconfig_update_rgb_matrix
# define rgblight_toggle rgb_matrix_toggle
# define rgblight_toggle_noeeprom rgb_matrix_toggle_noeeprom
# define rgblight_enable rgb_matrix_enable
@@ -215,5 +225,3 @@ extern last_hit_t g_last_hit_tracker;
#ifdef RGB_MATRIX_FRAMEBUFFER_EFFECTS
extern uint8_t g_rgb_frame_buffer[MATRIX_ROWS][MATRIX_COLS];
#endif
#endif

Some files were not shown because too many files have changed in this diff Show More