aboutsummaryrefslogtreecommitdiff
path: root/quantum
diff options
context:
space:
mode:
Diffstat (limited to 'quantum')
-rw-r--r--quantum/action.c5
-rw-r--r--quantum/api.c182
-rw-r--r--quantum/api.h55
-rw-r--r--quantum/api/api_sysex.c72
-rw-r--r--quantum/api/api_sysex.h25
-rw-r--r--quantum/audio/audio.h13
-rw-r--r--quantum/audio/driver_avr_pwm.h17
-rw-r--r--quantum/audio/driver_avr_pwm_hardware.c332
-rw-r--r--quantum/audio/driver_chibios_dac.h126
-rw-r--r--quantum/audio/driver_chibios_dac_additive.c335
-rw-r--r--quantum/audio/driver_chibios_dac_basic.c245
-rw-r--r--quantum/audio/driver_chibios_pwm.h40
-rw-r--r--quantum/audio/driver_chibios_pwm_hardware.c144
-rw-r--r--quantum/audio/driver_chibios_pwm_software.c164
-rw-r--r--quantum/audio/song_list.h8
-rw-r--r--quantum/backlight/backlight_chibios.c10
-rw-r--r--quantum/eeconfig.c11
-rw-r--r--quantum/eeconfig.h26
-rw-r--r--quantum/keyboard.c48
-rw-r--r--quantum/keymap_extras/keymap_turkish_f.h2
-rw-r--r--quantum/led_matrix/led_matrix.c32
-rw-r--r--quantum/led_matrix/led_matrix_drivers.c133
-rw-r--r--quantum/matrix.c28
-rw-r--r--quantum/mcu_selection.mk70
-rw-r--r--quantum/process_keycode/process_haptic.c1
-rw-r--r--quantum/process_keycode/process_programmable_button.c31
-rw-r--r--quantum/process_keycode/process_programmable_button.h23
-rw-r--r--quantum/process_keycode/process_unicode_common.c47
-rw-r--r--quantum/programmable_button.c37
-rw-r--r--quantum/programmable_button.h30
-rw-r--r--quantum/quantum.c122
-rw-r--r--quantum/quantum.h6
-rw-r--r--quantum/quantum_keycodes.h70
-rw-r--r--quantum/rgb_matrix/rgb_matrix.c32
-rw-r--r--quantum/rgb_matrix/rgb_matrix_drivers.c127
-rw-r--r--quantum/sequencer/tests/rules.mk2
-rw-r--r--quantum/serial_link/LICENSE19
-rw-r--r--quantum/serial_link/README.md1
-rw-r--r--quantum/serial_link/protocol/byte_stuffer.c135
-rw-r--r--quantum/serial_link/protocol/byte_stuffer.h34
-rw-r--r--quantum/serial_link/protocol/frame_router.c64
-rw-r--r--quantum/serial_link/protocol/frame_router.h35
-rw-r--r--quantum/serial_link/protocol/frame_validator.c57
-rw-r--r--quantum/serial_link/protocol/frame_validator.h31
-rw-r--r--quantum/serial_link/protocol/physical.h27
-rw-r--r--quantum/serial_link/protocol/transport.c121
-rw-r--r--quantum/serial_link/protocol/transport.h139
-rw-r--r--quantum/serial_link/protocol/triple_buffered_object.c77
-rw-r--r--quantum/serial_link/protocol/triple_buffered_object.h44
-rw-r--r--quantum/serial_link/system/serial_link.c250
-rw-r--r--quantum/serial_link/system/serial_link.h54
-rw-r--r--quantum/serial_link/tests/Makefile61
-rw-r--r--quantum/serial_link/tests/byte_stuffer_tests.cpp450
-rw-r--r--quantum/serial_link/tests/frame_router_tests.cpp204
-rw-r--r--quantum/serial_link/tests/frame_validator_tests.cpp100
-rw-r--r--quantum/serial_link/tests/rules.mk22
-rw-r--r--quantum/serial_link/tests/testlist.mk6
-rw-r--r--quantum/serial_link/tests/transport_tests.cpp184
-rw-r--r--quantum/serial_link/tests/triple_buffered_object_tests.cpp80
-rw-r--r--quantum/usb_device_state.c51
-rw-r--r--quantum/usb_device_state.h39
-rw-r--r--quantum/visualizer/LICENSE.md29
-rw-r--r--quantum/visualizer/common_gfxconf.h354
-rw-r--r--quantum/visualizer/default_animations.c177
-rw-r--r--quantum/visualizer/default_animations.h27
-rw-r--r--quantum/visualizer/lcd_backlight.c87
-rw-r--r--quantum/visualizer/lcd_backlight.h43
-rw-r--r--quantum/visualizer/lcd_backlight_keyframes.c69
-rw-r--r--quantum/visualizer/lcd_backlight_keyframes.h27
-rw-r--r--quantum/visualizer/lcd_keyframes.c184
-rw-r--r--quantum/visualizer/lcd_keyframes.h35
-rw-r--r--quantum/visualizer/led_backlight_keyframes.c143
-rw-r--r--quantum/visualizer/led_backlight_keyframes.h40
-rw-r--r--quantum/visualizer/readme.md18
-rw-r--r--quantum/visualizer/resources/lcd_logo.c45
-rw-r--r--quantum/visualizer/resources/lcd_logo.pngbin271 -> 0 bytes
-rw-r--r--quantum/visualizer/resources/resources.h23
-rw-r--r--quantum/visualizer/visualizer.c483
-rw-r--r--quantum/visualizer/visualizer.h154
-rw-r--r--quantum/visualizer/visualizer.mk123
-rw-r--r--quantum/visualizer/visualizer_keyframes.c23
-rw-r--r--quantum/visualizer/visualizer_keyframes.h23
82 files changed, 723 insertions, 6320 deletions
diff --git a/quantum/action.c b/quantum/action.c
index be135f18f..95f39d23d 100644
--- a/quantum/action.c
+++ b/quantum/action.c
@@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
18#include "keycode.h" 18#include "keycode.h"
19#include "keyboard.h" 19#include "keyboard.h"
20#include "mousekey.h" 20#include "mousekey.h"
21#include "programmable_button.h"
21#include "command.h" 22#include "command.h"
22#include "led.h" 23#include "led.h"
23#include "action_layer.h" 24#include "action_layer.h"
@@ -988,6 +989,10 @@ void clear_keyboard_but_mods_and_keys() {
988 mousekey_clear(); 989 mousekey_clear();
989 mousekey_send(); 990 mousekey_send();
990#endif 991#endif
992#ifdef PROGRAMMABLE_BUTTON_ENABLE
993 programmable_button_clear();
994 programmable_button_send();
995#endif
991} 996}
992 997
993/** \brief Utilities for actions. (FIXME: Needs better description) 998/** \brief Utilities for actions. (FIXME: Needs better description)
diff --git a/quantum/api.c b/quantum/api.c
deleted file mode 100644
index 168574458..000000000
--- a/quantum/api.c
+++ /dev/null
@@ -1,182 +0,0 @@
1/* Copyright 2016 Jack Humbert
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "api.h"
18#include "quantum.h"
19
20void dword_to_bytes(uint32_t dword, uint8_t* bytes) {
21 bytes[0] = (dword >> 24) & 0xFF;
22 bytes[1] = (dword >> 16) & 0xFF;
23 bytes[2] = (dword >> 8) & 0xFF;
24 bytes[3] = (dword >> 0) & 0xFF;
25}
26
27uint32_t bytes_to_dword(uint8_t* bytes, uint8_t index) { return ((uint32_t)bytes[index + 0] << 24) | ((uint32_t)bytes[index + 1] << 16) | ((uint32_t)bytes[index + 2] << 8) | (uint32_t)bytes[index + 3]; }
28
29__attribute__((weak)) bool process_api_quantum(uint8_t length, uint8_t* data) { return process_api_keyboard(length, data); }
30
31__attribute__((weak)) bool process_api_keyboard(uint8_t length, uint8_t* data) { return process_api_user(length, data); }
32
33__attribute__((weak)) bool process_api_user(uint8_t length, uint8_t* data) { return true; }
34
35void process_api(uint16_t length, uint8_t* data) {
36 // SEND_STRING("\nRX: ");
37 // for (uint8_t i = 0; i < length; i++) {
38 // send_byte(data[i]);
39 // SEND_STRING(" ");
40 // }
41 if (!process_api_quantum(length, data)) return;
42
43 switch (data[0]) {
44 case MT_SET_DATA:
45 switch (data[1]) {
46 case DT_DEFAULT_LAYER: {
47 eeconfig_update_default_layer(data[2]);
48 default_layer_set((uint32_t)(data[2]));
49 break;
50 }
51 case DT_KEYMAP_OPTIONS: {
52 eeconfig_update_keymap(data[2]);
53 break;
54 }
55 case DT_RGBLIGHT: {
56#ifdef RGBLIGHT_ENABLE
57 uint32_t rgblight = bytes_to_dword(data, 2);
58 eeconfig_update_rgblight(rgblight);
59#endif
60 break;
61 }
62 }
63 case MT_GET_DATA:
64 switch (data[1]) {
65 case DT_HANDSHAKE: {
66 MT_GET_DATA_ACK(DT_HANDSHAKE, NULL, 0);
67 break;
68 }
69 case DT_DEBUG: {
70 uint8_t debug_bytes[1] = {eeprom_read_byte(EECONFIG_DEBUG)};
71 MT_GET_DATA_ACK(DT_DEBUG, debug_bytes, 1);
72 break;
73 }
74 case DT_DEFAULT_LAYER: {
75 uint8_t default_bytes[1] = {eeprom_read_byte(EECONFIG_DEFAULT_LAYER)};
76 MT_GET_DATA_ACK(DT_DEFAULT_LAYER, default_bytes, 1);
77 break;
78 }
79 case DT_CURRENT_LAYER: {
80 uint8_t layer_state_bytes[4];
81 dword_to_bytes(layer_state, layer_state_bytes);
82 MT_GET_DATA_ACK(DT_CURRENT_LAYER, layer_state_bytes, 4);
83 break;
84 }
85 case DT_AUDIO: {
86#ifdef AUDIO_ENABLE
87 uint8_t audio_bytes[1] = {eeprom_read_byte(EECONFIG_AUDIO)};
88 MT_GET_DATA_ACK(DT_AUDIO, audio_bytes, 1);
89#else
90 MT_GET_DATA_ACK(DT_AUDIO, NULL, 0);
91#endif
92 break;
93 }
94 case DT_BACKLIGHT: {
95#ifdef BACKLIGHT_ENABLE
96 uint8_t backlight_bytes[1] = {eeprom_read_byte(EECONFIG_BACKLIGHT)};
97 MT_GET_DATA_ACK(DT_BACKLIGHT, backlight_bytes, 1);
98#else
99 MT_GET_DATA_ACK(DT_BACKLIGHT, NULL, 0);
100#endif
101 break;
102 }
103 case DT_RGBLIGHT: {
104#ifdef RGBLIGHT_ENABLE
105 uint8_t rgblight_bytes[4];
106 dword_to_bytes(eeconfig_read_rgblight(), rgblight_bytes);
107 MT_GET_DATA_ACK(DT_RGBLIGHT, rgblight_bytes, 4);
108#else
109 MT_GET_DATA_ACK(DT_RGBLIGHT, NULL, 0);
110#endif
111 break;
112 }
113 case DT_KEYMAP_OPTIONS: {
114 uint8_t keymap_bytes[1] = {eeconfig_read_keymap()};
115 MT_GET_DATA_ACK(DT_KEYMAP_OPTIONS, keymap_bytes, 1);
116 break;
117 }
118 case DT_KEYMAP_SIZE: {
119 uint8_t keymap_size[2] = {MATRIX_ROWS, MATRIX_COLS};
120 MT_GET_DATA_ACK(DT_KEYMAP_SIZE, keymap_size, 2);
121 break;
122 }
123 // This may be too much
124 // case DT_KEYMAP: {
125 // uint8_t keymap_data[MATRIX_ROWS * MATRIX_COLS * 4 + 3];
126 // keymap_data[0] = data[2];
127 // keymap_data[1] = MATRIX_ROWS;
128 // keymap_data[2] = MATRIX_COLS;
129 // for (int i = 0; i < MATRIX_ROWS; i++) {
130 // for (int j = 0; j < MATRIX_COLS; j++) {
131 // keymap_data[3 + (i*MATRIX_COLS*2) + (j*2)] = pgm_read_word(&keymaps[data[2]][i][j]) >> 8;
132 // keymap_data[3 + (i*MATRIX_COLS*2) + (j*2) + 1] = pgm_read_word(&keymaps[data[2]][i][j]) & 0xFF;
133 // }
134 // }
135 // MT_GET_DATA_ACK(DT_KEYMAP, keymap_data, MATRIX_ROWS * MATRIX_COLS * 4 + 3);
136 // // uint8_t keymap_data[5];
137 // // keymap_data[0] = data[2];
138 // // keymap_data[1] = data[3];
139 // // keymap_data[2] = data[4];
140 // // keymap_data[3] = pgm_read_word(&keymaps[data[2]][data[3]][data[4]]) >> 8;
141 // // keymap_data[4] = pgm_read_word(&keymaps[data[2]][data[3]][data[4]]) & 0xFF;
142
143 // // MT_GET_DATA_ACK(DT_KEYMAP, keymap_data, 5);
144 // break;
145 // }
146 default:
147 break;
148 }
149 break;
150 case MT_SET_DATA_ACK:
151 case MT_GET_DATA_ACK:
152 break;
153 case MT_SEND_DATA:
154 break;
155 case MT_SEND_DATA_ACK:
156 break;
157 case MT_EXE_ACTION:
158 break;
159 case MT_EXE_ACTION_ACK:
160 break;
161 case MT_TYPE_ERROR:
162 break;
163 default:; // command not recognised
164 SEND_BYTES(MT_TYPE_ERROR, DT_NONE, data, length);
165 break;
166
167 // #ifdef RGBLIGHT_ENABLE
168 // case 0x27: ; // RGB LED functions
169 // switch (*data++) {
170 // case 0x00: ; // Update HSV
171 // rgblight_sethsv((data[0] << 8 | data[1]) % 360, data[2], data[3]);
172 // break;
173 // case 0x01: ; // Update RGB
174 // break;
175 // case 0x02: ; // Update mode
176 // rgblight_mode(data[0]);
177 // break;
178 // }
179 // break;
180 // #endif
181 }
182}
diff --git a/quantum/api.h b/quantum/api.h
deleted file mode 100644
index 0a30e9d6c..000000000
--- a/quantum/api.h
+++ /dev/null
@@ -1,55 +0,0 @@
1/* Copyright 2016 Jack Humbert
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#ifdef __AVR__
20# include "lufa.h"
21#endif
22
23enum MESSAGE_TYPE {
24 MT_GET_DATA = 0x10, // Get data from keyboard
25 MT_GET_DATA_ACK = 0x11, // returned data to process (ACK)
26 MT_SET_DATA = 0x20, // Set data on keyboard
27 MT_SET_DATA_ACK = 0x21, // returned data to confirm (ACK)
28 MT_SEND_DATA = 0x30, // Sending data/action from keyboard
29 MT_SEND_DATA_ACK = 0x31, // returned data/action confirmation (ACK)
30 MT_EXE_ACTION = 0x40, // executing actions on keyboard
31 MT_EXE_ACTION_ACK = 0x41, // return confirmation/value (ACK)
32 MT_TYPE_ERROR = 0x80 // type not recognised (ACK)
33};
34
35enum DATA_TYPE { DT_NONE = 0x00, DT_HANDSHAKE, DT_DEFAULT_LAYER, DT_CURRENT_LAYER, DT_KEYMAP_OPTIONS, DT_BACKLIGHT, DT_RGBLIGHT, DT_UNICODE, DT_DEBUG, DT_AUDIO, DT_QUANTUM_ACTION, DT_KEYBOARD_ACTION, DT_USER_ACTION, DT_KEYMAP_SIZE, DT_KEYMAP };
36
37void dword_to_bytes(uint32_t dword, uint8_t* bytes);
38uint32_t bytes_to_dword(uint8_t* bytes, uint8_t index);
39
40#define MT_GET_DATA(data_type, data, length) SEND_BYTES(MT_GET_DATA, data_type, data, length)
41#define MT_GET_DATA_ACK(data_type, data, length) SEND_BYTES(MT_GET_DATA_ACK, data_type, data, length)
42#define MT_SET_DATA(data_type, data, length) SEND_BYTES(MT_SET_DATA, data_type, data, length)
43#define MT_SET_DATA_ACK(data_type, data, length) SEND_BYTES(MT_SET_DATA_ACK, data_type, data, length)
44#define MT_SEND_DATA(data_type, data, length) SEND_BYTES(MT_SEND_DATA, data_type, data, length)
45#define MT_SEND_DATA_ACK(data_type, data, length) SEND_BYTES(MT_SEND_DATA_ACK, data_type, data, length)
46#define MT_EXE_ACTION(data_type, data, length) SEND_BYTES(MT_EXE_ACTION, data_type, data, length)
47#define MT_EXE_ACTION_ACK(data_type, data, length) SEND_BYTES(MT_EXE_ACTION_ACK, data_type, data, length)
48
49void process_api(uint16_t length, uint8_t* data);
50
51__attribute__((weak)) bool process_api_quantum(uint8_t length, uint8_t* data);
52
53__attribute__((weak)) bool process_api_keyboard(uint8_t length, uint8_t* data);
54
55__attribute__((weak)) bool process_api_user(uint8_t length, uint8_t* data);
diff --git a/quantum/api/api_sysex.c b/quantum/api/api_sysex.c
deleted file mode 100644
index 07c90cf80..000000000
--- a/quantum/api/api_sysex.c
+++ /dev/null
@@ -1,72 +0,0 @@
1/* Copyright 2016 Jack Humbert, Fred Sundvik
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16#include "api_sysex.h"
17#include "sysex_tools.h"
18#include "print.h"
19#include "qmk_midi.h"
20
21void send_bytes_sysex(uint8_t message_type, uint8_t data_type, uint8_t* bytes, uint16_t length) {
22 // SEND_STRING("\nTX: ");
23 // for (uint8_t i = 0; i < length; i++) {
24 // send_byte(bytes[i]);
25 // SEND_STRING(" ");
26 // }
27 if (length > API_SYSEX_MAX_SIZE) {
28 xprintf("Sysex msg too big %d %d %d", message_type, data_type, length);
29 return;
30 }
31
32 // The buffer size required is calculated as the following
33 // API_SYSEX_MAX_SIZE is the maximum length
34 // In addition to that we have a two byte message header consisting of the message_type and data_type
35 // This has to be encoded with an additional overhead of one byte for every starting 7 bytes
36 // We just add one extra byte in case it's not divisible by 7
37 // Then we have an unencoded header consisting of 4 bytes
38 // Plus a one byte terminator
39 const unsigned message_header = 2;
40 const unsigned unencoded_message = API_SYSEX_MAX_SIZE + message_header;
41 const unsigned encoding_overhead = unencoded_message / 7 + 1;
42 const unsigned encoded_size = unencoded_message + encoding_overhead;
43 const unsigned unencoded_header = 4;
44 const unsigned terminator = 1;
45 const unsigned buffer_size = encoded_size + unencoded_header + terminator;
46 uint8_t buffer[encoded_size + unencoded_header + terminator];
47 // The unencoded header
48 buffer[0] = 0xF0;
49 buffer[1] = 0x00;
50 buffer[2] = 0x00;
51 buffer[3] = 0x00;
52
53 // We copy the message to the end of the array, this way we can do an inplace encoding, using the same
54 // buffer for both input and output
55 const unsigned message_size = length + message_header;
56 uint8_t* unencoded_start = buffer + buffer_size - message_size;
57 uint8_t* ptr = unencoded_start;
58 *(ptr++) = message_type;
59 *(ptr++) = data_type;
60 memcpy(ptr, bytes, length);
61
62 unsigned encoded_length = sysex_encode(buffer + unencoded_header, unencoded_start, message_size);
63 unsigned final_size = unencoded_header + encoded_length + terminator;
64 buffer[final_size - 1] = 0xF7;
65 midi_send_array(&midi_device, final_size, buffer);
66
67 // SEND_STRING("\nTD: ");
68 // for (uint8_t i = 0; i < encoded_length + 5; i++) {
69 // send_byte(buffer[i]);
70 // SEND_STRING(" ");
71 // }
72}
diff --git a/quantum/api/api_sysex.h b/quantum/api/api_sysex.h
deleted file mode 100644
index eb0a18848..000000000
--- a/quantum/api/api_sysex.h
+++ /dev/null
@@ -1,25 +0,0 @@
1/* Copyright 2016 Jack Humbert
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include "api.h"
20
21#define API_SYSEX_MAX_SIZE 32
22
23void send_bytes_sysex(uint8_t message_type, uint8_t data_type, uint8_t* bytes, uint16_t length);
24
25#define SEND_BYTES(mt, dt, b, l) send_bytes_sysex(mt, dt, b, l)
diff --git a/quantum/audio/audio.h b/quantum/audio/audio.h
index 56b9158a1..290d461f5 100644
--- a/quantum/audio/audio.h
+++ b/quantum/audio/audio.h
@@ -26,17 +26,12 @@
26 26
27#if defined(__AVR__) 27#if defined(__AVR__)
28# include <avr/io.h> 28# include <avr/io.h>
29# if defined(AUDIO_DRIVER_PWM)
30# include "driver_avr_pwm.h"
31# endif
32#endif 29#endif
33 30
34#if defined(PROTOCOL_CHIBIOS) 31#if defined(AUDIO_DRIVER_PWM)
35# if defined(AUDIO_DRIVER_PWM) 32# include "audio_pwm.h"
36# include "driver_chibios_pwm.h" 33#elif defined(AUDIO_DRIVER_DAC)
37# elif defined(AUDIO_DRIVER_DAC) 34# include "audio_dac.h"
38# include "driver_chibios_dac.h"
39# endif
40#endif 35#endif
41 36
42typedef union { 37typedef union {
diff --git a/quantum/audio/driver_avr_pwm.h b/quantum/audio/driver_avr_pwm.h
deleted file mode 100644
index d6eb3571d..000000000
--- a/quantum/audio/driver_avr_pwm.h
+++ /dev/null
@@ -1,17 +0,0 @@
1/* Copyright 2020 Jack Humbert
2 * Copyright 2020 JohSchneider
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17#pragma once
diff --git a/quantum/audio/driver_avr_pwm_hardware.c b/quantum/audio/driver_avr_pwm_hardware.c
deleted file mode 100644
index df03a4558..000000000
--- a/quantum/audio/driver_avr_pwm_hardware.c
+++ /dev/null
@@ -1,332 +0,0 @@
1/* Copyright 2016 Jack Humbert
2 * Copyright 2020 JohSchneider
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#if defined(__AVR__)
19# include <avr/pgmspace.h>
20# include <avr/interrupt.h>
21# include <avr/io.h>
22#endif
23
24#include "audio.h"
25
26extern bool playing_note;
27extern bool playing_melody;
28extern uint8_t note_timbre;
29
30#define CPU_PRESCALER 8
31
32/*
33 Audio Driver: PWM
34
35 drive up to two speakers through the AVR PWM hardware-peripheral, using timer1 and/or timer3 on Atmega32U4.
36
37 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
38 and an optional secondary channel_2 on either pin PB5, PB6 or PB7, with a PWM signal from timer1
39
40 alternatively, the PWM pins on PORTB can be used as only/primary speaker
41*/
42
43#if defined(AUDIO_PIN) && (AUDIO_PIN != C4) && (AUDIO_PIN != C5) && (AUDIO_PIN != C6) && (AUDIO_PIN != B5) && (AUDIO_PIN != B6) && (AUDIO_PIN != B7) && (AUDIO_PIN != D5)
44# error "Audio feature enabled, but no suitable pin selected as AUDIO_PIN - see docs/feature_audio under the AVR settings for available options."
45#endif
46
47#if (AUDIO_PIN == C4) || (AUDIO_PIN == C5) || (AUDIO_PIN == C6)
48# define AUDIO1_PIN_SET
49# define AUDIO1_TIMSKx TIMSK3
50# define AUDIO1_TCCRxA TCCR3A
51# define AUDIO1_TCCRxB TCCR3B
52# define AUDIO1_ICRx ICR3
53# define AUDIO1_WGMx0 WGM30
54# define AUDIO1_WGMx1 WGM31
55# define AUDIO1_WGMx2 WGM32
56# define AUDIO1_WGMx3 WGM33
57# define AUDIO1_CSx0 CS30
58# define AUDIO1_CSx1 CS31
59# define AUDIO1_CSx2 CS32
60
61# if (AUDIO_PIN == C6)
62# define AUDIO1_COMxy0 COM3A0
63# define AUDIO1_COMxy1 COM3A1
64# define AUDIO1_OCIExy OCIE3A
65# define AUDIO1_OCRxy OCR3A
66# define AUDIO1_PIN C6
67# define AUDIO1_TIMERx_COMPy_vect TIMER3_COMPA_vect
68# elif (AUDIO_PIN == C5)
69# define AUDIO1_COMxy0 COM3B0
70# define AUDIO1_COMxy1 COM3B1
71# define AUDIO1_OCIExy OCIE3B
72# define AUDIO1_OCRxy OCR3B
73# define AUDIO1_PIN C5
74# define AUDIO1_TIMERx_COMPy_vect TIMER3_COMPB_vect
75# elif (AUDIO_PIN == C4)
76# define AUDIO1_COMxy0 COM3C0
77# define AUDIO1_COMxy1 COM3C1
78# define AUDIO1_OCIExy OCIE3C
79# define AUDIO1_OCRxy OCR3C
80# define AUDIO1_PIN C4
81# define AUDIO1_TIMERx_COMPy_vect TIMER3_COMPC_vect
82# endif
83#endif
84
85#if defined(AUDIO_PIN) && defined(AUDIO_PIN_ALT) && (AUDIO_PIN == AUDIO_PIN_ALT)
86# error "Audio feature: AUDIO_PIN and AUDIO_PIN_ALT on the same pin makes no sense."
87#endif
88
89#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)))
90# error "Audio feature: PORTB as AUDIO_PIN and AUDIO_PIN_ALT at the same time is not supported."
91#endif
92
93#if defined(AUDIO_PIN_ALT) && (AUDIO_PIN_ALT != B5) && (AUDIO_PIN_ALT != B6) && (AUDIO_PIN_ALT != B7)
94# error "Audio feature: the pin selected as AUDIO_PIN_ALT is not supported."
95#endif
96
97#if (AUDIO_PIN == B5) || (AUDIO_PIN == B6) || (AUDIO_PIN == B7) || (AUDIO_PIN_ALT == B5) || (AUDIO_PIN_ALT == B6) || (AUDIO_PIN_ALT == B7) || (AUDIO_PIN == D5)
98# define AUDIO2_PIN_SET
99# define AUDIO2_TIMSKx TIMSK1
100# define AUDIO2_TCCRxA TCCR1A
101# define AUDIO2_TCCRxB TCCR1B
102# define AUDIO2_ICRx ICR1
103# define AUDIO2_WGMx0 WGM10
104# define AUDIO2_WGMx1 WGM11
105# define AUDIO2_WGMx2 WGM12
106# define AUDIO2_WGMx3 WGM13
107# define AUDIO2_CSx0 CS10
108# define AUDIO2_CSx1 CS11
109# define AUDIO2_CSx2 CS12
110
111# if (AUDIO_PIN == B5) || (AUDIO_PIN_ALT == B5)
112# define AUDIO2_COMxy0 COM1A0
113# define AUDIO2_COMxy1 COM1A1
114# define AUDIO2_OCIExy OCIE1A
115# define AUDIO2_OCRxy OCR1A
116# define AUDIO2_PIN B5
117# define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPA_vect
118# elif (AUDIO_PIN == B6) || (AUDIO_PIN_ALT == B6)
119# define AUDIO2_COMxy0 COM1B0
120# define AUDIO2_COMxy1 COM1B1
121# define AUDIO2_OCIExy OCIE1B
122# define AUDIO2_OCRxy OCR1B
123# define AUDIO2_PIN B6
124# define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPB_vect
125# elif (AUDIO_PIN == B7) || (AUDIO_PIN_ALT == B7)
126# define AUDIO2_COMxy0 COM1C0
127# define AUDIO2_COMxy1 COM1C1
128# define AUDIO2_OCIExy OCIE1C
129# define AUDIO2_OCRxy OCR1C
130# define AUDIO2_PIN B7
131# define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPC_vect
132# elif (AUDIO_PIN == D5) && defined(__AVR_ATmega32A__)
133# pragma message "Audio support for ATmega32A is experimental and can cause crashes."
134# undef AUDIO2_TIMSKx
135# define AUDIO2_TIMSKx TIMSK
136# define AUDIO2_COMxy0 COM1A0
137# define AUDIO2_COMxy1 COM1A1
138# define AUDIO2_OCIExy OCIE1A
139# define AUDIO2_OCRxy OCR1A
140# define AUDIO2_PIN D5
141# define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPA_vect
142# endif
143#endif
144
145// C6 seems to be the assumed default by many existing keyboard - but sill warn the user
146#if !defined(AUDIO1_PIN_SET) && !defined(AUDIO2_PIN_SET)
147# 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... :-)"
148// TODO: make this an error - go through the breaking-change-process and change all keyboards to the new define
149#endif
150// -----------------------------------------------------------------------------
151
152#ifdef AUDIO1_PIN_SET
153static float channel_1_frequency = 0.0f;
154void channel_1_set_frequency(float freq) {
155 if (freq == 0.0f) // a pause/rest is a valid "note" with freq=0
156 {
157 // disable the output, but keep the pwm-ISR going (with the previous
158 // frequency) so the audio-state keeps getting updated
159 // Note: setting the duty-cycle 0 is not possible on non-inverting PWM mode - see the AVR data-sheet
160 AUDIO1_TCCRxA &= ~(_BV(AUDIO1_COMxy1) | _BV(AUDIO1_COMxy0));
161 return;
162 } else {
163 AUDIO1_TCCRxA |= _BV(AUDIO1_COMxy1); // enable output, PWM mode
164 }
165
166 channel_1_frequency = freq;
167
168 // set pwm period
169 AUDIO1_ICRx = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER));
170 // and duty cycle
171 AUDIO1_OCRxy = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre / 100);
172}
173
174void channel_1_start(void) {
175 // enable timer-counter ISR
176 AUDIO1_TIMSKx |= _BV(AUDIO1_OCIExy);
177 // enable timer-counter output
178 AUDIO1_TCCRxA |= _BV(AUDIO1_COMxy1);
179}
180
181void channel_1_stop(void) {
182 // disable timer-counter ISR
183 AUDIO1_TIMSKx &= ~_BV(AUDIO1_OCIExy);
184 // disable timer-counter output
185 AUDIO1_TCCRxA &= ~(_BV(AUDIO1_COMxy1) | _BV(AUDIO1_COMxy0));
186}
187#endif
188
189#ifdef AUDIO2_PIN_SET
190static float channel_2_frequency = 0.0f;
191void channel_2_set_frequency(float freq) {
192 if (freq == 0.0f) {
193 AUDIO2_TCCRxA &= ~(_BV(AUDIO2_COMxy1) | _BV(AUDIO2_COMxy0));
194 return;
195 } else {
196 AUDIO2_TCCRxA |= _BV(AUDIO2_COMxy1);
197 }
198
199 channel_2_frequency = freq;
200
201 AUDIO2_ICRx = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER));
202 AUDIO2_OCRxy = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre / 100);
203}
204
205float channel_2_get_frequency(void) { return channel_2_frequency; }
206
207void channel_2_start(void) {
208 AUDIO2_TIMSKx |= _BV(AUDIO2_OCIExy);
209 AUDIO2_TCCRxA |= _BV(AUDIO2_COMxy1);
210}
211
212void channel_2_stop(void) {
213 AUDIO2_TIMSKx &= ~_BV(AUDIO2_OCIExy);
214 AUDIO2_TCCRxA &= ~(_BV(AUDIO2_COMxy1) | _BV(AUDIO2_COMxy0));
215}
216#endif
217
218void audio_driver_initialize() {
219#ifdef AUDIO1_PIN_SET
220 channel_1_stop();
221 setPinOutput(AUDIO1_PIN);
222#endif
223
224#ifdef AUDIO2_PIN_SET
225 channel_2_stop();
226 setPinOutput(AUDIO2_PIN);
227#endif
228
229 // TCCR3A / TCCR3B: Timer/Counter #3 Control Registers TCCR3A/TCCR3B, TCCR1A/TCCR1B
230 // Compare Output Mode (COM3An and COM1An) = 0b00 = Normal port operation
231 // OC3A -- PC6
232 // OC3B -- PC5
233 // OC3C -- PC4
234 // OC1A -- PB5
235 // OC1B -- PB6
236 // OC1C -- PB7
237
238 // Waveform Generation Mode (WGM3n) = 0b1110 = Fast PWM Mode 14. Period = ICR3, Duty Cycle OCR3A)
239 // OCR3A - PC6
240 // OCR3B - PC5
241 // OCR3C - PC4
242 // OCR1A - PB5
243 // OCR1B - PB6
244 // OCR1C - PB7
245
246 // Clock Select (CS3n) = 0b010 = Clock / 8
247#ifdef AUDIO1_PIN_SET
248 // initialize timer-counter
249 AUDIO1_TCCRxA = (0 << AUDIO1_COMxy1) | (0 << AUDIO1_COMxy0) | (1 << AUDIO1_WGMx1) | (0 << AUDIO1_WGMx0);
250 AUDIO1_TCCRxB = (1 << AUDIO1_WGMx3) | (1 << AUDIO1_WGMx2) | (0 << AUDIO1_CSx2) | (1 << AUDIO1_CSx1) | (0 << AUDIO1_CSx0);
251#endif
252
253#ifdef AUDIO2_PIN_SET
254 AUDIO2_TCCRxA = (0 << AUDIO2_COMxy1) | (0 << AUDIO2_COMxy0) | (1 << AUDIO2_WGMx1) | (0 << AUDIO2_WGMx0);
255 AUDIO2_TCCRxB = (1 << AUDIO2_WGMx3) | (1 << AUDIO2_WGMx2) | (0 << AUDIO2_CSx2) | (1 << AUDIO2_CSx1) | (0 << AUDIO2_CSx0);
256#endif
257}
258
259void audio_driver_stop() {
260#ifdef AUDIO1_PIN_SET
261 channel_1_stop();
262#endif
263
264#ifdef AUDIO2_PIN_SET
265 channel_2_stop();
266#endif
267}
268
269void audio_driver_start(void) {
270#ifdef AUDIO1_PIN_SET
271 channel_1_start();
272 if (playing_note) {
273 channel_1_set_frequency(audio_get_processed_frequency(0));
274 }
275#endif
276
277#if !defined(AUDIO1_PIN_SET) && defined(AUDIO2_PIN_SET)
278 channel_2_start();
279 if (playing_note) {
280 channel_2_set_frequency(audio_get_processed_frequency(0));
281 }
282#endif
283}
284
285static volatile uint32_t isr_counter = 0;
286#ifdef AUDIO1_PIN_SET
287ISR(AUDIO1_TIMERx_COMPy_vect) {
288 isr_counter++;
289 if (isr_counter < channel_1_frequency / (CPU_PRESCALER * 8)) return;
290
291 isr_counter = 0;
292 bool state_changed = audio_update_state();
293
294 if (!playing_note && !playing_melody) {
295 channel_1_stop();
296# ifdef AUDIO2_PIN_SET
297 channel_2_stop();
298# endif
299 return;
300 }
301
302 if (state_changed) {
303 channel_1_set_frequency(audio_get_processed_frequency(0));
304# ifdef AUDIO2_PIN_SET
305 if (audio_get_number_of_active_tones() > 1) {
306 channel_2_set_frequency(audio_get_processed_frequency(1));
307 } else {
308 channel_2_stop();
309 }
310# endif
311 }
312}
313#endif
314
315#if !defined(AUDIO1_PIN_SET) && defined(AUDIO2_PIN_SET)
316ISR(AUDIO2_TIMERx_COMPy_vect) {
317 isr_counter++;
318 if (isr_counter < channel_2_frequency / (CPU_PRESCALER * 8)) return;
319
320 isr_counter = 0;
321 bool state_changed = audio_update_state();
322
323 if (!playing_note && !playing_melody) {
324 channel_2_stop();
325 return;
326 }
327
328 if (state_changed) {
329 channel_2_set_frequency(audio_get_processed_frequency(0));
330 }
331}
332#endif
diff --git a/quantum/audio/driver_chibios_dac.h b/quantum/audio/driver_chibios_dac.h
deleted file mode 100644
index 07cd622ea..000000000
--- a/quantum/audio/driver_chibios_dac.h
+++ /dev/null
@@ -1,126 +0,0 @@
1/* Copyright 2019 Jack Humbert
2 * Copyright 2020 JohSchneider
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17#pragma once
18
19#ifndef A4
20# define A4 PAL_LINE(GPIOA, 4)
21#endif
22#ifndef A5
23# define A5 PAL_LINE(GPIOA, 5)
24#endif
25
26/**
27 * Size of the dac_buffer arrays. All must be the same size.
28 */
29#define AUDIO_DAC_BUFFER_SIZE 256U
30
31/**
32 * Highest value allowed sample value.
33
34 * since the DAC is limited to 12 bit, the absolute max is 0xfff = 4095U;
35 * lower values adjust the peak-voltage aka volume down.
36 * adjusting this value has only an effect on a sample-buffer whose values are
37 * are NOT pregenerated - see square-wave
38 */
39#ifndef AUDIO_DAC_SAMPLE_MAX
40# define AUDIO_DAC_SAMPLE_MAX 4095U
41#endif
42
43#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)
44# define AUDIO_DAC_QUALITY_SANE_MINIMUM
45#endif
46
47/**
48 * These presets allow you to quickly switch between quality settings for
49 * the DAC. The sample rate and maximum number of simultaneous tones roughly
50 * has an inverse relationship - slightly higher sample rates may be possible.
51 *
52 * NOTE: a high sample-rate results in a higher cpu-load, which might lead to
53 * (audible) discontinuities and/or starve other processes of cpu-time
54 * (like RGB-led back-lighting, ...)
55 */
56#ifdef AUDIO_DAC_QUALITY_VERY_LOW
57# define AUDIO_DAC_SAMPLE_RATE 11025U
58# define AUDIO_MAX_SIMULTANEOUS_TONES 8
59#endif
60
61#ifdef AUDIO_DAC_QUALITY_LOW
62# define AUDIO_DAC_SAMPLE_RATE 22050U
63# define AUDIO_MAX_SIMULTANEOUS_TONES 4
64#endif
65
66#ifdef AUDIO_DAC_QUALITY_HIGH
67# define AUDIO_DAC_SAMPLE_RATE 44100U
68# define AUDIO_MAX_SIMULTANEOUS_TONES 2
69#endif
70
71#ifdef AUDIO_DAC_QUALITY_VERY_HIGH
72# define AUDIO_DAC_SAMPLE_RATE 88200U
73# define AUDIO_MAX_SIMULTANEOUS_TONES 1
74#endif
75
76#ifdef AUDIO_DAC_QUALITY_SANE_MINIMUM
77/* a sane-minimum config: with a trade-off between cpu-load and tone-range
78 *
79 * the (currently) highest defined note is NOTE_B8 with 7902Hz; if we now
80 * aim for an even even multiple of the buffer-size, we end up with:
81 * ( roundUptoPow2(highest note / AUDIO_DAC_BUFFER_SIZE) * nyquist-rate * AUDIO_DAC_BUFFER_SIZE)
82 * 7902/256 = 30.867 * 2 * 256 ~= 16384
83 * which works out (but the 'scope shows some sampling artifacts with lower harmonics :-P)
84 */
85# define AUDIO_DAC_SAMPLE_RATE 16384U
86# define AUDIO_MAX_SIMULTANEOUS_TONES 8
87#endif
88
89/**
90 * Effective bit-rate of the DAC. 44.1khz is the standard for most audio - any
91 * lower will sacrifice perceptible audio quality. Any higher will limit the
92 * number of simultaneous tones. In most situations, a tenth (1/10) of the
93 * sample rate is where notes become unbearable.
94 */
95#ifndef AUDIO_DAC_SAMPLE_RATE
96# define AUDIO_DAC_SAMPLE_RATE 44100U
97#endif
98
99/**
100 * The number of tones that can be played simultaneously. If too high a value
101 * is used here, the keyboard will freeze and glitch-out when that many tones
102 * are being played.
103 */
104#ifndef AUDIO_MAX_SIMULTANEOUS_TONES
105# define AUDIO_MAX_SIMULTANEOUS_TONES 2
106#endif
107
108/**
109 * The default value of the DAC when not playing anything. Certain hardware
110 * setups may require a high (AUDIO_DAC_SAMPLE_MAX) or low (0) value here.
111 * Since multiple added sine waves tend to oscillate around the midpoint,
112 * and possibly never/rarely reach either 0 of MAX, 1/2 MAX can be a
113 * reasonable default value.
114 */
115#ifndef AUDIO_DAC_OFF_VALUE
116# define AUDIO_DAC_OFF_VALUE AUDIO_DAC_SAMPLE_MAX / 2
117#endif
118
119#if AUDIO_DAC_OFF_VALUE > AUDIO_DAC_SAMPLE_MAX
120# error "AUDIO_DAC: OFF_VALUE may not be larger than SAMPLE_MAX"
121#endif
122
123/**
124 *user overridable sample generation/processing
125 */
126uint16_t dac_value_generate(void);
diff --git a/quantum/audio/driver_chibios_dac_additive.c b/quantum/audio/driver_chibios_dac_additive.c
deleted file mode 100644
index db304adb8..000000000
--- a/quantum/audio/driver_chibios_dac_additive.c
+++ /dev/null
@@ -1,335 +0,0 @@
1/* Copyright 2016-2019 Jack Humbert
2 * Copyright 2020 JohSchneider
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "audio.h"
19#include <ch.h>
20#include <hal.h>
21
22/*
23 Audio Driver: DAC
24
25 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
26
27 it is also possible to have a custom sample-LUT by implementing/overriding 'dac_value_generate'
28
29 this driver allows for multiple simultaneous tones to be played through one single channel by doing additive wave-synthesis
30*/
31
32#if !defined(AUDIO_PIN)
33# error "Audio feature enabled, but no suitable pin selected as AUDIO_PIN - see docs/feature_audio under 'ARM (DAC additive)' for available options."
34#endif
35#if defined(AUDIO_PIN_ALT) && !defined(AUDIO_PIN_ALT_AS_NEGATIVE)
36# 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."
37#endif
38
39#if !defined(AUDIO_PIN_ALT)
40// no ALT pin defined is valid, but the c-ifs below need some value set
41# define AUDIO_PIN_ALT PAL_NOLINE
42#endif
43
44#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)
45# define AUDIO_DAC_SAMPLE_WAVEFORM_SINE
46#endif
47
48#ifdef AUDIO_DAC_SAMPLE_WAVEFORM_SINE
49/* one full sine wave over [0,2*pi], but shifted up one amplitude and left pi/4; for the samples to start at 0
50 */
51static const dacsample_t dac_buffer_sine[AUDIO_DAC_BUFFER_SIZE] = {
52 // 256 values, max 4095
53 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,
54 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};
55#endif // AUDIO_DAC_SAMPLE_WAVEFORM_SINE
56#ifdef AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE
57static const dacsample_t dac_buffer_triangle[AUDIO_DAC_BUFFER_SIZE] = {
58 // 256 values, max 4095
59 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,
60 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};
61#endif // AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE
62#ifdef AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE
63static const dacsample_t dac_buffer_square[AUDIO_DAC_BUFFER_SIZE] = {
64 [0 ... AUDIO_DAC_BUFFER_SIZE / 2 - 1] = 0, // first and
65 [AUDIO_DAC_BUFFER_SIZE / 2 ... AUDIO_DAC_BUFFER_SIZE - 1] = AUDIO_DAC_SAMPLE_MAX, // second half
66};
67#endif // AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE
68/*
69// four steps: 0, 1/3, 2/3 and 1
70static const dacsample_t dac_buffer_staircase[AUDIO_DAC_BUFFER_SIZE] = {
71 [0 ... AUDIO_DAC_BUFFER_SIZE/3 -1 ] = 0,
72 [AUDIO_DAC_BUFFER_SIZE / 4 ... AUDIO_DAC_BUFFER_SIZE / 2 -1 ] = AUDIO_DAC_SAMPLE_MAX / 3,
73 [AUDIO_DAC_BUFFER_SIZE / 2 ... 3 * AUDIO_DAC_BUFFER_SIZE / 4 -1 ] = 2 * AUDIO_DAC_SAMPLE_MAX / 3,
74 [3 * AUDIO_DAC_BUFFER_SIZE / 4 ... AUDIO_DAC_BUFFER_SIZE -1 ] = AUDIO_DAC_SAMPLE_MAX,
75}
76*/
77#ifdef AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID
78static 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,
79 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};
80#endif // AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID
81
82static dacsample_t dac_buffer_empty[AUDIO_DAC_BUFFER_SIZE] = {AUDIO_DAC_OFF_VALUE};
83
84/* keep track of the sample position for for each frequency */
85static float dac_if[AUDIO_MAX_SIMULTANEOUS_TONES] = {0.0};
86
87static float active_tones_snapshot[AUDIO_MAX_SIMULTANEOUS_TONES] = {0, 0};
88static uint8_t active_tones_snapshot_length = 0;
89
90typedef enum {
91 OUTPUT_SHOULD_START,
92 OUTPUT_RUN_NORMALLY,
93 // path 1: wait for zero, then change/update active tones
94 OUTPUT_TONES_CHANGED,
95 OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE,
96 // path 2: hardware should stop, wait for zero then turn output off = stop the timer
97 OUTPUT_SHOULD_STOP,
98 OUTPUT_REACHED_ZERO_BEFORE_OFF,
99 OUTPUT_OFF,
100 OUTPUT_OFF_1,
101 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
102 number_of_output_states
103} output_states_t;
104output_states_t state = OUTPUT_OFF_2;
105
106/**
107 * Generation of the waveform being passed to the callback. Declared weak so users
108 * can override it with their own wave-forms/noises.
109 */
110__attribute__((weak)) uint16_t dac_value_generate(void) {
111 // DAC is running/asking for values but snapshot length is zero -> must be playing a pause
112 if (active_tones_snapshot_length == 0) {
113 return AUDIO_DAC_OFF_VALUE;
114 }
115
116 /* doing additive wave synthesis over all currently playing tones = adding up
117 * sine-wave-samples for each frequency, scaled by the number of active tones
118 */
119 uint16_t value = 0;
120 float frequency = 0.0f;
121
122 for (uint8_t i = 0; i < active_tones_snapshot_length; i++) {
123 /* Note: a user implementation does not have to rely on the active_tones_snapshot, but
124 * could directly query the active frequencies through audio_get_processed_frequency */
125 frequency = active_tones_snapshot[i];
126
127 dac_if[i] = dac_if[i] + ((frequency * AUDIO_DAC_BUFFER_SIZE) / AUDIO_DAC_SAMPLE_RATE) * 2 / 3;
128 /*Note: the 2/3 are necessary to get the correct frequencies on the
129 * DAC output (as measured with an oscilloscope), since the gpt
130 * timer runs with 3*AUDIO_DAC_SAMPLE_RATE; and the DAC callback
131 * is called twice per conversion.*/
132
133 dac_if[i] = fmod(dac_if[i], AUDIO_DAC_BUFFER_SIZE);
134
135 // Wavetable generation/lookup
136 uint16_t dac_i = (uint16_t)dac_if[i];
137
138#if defined(AUDIO_DAC_SAMPLE_WAVEFORM_SINE)
139 value += dac_buffer_sine[dac_i] / active_tones_snapshot_length;
140#elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE)
141 value += dac_buffer_triangle[dac_i] / active_tones_snapshot_length;
142#elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID)
143 value += dac_buffer_trapezoid[dac_i] / active_tones_snapshot_length;
144#elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE)
145 value += dac_buffer_square[dac_i] / active_tones_snapshot_length;
146#endif
147 /*
148 // SINE
149 value += dac_buffer_sine[dac_i] / active_tones_snapshot_length / 3;
150 // TRIANGLE
151 value += dac_buffer_triangle[dac_i] / active_tones_snapshot_length / 3;
152 // SQUARE
153 value += dac_buffer_square[dac_i] / active_tones_snapshot_length / 3;
154 //NOTE: combination of these three wave-forms is more exemplary - and doesn't sound particularly good :-P
155 */
156
157 // STAIRS (mostly usefully as test-pattern)
158 // value_avg = dac_buffer_staircase[dac_i] / active_tones_snapshot_length;
159 }
160
161 return value;
162}
163
164/**
165 * DAC streaming callback. Does all of the main computing for playing songs.
166 *
167 * Note: chibios calls this CB twice: during the 'half buffer event', and the 'full buffer event'.
168 */
169static void dac_end(DACDriver *dacp) {
170 dacsample_t *sample_p = (dacp)->samples;
171
172 // work on the other half of the buffer
173 if (dacIsBufferComplete(dacp)) {
174 sample_p += AUDIO_DAC_BUFFER_SIZE / 2; // 'half_index'
175 }
176
177 for (uint8_t s = 0; s < AUDIO_DAC_BUFFER_SIZE / 2; s++) {
178 if (OUTPUT_OFF <= state) {
179 sample_p[s] = AUDIO_DAC_OFF_VALUE;
180 continue;
181 } else {
182 sample_p[s] = dac_value_generate();
183 }
184
185 /* zero crossing (or approach, whereas zero == DAC_OFF_VALUE, which can be configured to anything from 0 to DAC_SAMPLE_MAX)
186 * ============================*=*========================== AUDIO_DAC_SAMPLE_MAX
187 * * *
188 * * *
189 * ---------------------------------------------------------
190 * * * } AUDIO_DAC_SAMPLE_MAX/100
191 * --------------------------------------------------------- AUDIO_DAC_OFF_VALUE
192 * * * } AUDIO_DAC_SAMPLE_MAX/100
193 * ---------------------------------------------------------
194 * *
195 * * *
196 * * *
197 * =====*=*================================================= 0x0
198 */
199 if (((sample_p[s] + (AUDIO_DAC_SAMPLE_MAX / 100)) > AUDIO_DAC_OFF_VALUE) && // value approaches from below
200 (sample_p[s] < (AUDIO_DAC_OFF_VALUE + (AUDIO_DAC_SAMPLE_MAX / 100))) // or above
201 ) {
202 if ((OUTPUT_SHOULD_START == state) && (active_tones_snapshot_length > 0)) {
203 state = OUTPUT_RUN_NORMALLY;
204 } else if (OUTPUT_TONES_CHANGED == state) {
205 state = OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE;
206 } else if (OUTPUT_SHOULD_STOP == state) {
207 state = OUTPUT_REACHED_ZERO_BEFORE_OFF;
208 }
209 }
210
211 // still 'ramping up', reset the output to OFF_VALUE until the generated values reach that value, to do a smooth handover
212 if (OUTPUT_SHOULD_START == state) {
213 sample_p[s] = AUDIO_DAC_OFF_VALUE;
214 }
215
216 if ((OUTPUT_SHOULD_START == state) || (OUTPUT_REACHED_ZERO_BEFORE_OFF == state) || (OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE == state)) {
217 uint8_t active_tones = MIN(AUDIO_MAX_SIMULTANEOUS_TONES, audio_get_number_of_active_tones());
218 active_tones_snapshot_length = 0;
219 // update the snapshot - once, and only on occasion that something changed;
220 // -> saves cpu cycles (?)
221 for (uint8_t i = 0; i < active_tones; i++) {
222 float freq = audio_get_processed_frequency(i);
223 if (freq > 0) { // disregard 'rest' notes, with valid frequency 0.0f; which would only lower the resulting waveform volume during the additive synthesis step
224 active_tones_snapshot[active_tones_snapshot_length++] = freq;
225 }
226 }
227
228 if ((0 == active_tones_snapshot_length) && (OUTPUT_REACHED_ZERO_BEFORE_OFF == state)) {
229 state = OUTPUT_OFF;
230 }
231 if (OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE == state) {
232 state = OUTPUT_RUN_NORMALLY;
233 }
234 }
235 }
236
237 // update audio internal state (note position, current_note, ...)
238 if (audio_update_state()) {
239 if (OUTPUT_SHOULD_STOP != state) {
240 state = OUTPUT_TONES_CHANGED;
241 }
242 }
243
244 if (OUTPUT_OFF <= state) {
245 if (OUTPUT_OFF_2 == state) {
246 // stopping timer6 = stopping the DAC at whatever value it is currently pushing to the output = AUDIO_DAC_OFF_VALUE
247 gptStopTimer(&GPTD6);
248 } else {
249 state++;
250 }
251 }
252}
253
254static void dac_error(DACDriver *dacp, dacerror_t err) {
255 (void)dacp;
256 (void)err;
257
258 chSysHalt("DAC failure. halp");
259}
260
261static const GPTConfig gpt6cfg1 = {.frequency = AUDIO_DAC_SAMPLE_RATE * 3,
262 .callback = NULL,
263 .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */
264 .dier = 0U};
265
266static const DACConfig dac_conf = {.init = AUDIO_DAC_OFF_VALUE, .datamode = DAC_DHRM_12BIT_RIGHT};
267
268/**
269 * @note The DAC_TRG(0) here selects the Timer 6 TRGO event, which is triggered
270 * on the rising edge after 3 APB1 clock cycles, causing our gpt6cfg1.frequency
271 * to be a third of what we expect.
272 *
273 * Here are all the values for DAC_TRG (TSEL in the ref manual)
274 * TIM15_TRGO 0b011
275 * TIM2_TRGO 0b100
276 * TIM3_TRGO 0b001
277 * TIM6_TRGO 0b000
278 * TIM7_TRGO 0b010
279 * EXTI9 0b110
280 * SWTRIG 0b111
281 */
282static const DACConversionGroup dac_conv_cfg = {.num_channels = 1U, .end_cb = dac_end, .error_cb = dac_error, .trigger = DAC_TRG(0b000)};
283
284void audio_driver_initialize() {
285 if ((AUDIO_PIN == A4) || (AUDIO_PIN_ALT == A4)) {
286 palSetLineMode(A4, PAL_MODE_INPUT_ANALOG);
287 dacStart(&DACD1, &dac_conf);
288 }
289 if ((AUDIO_PIN == A5) || (AUDIO_PIN_ALT == A5)) {
290 palSetLineMode(A5, PAL_MODE_INPUT_ANALOG);
291 dacStart(&DACD2, &dac_conf);
292 }
293
294 /* enable the output buffer, to directly drive external loads with no additional circuitry
295 *
296 * see: AN4566 Application note: Extending the DAC performance of STM32 microcontrollers
297 * Note: Buffer-Off bit -> has to be set 0 to enable the output buffer
298 * Note: enabling the output buffer imparts an additional dc-offset of a couple mV
299 *
300 * this is done here, reaching directly into the stm32 registers since chibios has not implemented BOFF handling yet
301 * (see: chibios/os/hal/ports/STM32/todo.txt '- BOFF handling in DACv1.'
302 */
303 DACD1.params->dac->CR &= ~DAC_CR_BOFF1;
304 DACD2.params->dac->CR &= ~DAC_CR_BOFF2;
305
306 if (AUDIO_PIN == A4) {
307 dacStartConversion(&DACD1, &dac_conv_cfg, dac_buffer_empty, AUDIO_DAC_BUFFER_SIZE);
308 } else if (AUDIO_PIN == A5) {
309 dacStartConversion(&DACD2, &dac_conv_cfg, dac_buffer_empty, AUDIO_DAC_BUFFER_SIZE);
310 }
311
312 // no inverted/out-of-phase waveform (yet?), only pulling AUDIO_PIN_ALT to AUDIO_DAC_OFF_VALUE
313#if defined(AUDIO_PIN_ALT_AS_NEGATIVE)
314 if (AUDIO_PIN_ALT == A4) {
315 dacPutChannelX(&DACD1, 0, AUDIO_DAC_OFF_VALUE);
316 } else if (AUDIO_PIN_ALT == A5) {
317 dacPutChannelX(&DACD2, 0, AUDIO_DAC_OFF_VALUE);
318 }
319#endif
320
321 gptStart(&GPTD6, &gpt6cfg1);
322}
323
324void audio_driver_stop(void) { state = OUTPUT_SHOULD_STOP; }
325
326void audio_driver_start(void) {
327 gptStartContinuous(&GPTD6, 2U);
328
329 for (uint8_t i = 0; i < AUDIO_MAX_SIMULTANEOUS_TONES; i++) {
330 dac_if[i] = 0.0f;
331 active_tones_snapshot[i] = 0.0f;
332 }
333 active_tones_snapshot_length = 0;
334 state = OUTPUT_SHOULD_START;
335}
diff --git a/quantum/audio/driver_chibios_dac_basic.c b/quantum/audio/driver_chibios_dac_basic.c
deleted file mode 100644
index fac651350..000000000
--- a/quantum/audio/driver_chibios_dac_basic.c
+++ /dev/null
@@ -1,245 +0,0 @@
1/* Copyright 2016-2020 Jack Humbert
2 * Copyright 2020 JohSchneider
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "audio.h"
19#include "ch.h"
20#include "hal.h"
21
22/*
23 Audio Driver: DAC
24
25 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
26
27 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
28 OR
29 one speaker wired to A4+A5 with the AUDIO_PIN_ALT_AS_NEGATIVE define set - see docs/feature_audio
30
31*/
32
33#if !defined(AUDIO_PIN)
34# pragma message "Audio feature enabled, but no suitable pin selected as AUDIO_PIN - see docs/feature_audio under 'ARM (DAC basic)' for available options."
35// 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
36# define AUDIO_PIN A5
37#endif
38// check configuration for ONE speaker, connected to both DAC pins
39#if defined(AUDIO_PIN_ALT_AS_NEGATIVE) && !defined(AUDIO_PIN_ALT)
40# error "Audio feature: AUDIO_PIN_ALT_AS_NEGATIVE set, but no pin configured as AUDIO_PIN_ALT"
41#endif
42
43#ifndef AUDIO_PIN_ALT
44// no ALT pin defined is valid, but the c-ifs below need some value set
45# define AUDIO_PIN_ALT -1
46#endif
47
48#if !defined(AUDIO_STATE_TIMER)
49# define AUDIO_STATE_TIMER GPTD8
50#endif
51
52// square-wave
53static const dacsample_t dac_buffer_1[AUDIO_DAC_BUFFER_SIZE] = {
54 // First half is max, second half is 0
55 [0 ... AUDIO_DAC_BUFFER_SIZE / 2 - 1] = AUDIO_DAC_SAMPLE_MAX,
56 [AUDIO_DAC_BUFFER_SIZE / 2 ... AUDIO_DAC_BUFFER_SIZE - 1] = 0,
57};
58
59// square-wave
60static const dacsample_t dac_buffer_2[AUDIO_DAC_BUFFER_SIZE] = {
61 // opposite of dac_buffer above
62 [0 ... AUDIO_DAC_BUFFER_SIZE / 2 - 1] = 0,
63 [AUDIO_DAC_BUFFER_SIZE / 2 ... AUDIO_DAC_BUFFER_SIZE - 1] = AUDIO_DAC_SAMPLE_MAX,
64};
65
66GPTConfig gpt6cfg1 = {.frequency = AUDIO_DAC_SAMPLE_RATE,
67 .callback = NULL,
68 .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */
69 .dier = 0U};
70GPTConfig gpt7cfg1 = {.frequency = AUDIO_DAC_SAMPLE_RATE,
71 .callback = NULL,
72 .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */
73 .dier = 0U};
74
75static void gpt_audio_state_cb(GPTDriver *gptp);
76GPTConfig gptStateUpdateCfg = {.frequency = 10,
77 .callback = gpt_audio_state_cb,
78 .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */
79 .dier = 0U};
80
81static const DACConfig dac_conf_ch1 = {.init = AUDIO_DAC_OFF_VALUE, .datamode = DAC_DHRM_12BIT_RIGHT};
82static const DACConfig dac_conf_ch2 = {.init = AUDIO_DAC_OFF_VALUE, .datamode = DAC_DHRM_12BIT_RIGHT};
83
84/**
85 * @note The DAC_TRG(0) here selects the Timer 6 TRGO event, which is triggered
86 * on the rising edge after 3 APB1 clock cycles, causing our gpt6cfg1.frequency
87 * to be a third of what we expect.
88 *
89 * Here are all the values for DAC_TRG (TSEL in the ref manual)
90 * TIM15_TRGO 0b011
91 * TIM2_TRGO 0b100
92 * TIM3_TRGO 0b001
93 * TIM6_TRGO 0b000
94 * TIM7_TRGO 0b010
95 * EXTI9 0b110
96 * SWTRIG 0b111
97 */
98static const DACConversionGroup dac_conv_grp_ch1 = {.num_channels = 1U, .trigger = DAC_TRG(0b000)};
99static const DACConversionGroup dac_conv_grp_ch2 = {.num_channels = 1U, .trigger = DAC_TRG(0b010)};
100
101void channel_1_start(void) {
102 gptStart(&GPTD6, &gpt6cfg1);
103 gptStartContinuous(&GPTD6, 2U);
104 palSetPadMode(GPIOA, 4, PAL_MODE_INPUT_ANALOG);
105}
106
107void channel_1_stop(void) {
108 gptStopTimer(&GPTD6);
109 palSetPadMode(GPIOA, 4, PAL_MODE_OUTPUT_PUSHPULL);
110 palSetPad(GPIOA, 4);
111}
112
113static float channel_1_frequency = 0.0f;
114void channel_1_set_frequency(float freq) {
115 channel_1_frequency = freq;
116
117 channel_1_stop();
118 if (freq <= 0.0) // a pause/rest has freq=0
119 return;
120
121 gpt6cfg1.frequency = 2 * freq * AUDIO_DAC_BUFFER_SIZE;
122 channel_1_start();
123}
124float channel_1_get_frequency(void) { return channel_1_frequency; }
125
126void channel_2_start(void) {
127 gptStart(&GPTD7, &gpt7cfg1);
128 gptStartContinuous(&GPTD7, 2U);
129 palSetPadMode(GPIOA, 5, PAL_MODE_INPUT_ANALOG);
130}
131
132void channel_2_stop(void) {
133 gptStopTimer(&GPTD7);
134 palSetPadMode(GPIOA, 5, PAL_MODE_OUTPUT_PUSHPULL);
135 palSetPad(GPIOA, 5);
136}
137
138static float channel_2_frequency = 0.0f;
139void channel_2_set_frequency(float freq) {
140 channel_2_frequency = freq;
141
142 channel_2_stop();
143 if (freq <= 0.0) // a pause/rest has freq=0
144 return;
145
146 gpt7cfg1.frequency = 2 * freq * AUDIO_DAC_BUFFER_SIZE;
147 channel_2_start();
148}
149float channel_2_get_frequency(void) { return channel_2_frequency; }
150
151static void gpt_audio_state_cb(GPTDriver *gptp) {
152 if (audio_update_state()) {
153#if defined(AUDIO_PIN_ALT_AS_NEGATIVE)
154 // one piezo/speaker connected to both audio pins, the generated square-waves are inverted
155 channel_1_set_frequency(audio_get_processed_frequency(0));
156 channel_2_set_frequency(audio_get_processed_frequency(0));
157
158#else // two separate audio outputs/speakers
159 // primary speaker on A4, optional secondary on A5
160 if (AUDIO_PIN == A4) {
161 channel_1_set_frequency(audio_get_processed_frequency(0));
162 if (AUDIO_PIN_ALT == A5) {
163 if (audio_get_number_of_active_tones() > 1) {
164 channel_2_set_frequency(audio_get_processed_frequency(1));
165 } else {
166 channel_2_stop();
167 }
168 }
169 }
170
171 // primary speaker on A5, optional secondary on A4
172 if (AUDIO_PIN == A5) {
173 channel_2_set_frequency(audio_get_processed_frequency(0));
174 if (AUDIO_PIN_ALT == A4) {
175 if (audio_get_number_of_active_tones() > 1) {
176 channel_1_set_frequency(audio_get_processed_frequency(1));
177 } else {
178 channel_1_stop();
179 }
180 }
181 }
182#endif
183 }
184}
185
186void audio_driver_initialize() {
187 if ((AUDIO_PIN == A4) || (AUDIO_PIN_ALT == A4)) {
188 palSetPadMode(GPIOA, 4, PAL_MODE_INPUT_ANALOG);
189 dacStart(&DACD1, &dac_conf_ch1);
190
191 // initial setup of the dac-triggering timer is still required, even
192 // though it gets reconfigured and restarted later on
193 gptStart(&GPTD6, &gpt6cfg1);
194 }
195
196 if ((AUDIO_PIN == A5) || (AUDIO_PIN_ALT == A5)) {
197 palSetPadMode(GPIOA, 5, PAL_MODE_INPUT_ANALOG);
198 dacStart(&DACD2, &dac_conf_ch2);
199
200 gptStart(&GPTD7, &gpt7cfg1);
201 }
202
203 /* enable the output buffer, to directly drive external loads with no additional circuitry
204 *
205 * see: AN4566 Application note: Extending the DAC performance of STM32 microcontrollers
206 * Note: Buffer-Off bit -> has to be set 0 to enable the output buffer
207 * Note: enabling the output buffer imparts an additional dc-offset of a couple mV
208 *
209 * this is done here, reaching directly into the stm32 registers since chibios has not implemented BOFF handling yet
210 * (see: chibios/os/hal/ports/STM32/todo.txt '- BOFF handling in DACv1.'
211 */
212 DACD1.params->dac->CR &= ~DAC_CR_BOFF1;
213 DACD2.params->dac->CR &= ~DAC_CR_BOFF2;
214
215 // start state-updater
216 gptStart(&AUDIO_STATE_TIMER, &gptStateUpdateCfg);
217}
218
219void audio_driver_stop(void) {
220 if ((AUDIO_PIN == A4) || (AUDIO_PIN_ALT == A4)) {
221 gptStopTimer(&GPTD6);
222
223 // stop the ongoing conversion and put the output in a known state
224 dacStopConversion(&DACD1);
225 dacPutChannelX(&DACD1, 0, AUDIO_DAC_OFF_VALUE);
226 }
227
228 if ((AUDIO_PIN == A5) || (AUDIO_PIN_ALT == A5)) {
229 gptStopTimer(&GPTD7);
230
231 dacStopConversion(&DACD2);
232 dacPutChannelX(&DACD2, 0, AUDIO_DAC_OFF_VALUE);
233 }
234 gptStopTimer(&AUDIO_STATE_TIMER);
235}
236
237void audio_driver_start(void) {
238 if ((AUDIO_PIN == A4) || (AUDIO_PIN_ALT == A4)) {
239 dacStartConversion(&DACD1, &dac_conv_grp_ch1, (dacsample_t *)dac_buffer_1, AUDIO_DAC_BUFFER_SIZE);
240 }
241 if ((AUDIO_PIN == A5) || (AUDIO_PIN_ALT == A5)) {
242 dacStartConversion(&DACD2, &dac_conv_grp_ch2, (dacsample_t *)dac_buffer_2, AUDIO_DAC_BUFFER_SIZE);
243 }
244 gptStartContinuous(&AUDIO_STATE_TIMER, 2U);
245}
diff --git a/quantum/audio/driver_chibios_pwm.h b/quantum/audio/driver_chibios_pwm.h
deleted file mode 100644
index 86cab916e..000000000
--- a/quantum/audio/driver_chibios_pwm.h
+++ /dev/null
@@ -1,40 +0,0 @@
1/* Copyright 2020 Jack Humbert
2 * Copyright 2020 JohSchneider
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17#pragma once
18
19#if !defined(AUDIO_PWM_DRIVER)
20// NOTE: Timer2 seems to be used otherwise in QMK, otherwise we could default to A5 (= TIM2_CH1, with PWMD2 and alternate-function(1))
21# define AUDIO_PWM_DRIVER PWMD1
22#endif
23
24#if !defined(AUDIO_PWM_CHANNEL)
25// NOTE: sticking to the STM data-sheet numbering: TIMxCH1 to TIMxCH4
26// default: STM32F303CC PA8+TIM1_CH1 -> 1
27# define AUDIO_PWM_CHANNEL 1
28#endif
29
30#if !defined(AUDIO_PWM_PAL_MODE)
31// pin-alternate function: see the data-sheet for which pin needs what AF to connect to TIMx_CHy
32// default: STM32F303CC PA8+TIM1_CH1 -> 6
33# define AUDIO_PWM_PAL_MODE 6
34#endif
35
36#if !defined(AUDIO_STATE_TIMER)
37// timer used to trigger updates in the audio-system, configured/enabled in chibios mcuconf.
38// 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)
39# define AUDIO_STATE_TIMER GPTD6
40#endif
diff --git a/quantum/audio/driver_chibios_pwm_hardware.c b/quantum/audio/driver_chibios_pwm_hardware.c
deleted file mode 100644
index 3c7d89b29..000000000
--- a/quantum/audio/driver_chibios_pwm_hardware.c
+++ /dev/null
@@ -1,144 +0,0 @@
1/* Copyright 2020 Jack Humbert
2 * Copyright 2020 JohSchneider
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18/*
19Audio Driver: PWM
20
21the duty-cycle is always kept at 50%, and the pwm-period is adjusted to match the frequency of a note to be played back.
22
23this driver uses the chibios-PWM system to produce a square-wave on specific output pins that are connected to the PWM hardware.
24The 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.
25
26 */
27
28#include "audio.h"
29#include "ch.h"
30#include "hal.h"
31
32#if !defined(AUDIO_PIN)
33# error "Audio feature enabled, but no pin selected - see docs/feature_audio under the ARM PWM settings"
34#endif
35
36extern bool playing_note;
37extern bool playing_melody;
38extern uint8_t note_timbre;
39
40static PWMConfig pwmCFG = {
41 .frequency = 100000, /* PWM clock frequency */
42 // 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
43 .period = 2, /* initial PWM period (in ticks) 1S (1/10kHz=0.1mS 0.1ms*10000 ticks=1S) */
44 .callback = NULL, /* no callback, the hardware directly toggles the pin */
45 .channels =
46 {
47#if AUDIO_PWM_CHANNEL == 4
48 {PWM_OUTPUT_DISABLED, NULL}, /* channel 0 -> TIMx_CH1 */
49 {PWM_OUTPUT_DISABLED, NULL}, /* channel 1 -> TIMx_CH2 */
50 {PWM_OUTPUT_DISABLED, NULL}, /* channel 2 -> TIMx_CH3 */
51 {PWM_OUTPUT_ACTIVE_HIGH, NULL} /* channel 3 -> TIMx_CH4 */
52#elif AUDIO_PWM_CHANNEL == 3
53 {PWM_OUTPUT_DISABLED, NULL},
54 {PWM_OUTPUT_DISABLED, NULL},
55 {PWM_OUTPUT_ACTIVE_HIGH, NULL}, /* TIMx_CH3 */
56 {PWM_OUTPUT_DISABLED, NULL}
57#elif AUDIO_PWM_CHANNEL == 2
58 {PWM_OUTPUT_DISABLED, NULL},
59 {PWM_OUTPUT_ACTIVE_HIGH, NULL}, /* TIMx_CH2 */
60 {PWM_OUTPUT_DISABLED, NULL},
61 {PWM_OUTPUT_DISABLED, NULL}
62#else /*fallback to CH1 */
63 {PWM_OUTPUT_ACTIVE_HIGH, NULL}, /* TIMx_CH1 */
64 {PWM_OUTPUT_DISABLED, NULL},
65 {PWM_OUTPUT_DISABLED, NULL},
66 {PWM_OUTPUT_DISABLED, NULL}
67#endif
68 },
69};
70
71static float channel_1_frequency = 0.0f;
72void channel_1_set_frequency(float freq) {
73 channel_1_frequency = freq;
74
75 if (freq <= 0.0) // a pause/rest has freq=0
76 return;
77
78 pwmcnt_t period = (pwmCFG.frequency / freq);
79 pwmChangePeriod(&AUDIO_PWM_DRIVER, period);
80 pwmEnableChannel(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1,
81 // adjust the duty-cycle so that the output is for 'note_timbre' duration HIGH
82 PWM_PERCENTAGE_TO_WIDTH(&AUDIO_PWM_DRIVER, (100 - note_timbre) * 100));
83}
84
85float channel_1_get_frequency(void) { return channel_1_frequency; }
86
87void channel_1_start(void) {
88 pwmStop(&AUDIO_PWM_DRIVER);
89 pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG);
90}
91
92void channel_1_stop(void) { pwmStop(&AUDIO_PWM_DRIVER); }
93
94static void gpt_callback(GPTDriver *gptp);
95GPTConfig gptCFG = {
96 /* a whole note is one beat, which is - per definition in musical_notes.h - set to 64
97 the longest note is BREAVE_DOT=128+64=192, the shortest SIXTEENTH=4
98 the tempo (which might vary!) is in bpm (beats per minute)
99 therefore: if the timer ticks away at .frequency = (60*64)Hz,
100 and the .interval counts from 64 downwards - audio_update_state is
101 called just often enough to not miss any notes
102 */
103 .frequency = 60 * 64,
104 .callback = gpt_callback,
105};
106
107void audio_driver_initialize(void) {
108 pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG);
109
110 // connect the AUDIO_PIN to the PWM hardware
111#if defined(USE_GPIOV1) // STM32F103C8
112 palSetLineMode(AUDIO_PIN, PAL_MODE_STM32_ALTERNATE_PUSHPULL);
113#else // GPIOv2 (or GPIOv3 for f4xx, which is the same/compatible at this command)
114 palSetLineMode(AUDIO_PIN, PAL_STM32_MODE_ALTERNATE | PAL_STM32_ALTERNATE(AUDIO_PWM_PAL_MODE));
115#endif
116
117 gptStart(&AUDIO_STATE_TIMER, &gptCFG);
118}
119
120void audio_driver_start(void) {
121 channel_1_stop();
122 channel_1_start();
123
124 if (playing_note || playing_melody) {
125 gptStartContinuous(&AUDIO_STATE_TIMER, 64);
126 }
127}
128
129void audio_driver_stop(void) {
130 channel_1_stop();
131 gptStopTimer(&AUDIO_STATE_TIMER);
132}
133
134/* a regular timer task, that checks the note to be currently played
135 * and updates the pwm to output that frequency
136 */
137static void gpt_callback(GPTDriver *gptp) {
138 float freq; // TODO: freq_alt
139
140 if (audio_update_state()) {
141 freq = audio_get_processed_frequency(0); // freq_alt would be index=1
142 channel_1_set_frequency(freq);
143 }
144}
diff --git a/quantum/audio/driver_chibios_pwm_software.c b/quantum/audio/driver_chibios_pwm_software.c
deleted file mode 100644
index 15c3e98b6..000000000
--- a/quantum/audio/driver_chibios_pwm_software.c
+++ /dev/null
@@ -1,164 +0,0 @@
1/* Copyright 2020 Jack Humbert
2 * Copyright 2020 JohSchneider
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18/*
19Audio Driver: PWM
20
21the duty-cycle is always kept at 50%, and the pwm-period is adjusted to match the frequency of a note to be played back.
22
23this driver uses the chibios-PWM system to produce a square-wave on any given output pin in software
24- a pwm callback is used to set/clear the configured pin.
25
26 */
27#include "audio.h"
28#include "ch.h"
29#include "hal.h"
30
31#if !defined(AUDIO_PIN)
32# error "Audio feature enabled, but no pin selected - see docs/feature_audio under the ARM PWM settings"
33#endif
34extern bool playing_note;
35extern bool playing_melody;
36extern uint8_t note_timbre;
37
38static void pwm_audio_period_callback(PWMDriver *pwmp);
39static void pwm_audio_channel_interrupt_callback(PWMDriver *pwmp);
40
41static PWMConfig pwmCFG = {
42 .frequency = 100000, /* PWM clock frequency */
43 // 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
44 .period = 2, /* initial PWM period (in ticks) 1S (1/10kHz=0.1mS 0.1ms*10000 ticks=1S) */
45 .callback = pwm_audio_period_callback,
46 .channels =
47 {
48 // software-PWM just needs another callback on any channel
49 {PWM_OUTPUT_ACTIVE_HIGH, pwm_audio_channel_interrupt_callback}, /* channel 0 -> TIMx_CH1 */
50 {PWM_OUTPUT_DISABLED, NULL}, /* channel 1 -> TIMx_CH2 */
51 {PWM_OUTPUT_DISABLED, NULL}, /* channel 2 -> TIMx_CH3 */
52 {PWM_OUTPUT_DISABLED, NULL} /* channel 3 -> TIMx_CH4 */
53 },
54};
55
56static float channel_1_frequency = 0.0f;
57void channel_1_set_frequency(float freq) {
58 channel_1_frequency = freq;
59
60 if (freq <= 0.0) // a pause/rest has freq=0
61 return;
62
63 pwmcnt_t period = (pwmCFG.frequency / freq);
64 pwmChangePeriod(&AUDIO_PWM_DRIVER, period);
65
66 pwmEnableChannel(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1,
67 // adjust the duty-cycle so that the output is for 'note_timbre' duration HIGH
68 PWM_PERCENTAGE_TO_WIDTH(&AUDIO_PWM_DRIVER, (100 - note_timbre) * 100));
69}
70
71float channel_1_get_frequency(void) { return channel_1_frequency; }
72
73void channel_1_start(void) {
74 pwmStop(&AUDIO_PWM_DRIVER);
75 pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG);
76
77 pwmEnablePeriodicNotification(&AUDIO_PWM_DRIVER);
78 pwmEnableChannelNotification(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1);
79}
80
81void channel_1_stop(void) {
82 pwmStop(&AUDIO_PWM_DRIVER);
83
84 palClearLine(AUDIO_PIN); // leave the line low, after last note was played
85
86#if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE)
87 palClearLine(AUDIO_PIN_ALT); // leave the line low, after last note was played
88#endif
89}
90
91// generate a PWM signal on any pin, not necessarily the one connected to the timer
92static void pwm_audio_period_callback(PWMDriver *pwmp) {
93 (void)pwmp;
94 palClearLine(AUDIO_PIN);
95
96#if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE)
97 palSetLine(AUDIO_PIN_ALT);
98#endif
99}
100static void pwm_audio_channel_interrupt_callback(PWMDriver *pwmp) {
101 (void)pwmp;
102 if (channel_1_frequency > 0) {
103 palSetLine(AUDIO_PIN); // generate a PWM signal on any pin, not necessarily the one connected to the timer
104#if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE)
105 palClearLine(AUDIO_PIN_ALT);
106#endif
107 }
108}
109
110static void gpt_callback(GPTDriver *gptp);
111GPTConfig gptCFG = {
112 /* a whole note is one beat, which is - per definition in musical_notes.h - set to 64
113 the longest note is BREAVE_DOT=128+64=192, the shortest SIXTEENTH=4
114 the tempo (which might vary!) is in bpm (beats per minute)
115 therefore: if the timer ticks away at .frequency = (60*64)Hz,
116 and the .interval counts from 64 downwards - audio_update_state is
117 called just often enough to not miss anything
118 */
119 .frequency = 60 * 64,
120 .callback = gpt_callback,
121};
122
123void audio_driver_initialize(void) {
124 pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG);
125
126 palSetLineMode(AUDIO_PIN, PAL_MODE_OUTPUT_PUSHPULL);
127 palClearLine(AUDIO_PIN);
128
129#if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE)
130 palSetLineMode(AUDIO_PIN_ALT, PAL_MODE_OUTPUT_PUSHPULL);
131 palClearLine(AUDIO_PIN_ALT);
132#endif
133
134 pwmEnablePeriodicNotification(&AUDIO_PWM_DRIVER); // enable pwm callbacks
135 pwmEnableChannelNotification(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1);
136
137 gptStart(&AUDIO_STATE_TIMER, &gptCFG);
138}
139
140void audio_driver_start(void) {
141 channel_1_stop();
142 channel_1_start();
143
144 if (playing_note || playing_melody) {
145 gptStartContinuous(&AUDIO_STATE_TIMER, 64);
146 }
147}
148
149void audio_driver_stop(void) {
150 channel_1_stop();
151 gptStopTimer(&AUDIO_STATE_TIMER);
152}
153
154/* a regular timer task, that checks the note to be currently played
155 * and updates the pwm to output that frequency
156 */
157static void gpt_callback(GPTDriver *gptp) {
158 float freq; // TODO: freq_alt
159
160 if (audio_update_state()) {
161 freq = audio_get_processed_frequency(0); // freq_alt would be index=1
162 channel_1_set_frequency(freq);
163 }
164}
diff --git a/quantum/audio/song_list.h b/quantum/audio/song_list.h
index b54b397e1..8e80a016a 100644
--- a/quantum/audio/song_list.h
+++ b/quantum/audio/song_list.h
@@ -20,11 +20,9 @@
20 20
21#include "musical_notes.h" 21#include "musical_notes.h"
22 22
23#if __GNUC__ > 5 // don't use for older gcc compilers since check isn't supported. 23#if __has_include("user_song_list.h")
24# if __has_include("user_song_list.h") 24# include "user_song_list.h"
25# include "user_song_list.h" 25#endif // if file exists
26# endif // if file exists
27#endif // __GNUC__
28 26
29#define NO_SOUND 27#define NO_SOUND
30 28
diff --git a/quantum/backlight/backlight_chibios.c b/quantum/backlight/backlight_chibios.c
index 4d5a69e14..7c6edd10d 100644
--- a/quantum/backlight/backlight_chibios.c
+++ b/quantum/backlight/backlight_chibios.c
@@ -8,9 +8,13 @@
8# define BACKLIGHT_LIMIT_VAL 255 8# define BACKLIGHT_LIMIT_VAL 255
9#endif 9#endif
10 10
11// GPIOV2 && GPIOV3
12#ifndef BACKLIGHT_PAL_MODE 11#ifndef BACKLIGHT_PAL_MODE
13# define BACKLIGHT_PAL_MODE 2 12# if defined(USE_GPIOV1)
13# define BACKLIGHT_PAL_MODE PAL_MODE_ALTERNATE_PUSHPULL
14# else
15// GPIOV2 && GPIOV3
16# define BACKLIGHT_PAL_MODE 5
17# endif
14#endif 18#endif
15 19
16// GENERIC 20// GENERIC
@@ -70,7 +74,7 @@ static uint32_t rescale_limit_val(uint32_t val) {
70 74
71void backlight_init_ports(void) { 75void backlight_init_ports(void) {
72#ifdef USE_GPIOV1 76#ifdef USE_GPIOV1
73 palSetPadMode(PAL_PORT(BACKLIGHT_PIN), PAL_PAD(BACKLIGHT_PIN), PAL_MODE_STM32_ALTERNATE_PUSHPULL); 77 palSetPadMode(PAL_PORT(BACKLIGHT_PIN), PAL_PAD(BACKLIGHT_PIN), BACKLIGHT_PAL_MODE);
74#else 78#else
75 palSetPadMode(PAL_PORT(BACKLIGHT_PIN), PAL_PAD(BACKLIGHT_PIN), PAL_MODE_ALTERNATE(BACKLIGHT_PAL_MODE)); 79 palSetPadMode(PAL_PORT(BACKLIGHT_PIN), PAL_PAD(BACKLIGHT_PIN), PAL_MODE_ALTERNATE(BACKLIGHT_PAL_MODE));
76#endif 80#endif
diff --git a/quantum/eeconfig.c b/quantum/eeconfig.c
index 92f0ac443..4c2ad2490 100644
--- a/quantum/eeconfig.c
+++ b/quantum/eeconfig.c
@@ -4,11 +4,6 @@
4#include "eeconfig.h" 4#include "eeconfig.h"
5#include "action_layer.h" 5#include "action_layer.h"
6 6
7#ifdef STM32_EEPROM_ENABLE
8# include <hal.h>
9# include "eeprom_stm32.h"
10#endif
11
12#if defined(EEPROM_DRIVER) 7#if defined(EEPROM_DRIVER)
13# include "eeprom_driver.h" 8# include "eeprom_driver.h"
14#endif 9#endif
@@ -43,9 +38,6 @@ __attribute__((weak)) void eeconfig_init_kb(void) {
43 * FIXME: needs doc 38 * FIXME: needs doc
44 */ 39 */
45void eeconfig_init_quantum(void) { 40void eeconfig_init_quantum(void) {
46#ifdef STM32_EEPROM_ENABLE
47 EEPROM_Erase();
48#endif
49#if defined(EEPROM_DRIVER) 41#if defined(EEPROM_DRIVER)
50 eeprom_driver_erase(); 42 eeprom_driver_erase();
51#endif 43#endif
@@ -111,9 +103,6 @@ void eeconfig_enable(void) { eeprom_update_word(EECONFIG_MAGIC, EECONFIG_MAGIC_N
111 * FIXME: needs doc 103 * FIXME: needs doc
112 */ 104 */
113void eeconfig_disable(void) { 105void eeconfig_disable(void) {
114#ifdef STM32_EEPROM_ENABLE
115 EEPROM_Erase();
116#endif
117#if defined(EEPROM_DRIVER) 106#if defined(EEPROM_DRIVER)
118 eeprom_driver_erase(); 107 eeprom_driver_erase();
119#endif 108#endif
diff --git a/quantum/eeconfig.h b/quantum/eeconfig.h
index bd39971b2..22d874273 100644
--- a/quantum/eeconfig.h
+++ b/quantum/eeconfig.h
@@ -111,3 +111,29 @@ void eeconfig_update_haptic(uint32_t val);
111 111
112bool eeconfig_read_handedness(void); 112bool eeconfig_read_handedness(void);
113void eeconfig_update_handedness(bool val); 113void eeconfig_update_handedness(bool val);
114
115#define EECONFIG_DEBOUNCE_HELPER(name, offset, config) \
116 static uint8_t dirty_##name = false; \
117 \
118 static inline void eeconfig_init_##name(void) { \
119 eeprom_read_block(&config, offset, sizeof(config)); \
120 dirty_##name = false; \
121 } \
122 static inline void eeconfig_flush_##name(bool force) { \
123 if (force || dirty_##name) { \
124 eeprom_update_block(&config, offset, sizeof(config)); \
125 dirty_##name = false; \
126 } \
127 } \
128 static inline void eeconfig_flush_##name##_task(uint16_t timeout) { \
129 static uint16_t flush_timer = 0; \
130 if (timer_elapsed(flush_timer) > timeout) { \
131 eeconfig_flush_##name(false); \
132 flush_timer = timer_read(); \
133 } \
134 } \
135 static inline void eeconfig_flag_##name(bool v) { dirty_##name |= v; } \
136 static inline void eeconfig_write_##name(typeof(config) conf) { \
137 memcpy(&config, &conf, sizeof(config)); \
138 eeconfig_flag_##name(true); \
139 }
diff --git a/quantum/keyboard.c b/quantum/keyboard.c
index b98fc64e4..f2a0889c1 100644
--- a/quantum/keyboard.c
+++ b/quantum/keyboard.c
@@ -43,9 +43,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
43#ifdef SERIAL_MOUSE_ENABLE 43#ifdef SERIAL_MOUSE_ENABLE
44# include "serial_mouse.h" 44# include "serial_mouse.h"
45#endif 45#endif
46#ifdef ADB_MOUSE_ENABLE
47# include "adb.h"
48#endif
49#ifdef RGBLIGHT_ENABLE 46#ifdef RGBLIGHT_ENABLE
50# include "rgblight.h" 47# include "rgblight.h"
51#endif 48#endif
@@ -61,12 +58,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
61#ifdef STENO_ENABLE 58#ifdef STENO_ENABLE
62# include "process_steno.h" 59# include "process_steno.h"
63#endif 60#endif
64#ifdef SERIAL_LINK_ENABLE
65# include "serial_link/system/serial_link.h"
66#endif
67#ifdef VISUALIZER_ENABLE
68# include "visualizer/visualizer.h"
69#endif
70#ifdef POINTING_DEVICE_ENABLE 61#ifdef POINTING_DEVICE_ENABLE
71# include "pointing_device.h" 62# include "pointing_device.h"
72#endif 63#endif
@@ -76,12 +67,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
76#ifdef JOYSTICK_ENABLE 67#ifdef JOYSTICK_ENABLE
77# include "process_joystick.h" 68# include "process_joystick.h"
78#endif 69#endif
70#ifdef PROGRAMMABLE_BUTTON_ENABLE
71# include "programmable_button.h"
72#endif
79#ifdef HD44780_ENABLE 73#ifdef HD44780_ENABLE
80# include "hd44780.h" 74# include "hd44780.h"
81#endif 75#endif
82#ifdef QWIIC_ENABLE
83# include "qwiic.h"
84#endif
85#ifdef OLED_ENABLE 76#ifdef OLED_ENABLE
86# include "oled_driver.h" 77# include "oled_driver.h"
87#endif 78#endif
@@ -97,9 +88,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
97#ifdef DIP_SWITCH_ENABLE 88#ifdef DIP_SWITCH_ENABLE
98# include "dip_switch.h" 89# include "dip_switch.h"
99#endif 90#endif
100#ifdef STM32_EEPROM_ENABLE
101# include "eeprom_stm32.h"
102#endif
103#ifdef EEPROM_DRIVER 91#ifdef EEPROM_DRIVER
104# include "eeprom_driver.h" 92# include "eeprom_driver.h"
105#endif 93#endif
@@ -246,9 +234,6 @@ void keyboard_setup(void) {
246 disable_jtag(); 234 disable_jtag();
247#endif 235#endif
248 print_set_sendchar(sendchar); 236 print_set_sendchar(sendchar);
249#ifdef STM32_EEPROM_ENABLE
250 EEPROM_Init();
251#endif
252#ifdef EEPROM_DRIVER 237#ifdef EEPROM_DRIVER
253 eeprom_driver_init(); 238 eeprom_driver_init();
254#endif 239#endif
@@ -316,9 +301,6 @@ void keyboard_init(void) {
316#if defined(CRC_ENABLE) 301#if defined(CRC_ENABLE)
317 crc_init(); 302 crc_init();
318#endif 303#endif
319#ifdef QWIIC_ENABLE
320 qwiic_init();
321#endif
322#ifdef OLED_ENABLE 304#ifdef OLED_ENABLE
323 oled_init(OLED_ROTATION_0); 305 oled_init(OLED_ROTATION_0);
324#endif 306#endif
@@ -331,9 +313,6 @@ void keyboard_init(void) {
331#ifdef SERIAL_MOUSE_ENABLE 313#ifdef SERIAL_MOUSE_ENABLE
332 serial_mouse_init(); 314 serial_mouse_init();
333#endif 315#endif
334#ifdef ADB_MOUSE_ENABLE
335 adb_mouse_init();
336#endif
337#ifdef BACKLIGHT_ENABLE 316#ifdef BACKLIGHT_ENABLE
338 backlight_init(); 317 backlight_init();
339#endif 318#endif
@@ -384,7 +363,6 @@ void switch_events(uint8_t row, uint8_t col, bool pressed) {
384 * 363 *
385 * * scan matrix 364 * * scan matrix
386 * * handle mouse movements 365 * * handle mouse movements
387 * * run visualizer code
388 * * handle midi commands 366 * * handle midi commands
389 * * light LEDs 367 * * light LEDs
390 * 368 *
@@ -473,10 +451,6 @@ MATRIX_LOOP_END:
473 if (encoders_changed) last_encoder_activity_trigger(); 451 if (encoders_changed) last_encoder_activity_trigger();
474#endif 452#endif
475 453
476#ifdef QWIIC_ENABLE
477 qwiic_task();
478#endif
479
480#ifdef OLED_ENABLE 454#ifdef OLED_ENABLE
481 oled_task(); 455 oled_task();
482# if OLED_TIMEOUT > 0 456# if OLED_TIMEOUT > 0
@@ -514,18 +488,6 @@ MATRIX_LOOP_END:
514 serial_mouse_task(); 488 serial_mouse_task();
515#endif 489#endif
516 490
517#ifdef ADB_MOUSE_ENABLE
518 adb_mouse_task();
519#endif
520
521#ifdef SERIAL_LINK_ENABLE
522 serial_link_update();
523#endif
524
525#ifdef VISUALIZER_ENABLE
526 visualizer_update(default_layer_state, layer_state, visualizer_get_mods(), host_keyboard_leds());
527#endif
528
529#ifdef POINTING_DEVICE_ENABLE 491#ifdef POINTING_DEVICE_ENABLE
530 pointing_device_task(); 492 pointing_device_task();
531#endif 493#endif
@@ -548,6 +510,10 @@ MATRIX_LOOP_END:
548 digitizer_task(); 510 digitizer_task();
549#endif 511#endif
550 512
513#ifdef PROGRAMMABLE_BUTTON_ENABLE
514 programmable_button_send();
515#endif
516
551 // update LED 517 // update LED
552 if (led_status != host_keyboard_leds()) { 518 if (led_status != host_keyboard_leds()) {
553 led_status = host_keyboard_leds(); 519 led_status = host_keyboard_leds();
diff --git a/quantum/keymap_extras/keymap_turkish_f.h b/quantum/keymap_extras/keymap_turkish_f.h
index 226f8cbeb..f86ef2154 100644
--- a/quantum/keymap_extras/keymap_turkish_f.h
+++ b/quantum/keymap_extras/keymap_turkish_f.h
@@ -111,7 +111,7 @@
111#define TR_LPRN S(TR_8) // ( 111#define TR_LPRN S(TR_8) // (
112#define TR_RPRN S(TR_9) // ) 112#define TR_RPRN S(TR_9) // )
113#define TR_EQL S(TR_0) // = 113#define TR_EQL S(TR_0) // =
114#define TR_QUES S(TR_ASTR) // ? 114#define TR_QUES S(TR_SLSH) // ?
115#define TR_UNDS S(TR_MINS) // _ 115#define TR_UNDS S(TR_MINS) // _
116// Row 4 116// Row 4
117#define TR_RABK S(TR_LABK) // > 117#define TR_RABK S(TR_LABK) // >
diff --git a/quantum/led_matrix/led_matrix.c b/quantum/led_matrix/led_matrix.c
index 50510e49a..85556d157 100644
--- a/quantum/led_matrix/led_matrix.c
+++ b/quantum/led_matrix/led_matrix.c
@@ -33,14 +33,6 @@ const led_point_t k_led_matrix_center = {112, 32};
33const led_point_t k_led_matrix_center = LED_MATRIX_CENTER; 33const led_point_t k_led_matrix_center = LED_MATRIX_CENTER;
34#endif 34#endif
35 35
36// clang-format off
37#ifndef LED_MATRIX_IMMEDIATE_EEPROM
38# define led_eeconfig_update(v) led_update_eeprom |= v
39#else
40# define led_eeconfig_update(v) if (v) eeconfig_update_led_matrix()
41#endif
42// clang-format on
43
44// Generic effect runners 36// Generic effect runners
45#include "led_matrix_runners.inc" 37#include "led_matrix_runners.inc"
46 38
@@ -107,7 +99,6 @@ last_hit_t g_last_hit_tracker;
107 99
108// internals 100// internals
109static bool suspend_state = false; 101static bool suspend_state = false;
110static bool led_update_eeprom = false;
111static uint8_t led_last_enable = UINT8_MAX; 102static uint8_t led_last_enable = UINT8_MAX;
112static uint8_t led_last_effect = UINT8_MAX; 103static uint8_t led_last_effect = UINT8_MAX;
113static effect_params_t led_effect_params = {0, LED_FLAG_ALL, false}; 104static effect_params_t led_effect_params = {0, LED_FLAG_ALL, false};
@@ -127,9 +118,9 @@ static last_hit_t last_hit_buffer;
127const uint8_t k_led_matrix_split[2] = LED_MATRIX_SPLIT; 118const uint8_t k_led_matrix_split[2] = LED_MATRIX_SPLIT;
128#endif 119#endif
129 120
130void eeconfig_read_led_matrix(void) { eeprom_read_block(&led_matrix_eeconfig, EECONFIG_LED_MATRIX, sizeof(led_matrix_eeconfig)); } 121EECONFIG_DEBOUNCE_HELPER(led_matrix, EECONFIG_LED_MATRIX, led_matrix_eeconfig);
131 122
132void eeconfig_update_led_matrix(void) { eeprom_update_block(&led_matrix_eeconfig, EECONFIG_LED_MATRIX, sizeof(led_matrix_eeconfig)); } 123void eeconfig_update_led_matrix(void) { eeconfig_flush_led_matrix(true); }
133 124
134void eeconfig_update_led_matrix_default(void) { 125void eeconfig_update_led_matrix_default(void) {
135 dprintf("eeconfig_update_led_matrix_default\n"); 126 dprintf("eeconfig_update_led_matrix_default\n");
@@ -138,7 +129,7 @@ void eeconfig_update_led_matrix_default(void) {
138 led_matrix_eeconfig.val = LED_MATRIX_STARTUP_VAL; 129 led_matrix_eeconfig.val = LED_MATRIX_STARTUP_VAL;
139 led_matrix_eeconfig.speed = LED_MATRIX_STARTUP_SPD; 130 led_matrix_eeconfig.speed = LED_MATRIX_STARTUP_SPD;
140 led_matrix_eeconfig.flags = LED_FLAG_ALL; 131 led_matrix_eeconfig.flags = LED_FLAG_ALL;
141 eeconfig_update_led_matrix(); 132 eeconfig_flush_led_matrix(true);
142} 133}
143 134
144void eeconfig_debug_led_matrix(void) { 135void eeconfig_debug_led_matrix(void) {
@@ -279,9 +270,8 @@ static void led_task_timers(void) {
279} 270}
280 271
281static void led_task_sync(void) { 272static void led_task_sync(void) {
273 eeconfig_flush_led_matrix(false);
282 // next task 274 // next task
283 if (led_update_eeprom) eeconfig_update_led_matrix();
284 led_update_eeprom = false;
285 if (sync_timer_elapsed32(g_led_timer) >= LED_MATRIX_LED_FLUSH_LIMIT) led_task_state = STARTING; 275 if (sync_timer_elapsed32(g_led_timer) >= LED_MATRIX_LED_FLUSH_LIMIT) led_task_state = STARTING;
286} 276}
287 277
@@ -449,7 +439,7 @@ void led_matrix_init(void) {
449 eeconfig_update_led_matrix_default(); 439 eeconfig_update_led_matrix_default();
450 } 440 }
451 441
452 eeconfig_read_led_matrix(); 442 eeconfig_init_led_matrix();
453 if (!led_matrix_eeconfig.mode) { 443 if (!led_matrix_eeconfig.mode) {
454 dprintf("led_matrix_init_drivers led_matrix_eeconfig.mode = 0. Write default values to EEPROM.\n"); 444 dprintf("led_matrix_init_drivers led_matrix_eeconfig.mode = 0. Write default values to EEPROM.\n");
455 eeconfig_update_led_matrix_default(); 445 eeconfig_update_led_matrix_default();
@@ -472,7 +462,7 @@ bool led_matrix_get_suspend_state(void) { return suspend_state; }
472void led_matrix_toggle_eeprom_helper(bool write_to_eeprom) { 462void led_matrix_toggle_eeprom_helper(bool write_to_eeprom) {
473 led_matrix_eeconfig.enable ^= 1; 463 led_matrix_eeconfig.enable ^= 1;
474 led_task_state = STARTING; 464 led_task_state = STARTING;
475 led_eeconfig_update(write_to_eeprom); 465 eeconfig_flag_led_matrix(write_to_eeprom);
476 dprintf("led matrix toggle [%s]: led_matrix_eeconfig.enable = %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.enable); 466 dprintf("led matrix toggle [%s]: led_matrix_eeconfig.enable = %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.enable);
477} 467}
478void led_matrix_toggle_noeeprom(void) { led_matrix_toggle_eeprom_helper(false); } 468void led_matrix_toggle_noeeprom(void) { led_matrix_toggle_eeprom_helper(false); }
@@ -480,7 +470,7 @@ void led_matrix_toggle(void) { led_matrix_toggle_eeprom_helper(true); }
480 470
481void led_matrix_enable(void) { 471void led_matrix_enable(void) {
482 led_matrix_enable_noeeprom(); 472 led_matrix_enable_noeeprom();
483 led_eeconfig_update(true); 473 eeconfig_flag_led_matrix(true);
484} 474}
485 475
486void led_matrix_enable_noeeprom(void) { 476void led_matrix_enable_noeeprom(void) {
@@ -490,7 +480,7 @@ void led_matrix_enable_noeeprom(void) {
490 480
491void led_matrix_disable(void) { 481void led_matrix_disable(void) {
492 led_matrix_disable_noeeprom(); 482 led_matrix_disable_noeeprom();
493 led_eeconfig_update(true); 483 eeconfig_flag_led_matrix(true);
494} 484}
495 485
496void led_matrix_disable_noeeprom(void) { 486void led_matrix_disable_noeeprom(void) {
@@ -512,7 +502,7 @@ void led_matrix_mode_eeprom_helper(uint8_t mode, bool write_to_eeprom) {
512 led_matrix_eeconfig.mode = mode; 502 led_matrix_eeconfig.mode = mode;
513 } 503 }
514 led_task_state = STARTING; 504 led_task_state = STARTING;
515 led_eeconfig_update(write_to_eeprom); 505 eeconfig_flag_led_matrix(write_to_eeprom);
516 dprintf("led matrix mode [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.mode); 506 dprintf("led matrix mode [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.mode);
517} 507}
518void led_matrix_mode_noeeprom(uint8_t mode) { led_matrix_mode_eeprom_helper(mode, false); } 508void led_matrix_mode_noeeprom(uint8_t mode) { led_matrix_mode_eeprom_helper(mode, false); }
@@ -539,7 +529,7 @@ void led_matrix_set_val_eeprom_helper(uint8_t val, bool write_to_eeprom) {
539 return; 529 return;
540 } 530 }
541 led_matrix_eeconfig.val = (val > LED_MATRIX_MAXIMUM_BRIGHTNESS) ? LED_MATRIX_MAXIMUM_BRIGHTNESS : val; 531 led_matrix_eeconfig.val = (val > LED_MATRIX_MAXIMUM_BRIGHTNESS) ? LED_MATRIX_MAXIMUM_BRIGHTNESS : val;
542 led_eeconfig_update(write_to_eeprom); 532 eeconfig_flag_led_matrix(write_to_eeprom);
543 dprintf("led matrix set val [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.val); 533 dprintf("led matrix set val [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.val);
544} 534}
545void led_matrix_set_val_noeeprom(uint8_t val) { led_matrix_set_val_eeprom_helper(val, false); } 535void led_matrix_set_val_noeeprom(uint8_t val) { led_matrix_set_val_eeprom_helper(val, false); }
@@ -557,7 +547,7 @@ void led_matrix_decrease_val(void) { led_matrix_decrease_val_helper(true); }
557 547
558void led_matrix_set_speed_eeprom_helper(uint8_t speed, bool write_to_eeprom) { 548void led_matrix_set_speed_eeprom_helper(uint8_t speed, bool write_to_eeprom) {
559 led_matrix_eeconfig.speed = speed; 549 led_matrix_eeconfig.speed = speed;
560 led_eeconfig_update(write_to_eeprom); 550 eeconfig_flag_led_matrix(write_to_eeprom);
561 dprintf("led matrix set speed [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.speed); 551 dprintf("led matrix set speed [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.speed);
562} 552}
563void led_matrix_set_speed_noeeprom(uint8_t speed) { led_matrix_set_speed_eeprom_helper(speed, false); } 553void led_matrix_set_speed_noeeprom(uint8_t speed) { led_matrix_set_speed_eeprom_helper(speed, false); }
diff --git a/quantum/led_matrix/led_matrix_drivers.c b/quantum/led_matrix/led_matrix_drivers.c
index 1d46b2c50..2157619a0 100644
--- a/quantum/led_matrix/led_matrix_drivers.c
+++ b/quantum/led_matrix/led_matrix_drivers.c
@@ -26,128 +26,123 @@
26 */ 26 */
27 27
28#if defined(IS31FL3731) || defined(IS31FL3733) 28#if defined(IS31FL3731) || defined(IS31FL3733)
29
30# include "i2c_master.h" 29# include "i2c_master.h"
31 30
32static void init(void) { 31static void init(void) {
33 i2c_init(); 32 i2c_init();
34# ifdef IS31FL3731 33
35# ifdef LED_DRIVER_ADDR_1 34# if defined(IS31FL3731)
36 IS31FL3731_init(LED_DRIVER_ADDR_1); 35 IS31FL3731_init(LED_DRIVER_ADDR_1);
37# endif 36# if defined(LED_DRIVER_ADDR_2)
38# ifdef LED_DRIVER_ADDR_2
39 IS31FL3731_init(LED_DRIVER_ADDR_2); 37 IS31FL3731_init(LED_DRIVER_ADDR_2);
40# endif 38# if defined(LED_DRIVER_ADDR_3)
41# ifdef LED_DRIVER_ADDR_3
42 IS31FL3731_init(LED_DRIVER_ADDR_3); 39 IS31FL3731_init(LED_DRIVER_ADDR_3);
43# endif 40# if defined(LED_DRIVER_ADDR_4)
44# ifdef LED_DRIVER_ADDR_4
45 IS31FL3731_init(LED_DRIVER_ADDR_4); 41 IS31FL3731_init(LED_DRIVER_ADDR_4);
46# endif 42# endif
47# else
48# ifdef LED_DRIVER_ADDR_1
49# ifndef LED_DRIVER_SYNC_1
50# define LED_DRIVER_SYNC_1 0
51# endif 43# endif
52 IS31FL3733_init(LED_DRIVER_ADDR_1, LED_DRIVER_SYNC_1);
53# endif 44# endif
54# ifdef LED_DRIVER_ADDR_2 45
55# ifndef LED_DRIVER_SYNC_2 46# elif defined(IS31FL3733)
47# if !defined(LED_DRIVER_SYNC_1)
48# define LED_DRIVER_SYNC_1 0
49# endif
50 IS31FL3733_init(LED_DRIVER_ADDR_1, LED_DRIVER_SYNC_1);
51# if defined(LED_DRIVER_ADDR_2)
52# if !defined(LED_DRIVER_SYNC_2)
56# define LED_DRIVER_SYNC_2 0 53# define LED_DRIVER_SYNC_2 0
57# endif 54# endif
58 IS31FL3733_init(LED_DRIVER_ADDR_2, LED_DRIVER_SYNC_2); 55 IS31FL3733_init(LED_DRIVER_ADDR_2, LED_DRIVER_SYNC_2);
59# endif 56# if defined(LED_DRIVER_ADDR_3)
60# ifdef LED_DRIVER_ADDR_3 57# if !defined(LED_DRIVER_SYNC_3)
61# ifndef LED_DRIVER_SYNC_3 58# define LED_DRIVER_SYNC_3 0
62# define LED_DRIVER_SYNC_3 0 59# endif
63# endif
64 IS31FL3733_init(LED_DRIVER_ADDR_3, LED_DRIVER_SYNC_3); 60 IS31FL3733_init(LED_DRIVER_ADDR_3, LED_DRIVER_SYNC_3);
65# endif 61# if defined(LED_DRIVER_ADDR_4)
66# ifdef LED_DRIVER_ADDR_4 62# if !defined(LED_DRIVER_SYNC_4)
67# ifndef LED_DRIVER_SYNC_4 63# define LED_DRIVER_SYNC_4 0
68# define LED_DRIVER_SYNC_4 0 64# endif
69# endif
70 IS31FL3733_init(LED_DRIVER_ADDR_4, LED_DRIVER_SYNC_4); 65 IS31FL3733_init(LED_DRIVER_ADDR_4, LED_DRIVER_SYNC_4);
66# endif
67# endif
71# endif 68# endif
72# endif 69# endif
73 70
74 for (int index = 0; index < DRIVER_LED_TOTAL; index++) { 71 for (int index = 0; index < DRIVER_LED_TOTAL; index++) {
75# ifdef IS31FL3731 72# if defined(IS31FL3731)
76 IS31FL3731_set_led_control_register(index, true); 73 IS31FL3731_set_led_control_register(index, true);
77# else 74# elif defined(IS31FL3733)
78 IS31FL3733_set_led_control_register(index, true); 75 IS31FL3733_set_led_control_register(index, true);
79# endif 76# endif
80 } 77 }
78
81// This actually updates the LED drivers 79// This actually updates the LED drivers
82# ifdef IS31FL3731 80# if defined(IS31FL3731)
83# ifdef LED_DRIVER_ADDR_1
84 IS31FL3731_update_led_control_registers(LED_DRIVER_ADDR_1, 0); 81 IS31FL3731_update_led_control_registers(LED_DRIVER_ADDR_1, 0);
85# endif 82# if defined(LED_DRIVER_ADDR_2)
86# ifdef LED_DRIVER_ADDR_2
87 IS31FL3731_update_led_control_registers(LED_DRIVER_ADDR_2, 1); 83 IS31FL3731_update_led_control_registers(LED_DRIVER_ADDR_2, 1);
88# endif 84# if defined(LED_DRIVER_ADDR_3)
89# ifdef LED_DRIVER_ADDR_3
90 IS31FL3731_update_led_control_registers(LED_DRIVER_ADDR_3, 2); 85 IS31FL3731_update_led_control_registers(LED_DRIVER_ADDR_3, 2);
91# endif 86# if defined(LED_DRIVER_ADDR_4)
92# ifdef LED_DRIVER_ADDR_4
93 IS31FL3731_update_led_control_registers(LED_DRIVER_ADDR_4, 3); 87 IS31FL3731_update_led_control_registers(LED_DRIVER_ADDR_4, 3);
88# endif
89# endif
94# endif 90# endif
95# else 91
96# ifdef LED_DRIVER_ADDR_1 92# elif defined(IS31FL3733)
97 IS31FL3733_update_led_control_registers(LED_DRIVER_ADDR_1, 0); 93 IS31FL3733_update_led_control_registers(LED_DRIVER_ADDR_1, 0);
98# endif 94# if defined(LED_DRIVER_ADDR_2)
99# ifdef LED_DRIVER_ADDR_2
100 IS31FL3733_update_led_control_registers(LED_DRIVER_ADDR_2, 1); 95 IS31FL3733_update_led_control_registers(LED_DRIVER_ADDR_2, 1);
101# endif 96# if defined(LED_DRIVER_ADDR_3)
102# ifdef LED_DRIVER_ADDR_3
103 IS31FL3733_update_led_control_registers(LED_DRIVER_ADDR_3, 2); 97 IS31FL3733_update_led_control_registers(LED_DRIVER_ADDR_3, 2);
104# endif 98# if defined(LED_DRIVER_ADDR_4)
105# ifdef LED_DRIVER_ADDR_4
106 IS31FL3733_update_led_control_registers(LED_DRIVER_ADDR_4, 3); 99 IS31FL3733_update_led_control_registers(LED_DRIVER_ADDR_4, 3);
100# endif
101# endif
107# endif 102# endif
108# endif 103# endif
109} 104}
110 105
106# if defined(IS31FL3731)
111static void flush(void) { 107static void flush(void) {
112# ifdef IS31FL3731
113# ifdef LED_DRIVER_ADDR_1
114 IS31FL3731_update_pwm_buffers(LED_DRIVER_ADDR_1, 0); 108 IS31FL3731_update_pwm_buffers(LED_DRIVER_ADDR_1, 0);
115# endif 109# if defined(LED_DRIVER_ADDR_2)
116# ifdef LED_DRIVER_ADDR_2
117 IS31FL3731_update_pwm_buffers(LED_DRIVER_ADDR_2, 1); 110 IS31FL3731_update_pwm_buffers(LED_DRIVER_ADDR_2, 1);
118# endif 111# if defined(LED_DRIVER_ADDR_3)
119# ifdef LED_DRIVER_ADDR_3
120 IS31FL3731_update_pwm_buffers(LED_DRIVER_ADDR_3, 2); 112 IS31FL3731_update_pwm_buffers(LED_DRIVER_ADDR_3, 2);
121# endif 113# if defined(LED_DRIVER_ADDR_4)
122# ifdef LED_DRIVER_ADDR_4
123 IS31FL3731_update_pwm_buffers(LED_DRIVER_ADDR_4, 3); 114 IS31FL3731_update_pwm_buffers(LED_DRIVER_ADDR_4, 3);
115# endif
116# endif
124# endif 117# endif
125# else 118}
126# ifdef LED_DRIVER_ADDR_1 119
120const led_matrix_driver_t led_matrix_driver = {
121 .init = init,
122 .flush = flush,
123 .set_value = IS31FL3731_set_value,
124 .set_value_all = IS31FL3731_set_value_all,
125};
126
127# elif defined(IS31FL3733)
128static void flush(void) {
127 IS31FL3733_update_pwm_buffers(LED_DRIVER_ADDR_1, 0); 129 IS31FL3733_update_pwm_buffers(LED_DRIVER_ADDR_1, 0);
128# endif 130# if defined(LED_DRIVER_ADDR_2)
129# ifdef LED_DRIVER_ADDR_2
130 IS31FL3733_update_pwm_buffers(LED_DRIVER_ADDR_2, 1); 131 IS31FL3733_update_pwm_buffers(LED_DRIVER_ADDR_2, 1);
131# endif 132# if defined(LED_DRIVER_ADDR_3)
132# ifdef LED_DRIVER_ADDR_3
133 IS31FL3733_update_pwm_buffers(LED_DRIVER_ADDR_3, 2); 133 IS31FL3733_update_pwm_buffers(LED_DRIVER_ADDR_3, 2);
134# endif 134# if defined(LED_DRIVER_ADDR_4)
135# ifdef LED_DRIVER_ADDR_4
136 IS31FL3733_update_pwm_buffers(LED_DRIVER_ADDR_4, 3); 135 IS31FL3733_update_pwm_buffers(LED_DRIVER_ADDR_4, 3);
136# endif
137# endif
137# endif 138# endif
138# endif
139} 139}
140 140
141const led_matrix_driver_t led_matrix_driver = { 141const led_matrix_driver_t led_matrix_driver = {
142 .init = init, 142 .init = init,
143 .flush = flush, 143 .flush = flush,
144# ifdef IS31FL3731
145 .set_value = IS31FL3731_set_value,
146 .set_value_all = IS31FL3731_set_value_all,
147# else
148 .set_value = IS31FL3733_set_value, 144 .set_value = IS31FL3733_set_value,
149 .set_value_all = IS31FL3733_set_value_all, 145 .set_value_all = IS31FL3733_set_value_all,
150# endif
151}; 146};
152 147# endif
153#endif 148#endif
diff --git a/quantum/matrix.c b/quantum/matrix.c
index 33586c431..4fbcc2419 100644
--- a/quantum/matrix.c
+++ b/quantum/matrix.c
@@ -288,10 +288,8 @@ void matrix_init(void) {
288 matrix_init_pins(); 288 matrix_init_pins();
289 289
290 // initialize matrix state: all keys off 290 // initialize matrix state: all keys off
291 for (uint8_t i = 0; i < MATRIX_ROWS; i++) { 291 memset(matrix, 0, sizeof(matrix));
292 raw_matrix[i] = 0; 292 memset(raw_matrix, 0, sizeof(raw_matrix));
293 matrix[i] = 0;
294 }
295 293
296 debounce_init(ROWS_PER_HAND); 294 debounce_init(ROWS_PER_HAND);
297 295
@@ -312,24 +310,22 @@ __attribute__((weak)) bool transport_master_if_connected(matrix_row_t master_mat
312bool matrix_post_scan(void) { 310bool matrix_post_scan(void) {
313 bool changed = false; 311 bool changed = false;
314 if (is_keyboard_master()) { 312 if (is_keyboard_master()) {
313 static bool last_connected = false;
315 matrix_row_t slave_matrix[ROWS_PER_HAND] = {0}; 314 matrix_row_t slave_matrix[ROWS_PER_HAND] = {0};
316 if (transport_master_if_connected(matrix + thisHand, slave_matrix)) { 315 if (transport_master_if_connected(matrix + thisHand, slave_matrix)) {
317 for (int i = 0; i < ROWS_PER_HAND; ++i) { 316 changed = memcmp(matrix + thatHand, slave_matrix, sizeof(slave_matrix)) != 0;
318 if (matrix[thatHand + i] != slave_matrix[i]) {
319 matrix[thatHand + i] = slave_matrix[i];
320 changed = true;
321 }
322 }
323 } else {
324 // reset other half if disconnected
325 for (int i = 0; i < ROWS_PER_HAND; ++i) {
326 matrix[thatHand + i] = 0;
327 slave_matrix[i] = 0;
328 }
329 317
318 last_connected = true;
319 } else if (last_connected) {
320 // reset other half when disconnected
321 memset(slave_matrix, 0, sizeof(slave_matrix));
330 changed = true; 322 changed = true;
323
324 last_connected = false;
331 } 325 }
332 326
327 if (changed) memcpy(matrix + thatHand, slave_matrix, sizeof(slave_matrix));
328
333 matrix_scan_quantum(); 329 matrix_scan_quantum();
334 } else { 330 } else {
335 transport_slave(matrix + thatHand, matrix + thisHand); 331 transport_slave(matrix + thatHand, matrix + thisHand);
diff --git a/quantum/mcu_selection.mk b/quantum/mcu_selection.mk
index f7eaeec8a..7c4a617af 100644
--- a/quantum/mcu_selection.mk
+++ b/quantum/mcu_selection.mk
@@ -81,7 +81,7 @@ ifneq ($(findstring MK20DX256, $(MCU)),)
81 BOARD ?= PJRC_TEENSY_3_1 81 BOARD ?= PJRC_TEENSY_3_1
82endif 82endif
83 83
84ifneq ($(findstring MK66F18, $(MCU)),) 84ifneq ($(findstring MK66FX1M0, $(MCU)),)
85 # Cortex version 85 # Cortex version
86 MCU = cortex-m4 86 MCU = cortex-m4
87 87
@@ -138,6 +138,11 @@ ifneq ($(findstring STM32F042, $(MCU)),)
138 138
139 # UF2 settings 139 # UF2 settings
140 UF2_FAMILY ?= STM32F0 140 UF2_FAMILY ?= STM32F0
141
142 # Stack sizes: Since this chip has limited RAM capacity, the stack area needs to be reduced.
143 # This ensures that the EEPROM page buffer fits into RAM
144 USE_PROCESS_STACKSIZE = 0x600
145 USE_EXCEPTIONS_STACKSIZE = 0x300
141endif 146endif
142 147
143ifneq ($(findstring STM32F072, $(MCU)),) 148ifneq ($(findstring STM32F072, $(MCU)),)
@@ -273,6 +278,38 @@ ifneq ($(findstring STM32F401, $(MCU)),)
273 UF2_FAMILY ?= STM32F4 278 UF2_FAMILY ?= STM32F4
274endif 279endif
275 280
281ifneq ($(findstring STM32F405, $(MCU)),)
282 # Cortex version
283 MCU = cortex-m4
284
285 # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
286 ARMV = 7
287
288 ## chip/board settings
289 # - the next two should match the directories in
290 # <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
291 MCU_FAMILY = STM32
292 MCU_SERIES = STM32F4xx
293
294 # Linker script to use
295 # - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
296 # or <keyboard_dir>/ld/
297 MCU_LDSCRIPT ?= STM32F405xG
298
299 # Startup code to use
300 # - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/
301 MCU_STARTUP ?= stm32f4xx
302
303 # Board: it should exist either in <chibios>/os/hal/boards/,
304 # <keyboard_dir>/boards/, or drivers/boards/
305 BOARD ?= GENERIC_STM32_F405XG
306
307 USE_FPU ?= yes
308
309 # UF2 settings
310 UF2_FAMILY ?= STM32F4
311endif
312
276ifneq ($(findstring STM32F407, $(MCU)),) 313ifneq ($(findstring STM32F407, $(MCU)),)
277 # Cortex version 314 # Cortex version
278 MCU = cortex-m4 315 MCU = cortex-m4
@@ -504,6 +541,37 @@ ifneq (,$(filter $(MCU),STM32L412 STM32L422))
504 UF2_FAMILY ?= STM32L4 541 UF2_FAMILY ?= STM32L4
505endif 542endif
506 543
544ifneq ($(findstring GD32VF103, $(MCU)),)
545 # RISC-V
546 MCU = risc-v
547
548 # RISC-V extensions and abi configuration
549 MCU_ARCH = rv32imac
550 MCU_ABI = ilp32
551 MCU_CMODEL = medlow
552
553 ## chip/board settings
554 # - the next two should match the directories in
555 # <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
556 MCU_FAMILY = GD32V
557 MCU_SERIES = GD32VF103
558
559 # Linker script to use
560 # - it should exist either in <chibios>/os/common/startup/RISCV-ECLIC/compilers/GCC/ld/
561 # or <keyboard_dir>/ld/
562 MCU_LDSCRIPT ?= GD32VF103xB
563
564 # Startup code to use
565 # - it should exist in <chibios>/os/common/startup/RISCV-ECLIC/compilers/GCC/mk/
566 MCU_STARTUP ?= gd32vf103
567
568 # Board: it should exist either in <chibios>/os/hal/boards/,
569 # <keyboard_dir>/boards/, or drivers/boards/
570 BOARD ?= SIPEED_LONGAN_NANO
571
572 USE_FPU ?= no
573endif
574
507ifneq (,$(filter $(MCU),at90usb162 atmega16u2 atmega32u2 atmega16u4 atmega32u4 at90usb646 at90usb647 at90usb1286 at90usb1287)) 575ifneq (,$(filter $(MCU),at90usb162 atmega16u2 atmega32u2 atmega16u4 atmega32u4 at90usb646 at90usb647 at90usb1286 at90usb1287))
508 PROTOCOL = LUFA 576 PROTOCOL = LUFA
509 577
diff --git a/quantum/process_keycode/process_haptic.c b/quantum/process_keycode/process_haptic.c
index 64d455d00..1b9c2f24f 100644
--- a/quantum/process_keycode/process_haptic.c
+++ b/quantum/process_keycode/process_haptic.c
@@ -32,6 +32,7 @@ __attribute__((weak)) bool get_haptic_enabled_key(uint16_t keycode, keyrecord_t
32 break; 32 break;
33 case KC_LCTRL ... KC_RGUI: 33 case KC_LCTRL ... KC_RGUI:
34 case QK_MOMENTARY ... QK_MOMENTARY_MAX: 34 case QK_MOMENTARY ... QK_MOMENTARY_MAX:
35 case QK_LAYER_MOD ... QK_LAYER_MOD_MAX:
35#endif 36#endif
36#ifdef NO_HAPTIC_FN 37#ifdef NO_HAPTIC_FN
37 case KC_FN0 ... KC_FN31: 38 case KC_FN0 ... KC_FN31:
diff --git a/quantum/process_keycode/process_programmable_button.c b/quantum/process_keycode/process_programmable_button.c
new file mode 100644
index 000000000..c6e77faac
--- /dev/null
+++ b/quantum/process_keycode/process_programmable_button.c
@@ -0,0 +1,31 @@
1/*
2Copyright 2021 Thomas Weißschuh <thomas@t-8ch.de>
3
4This program is free software: you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation, either version 2 of the License, or
7(at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17
18#include "process_programmable_button.h"
19#include "programmable_button.h"
20
21bool process_programmable_button(uint16_t keycode, keyrecord_t *record) {
22 if (keycode >= PROGRAMMABLE_BUTTON_MIN && keycode <= PROGRAMMABLE_BUTTON_MAX) {
23 uint8_t button = keycode - PROGRAMMABLE_BUTTON_MIN + 1;
24 if (record->event.pressed) {
25 programmable_button_on(button);
26 } else {
27 programmable_button_off(button);
28 }
29 }
30 return true;
31}
diff --git a/quantum/process_keycode/process_programmable_button.h b/quantum/process_keycode/process_programmable_button.h
new file mode 100644
index 000000000..47c6ce561
--- /dev/null
+++ b/quantum/process_keycode/process_programmable_button.h
@@ -0,0 +1,23 @@
1/*
2Copyright 2021 Thomas Weißschuh <thomas@t-8ch.de>
3
4This program is free software: you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation, either version 2 of the License, or
7(at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17
18#pragma once
19
20#include <stdint.h>
21#include "quantum.h"
22
23bool process_programmable_button(uint16_t keycode, keyrecord_t *record);
diff --git a/quantum/process_keycode/process_unicode_common.c b/quantum/process_keycode/process_unicode_common.c
index 46fcaaa86..7853c22c5 100644
--- a/quantum/process_keycode/process_unicode_common.c
+++ b/quantum/process_keycode/process_unicode_common.c
@@ -22,6 +22,7 @@
22unicode_config_t unicode_config; 22unicode_config_t unicode_config;
23uint8_t unicode_saved_mods; 23uint8_t unicode_saved_mods;
24bool unicode_saved_caps_lock; 24bool unicode_saved_caps_lock;
25bool unicode_saved_num_lock;
25 26
26#if UNICODE_SELECTED_MODES != -1 27#if UNICODE_SELECTED_MODES != -1
27static uint8_t selected[] = {UNICODE_SELECTED_MODES}; 28static uint8_t selected[] = {UNICODE_SELECTED_MODES};
@@ -79,13 +80,14 @@ void persist_unicode_input_mode(void) { eeprom_update_byte(EECONFIG_UNICODEMODE,
79 80
80__attribute__((weak)) void unicode_input_start(void) { 81__attribute__((weak)) void unicode_input_start(void) {
81 unicode_saved_caps_lock = host_keyboard_led_state().caps_lock; 82 unicode_saved_caps_lock = host_keyboard_led_state().caps_lock;
83 unicode_saved_num_lock = host_keyboard_led_state().num_lock;
82 84
83 // Note the order matters here! 85 // Note the order matters here!
84 // Need to do this before we mess around with the mods, or else 86 // Need to do this before we mess around with the mods, or else
85 // UNICODE_KEY_LNX (which is usually Ctrl-Shift-U) might not work 87 // UNICODE_KEY_LNX (which is usually Ctrl-Shift-U) might not work
86 // correctly in the shifted case. 88 // correctly in the shifted case.
87 if (unicode_config.input_mode == UC_LNX && unicode_saved_caps_lock) { 89 if (unicode_config.input_mode == UC_LNX && unicode_saved_caps_lock) {
88 tap_code(KC_CAPS); 90 tap_code(KC_CAPSLOCK);
89 } 91 }
90 92
91 unicode_saved_mods = get_mods(); // Save current mods 93 unicode_saved_mods = get_mods(); // Save current mods
@@ -99,8 +101,12 @@ __attribute__((weak)) void unicode_input_start(void) {
99 tap_code16(UNICODE_KEY_LNX); 101 tap_code16(UNICODE_KEY_LNX);
100 break; 102 break;
101 case UC_WIN: 103 case UC_WIN:
104 // For increased reliability, use numpad keys for inputting digits
105 if (!unicode_saved_num_lock) {
106 tap_code(KC_NUMLOCK);
107 }
102 register_code(KC_LALT); 108 register_code(KC_LALT);
103 tap_code(KC_PPLS); 109 tap_code(KC_KP_PLUS);
104 break; 110 break;
105 case UC_WINC: 111 case UC_WINC:
106 tap_code(UNICODE_KEY_WINC); 112 tap_code(UNICODE_KEY_WINC);
@@ -117,13 +123,16 @@ __attribute__((weak)) void unicode_input_finish(void) {
117 unregister_code(UNICODE_KEY_MAC); 123 unregister_code(UNICODE_KEY_MAC);
118 break; 124 break;
119 case UC_LNX: 125 case UC_LNX:
120 tap_code(KC_SPC); 126 tap_code(KC_SPACE);
121 if (unicode_saved_caps_lock) { 127 if (unicode_saved_caps_lock) {
122 tap_code(KC_CAPS); 128 tap_code(KC_CAPSLOCK);
123 } 129 }
124 break; 130 break;
125 case UC_WIN: 131 case UC_WIN:
126 unregister_code(KC_LALT); 132 unregister_code(KC_LALT);
133 if (!unicode_saved_num_lock) {
134 tap_code(KC_NUMLOCK);
135 }
127 break; 136 break;
128 case UC_WINC: 137 case UC_WINC:
129 tap_code(KC_ENTER); 138 tap_code(KC_ENTER);
@@ -139,26 +148,44 @@ __attribute__((weak)) void unicode_input_cancel(void) {
139 unregister_code(UNICODE_KEY_MAC); 148 unregister_code(UNICODE_KEY_MAC);
140 break; 149 break;
141 case UC_LNX: 150 case UC_LNX:
142 tap_code(KC_ESC); 151 tap_code(KC_ESCAPE);
143 if (unicode_saved_caps_lock) { 152 if (unicode_saved_caps_lock) {
144 tap_code(KC_CAPS); 153 tap_code(KC_CAPSLOCK);
145 } 154 }
146 break; 155 break;
147 case UC_WINC: 156 case UC_WINC:
148 tap_code(KC_ESC); 157 tap_code(KC_ESCAPE);
149 break; 158 break;
150 case UC_WIN: 159 case UC_WIN:
151 unregister_code(KC_LALT); 160 unregister_code(KC_LALT);
161 if (!unicode_saved_num_lock) {
162 tap_code(KC_NUMLOCK);
163 }
152 break; 164 break;
153 } 165 }
154 166
155 set_mods(unicode_saved_mods); // Reregister previously set mods 167 set_mods(unicode_saved_mods); // Reregister previously set mods
156} 168}
157 169
170// clang-format off
171
172static void send_nibble_wrapper(uint8_t digit) {
173 if (unicode_config.input_mode == UC_WIN) {
174 uint8_t kc = digit < 10
175 ? KC_KP_1 + (10 + digit - 1) % 10
176 : KC_A + (digit - 10);
177 tap_code(kc);
178 return;
179 }
180 send_nibble(digit);
181}
182
183// clang-format on
184
158void register_hex(uint16_t hex) { 185void register_hex(uint16_t hex) {
159 for (int i = 3; i >= 0; i--) { 186 for (int i = 3; i >= 0; i--) {
160 uint8_t digit = ((hex >> (i * 4)) & 0xF); 187 uint8_t digit = ((hex >> (i * 4)) & 0xF);
161 send_nibble(digit); 188 send_nibble_wrapper(digit);
162 } 189 }
163} 190}
164 191
@@ -171,10 +198,10 @@ void register_hex32(uint32_t hex) {
171 uint8_t digit = ((hex >> (i * 4)) & 0xF); 198 uint8_t digit = ((hex >> (i * 4)) & 0xF);
172 if (digit == 0) { 199 if (digit == 0) {
173 if (!onzerostart) { 200 if (!onzerostart) {
174 send_nibble(digit); 201 send_nibble_wrapper(digit);
175 } 202 }
176 } else { 203 } else {
177 send_nibble(digit); 204 send_nibble_wrapper(digit);
178 onzerostart = false; 205 onzerostart = false;
179 } 206 }
180 } 207 }
diff --git a/quantum/programmable_button.c b/quantum/programmable_button.c
new file mode 100644
index 000000000..be828fd17
--- /dev/null
+++ b/quantum/programmable_button.c
@@ -0,0 +1,37 @@
1/*
2Copyright 2021 Thomas Weißschuh <thomas@t-8ch.de>
3
4This program is free software: you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation, either version 2 of the License, or
7(at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17
18#include "programmable_button.h"
19#include "host.h"
20
21#define REPORT_BIT(index) (((uint32_t)1) << (index - 1))
22
23static uint32_t programmable_button_report = 0;
24
25void programmable_button_clear(void) { programmable_button_report = 0; }
26
27void programmable_button_send(void) { host_programmable_button_send(programmable_button_report); }
28
29void programmable_button_on(uint8_t index) { programmable_button_report |= REPORT_BIT(index); }
30
31void programmable_button_off(uint8_t index) { programmable_button_report &= ~REPORT_BIT(index); }
32
33bool programmable_button_is_on(uint8_t index) { return !!(programmable_button_report & REPORT_BIT(index)); };
34
35uint32_t programmable_button_get_report(void) { return programmable_button_report; };
36
37void programmable_button_set_report(uint32_t report) { programmable_button_report = report; }
diff --git a/quantum/programmable_button.h b/quantum/programmable_button.h
new file mode 100644
index 000000000..e89b8b9fd
--- /dev/null
+++ b/quantum/programmable_button.h
@@ -0,0 +1,30 @@
1/*
2Copyright 2021 Thomas Weißschuh <thomas@t-8ch.de>
3
4This program is free software: you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation, either version 2 of the License, or
7(at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17
18#pragma once
19
20#include <stdint.h>
21#include <stdbool.h>
22#include "report.h"
23
24void programmable_button_clear(void);
25void programmable_button_send(void);
26void programmable_button_on(uint8_t index);
27void programmable_button_off(uint8_t index);
28bool programmable_button_is_on(uint8_t index);
29uint32_t programmable_button_get_report(void);
30void programmable_button_set_report(uint32_t report);
diff --git a/quantum/quantum.c b/quantum/quantum.c
index e60378afe..ac8857df8 100644
--- a/quantum/quantum.c
+++ b/quantum/quantum.c
@@ -25,10 +25,6 @@
25# include "backlight.h" 25# include "backlight.h"
26#endif 26#endif
27 27
28#ifdef API_ENABLE
29# include "api.h"
30#endif
31
32#ifdef MIDI_ENABLE 28#ifdef MIDI_ENABLE
33# include "process_midi.h" 29# include "process_midi.h"
34#endif 30#endif
@@ -145,12 +141,13 @@ void reset_keyboard(void) {
145/* Convert record into usable keycode via the contained event. */ 141/* Convert record into usable keycode via the contained event. */
146uint16_t get_record_keycode(keyrecord_t *record, bool update_layer_cache) { 142uint16_t get_record_keycode(keyrecord_t *record, bool update_layer_cache) {
147#ifdef COMBO_ENABLE 143#ifdef COMBO_ENABLE
148 if (record->keycode) { return record->keycode; } 144 if (record->keycode) {
145 return record->keycode;
146 }
149#endif 147#endif
150 return get_event_keycode(record->event, update_layer_cache); 148 return get_event_keycode(record->event, update_layer_cache);
151} 149}
152 150
153
154/* Convert event into usable keycode. Checks the layer cache to ensure that it 151/* Convert event into usable keycode. Checks the layer cache to ensure that it
155 * retains the correct keycode after a layer change, if the key is still pressed. 152 * retains the correct keycode after a layer change, if the key is still pressed.
156 * "update_layer_cache" is to ensure that it only updates the layer cache when 153 * "update_layer_cache" is to ensure that it only updates the layer cache when
@@ -179,12 +176,12 @@ uint16_t get_event_keycode(keyevent_t event, bool update_layer_cache) {
179bool pre_process_record_quantum(keyrecord_t *record) { 176bool pre_process_record_quantum(keyrecord_t *record) {
180 if (!( 177 if (!(
181#ifdef COMBO_ENABLE 178#ifdef COMBO_ENABLE
182 process_combo(get_record_keycode(record, true), record) && 179 process_combo(get_record_keycode(record, true), record) &&
183#endif 180#endif
184 true)) { 181 true)) {
185 return false; 182 return false;
186 } 183 }
187 return true; // continue processing 184 return true; // continue processing
188} 185}
189 186
190/* Get keycode, and then call keyboard function */ 187/* Get keycode, and then call keyboard function */
@@ -296,6 +293,9 @@ bool process_record_quantum(keyrecord_t *record) {
296#ifdef JOYSTICK_ENABLE 293#ifdef JOYSTICK_ENABLE
297 process_joystick(keycode, record) && 294 process_joystick(keycode, record) &&
298#endif 295#endif
296#ifdef PROGRAMMABLE_BUTTON_ENABLE
297 process_programmable_button(keycode, record) &&
298#endif
299 true)) { 299 true)) {
300 return false; 300 return false;
301 } 301 }
@@ -465,14 +465,6 @@ void matrix_scan_quantum() {
465# include "hd44780.h" 465# include "hd44780.h"
466#endif 466#endif
467 467
468void api_send_unicode(uint32_t unicode) {
469#ifdef API_ENABLE
470 uint8_t chunk[4];
471 dword_to_bytes(unicode, chunk);
472 MT_SEND_DATA(DT_UNICODE, chunk, 5);
473#endif
474}
475
476//------------------------------------------------------------------------------ 468//------------------------------------------------------------------------------
477// Override these functions in your keymap file to play different tunes on 469// Override these functions in your keymap file to play different tunes on
478// different events such as startup and bootloader jump 470// different events such as startup and bootloader jump
@@ -480,3 +472,99 @@ void api_send_unicode(uint32_t unicode) {
480__attribute__((weak)) void startup_user() {} 472__attribute__((weak)) void startup_user() {}
481 473
482__attribute__((weak)) void shutdown_user() {} 474__attribute__((weak)) void shutdown_user() {}
475
476/** \brief Run keyboard level Power down
477 *
478 * FIXME: needs doc
479 */
480__attribute__((weak)) void suspend_power_down_user(void) {}
481/** \brief Run keyboard level Power down
482 *
483 * FIXME: needs doc
484 */
485__attribute__((weak)) void suspend_power_down_kb(void) { suspend_power_down_user(); }
486
487void suspend_power_down_quantum(void) {
488#ifndef NO_SUSPEND_POWER_DOWN
489// Turn off backlight
490# ifdef BACKLIGHT_ENABLE
491 backlight_set(0);
492# endif
493
494# ifdef LED_MATRIX_ENABLE
495 led_matrix_task();
496# endif
497# ifdef RGB_MATRIX_ENABLE
498 rgb_matrix_task();
499# endif
500
501 // Turn off LED indicators
502 uint8_t leds_off = 0;
503# if defined(BACKLIGHT_CAPS_LOCK) && defined(BACKLIGHT_ENABLE)
504 if (is_backlight_enabled()) {
505 // Don't try to turn off Caps Lock indicator as it is backlight and backlight is already off
506 leds_off |= (1 << USB_LED_CAPS_LOCK);
507 }
508# endif
509 led_set(leds_off);
510
511// Turn off audio
512# ifdef AUDIO_ENABLE
513 stop_all_notes();
514# endif
515
516// Turn off underglow
517# if defined(RGBLIGHT_SLEEP) && defined(RGBLIGHT_ENABLE)
518 rgblight_suspend();
519# endif
520
521# if defined(LED_MATRIX_ENABLE)
522 led_matrix_set_suspend_state(true);
523# endif
524# if defined(RGB_MATRIX_ENABLE)
525 rgb_matrix_set_suspend_state(true);
526# endif
527
528# ifdef OLED_ENABLE
529 oled_off();
530# endif
531# ifdef ST7565_ENABLE
532 st7565_off();
533# endif
534#endif
535}
536
537/** \brief run user level code immediately after wakeup
538 *
539 * FIXME: needs doc
540 */
541__attribute__((weak)) void suspend_wakeup_init_user(void) {}
542
543/** \brief run keyboard level code immediately after wakeup
544 *
545 * FIXME: needs doc
546 */
547__attribute__((weak)) void suspend_wakeup_init_kb(void) { suspend_wakeup_init_user(); }
548
549__attribute__((weak)) void suspend_wakeup_init_quantum(void) {
550// Turn on backlight
551#ifdef BACKLIGHT_ENABLE
552 backlight_init();
553#endif
554
555 // Restore LED indicators
556 led_set(host_keyboard_leds());
557
558// Wake up underglow
559#if defined(RGBLIGHT_SLEEP) && defined(RGBLIGHT_ENABLE)
560 rgblight_wakeup();
561#endif
562
563#if defined(LED_MATRIX_ENABLE)
564 led_matrix_set_suspend_state(false);
565#endif
566#if defined(RGB_MATRIX_ENABLE)
567 rgb_matrix_set_suspend_state(false);
568#endif
569 suspend_wakeup_init_kb();
570}
diff --git a/quantum/quantum.h b/quantum/quantum.h
index 86b717e44..9250f5acc 100644
--- a/quantum/quantum.h
+++ b/quantum/quantum.h
@@ -147,6 +147,10 @@ extern layer_state_t layer_state;
147# include "process_joystick.h" 147# include "process_joystick.h"
148#endif 148#endif
149 149
150#ifdef PROGRAMMABLE_BUTTON_ENABLE
151# include "process_programmable_button.h"
152#endif
153
150#ifdef GRAVE_ESC_ENABLE 154#ifdef GRAVE_ESC_ENABLE
151# include "process_grave_esc.h" 155# include "process_grave_esc.h"
152#endif 156#endif
@@ -233,5 +237,3 @@ void led_set_user(uint8_t usb_led);
233void led_set_kb(uint8_t usb_led); 237void led_set_kb(uint8_t usb_led);
234bool led_update_user(led_t led_state); 238bool led_update_user(led_t led_state);
235bool led_update_kb(led_t led_state); 239bool led_update_kb(led_t led_state);
236
237void api_send_unicode(uint32_t unicode);
diff --git a/quantum/quantum_keycodes.h b/quantum/quantum_keycodes.h
index ef4b0f457..2ea81dd4c 100644
--- a/quantum/quantum_keycodes.h
+++ b/quantum/quantum_keycodes.h
@@ -524,6 +524,40 @@ enum quantum_keycodes {
524 // Additional magic key 524 // Additional magic key
525 MAGIC_TOGGLE_GUI, 525 MAGIC_TOGGLE_GUI,
526 526
527 // Programmable Button
528 PROGRAMMABLE_BUTTON_1,
529 PROGRAMMABLE_BUTTON_2,
530 PROGRAMMABLE_BUTTON_3,
531 PROGRAMMABLE_BUTTON_4,
532 PROGRAMMABLE_BUTTON_5,
533 PROGRAMMABLE_BUTTON_6,
534 PROGRAMMABLE_BUTTON_7,
535 PROGRAMMABLE_BUTTON_8,
536 PROGRAMMABLE_BUTTON_9,
537 PROGRAMMABLE_BUTTON_10,
538 PROGRAMMABLE_BUTTON_11,
539 PROGRAMMABLE_BUTTON_12,
540 PROGRAMMABLE_BUTTON_13,
541 PROGRAMMABLE_BUTTON_14,
542 PROGRAMMABLE_BUTTON_15,
543 PROGRAMMABLE_BUTTON_16,
544 PROGRAMMABLE_BUTTON_17,
545 PROGRAMMABLE_BUTTON_18,
546 PROGRAMMABLE_BUTTON_19,
547 PROGRAMMABLE_BUTTON_20,
548 PROGRAMMABLE_BUTTON_21,
549 PROGRAMMABLE_BUTTON_22,
550 PROGRAMMABLE_BUTTON_23,
551 PROGRAMMABLE_BUTTON_24,
552 PROGRAMMABLE_BUTTON_25,
553 PROGRAMMABLE_BUTTON_26,
554 PROGRAMMABLE_BUTTON_27,
555 PROGRAMMABLE_BUTTON_28,
556 PROGRAMMABLE_BUTTON_29,
557 PROGRAMMABLE_BUTTON_30,
558 PROGRAMMABLE_BUTTON_31,
559 PROGRAMMABLE_BUTTON_32,
560
527 // Start of custom keycode range for keyboards and keymaps - always leave at the end 561 // Start of custom keycode range for keyboards and keymaps - always leave at the end
528 SAFE_RANGE 562 SAFE_RANGE
529}; 563};
@@ -854,3 +888,39 @@ enum quantum_keycodes {
854#define OS_TOGG ONESHOT_TOGGLE 888#define OS_TOGG ONESHOT_TOGGLE
855#define OS_ON ONESHOT_ENABLE 889#define OS_ON ONESHOT_ENABLE
856#define OS_OFF ONESHOT_DISABLE 890#define OS_OFF ONESHOT_DISABLE
891
892// Programmable Button aliases
893#define PB_1 PROGRAMMABLE_BUTTON_1
894#define PB_2 PROGRAMMABLE_BUTTON_2
895#define PB_3 PROGRAMMABLE_BUTTON_3
896#define PB_4 PROGRAMMABLE_BUTTON_4
897#define PB_5 PROGRAMMABLE_BUTTON_5
898#define PB_6 PROGRAMMABLE_BUTTON_6
899#define PB_7 PROGRAMMABLE_BUTTON_7
900#define PB_8 PROGRAMMABLE_BUTTON_8
901#define PB_9 PROGRAMMABLE_BUTTON_9
902#define PB_10 PROGRAMMABLE_BUTTON_10
903#define PB_11 PROGRAMMABLE_BUTTON_11
904#define PB_12 PROGRAMMABLE_BUTTON_12
905#define PB_13 PROGRAMMABLE_BUTTON_13
906#define PB_14 PROGRAMMABLE_BUTTON_14
907#define PB_15 PROGRAMMABLE_BUTTON_15
908#define PB_16 PROGRAMMABLE_BUTTON_16
909#define PB_17 PROGRAMMABLE_BUTTON_17
910#define PB_18 PROGRAMMABLE_BUTTON_18
911#define PB_19 PROGRAMMABLE_BUTTON_19
912#define PB_20 PROGRAMMABLE_BUTTON_20
913#define PB_21 PROGRAMMABLE_BUTTON_21
914#define PB_22 PROGRAMMABLE_BUTTON_22
915#define PB_23 PROGRAMMABLE_BUTTON_23
916#define PB_24 PROGRAMMABLE_BUTTON_24
917#define PB_25 PROGRAMMABLE_BUTTON_25
918#define PB_26 PROGRAMMABLE_BUTTON_26
919#define PB_27 PROGRAMMABLE_BUTTON_27
920#define PB_28 PROGRAMMABLE_BUTTON_28
921#define PB_29 PROGRAMMABLE_BUTTON_29
922#define PB_30 PROGRAMMABLE_BUTTON_30
923#define PB_31 PROGRAMMABLE_BUTTON_31
924#define PB_32 PROGRAMMABLE_BUTTON_32
925#define PROGRAMMABLE_BUTTON_MIN PROGRAMMABLE_BUTTON_1
926#define PROGRAMMABLE_BUTTON_MAX PROGRAMMABLE_BUTTON_32
diff --git a/quantum/rgb_matrix/rgb_matrix.c b/quantum/rgb_matrix/rgb_matrix.c
index 8f00b4087..c260b6e1e 100644
--- a/quantum/rgb_matrix/rgb_matrix.c
+++ b/quantum/rgb_matrix/rgb_matrix.c
@@ -31,14 +31,6 @@ const led_point_t k_rgb_matrix_center = {112, 32};
31const led_point_t k_rgb_matrix_center = RGB_MATRIX_CENTER; 31const led_point_t k_rgb_matrix_center = RGB_MATRIX_CENTER;
32#endif 32#endif
33 33
34// clang-format off
35#ifndef RGB_MATRIX_IMMEDIATE_EEPROM
36# define rgb_eeconfig_update(v) rgb_update_eeprom |= v
37#else
38# define rgb_eeconfig_update(v) if (v) eeconfig_update_rgb_matrix()
39#endif
40// clang-format on
41
42__attribute__((weak)) RGB rgb_matrix_hsv_to_rgb(HSV hsv) { return hsv_to_rgb(hsv); } 34__attribute__((weak)) RGB rgb_matrix_hsv_to_rgb(HSV hsv) { return hsv_to_rgb(hsv); }
43 35
44// Generic effect runners 36// Generic effect runners
@@ -128,7 +120,6 @@ last_hit_t g_last_hit_tracker;
128 120
129// internals 121// internals
130static bool suspend_state = false; 122static bool suspend_state = false;
131static bool rgb_update_eeprom = false;
132static uint8_t rgb_last_enable = UINT8_MAX; 123static uint8_t rgb_last_enable = UINT8_MAX;
133static uint8_t rgb_last_effect = UINT8_MAX; 124static uint8_t rgb_last_effect = UINT8_MAX;
134static effect_params_t rgb_effect_params = {0, LED_FLAG_ALL, false}; 125static effect_params_t rgb_effect_params = {0, LED_FLAG_ALL, false};
@@ -148,9 +139,9 @@ static last_hit_t last_hit_buffer;
148const uint8_t k_rgb_matrix_split[2] = RGB_MATRIX_SPLIT; 139const uint8_t k_rgb_matrix_split[2] = RGB_MATRIX_SPLIT;
149#endif 140#endif
150 141
151void eeconfig_read_rgb_matrix(void) { eeprom_read_block(&rgb_matrix_config, EECONFIG_RGB_MATRIX, sizeof(rgb_matrix_config)); } 142EECONFIG_DEBOUNCE_HELPER(rgb_matrix, EECONFIG_RGB_MATRIX, rgb_matrix_config);
152 143
153void eeconfig_update_rgb_matrix(void) { eeprom_update_block(&rgb_matrix_config, EECONFIG_RGB_MATRIX, sizeof(rgb_matrix_config)); } 144void eeconfig_update_rgb_matrix(void) { eeconfig_flush_rgb_matrix(true); }
154 145
155void eeconfig_update_rgb_matrix_default(void) { 146void eeconfig_update_rgb_matrix_default(void) {
156 dprintf("eeconfig_update_rgb_matrix_default\n"); 147 dprintf("eeconfig_update_rgb_matrix_default\n");
@@ -159,7 +150,7 @@ void eeconfig_update_rgb_matrix_default(void) {
159 rgb_matrix_config.hsv = (HSV){RGB_MATRIX_STARTUP_HUE, RGB_MATRIX_STARTUP_SAT, RGB_MATRIX_STARTUP_VAL}; 150 rgb_matrix_config.hsv = (HSV){RGB_MATRIX_STARTUP_HUE, RGB_MATRIX_STARTUP_SAT, RGB_MATRIX_STARTUP_VAL};
160 rgb_matrix_config.speed = RGB_MATRIX_STARTUP_SPD; 151 rgb_matrix_config.speed = RGB_MATRIX_STARTUP_SPD;
161 rgb_matrix_config.flags = LED_FLAG_ALL; 152 rgb_matrix_config.flags = LED_FLAG_ALL;
162 eeconfig_update_rgb_matrix(); 153 eeconfig_flush_rgb_matrix(true);
163} 154}
164 155
165void eeconfig_debug_rgb_matrix(void) { 156void eeconfig_debug_rgb_matrix(void) {
@@ -314,9 +305,8 @@ static void rgb_task_timers(void) {
314} 305}
315 306
316static void rgb_task_sync(void) { 307static void rgb_task_sync(void) {
308 eeconfig_flush_rgb_matrix(false);
317 // next task 309 // next task
318 if (rgb_update_eeprom) eeconfig_update_rgb_matrix();
319 rgb_update_eeprom = false;
320 if (sync_timer_elapsed32(g_rgb_timer) >= RGB_MATRIX_LED_FLUSH_LIMIT) rgb_task_state = STARTING; 310 if (sync_timer_elapsed32(g_rgb_timer) >= RGB_MATRIX_LED_FLUSH_LIMIT) rgb_task_state = STARTING;
321} 311}
322 312
@@ -491,7 +481,7 @@ void rgb_matrix_init(void) {
491 eeconfig_update_rgb_matrix_default(); 481 eeconfig_update_rgb_matrix_default();
492 } 482 }
493 483
494 eeconfig_read_rgb_matrix(); 484 eeconfig_init_rgb_matrix();
495 if (!rgb_matrix_config.mode) { 485 if (!rgb_matrix_config.mode) {
496 dprintf("rgb_matrix_init_drivers rgb_matrix_config.mode = 0. Write default values to EEPROM.\n"); 486 dprintf("rgb_matrix_init_drivers rgb_matrix_config.mode = 0. Write default values to EEPROM.\n");
497 eeconfig_update_rgb_matrix_default(); 487 eeconfig_update_rgb_matrix_default();
@@ -514,7 +504,7 @@ bool rgb_matrix_get_suspend_state(void) { return suspend_state; }
514void rgb_matrix_toggle_eeprom_helper(bool write_to_eeprom) { 504void rgb_matrix_toggle_eeprom_helper(bool write_to_eeprom) {
515 rgb_matrix_config.enable ^= 1; 505 rgb_matrix_config.enable ^= 1;
516 rgb_task_state = STARTING; 506 rgb_task_state = STARTING;
517 rgb_eeconfig_update(write_to_eeprom); 507 eeconfig_flag_rgb_matrix(write_to_eeprom);
518 dprintf("rgb matrix toggle [%s]: rgb_matrix_config.enable = %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", rgb_matrix_config.enable); 508 dprintf("rgb matrix toggle [%s]: rgb_matrix_config.enable = %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", rgb_matrix_config.enable);
519} 509}
520void rgb_matrix_toggle_noeeprom(void) { rgb_matrix_toggle_eeprom_helper(false); } 510void rgb_matrix_toggle_noeeprom(void) { rgb_matrix_toggle_eeprom_helper(false); }
@@ -522,7 +512,7 @@ void rgb_matrix_toggle(void) { rgb_matrix_toggle_eeprom_helper(true); }
522 512
523void rgb_matrix_enable(void) { 513void rgb_matrix_enable(void) {
524 rgb_matrix_enable_noeeprom(); 514 rgb_matrix_enable_noeeprom();
525 rgb_eeconfig_update(true); 515 eeconfig_flag_rgb_matrix(true);
526} 516}
527 517
528void rgb_matrix_enable_noeeprom(void) { 518void rgb_matrix_enable_noeeprom(void) {
@@ -532,7 +522,7 @@ void rgb_matrix_enable_noeeprom(void) {
532 522
533void rgb_matrix_disable(void) { 523void rgb_matrix_disable(void) {
534 rgb_matrix_disable_noeeprom(); 524 rgb_matrix_disable_noeeprom();
535 rgb_eeconfig_update(true); 525 eeconfig_flag_rgb_matrix(true);
536} 526}
537 527
538void rgb_matrix_disable_noeeprom(void) { 528void rgb_matrix_disable_noeeprom(void) {
@@ -554,7 +544,7 @@ void rgb_matrix_mode_eeprom_helper(uint8_t mode, bool write_to_eeprom) {
554 rgb_matrix_config.mode = mode; 544 rgb_matrix_config.mode = mode;
555 } 545 }
556 rgb_task_state = STARTING; 546 rgb_task_state = STARTING;
557 rgb_eeconfig_update(write_to_eeprom); 547 eeconfig_flag_rgb_matrix(write_to_eeprom);
558 dprintf("rgb matrix mode [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", rgb_matrix_config.mode); 548 dprintf("rgb matrix mode [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", rgb_matrix_config.mode);
559} 549}
560void rgb_matrix_mode_noeeprom(uint8_t mode) { rgb_matrix_mode_eeprom_helper(mode, false); } 550void rgb_matrix_mode_noeeprom(uint8_t mode) { rgb_matrix_mode_eeprom_helper(mode, false); }
@@ -583,7 +573,7 @@ void rgb_matrix_sethsv_eeprom_helper(uint16_t hue, uint8_t sat, uint8_t val, boo
583 rgb_matrix_config.hsv.h = hue; 573 rgb_matrix_config.hsv.h = hue;
584 rgb_matrix_config.hsv.s = sat; 574 rgb_matrix_config.hsv.s = sat;
585 rgb_matrix_config.hsv.v = (val > RGB_MATRIX_MAXIMUM_BRIGHTNESS) ? RGB_MATRIX_MAXIMUM_BRIGHTNESS : val; 575 rgb_matrix_config.hsv.v = (val > RGB_MATRIX_MAXIMUM_BRIGHTNESS) ? RGB_MATRIX_MAXIMUM_BRIGHTNESS : val;
586 rgb_eeconfig_update(write_to_eeprom); 576 eeconfig_flag_rgb_matrix(write_to_eeprom);
587 dprintf("rgb matrix set hsv [%s]: %u,%u,%u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", rgb_matrix_config.hsv.h, rgb_matrix_config.hsv.s, rgb_matrix_config.hsv.v); 577 dprintf("rgb matrix set hsv [%s]: %u,%u,%u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", rgb_matrix_config.hsv.h, rgb_matrix_config.hsv.s, rgb_matrix_config.hsv.v);
588} 578}
589void rgb_matrix_sethsv_noeeprom(uint16_t hue, uint8_t sat, uint8_t val) { rgb_matrix_sethsv_eeprom_helper(hue, sat, val, false); } 579void rgb_matrix_sethsv_noeeprom(uint16_t hue, uint8_t sat, uint8_t val) { rgb_matrix_sethsv_eeprom_helper(hue, sat, val, false); }
@@ -620,7 +610,7 @@ void rgb_matrix_decrease_val(void) { rgb_matrix_decrease_val_helper(true); }
620 610
621void rgb_matrix_set_speed_eeprom_helper(uint8_t speed, bool write_to_eeprom) { 611void rgb_matrix_set_speed_eeprom_helper(uint8_t speed, bool write_to_eeprom) {
622 rgb_matrix_config.speed = speed; 612 rgb_matrix_config.speed = speed;
623 rgb_eeconfig_update(write_to_eeprom); 613 eeconfig_flag_rgb_matrix(write_to_eeprom);
624 dprintf("rgb matrix set speed [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", rgb_matrix_config.speed); 614 dprintf("rgb matrix set speed [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", rgb_matrix_config.speed);
625} 615}
626void rgb_matrix_set_speed_noeeprom(uint8_t speed) { rgb_matrix_set_speed_eeprom_helper(speed, false); } 616void rgb_matrix_set_speed_noeeprom(uint8_t speed) { rgb_matrix_set_speed_eeprom_helper(speed, false); }
diff --git a/quantum/rgb_matrix/rgb_matrix_drivers.c b/quantum/rgb_matrix/rgb_matrix_drivers.c
index 2cec162e2..4335088eb 100644
--- a/quantum/rgb_matrix/rgb_matrix_drivers.c
+++ b/quantum/rgb_matrix/rgb_matrix_drivers.c
@@ -24,110 +24,126 @@
24 */ 24 */
25 25
26#if defined(IS31FL3731) || defined(IS31FL3733) || defined(IS31FL3737) || defined(IS31FL3741) 26#if defined(IS31FL3731) || defined(IS31FL3733) || defined(IS31FL3737) || defined(IS31FL3741)
27
28# include "i2c_master.h" 27# include "i2c_master.h"
29 28
29// TODO: Remove this at some later date
30# if defined(DRIVER_ADDR_1) && defined(DRIVER_ADDR_2)
31# if DRIVER_ADDR_1 == DRIVER_ADDR_2
32# error "Setting DRIVER_ADDR_2 == DRIVER_ADDR_1 is obsolete. If you are only using one ISSI driver, set DRIVER_COUNT to 1 and remove DRIVER_ADDR_2"
33# endif
34# endif
35
30static void init(void) { 36static void init(void) {
31 i2c_init(); 37 i2c_init();
32# ifdef IS31FL3731 38
39# if defined(IS31FL3731)
33 IS31FL3731_init(DRIVER_ADDR_1); 40 IS31FL3731_init(DRIVER_ADDR_1);
34# ifdef DRIVER_ADDR_2 41# if defined(DRIVER_ADDR_2)
35 IS31FL3731_init(DRIVER_ADDR_2); 42 IS31FL3731_init(DRIVER_ADDR_2);
36# endif 43# if defined(DRIVER_ADDR_3)
37# ifdef DRIVER_ADDR_3
38 IS31FL3731_init(DRIVER_ADDR_3); 44 IS31FL3731_init(DRIVER_ADDR_3);
39# endif 45# if defined(DRIVER_ADDR_4)
40# ifdef DRIVER_ADDR_4
41 IS31FL3731_init(DRIVER_ADDR_4); 46 IS31FL3731_init(DRIVER_ADDR_4);
47# endif
48# endif
42# endif 49# endif
50
43# elif defined(IS31FL3733) 51# elif defined(IS31FL3733)
44# ifndef DRIVER_SYNC_1 52# if !defined(DRIVER_SYNC_1)
45# define DRIVER_SYNC_1 0 53# define DRIVER_SYNC_1 0
46# endif 54# endif
47 IS31FL3733_init(DRIVER_ADDR_1, DRIVER_SYNC_1); 55 IS31FL3733_init(DRIVER_ADDR_1, DRIVER_SYNC_1);
48# if defined DRIVER_ADDR_2 && (DRIVER_ADDR_1 != DRIVER_ADDR_2) 56# if defined(DRIVER_ADDR_2)
49# ifndef DRIVER_SYNC_2 57# if !defined(DRIVER_SYNC_2)
50# define DRIVER_SYNC_2 0 58# define DRIVER_SYNC_2 0
51# endif 59# endif
52 IS31FL3733_init(DRIVER_ADDR_2, DRIVER_SYNC_2); 60 IS31FL3733_init(DRIVER_ADDR_2, DRIVER_SYNC_2);
53# endif 61# if defined(DRIVER_ADDR_3)
54# ifdef DRIVER_ADDR_3 62# if !defined(DRIVER_SYNC_3)
55# ifndef DRIVER_SYNC_3 63# define DRIVER_SYNC_3 0
56# define DRIVER_SYNC_3 0 64# endif
57# endif
58 IS31FL3733_init(DRIVER_ADDR_3, DRIVER_SYNC_3); 65 IS31FL3733_init(DRIVER_ADDR_3, DRIVER_SYNC_3);
59# endif 66# if defined(DRIVER_ADDR_4)
60# ifdef DRIVER_ADDR_4 67# if !defined(DRIVER_SYNC_4)
61# ifndef DRIVER_SYNC_4 68# define DRIVER_SYNC_4 0
62# define DRIVER_SYNC_4 0 69# endif
63# endif
64 IS31FL3733_init(DRIVER_ADDR_4, DRIVER_SYNC_4); 70 IS31FL3733_init(DRIVER_ADDR_4, DRIVER_SYNC_4);
71# endif
72# endif
65# endif 73# endif
74
66# elif defined(IS31FL3737) 75# elif defined(IS31FL3737)
67 IS31FL3737_init(DRIVER_ADDR_1); 76 IS31FL3737_init(DRIVER_ADDR_1);
68# if defined(DRIVER_ADDR_2) && (DRIVER_ADDR_2 != DRIVER_ADDR_1) // provides backward compatibility 77# if defined(DRIVER_ADDR_2)
69 IS31FL3737_init(DRIVER_ADDR_2); 78 IS31FL3737_init(DRIVER_ADDR_2);
70# endif 79# endif
71# else 80
81# elif defined(IS31FL3741)
72 IS31FL3741_init(DRIVER_ADDR_1); 82 IS31FL3741_init(DRIVER_ADDR_1);
73# endif 83# endif
84
74 for (int index = 0; index < DRIVER_LED_TOTAL; index++) { 85 for (int index = 0; index < DRIVER_LED_TOTAL; index++) {
75 bool enabled = true; 86 bool enabled = true;
87
76 // This only caches it for later 88 // This only caches it for later
77# ifdef IS31FL3731 89# if defined(IS31FL3731)
78 IS31FL3731_set_led_control_register(index, enabled, enabled, enabled); 90 IS31FL3731_set_led_control_register(index, enabled, enabled, enabled);
79# elif defined(IS31FL3733) 91# elif defined(IS31FL3733)
80 IS31FL3733_set_led_control_register(index, enabled, enabled, enabled); 92 IS31FL3733_set_led_control_register(index, enabled, enabled, enabled);
81# elif defined(IS31FL3737) 93# elif defined(IS31FL3737)
82 IS31FL3737_set_led_control_register(index, enabled, enabled, enabled); 94 IS31FL3737_set_led_control_register(index, enabled, enabled, enabled);
83# else 95# elif defined(IS31FL3741)
84 IS31FL3741_set_led_control_register(index, enabled, enabled, enabled); 96 IS31FL3741_set_led_control_register(index, enabled, enabled, enabled);
85# endif 97# endif
86 } 98 }
99
87 // This actually updates the LED drivers 100 // This actually updates the LED drivers
88# ifdef IS31FL3731 101# if defined(IS31FL3731)
89 IS31FL3731_update_led_control_registers(DRIVER_ADDR_1, 0); 102 IS31FL3731_update_led_control_registers(DRIVER_ADDR_1, 0);
90# ifdef DRIVER_ADDR_2 103# if defined(DRIVER_ADDR_2)
91 IS31FL3731_update_led_control_registers(DRIVER_ADDR_2, 1); 104 IS31FL3731_update_led_control_registers(DRIVER_ADDR_2, 1);
92# endif 105# if defined(DRIVER_ADDR_3)
93# ifdef DRIVER_ADDR_3
94 IS31FL3731_update_led_control_registers(DRIVER_ADDR_3, 2); 106 IS31FL3731_update_led_control_registers(DRIVER_ADDR_3, 2);
95# endif 107# if defined(DRIVER_ADDR_4)
96# ifdef DRIVER_ADDR_4
97 IS31FL3731_update_led_control_registers(DRIVER_ADDR_4, 3); 108 IS31FL3731_update_led_control_registers(DRIVER_ADDR_4, 3);
109# endif
110# endif
98# endif 111# endif
112
99# elif defined(IS31FL3733) 113# elif defined(IS31FL3733)
100 IS31FL3733_update_led_control_registers(DRIVER_ADDR_1, 0); 114 IS31FL3733_update_led_control_registers(DRIVER_ADDR_1, 0);
101# ifdef DRIVER_ADDR_2 115# if defined(DRIVER_ADDR_2)
102 IS31FL3733_update_led_control_registers(DRIVER_ADDR_2, 1); 116 IS31FL3733_update_led_control_registers(DRIVER_ADDR_2, 1);
103# endif 117# if defined(DRIVER_ADDR_3)
104# ifdef DRIVER_ADDR_3
105 IS31FL3733_update_led_control_registers(DRIVER_ADDR_3, 2); 118 IS31FL3733_update_led_control_registers(DRIVER_ADDR_3, 2);
106# endif 119# if defined(DRIVER_ADDR_4)
107# ifdef DRIVER_ADDR_4
108 IS31FL3733_update_led_control_registers(DRIVER_ADDR_4, 3); 120 IS31FL3733_update_led_control_registers(DRIVER_ADDR_4, 3);
121# endif
122# endif
109# endif 123# endif
124
110# elif defined(IS31FL3737) 125# elif defined(IS31FL3737)
111 IS31FL3737_update_led_control_registers(DRIVER_ADDR_1, 0); 126 IS31FL3737_update_led_control_registers(DRIVER_ADDR_1, 0);
112# if defined(DRIVER_ADDR_2) && (DRIVER_ADDR_2 != DRIVER_ADDR_1) // provides backward compatibility 127# if defined(DRIVER_ADDR_2)
113 IS31FL3737_update_led_control_registers(DRIVER_ADDR_2, 1); 128 IS31FL3737_update_led_control_registers(DRIVER_ADDR_2, 1);
114# endif 129# endif
115# else 130
131# elif defined(IS31FL3741)
116 IS31FL3741_update_led_control_registers(DRIVER_ADDR_1, 0); 132 IS31FL3741_update_led_control_registers(DRIVER_ADDR_1, 0);
117# endif 133# endif
118} 134}
119 135
120# ifdef IS31FL3731 136# if defined(IS31FL3731)
121static void flush(void) { 137static void flush(void) {
122 IS31FL3731_update_pwm_buffers(DRIVER_ADDR_1, 0); 138 IS31FL3731_update_pwm_buffers(DRIVER_ADDR_1, 0);
123# ifdef DRIVER_ADDR_2 139# if defined(DRIVER_ADDR_2)
124 IS31FL3731_update_pwm_buffers(DRIVER_ADDR_2, 1); 140 IS31FL3731_update_pwm_buffers(DRIVER_ADDR_2, 1);
125# endif 141# if defined(DRIVER_ADDR_3)
126# ifdef DRIVER_ADDR_3
127 IS31FL3731_update_pwm_buffers(DRIVER_ADDR_3, 2); 142 IS31FL3731_update_pwm_buffers(DRIVER_ADDR_3, 2);
128# endif 143# if defined(DRIVER_ADDR_4)
129# ifdef DRIVER_ADDR_4
130 IS31FL3731_update_pwm_buffers(DRIVER_ADDR_4, 3); 144 IS31FL3731_update_pwm_buffers(DRIVER_ADDR_4, 3);
145# endif
146# endif
131# endif 147# endif
132} 148}
133 149
@@ -137,17 +153,18 @@ const rgb_matrix_driver_t rgb_matrix_driver = {
137 .set_color = IS31FL3731_set_color, 153 .set_color = IS31FL3731_set_color,
138 .set_color_all = IS31FL3731_set_color_all, 154 .set_color_all = IS31FL3731_set_color_all,
139}; 155};
156
140# elif defined(IS31FL3733) 157# elif defined(IS31FL3733)
141static void flush(void) { 158static void flush(void) {
142 IS31FL3733_update_pwm_buffers(DRIVER_ADDR_1, 0); 159 IS31FL3733_update_pwm_buffers(DRIVER_ADDR_1, 0);
143# ifdef DRIVER_ADDR_2 160# if defined(DRIVER_ADDR_2)
144 IS31FL3733_update_pwm_buffers(DRIVER_ADDR_2, 1); 161 IS31FL3733_update_pwm_buffers(DRIVER_ADDR_2, 1);
145# endif 162# if defined(DRIVER_ADDR_3)
146# ifdef DRIVER_ADDR_3
147 IS31FL3733_update_pwm_buffers(DRIVER_ADDR_3, 2); 163 IS31FL3733_update_pwm_buffers(DRIVER_ADDR_3, 2);
148# endif 164# if defined(DRIVER_ADDR_4)
149# ifdef DRIVER_ADDR_4
150 IS31FL3733_update_pwm_buffers(DRIVER_ADDR_4, 3); 165 IS31FL3733_update_pwm_buffers(DRIVER_ADDR_4, 3);
166# endif
167# endif
151# endif 168# endif
152} 169}
153 170
@@ -157,10 +174,11 @@ const rgb_matrix_driver_t rgb_matrix_driver = {
157 .set_color = IS31FL3733_set_color, 174 .set_color = IS31FL3733_set_color,
158 .set_color_all = IS31FL3733_set_color_all, 175 .set_color_all = IS31FL3733_set_color_all,
159}; 176};
177
160# elif defined(IS31FL3737) 178# elif defined(IS31FL3737)
161static void flush(void) { 179static void flush(void) {
162 IS31FL3737_update_pwm_buffers(DRIVER_ADDR_1, 0); 180 IS31FL3737_update_pwm_buffers(DRIVER_ADDR_1, 0);
163# if defined(DRIVER_ADDR_2) && (DRIVER_ADDR_2 != DRIVER_ADDR_1) // provides backward compatibility 181# if defined(DRIVER_ADDR_2)
164 IS31FL3737_update_pwm_buffers(DRIVER_ADDR_2, 1); 182 IS31FL3737_update_pwm_buffers(DRIVER_ADDR_2, 1);
165# endif 183# endif
166} 184}
@@ -171,10 +189,11 @@ const rgb_matrix_driver_t rgb_matrix_driver = {
171 .set_color = IS31FL3737_set_color, 189 .set_color = IS31FL3737_set_color,
172 .set_color_all = IS31FL3737_set_color_all, 190 .set_color_all = IS31FL3737_set_color_all,
173}; 191};
174# else 192
193# elif defined(IS31FL3741)
175static void flush(void) { 194static void flush(void) {
176 IS31FL3741_update_pwm_buffers(DRIVER_ADDR_1, 0); 195 IS31FL3741_update_pwm_buffers(DRIVER_ADDR_1, 0);
177# if defined(DRIVER_ADDR_2) && (DRIVER_ADDR_2 != DRIVER_ADDR_1) // provides backward compatibility 196# if defined(DRIVER_ADDR_2)
178 IS31FL3741_update_pwm_buffers(DRIVER_ADDR_2, 1); 197 IS31FL3741_update_pwm_buffers(DRIVER_ADDR_2, 1);
179# endif 198# endif
180} 199}
@@ -189,17 +208,19 @@ const rgb_matrix_driver_t rgb_matrix_driver = {
189 208
190#elif defined(AW20216) 209#elif defined(AW20216)
191# include "spi_master.h" 210# include "spi_master.h"
211
192static void init(void) { 212static void init(void) {
193 spi_init(); 213 spi_init();
214
194 AW20216_init(DRIVER_1_CS, DRIVER_1_EN); 215 AW20216_init(DRIVER_1_CS, DRIVER_1_EN);
195# ifdef DRIVER_2_CS 216# if defined(DRIVER_2_CS)
196 AW20216_init(DRIVER_2_CS, DRIVER_2_EN); 217 AW20216_init(DRIVER_2_CS, DRIVER_2_EN);
197# endif 218# endif
198} 219}
199 220
200static void flush(void) { 221static void flush(void) {
201 AW20216_update_pwm_buffers(DRIVER_1_CS, 0); 222 AW20216_update_pwm_buffers(DRIVER_1_CS, 0);
202# ifdef DRIVER_2_CS 223# if defined(DRIVER_2_CS)
203 AW20216_update_pwm_buffers(DRIVER_2_CS, 1); 224 AW20216_update_pwm_buffers(DRIVER_2_CS, 1);
204# endif 225# endif
205} 226}
diff --git a/quantum/sequencer/tests/rules.mk b/quantum/sequencer/tests/rules.mk
index 76c221cf9..87a204669 100644
--- a/quantum/sequencer/tests/rules.mk
+++ b/quantum/sequencer/tests/rules.mk
@@ -1,5 +1,5 @@
1# The letter case of these variables might seem odd. However: 1# The letter case of these variables might seem odd. However:
2# - it is consistent with the serial_link example that is used as a reference in the Unit Testing article (https://docs.qmk.fm/#/unit_testing?id=adding-tests-for-new-or-existing-features) 2# - it is consistent with the example that is used as a reference in the Unit Testing article (https://docs.qmk.fm/#/unit_testing?id=adding-tests-for-new-or-existing-features)
3# - Neither `make test:sequencer` or `make test:SEQUENCER` work when using SCREAMING_SNAKE_CASE 3# - Neither `make test:sequencer` or `make test:SEQUENCER` work when using SCREAMING_SNAKE_CASE
4 4
5sequencer_DEFS := -DNO_DEBUG -DMIDI_MOCKED 5sequencer_DEFS := -DNO_DEBUG -DMIDI_MOCKED
diff --git a/quantum/serial_link/LICENSE b/quantum/serial_link/LICENSE
deleted file mode 100644
index d13cc4b26..000000000
--- a/quantum/serial_link/LICENSE
+++ /dev/null
@@ -1,19 +0,0 @@
1The MIT License (MIT)
2
3Permission is hereby granted, free of charge, to any person obtaining a copy
4of this software and associated documentation files (the "Software"), to deal
5in the Software without restriction, including without limitation the rights
6to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7copies of the Software, and to permit persons to whom the Software is
8furnished to do so, subject to the following conditions:
9
10The above copyright notice and this permission notice shall be included in all
11copies or substantial portions of the Software.
12
13THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19SOFTWARE.
diff --git a/quantum/serial_link/README.md b/quantum/serial_link/README.md
deleted file mode 100644
index 05871dbdf..000000000
--- a/quantum/serial_link/README.md
+++ /dev/null
@@ -1 +0,0 @@
1# qmk_serial_link
diff --git a/quantum/serial_link/protocol/byte_stuffer.c b/quantum/serial_link/protocol/byte_stuffer.c
deleted file mode 100644
index d3a91d828..000000000
--- a/quantum/serial_link/protocol/byte_stuffer.c
+++ /dev/null
@@ -1,135 +0,0 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#include "serial_link/protocol/byte_stuffer.h"
26#include "serial_link/protocol/frame_validator.h"
27#include "serial_link/protocol/physical.h"
28#include <stdbool.h>
29
30// This implements the "Consistent overhead byte stuffing protocol"
31// https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing
32// http://www.stuartcheshire.org/papers/COBSforToN.pdf
33
34typedef struct byte_stuffer_state {
35 uint16_t next_zero;
36 uint16_t data_pos;
37 bool long_frame;
38 uint8_t data[MAX_FRAME_SIZE];
39} byte_stuffer_state_t;
40
41static byte_stuffer_state_t states[NUM_LINKS];
42
43void init_byte_stuffer_state(byte_stuffer_state_t* state) {
44 state->next_zero = 0;
45 state->data_pos = 0;
46 state->long_frame = false;
47}
48
49void init_byte_stuffer(void) {
50 int i;
51 for (i = 0; i < NUM_LINKS; i++) {
52 init_byte_stuffer_state(&states[i]);
53 }
54}
55
56void byte_stuffer_recv_byte(uint8_t link, uint8_t data) {
57 byte_stuffer_state_t* state = &states[link];
58 // Start of a new frame
59 if (state->next_zero == 0) {
60 state->next_zero = data;
61 state->long_frame = data == 0xFF;
62 state->data_pos = 0;
63 return;
64 }
65
66 state->next_zero--;
67 if (data == 0) {
68 if (state->next_zero == 0) {
69 // The frame is completed
70 if (state->data_pos > 0) {
71 validator_recv_frame(link, state->data, state->data_pos);
72 }
73 } else {
74 // The frame is invalid, so reset
75 init_byte_stuffer_state(state);
76 }
77 } else {
78 if (state->data_pos == MAX_FRAME_SIZE) {
79 // We exceeded our maximum frame size
80 // therefore there's nothing else to do than reset to a new frame
81 state->next_zero = data;
82 state->long_frame = data == 0xFF;
83 state->data_pos = 0;
84 } else if (state->next_zero == 0) {
85 if (state->long_frame) {
86 // This is part of a long frame, so continue
87 state->next_zero = data;
88 state->long_frame = data == 0xFF;
89 } else {
90 // Special case for zeroes
91 state->next_zero = data;
92 state->data[state->data_pos++] = 0;
93 }
94 } else {
95 state->data[state->data_pos++] = data;
96 }
97 }
98}
99
100static void send_block(uint8_t link, uint8_t* start, uint8_t* end, uint8_t num_non_zero) {
101 send_data(link, &num_non_zero, 1);
102 if (end > start) {
103 send_data(link, start, end - start);
104 }
105}
106
107void byte_stuffer_send_frame(uint8_t link, uint8_t* data, uint16_t size) {
108 const uint8_t zero = 0;
109 if (size > 0) {
110 uint16_t num_non_zero = 1;
111 uint8_t* end = data + size;
112 uint8_t* start = data;
113 while (data < end) {
114 if (num_non_zero == 0xFF) {
115 // There's more data after big non-zero block
116 // So send it, and start a new block
117 send_block(link, start, data, num_non_zero);
118 start = data;
119 num_non_zero = 1;
120 } else {
121 if (*data == 0) {
122 // A zero encountered, so send the block
123 send_block(link, start, data, num_non_zero);
124 start = data + 1;
125 num_non_zero = 1;
126 } else {
127 num_non_zero++;
128 }
129 ++data;
130 }
131 }
132 send_block(link, start, data, num_non_zero);
133 send_data(link, &zero, 1);
134 }
135}
diff --git a/quantum/serial_link/protocol/byte_stuffer.h b/quantum/serial_link/protocol/byte_stuffer.h
deleted file mode 100644
index 397ed3baa..000000000
--- a/quantum/serial_link/protocol/byte_stuffer.h
+++ /dev/null
@@ -1,34 +0,0 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#pragma once
26
27#include <stdint.h>
28
29#define MAX_FRAME_SIZE 1024
30#define NUM_LINKS 2
31
32void init_byte_stuffer(void);
33void byte_stuffer_recv_byte(uint8_t link, uint8_t data);
34void byte_stuffer_send_frame(uint8_t link, uint8_t* data, uint16_t size);
diff --git a/quantum/serial_link/protocol/frame_router.c b/quantum/serial_link/protocol/frame_router.c
deleted file mode 100644
index 529267370..000000000
--- a/quantum/serial_link/protocol/frame_router.c
+++ /dev/null
@@ -1,64 +0,0 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#include "serial_link/protocol/frame_router.h"
26#include "serial_link/protocol/transport.h"
27#include "serial_link/protocol/frame_validator.h"
28
29static bool is_master;
30
31void router_set_master(bool master) { is_master = master; }
32
33void route_incoming_frame(uint8_t link, uint8_t* data, uint16_t size) {
34 if (is_master) {
35 if (link == DOWN_LINK) {
36 transport_recv_frame(data[size - 1], data, size - 1);
37 }
38 } else {
39 if (link == UP_LINK) {
40 if (data[size - 1] & 1) {
41 transport_recv_frame(0, data, size - 1);
42 }
43 data[size - 1] >>= 1;
44 validator_send_frame(DOWN_LINK, data, size);
45 } else {
46 data[size - 1]++;
47 validator_send_frame(UP_LINK, data, size);
48 }
49 }
50}
51
52void router_send_frame(uint8_t destination, uint8_t* data, uint16_t size) {
53 if (destination == 0) {
54 if (!is_master) {
55 data[size] = 1;
56 validator_send_frame(UP_LINK, data, size + 1);
57 }
58 } else {
59 if (is_master) {
60 data[size] = destination;
61 validator_send_frame(DOWN_LINK, data, size + 1);
62 }
63 }
64}
diff --git a/quantum/serial_link/protocol/frame_router.h b/quantum/serial_link/protocol/frame_router.h
deleted file mode 100644
index 9325fe4ee..000000000
--- a/quantum/serial_link/protocol/frame_router.h
+++ /dev/null
@@ -1,35 +0,0 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#pragma once
26
27#include <stdint.h>
28#include <stdbool.h>
29
30#define UP_LINK 0
31#define DOWN_LINK 1
32
33void router_set_master(bool master);
34void route_incoming_frame(uint8_t link, uint8_t* data, uint16_t size);
35void router_send_frame(uint8_t destination, uint8_t* data, uint16_t size);
diff --git a/quantum/serial_link/protocol/frame_validator.c b/quantum/serial_link/protocol/frame_validator.c
deleted file mode 100644
index bc9136f70..000000000
--- a/quantum/serial_link/protocol/frame_validator.c
+++ /dev/null
@@ -1,57 +0,0 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#include "serial_link/protocol/frame_validator.h"
26#include "serial_link/protocol/frame_router.h"
27#include "serial_link/protocol/byte_stuffer.h"
28#include <string.h>
29
30const uint32_t poly8_lookup[256] = {0, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
31 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
32 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
33 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D};
34
35static uint32_t crc32_byte(uint8_t* p, uint32_t bytelength) {
36 uint32_t crc = 0xffffffff;
37 while (bytelength-- != 0) crc = poly8_lookup[((uint8_t)crc ^ *(p++))] ^ (crc >> 8);
38 // return (~crc); also works
39 return (crc ^ 0xffffffff);
40}
41
42void validator_recv_frame(uint8_t link, uint8_t* data, uint16_t size) {
43 if (size > 4) {
44 uint32_t frame_crc;
45 memcpy(&frame_crc, data + size - 4, 4);
46 uint32_t expected_crc = crc32_byte(data, size - 4);
47 if (frame_crc == expected_crc) {
48 route_incoming_frame(link, data, size - 4);
49 }
50 }
51}
52
53void validator_send_frame(uint8_t link, uint8_t* data, uint16_t size) {
54 uint32_t crc = crc32_byte(data, size);
55 memcpy(data + size, &crc, 4);
56 byte_stuffer_send_frame(link, data, size + 4);
57}
diff --git a/quantum/serial_link/protocol/frame_validator.h b/quantum/serial_link/protocol/frame_validator.h
deleted file mode 100644
index 0f78768a0..000000000
--- a/quantum/serial_link/protocol/frame_validator.h
+++ /dev/null
@@ -1,31 +0,0 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#pragma once
26
27#include <stdint.h>
28
29void validator_recv_frame(uint8_t link, uint8_t* data, uint16_t size);
30// The buffer pointed to by the data needs 4 additional bytes
31void validator_send_frame(uint8_t link, uint8_t* data, uint16_t size);
diff --git a/quantum/serial_link/protocol/physical.h b/quantum/serial_link/protocol/physical.h
deleted file mode 100644
index 399c9d1f7..000000000
--- a/quantum/serial_link/protocol/physical.h
+++ /dev/null
@@ -1,27 +0,0 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#pragma once
26
27void send_data(uint8_t link, const uint8_t* data, uint16_t size);
diff --git a/quantum/serial_link/protocol/transport.c b/quantum/serial_link/protocol/transport.c
deleted file mode 100644
index 73b8dc62e..000000000
--- a/quantum/serial_link/protocol/transport.c
+++ /dev/null
@@ -1,121 +0,0 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#include "serial_link/protocol/transport.h"
26#include "serial_link/protocol/frame_router.h"
27#include "serial_link/protocol/triple_buffered_object.h"
28#include <string.h>
29
30#define MAX_REMOTE_OBJECTS 16
31static remote_object_t* remote_objects[MAX_REMOTE_OBJECTS];
32static uint32_t num_remote_objects = 0;
33
34void reinitialize_serial_link_transport(void) { num_remote_objects = 0; }
35
36void add_remote_objects(remote_object_t** _remote_objects, uint32_t _num_remote_objects) {
37 unsigned int i;
38 for (i = 0; i < _num_remote_objects; i++) {
39 remote_object_t* obj = _remote_objects[i];
40 remote_objects[num_remote_objects++] = obj;
41 if (obj->object_type == MASTER_TO_ALL_SLAVES) {
42 triple_buffer_object_t* tb = (triple_buffer_object_t*)obj->buffer;
43 triple_buffer_init(tb);
44 uint8_t* start = obj->buffer + LOCAL_OBJECT_SIZE(obj->object_size);
45 tb = (triple_buffer_object_t*)start;
46 triple_buffer_init(tb);
47 } else if (obj->object_type == MASTER_TO_SINGLE_SLAVE) {
48 uint8_t* start = obj->buffer;
49 unsigned int j;
50 for (j = 0; j < NUM_SLAVES; j++) {
51 triple_buffer_object_t* tb = (triple_buffer_object_t*)start;
52 triple_buffer_init(tb);
53 start += LOCAL_OBJECT_SIZE(obj->object_size);
54 }
55 triple_buffer_object_t* tb = (triple_buffer_object_t*)start;
56 triple_buffer_init(tb);
57 } else {
58 uint8_t* start = obj->buffer;
59 triple_buffer_object_t* tb = (triple_buffer_object_t*)start;
60 triple_buffer_init(tb);
61 start += LOCAL_OBJECT_SIZE(obj->object_size);
62 unsigned int j;
63 for (j = 0; j < NUM_SLAVES; j++) {
64 tb = (triple_buffer_object_t*)start;
65 triple_buffer_init(tb);
66 start += REMOTE_OBJECT_SIZE(obj->object_size);
67 }
68 }
69 }
70}
71
72void transport_recv_frame(uint8_t from, uint8_t* data, uint16_t size) {
73 uint8_t id = data[size - 1];
74 if (id < num_remote_objects) {
75 remote_object_t* obj = remote_objects[id];
76 if (obj->object_size == size - 1) {
77 uint8_t* start;
78 if (obj->object_type == MASTER_TO_ALL_SLAVES) {
79 start = obj->buffer + LOCAL_OBJECT_SIZE(obj->object_size);
80 } else if (obj->object_type == SLAVE_TO_MASTER) {
81 start = obj->buffer + LOCAL_OBJECT_SIZE(obj->object_size);
82 start += (from - 1) * REMOTE_OBJECT_SIZE(obj->object_size);
83 } else {
84 start = obj->buffer + NUM_SLAVES * LOCAL_OBJECT_SIZE(obj->object_size);
85 }
86 triple_buffer_object_t* tb = (triple_buffer_object_t*)start;
87 void* ptr = triple_buffer_begin_write_internal(obj->object_size, tb);
88 memcpy(ptr, data, size - 1);
89 triple_buffer_end_write_internal(tb);
90 }
91 }
92}
93
94void update_transport(void) {
95 unsigned int i;
96 for (i = 0; i < num_remote_objects; i++) {
97 remote_object_t* obj = remote_objects[i];
98 if (obj->object_type == MASTER_TO_ALL_SLAVES || obj->object_type == SLAVE_TO_MASTER) {
99 triple_buffer_object_t* tb = (triple_buffer_object_t*)obj->buffer;
100 uint8_t* ptr = (uint8_t*)triple_buffer_read_internal(obj->object_size + LOCAL_OBJECT_EXTRA, tb);
101 if (ptr) {
102 ptr[obj->object_size] = i;
103 uint8_t dest = obj->object_type == MASTER_TO_ALL_SLAVES ? 0xFF : 0;
104 router_send_frame(dest, ptr, obj->object_size + 1);
105 }
106 } else {
107 uint8_t* start = obj->buffer;
108 unsigned int j;
109 for (j = 0; j < NUM_SLAVES; j++) {
110 triple_buffer_object_t* tb = (triple_buffer_object_t*)start;
111 uint8_t* ptr = (uint8_t*)triple_buffer_read_internal(obj->object_size + LOCAL_OBJECT_EXTRA, tb);
112 if (ptr) {
113 ptr[obj->object_size] = i;
114 uint8_t dest = j + 1;
115 router_send_frame(dest, ptr, obj->object_size + 1);
116 }
117 start += LOCAL_OBJECT_SIZE(obj->object_size);
118 }
119 }
120 }
121}
diff --git a/quantum/serial_link/protocol/transport.h b/quantum/serial_link/protocol/transport.h
deleted file mode 100644
index 3ce0c9fe4..000000000
--- a/quantum/serial_link/protocol/transport.h
+++ /dev/null
@@ -1,139 +0,0 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#pragma once
26
27#include "serial_link/protocol/triple_buffered_object.h"
28#include "serial_link/system/serial_link.h"
29
30#define NUM_SLAVES 8
31#define LOCAL_OBJECT_EXTRA 16
32
33// master -> slave = 1 local(target all), 1 remote object
34// slave -> master = 1 local(target 0), multiple remote objects
35// master -> single slave (multiple local, target id), 1 remote object
36typedef enum {
37 MASTER_TO_ALL_SLAVES,
38 MASTER_TO_SINGLE_SLAVE,
39 SLAVE_TO_MASTER,
40} remote_object_type;
41
42typedef struct {
43 remote_object_type object_type;
44 uint16_t object_size;
45 uint8_t buffer[] __attribute__((aligned(4)));
46} remote_object_t;
47
48#define REMOTE_OBJECT_SIZE(objectsize) (sizeof(triple_buffer_object_t) + objectsize * 3)
49#define LOCAL_OBJECT_SIZE(objectsize) (sizeof(triple_buffer_object_t) + (objectsize + LOCAL_OBJECT_EXTRA) * 3)
50
51#define REMOTE_OBJECT_HELPER(name, type, num_local, num_remote) \
52 typedef struct { \
53 remote_object_t object; \
54 uint8_t buffer[num_remote * REMOTE_OBJECT_SIZE(sizeof(type)) + num_local * LOCAL_OBJECT_SIZE(sizeof(type))]; \
55 } remote_object_##name##_t;
56
57#define MASTER_TO_ALL_SLAVES_OBJECT(name, type) \
58 REMOTE_OBJECT_HELPER(name, type, 1, 1) \
59 remote_object_##name##_t remote_object_##name = {.object = { \
60 .object_type = MASTER_TO_ALL_SLAVES, \
61 .object_size = sizeof(type), \
62 }}; \
63 type* begin_write_##name(void) { \
64 remote_object_t* obj = (remote_object_t*)&remote_object_##name; \
65 triple_buffer_object_t* tb = (triple_buffer_object_t*)obj->buffer; \
66 return (type*)triple_buffer_begin_write_internal(sizeof(type) + LOCAL_OBJECT_EXTRA, tb); \
67 } \
68 void end_write_##name(void) { \
69 remote_object_t* obj = (remote_object_t*)&remote_object_##name; \
70 triple_buffer_object_t* tb = (triple_buffer_object_t*)obj->buffer; \
71 triple_buffer_end_write_internal(tb); \
72 signal_data_written(); \
73 } \
74 type* read_##name(void) { \
75 remote_object_t* obj = (remote_object_t*)&remote_object_##name; \
76 uint8_t* start = obj->buffer + LOCAL_OBJECT_SIZE(obj->object_size); \
77 triple_buffer_object_t* tb = (triple_buffer_object_t*)start; \
78 return (type*)triple_buffer_read_internal(obj->object_size, tb); \
79 }
80
81#define MASTER_TO_SINGLE_SLAVE_OBJECT(name, type) \
82 REMOTE_OBJECT_HELPER(name, type, NUM_SLAVES, 1) \
83 remote_object_##name##_t remote_object_##name = {.object = { \
84 .object_type = MASTER_TO_SINGLE_SLAVE, \
85 .object_size = sizeof(type), \
86 }}; \
87 type* begin_write_##name(uint8_t slave) { \
88 remote_object_t* obj = (remote_object_t*)&remote_object_##name; \
89 uint8_t* start = obj->buffer; \
90 start += slave * LOCAL_OBJECT_SIZE(obj->object_size); \
91 triple_buffer_object_t* tb = (triple_buffer_object_t*)start; \
92 return (type*)triple_buffer_begin_write_internal(sizeof(type) + LOCAL_OBJECT_EXTRA, tb); \
93 } \
94 void end_write_##name(uint8_t slave) { \
95 remote_object_t* obj = (remote_object_t*)&remote_object_##name; \
96 uint8_t* start = obj->buffer; \
97 start += slave * LOCAL_OBJECT_SIZE(obj->object_size); \
98 triple_buffer_object_t* tb = (triple_buffer_object_t*)start; \
99 triple_buffer_end_write_internal(tb); \
100 signal_data_written(); \
101 } \
102 type* read_##name() { \
103 remote_object_t* obj = (remote_object_t*)&remote_object_##name; \
104 uint8_t* start = obj->buffer + NUM_SLAVES * LOCAL_OBJECT_SIZE(obj->object_size); \
105 triple_buffer_object_t* tb = (triple_buffer_object_t*)start; \
106 return (type*)triple_buffer_read_internal(obj->object_size, tb); \
107 }
108
109#define SLAVE_TO_MASTER_OBJECT(name, type) \
110 REMOTE_OBJECT_HELPER(name, type, 1, NUM_SLAVES) \
111 remote_object_##name##_t remote_object_##name = {.object = { \
112 .object_type = SLAVE_TO_MASTER, \
113 .object_size = sizeof(type), \
114 }}; \
115 type* begin_write_##name(void) { \
116 remote_object_t* obj = (remote_object_t*)&remote_object_##name; \
117 triple_buffer_object_t* tb = (triple_buffer_object_t*)obj->buffer; \
118 return (type*)triple_buffer_begin_write_internal(sizeof(type) + LOCAL_OBJECT_EXTRA, tb); \
119 } \
120 void end_write_##name(void) { \
121 remote_object_t* obj = (remote_object_t*)&remote_object_##name; \
122 triple_buffer_object_t* tb = (triple_buffer_object_t*)obj->buffer; \
123 triple_buffer_end_write_internal(tb); \
124 signal_data_written(); \
125 } \
126 type* read_##name(uint8_t slave) { \
127 remote_object_t* obj = (remote_object_t*)&remote_object_##name; \
128 uint8_t* start = obj->buffer + LOCAL_OBJECT_SIZE(obj->object_size); \
129 start += slave * REMOTE_OBJECT_SIZE(obj->object_size); \
130 triple_buffer_object_t* tb = (triple_buffer_object_t*)start; \
131 return (type*)triple_buffer_read_internal(obj->object_size, tb); \
132 }
133
134#define REMOTE_OBJECT(name) (remote_object_t*)&remote_object_##name
135
136void add_remote_objects(remote_object_t** remote_objects, uint32_t num_remote_objects);
137void reinitialize_serial_link_transport(void);
138void transport_recv_frame(uint8_t from, uint8_t* data, uint16_t size);
139void update_transport(void);
diff --git a/quantum/serial_link/protocol/triple_buffered_object.c b/quantum/serial_link/protocol/triple_buffered_object.c
deleted file mode 100644
index e0c6d702a..000000000
--- a/quantum/serial_link/protocol/triple_buffered_object.c
+++ /dev/null
@@ -1,77 +0,0 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#include "serial_link/protocol/triple_buffered_object.h"
26#include "serial_link/system/serial_link.h"
27#include <stdbool.h>
28#include <stddef.h>
29
30#define GET_READ_INDEX() object->state & 3
31#define GET_WRITE_INDEX() (object->state >> 2) & 3
32#define GET_SHARED_INDEX() (object->state >> 4) & 3
33#define GET_DATA_AVAILABLE() (object->state >> 6) & 1
34
35#define SET_READ_INDEX(i) object->state = ((object->state & ~3) | i)
36#define SET_WRITE_INDEX(i) object->state = ((object->state & ~(3 << 2)) | (i << 2))
37#define SET_SHARED_INDEX(i) object->state = ((object->state & ~(3 << 4)) | (i << 4))
38#define SET_DATA_AVAILABLE(i) object->state = ((object->state & ~(1 << 6)) | (i << 6))
39
40void triple_buffer_init(triple_buffer_object_t* object) {
41 object->state = 0;
42 SET_WRITE_INDEX(0);
43 SET_READ_INDEX(1);
44 SET_SHARED_INDEX(2);
45 SET_DATA_AVAILABLE(0);
46}
47
48void* triple_buffer_read_internal(uint16_t object_size, triple_buffer_object_t* object) {
49 serial_link_lock();
50 if (GET_DATA_AVAILABLE()) {
51 uint8_t shared_index = GET_SHARED_INDEX();
52 uint8_t read_index = GET_READ_INDEX();
53 SET_READ_INDEX(shared_index);
54 SET_SHARED_INDEX(read_index);
55 SET_DATA_AVAILABLE(false);
56 serial_link_unlock();
57 return object->buffer + object_size * shared_index;
58 } else {
59 serial_link_unlock();
60 return NULL;
61 }
62}
63
64void* triple_buffer_begin_write_internal(uint16_t object_size, triple_buffer_object_t* object) {
65 uint8_t write_index = GET_WRITE_INDEX();
66 return object->buffer + object_size * write_index;
67}
68
69void triple_buffer_end_write_internal(triple_buffer_object_t* object) {
70 serial_link_lock();
71 uint8_t shared_index = GET_SHARED_INDEX();
72 uint8_t write_index = GET_WRITE_INDEX();
73 SET_SHARED_INDEX(write_index);
74 SET_WRITE_INDEX(shared_index);
75 SET_DATA_AVAILABLE(true);
76 serial_link_unlock();
77}
diff --git a/quantum/serial_link/protocol/triple_buffered_object.h b/quantum/serial_link/protocol/triple_buffered_object.h
deleted file mode 100644
index 717d6d7b8..000000000
--- a/quantum/serial_link/protocol/triple_buffered_object.h
+++ /dev/null
@@ -1,44 +0,0 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#pragma once
26
27#include <stdint.h>
28
29typedef struct {
30 uint8_t state;
31 uint8_t buffer[] __attribute__((aligned(4)));
32} triple_buffer_object_t;
33
34void triple_buffer_init(triple_buffer_object_t* object);
35
36#define triple_buffer_begin_write(object) (typeof(*object.buffer[0])*)triple_buffer_begin_write_internal(sizeof(*object.buffer[0]), (triple_buffer_object_t*)object)
37
38#define triple_buffer_end_write(object) triple_buffer_end_write_internal((triple_buffer_object_t*)object)
39
40#define triple_buffer_read(object) (typeof(*object.buffer[0])*)triple_buffer_read_internal(sizeof(*object.buffer[0]), (triple_buffer_object_t*)object)
41
42void* triple_buffer_begin_write_internal(uint16_t object_size, triple_buffer_object_t* object);
43void triple_buffer_end_write_internal(triple_buffer_object_t* object);
44void* triple_buffer_read_internal(uint16_t object_size, triple_buffer_object_t* object);
diff --git a/quantum/serial_link/system/serial_link.c b/quantum/serial_link/system/serial_link.c
deleted file mode 100644
index 6363f8ff3..000000000
--- a/quantum/serial_link/system/serial_link.c
+++ /dev/null
@@ -1,250 +0,0 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24#include "report.h"
25#include "host_driver.h"
26#include "serial_link/system/serial_link.h"
27#include <hal.h>
28#include "serial_link/protocol/byte_stuffer.h"
29#include "serial_link/protocol/transport.h"
30#include "serial_link/protocol/frame_router.h"
31#include "matrix.h"
32#include "sync_timer.h"
33#include <stdbool.h>
34#include "print.h"
35#include "config.h"
36
37#define SYNC_TIMER_OFFSET 2
38
39static event_source_t new_data_event;
40static bool serial_link_connected;
41static bool is_master = false;
42
43static uint8_t keyboard_leds(void);
44static void send_keyboard(report_keyboard_t* report);
45static void send_mouse(report_mouse_t* report);
46static void send_system(uint16_t data);
47static void send_consumer(uint16_t data);
48
49host_driver_t serial_driver = {keyboard_leds, send_keyboard, send_mouse, send_system, send_consumer};
50
51// Define these in your Config.h file
52#ifndef SERIAL_LINK_BAUD
53# error "Serial link baud is not set"
54#endif
55
56#ifndef SERIAL_LINK_THREAD_PRIORITY
57# error "Serial link thread priority not set"
58#endif
59
60static SerialConfig config = {.sc_speed = SERIAL_LINK_BAUD};
61
62//#define DEBUG_LINK_ERRORS
63
64static uint32_t read_from_serial(SerialDriver* driver, uint8_t link) {
65 const uint32_t buffer_size = 16;
66 uint8_t buffer[buffer_size];
67 uint32_t bytes_read = sdAsynchronousRead(driver, buffer, buffer_size);
68 uint8_t* current = buffer;
69 uint8_t* end = current + bytes_read;
70 while (current < end) {
71 byte_stuffer_recv_byte(link, *current);
72 current++;
73 }
74 return bytes_read;
75}
76
77static void print_error(char* str, eventflags_t flags, SerialDriver* driver) {
78#ifdef DEBUG_LINK_ERRORS
79 if (flags & SD_PARITY_ERROR) {
80 print(str);
81 print(" Parity error\n");
82 }
83 if (flags & SD_FRAMING_ERROR) {
84 print(str);
85 print(" Framing error\n");
86 }
87 if (flags & SD_OVERRUN_ERROR) {
88 print(str);
89 uint32_t size = qSpaceI(&(driver->iqueue));
90 xprintf(" Overrun error, queue size %d\n", size);
91 }
92 if (flags & SD_NOISE_ERROR) {
93 print(str);
94 print(" Noise error\n");
95 }
96 if (flags & SD_BREAK_DETECTED) {
97 print(str);
98 print(" Break detected\n");
99 }
100#else
101 (void)str;
102 (void)flags;
103 (void)driver;
104#endif
105}
106
107bool is_serial_link_master(void) { return is_master; }
108
109// TODO: Optimize the stack size, this is probably way too big
110static THD_WORKING_AREA(serialThreadStack, 1024);
111static THD_FUNCTION(serialThread, arg) {
112 (void)arg;
113 event_listener_t new_data_listener;
114 event_listener_t sd1_listener;
115 event_listener_t sd2_listener;
116 chEvtRegister(&new_data_event, &new_data_listener, 0);
117 eventflags_t events = CHN_INPUT_AVAILABLE | SD_PARITY_ERROR | SD_FRAMING_ERROR | SD_OVERRUN_ERROR | SD_NOISE_ERROR | SD_BREAK_DETECTED;
118 chEvtRegisterMaskWithFlags(chnGetEventSource(&SD1), &sd1_listener, EVENT_MASK(1), events);
119 chEvtRegisterMaskWithFlags(chnGetEventSource(&SD2), &sd2_listener, EVENT_MASK(2), events);
120 bool need_wait = false;
121 while (true) {
122 eventflags_t flags1 = 0;
123 eventflags_t flags2 = 0;
124 if (need_wait) {
125 eventmask_t mask = chEvtWaitAnyTimeout(ALL_EVENTS, TIME_MS2I(1000));
126 if (mask & EVENT_MASK(1)) {
127 flags1 = chEvtGetAndClearFlags(&sd1_listener);
128 print_error("DOWNLINK", flags1, &SD1);
129 }
130 if (mask & EVENT_MASK(2)) {
131 flags2 = chEvtGetAndClearFlags(&sd2_listener);
132 print_error("UPLINK", flags2, &SD2);
133 }
134 }
135
136 // Always stay as master, even if the USB goes into sleep mode
137 is_master |= usbGetDriverStateI(&USBD1) == USB_ACTIVE;
138 router_set_master(is_master);
139
140 need_wait = true;
141 need_wait &= read_from_serial(&SD2, UP_LINK) == 0;
142 need_wait &= read_from_serial(&SD1, DOWN_LINK) == 0;
143 update_transport();
144 }
145}
146
147void send_data(uint8_t link, const uint8_t* data, uint16_t size) {
148 if (link == DOWN_LINK) {
149 sdWrite(&SD1, data, size);
150 } else {
151 sdWrite(&SD2, data, size);
152 }
153}
154
155static systime_t last_update = 0;
156
157typedef struct {
158 matrix_row_t rows[MATRIX_ROWS];
159} matrix_object_t;
160
161static matrix_object_t last_matrix = {};
162
163SLAVE_TO_MASTER_OBJECT(keyboard_matrix, matrix_object_t);
164MASTER_TO_ALL_SLAVES_OBJECT(serial_link_connected, bool);
165#ifndef DISABLE_SYNC_TIMER
166MASTER_TO_ALL_SLAVES_OBJECT(sync_timer, uint32_t);
167#endif
168
169static remote_object_t* remote_objects[] = {
170 REMOTE_OBJECT(serial_link_connected),
171 REMOTE_OBJECT(keyboard_matrix),
172#ifndef DISABLE_SYNC_TIMER
173 REMOTE_OBJECT(sync_timer),
174#endif
175};
176
177void init_serial_link(void) {
178 serial_link_connected = false;
179 init_serial_link_hal();
180 add_remote_objects(remote_objects, sizeof(remote_objects) / sizeof(remote_object_t*));
181 init_byte_stuffer();
182 sdStart(&SD1, &config);
183 sdStart(&SD2, &config);
184 chEvtObjectInit(&new_data_event);
185 (void)chThdCreateStatic(serialThreadStack, sizeof(serialThreadStack), SERIAL_LINK_THREAD_PRIORITY, serialThread, NULL);
186}
187
188void matrix_set_remote(matrix_row_t* rows, uint8_t index);
189
190void serial_link_update(void) {
191 if (read_serial_link_connected()) {
192 serial_link_connected = true;
193 }
194
195 matrix_object_t matrix;
196 bool changed = false;
197 for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
198 matrix.rows[i] = matrix_get_row(i);
199 changed |= matrix.rows[i] != last_matrix.rows[i];
200 }
201
202 systime_t current_time = chVTGetSystemTimeX();
203 systime_t delta = current_time - last_update;
204 if (changed || delta > TIME_US2I(5000)) {
205 last_update = current_time;
206 last_matrix = matrix;
207 matrix_object_t* m = begin_write_keyboard_matrix();
208 for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
209 m->rows[i] = matrix.rows[i];
210 }
211 end_write_keyboard_matrix();
212
213 *begin_write_serial_link_connected() = true;
214 end_write_serial_link_connected();
215
216#ifndef DISABLE_SYNC_TIMER
217 *begin_write_sync_timer() = sync_timer_read32() + SYNC_TIMER_OFFSET;
218 end_write_sync_timer();
219#endif
220 }
221
222 matrix_object_t* m = read_keyboard_matrix(0);
223 if (m) {
224 matrix_set_remote(m->rows, 0);
225 }
226
227#ifndef DISABLE_SYNC_TIMER
228 uint32_t* t = read_sync_timer();
229 if (t) {
230 sync_timer_update(*t);
231 }
232#endif
233}
234
235void signal_data_written(void) { chEvtBroadcast(&new_data_event); }
236
237bool is_serial_link_connected(void) { return serial_link_connected; }
238
239host_driver_t* get_serial_link_driver(void) { return &serial_driver; }
240
241// NOTE: The driver does nothing, because the master handles everything
242uint8_t keyboard_leds(void) { return 0; }
243
244void send_keyboard(report_keyboard_t* report) { (void)report; }
245
246void send_mouse(report_mouse_t* report) { (void)report; }
247
248void send_system(uint16_t data) { (void)data; }
249
250void send_consumer(uint16_t data) { (void)data; }
diff --git a/quantum/serial_link/system/serial_link.h b/quantum/serial_link/system/serial_link.h
deleted file mode 100644
index adc1f6e93..000000000
--- a/quantum/serial_link/system/serial_link.h
+++ /dev/null
@@ -1,54 +0,0 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#pragma once
26
27#include "host_driver.h"
28#include <stdbool.h>
29
30void init_serial_link(void);
31void init_serial_link_hal(void);
32bool is_serial_link_connected(void);
33bool is_serial_link_master(void);
34host_driver_t* get_serial_link_driver(void);
35void serial_link_update(void);
36
37#if defined(PROTOCOL_CHIBIOS)
38# include <ch.h>
39
40static inline void serial_link_lock(void) { chSysLock(); }
41
42static inline void serial_link_unlock(void) { chSysUnlock(); }
43
44void signal_data_written(void);
45
46#else
47
48inline void serial_link_lock(void) {}
49
50inline void serial_link_unlock(void) {}
51
52void signal_data_written(void);
53
54#endif
diff --git a/quantum/serial_link/tests/Makefile b/quantum/serial_link/tests/Makefile
deleted file mode 100644
index 11dd355b2..000000000
--- a/quantum/serial_link/tests/Makefile
+++ /dev/null
@@ -1,61 +0,0 @@
1# The MIT License (MIT)
2#
3# Copyright (c) 2016 Fred Sundvik
4#
5# Permission is hereby granted, free of charge, to any person obtaining a copy
6# of this software and associated documentation files (the "Software"), to deal
7# in the Software without restriction, including without limitation the rights
8# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9# copies of the Software, and to permit persons to whom the Software is
10# furnished to do so, subject to the following conditions:
11#
12# The above copyright notice and this permission notice shall be included in all
13# copies or substantial portions of the Software.
14#
15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21# SOFTWARE.
22
23CC = gcc
24CFLAGS =
25INCLUDES = -I. -I../../
26LDFLAGS = -L$(BUILDDIR)/cgreen/build-c/src -shared
27LDLIBS = -lcgreen
28UNITOBJ = $(BUILDDIR)/serialtest/unitobj
29DEPDIR = $(BUILDDIR)/serialtest/unit.d
30UNITTESTS = $(BUILDDIR)/serialtest/unittests
31DEPFLAGS = -MT $@ -MMD -MP -MF $(DEPDIR)/$*.Td
32EXT = .so
33UNAME := $(shell uname)
34ifneq (, $(findstring MINGW, $(UNAME)))
35 EXT = .dll
36endif
37ifneq (, $(findstring CYGWIN, $(UNAME)))
38 EXT = .dll
39endif
40
41SRC = $(wildcard *.c)
42TESTFILES = $(patsubst %.c, $(UNITTESTS)/%$(EXT), $(SRC))
43$(shell mkdir -p $(DEPDIR) >/dev/null)
44
45test: $(TESTFILES)
46 @$(BUILDDIR)/cgreen/build-c/tools/cgreen-runner --color $(TESTFILES)
47
48$(UNITTESTS)/%$(EXT): $(UNITOBJ)/%.o
49 @mkdir -p $(UNITTESTS)
50 $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)
51
52$(UNITOBJ)/%.o : %.c
53$(UNITOBJ)/%.o: %.c $(DEPDIR)/%.d
54 @mkdir -p $(UNITOBJ)
55 $(CC) $(CFLAGS) $(DEPFLAGS) $(INCLUDES) -c $< -o $@
56 @mv -f $(DEPDIR)/$*.Td $(DEPDIR)/$*.d
57
58$(DEPDIR)/%.d: ;
59.PRECIOUS: $(DEPDIR)/%.d
60
61-include $(patsubst %,$(DEPDIR)/%.d,$(basename $(SRC)))
diff --git a/quantum/serial_link/tests/byte_stuffer_tests.cpp b/quantum/serial_link/tests/byte_stuffer_tests.cpp
deleted file mode 100644
index 9e4e1768f..000000000
--- a/quantum/serial_link/tests/byte_stuffer_tests.cpp
+++ /dev/null
@@ -1,450 +0,0 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#include "gtest/gtest.h"
26#include "gmock/gmock.h"
27#include <vector>
28#include <algorithm>
29extern "C" {
30#include "serial_link/protocol/byte_stuffer.h"
31#include "serial_link/protocol/frame_validator.h"
32#include "serial_link/protocol/physical.h"
33}
34
35using testing::_;
36using testing::Args;
37using testing::ElementsAreArray;
38
39class ByteStuffer : public ::testing::Test {
40 public:
41 ByteStuffer() {
42 Instance = this;
43 init_byte_stuffer();
44 }
45
46 ~ByteStuffer() { Instance = nullptr; }
47
48 MOCK_METHOD3(validator_recv_frame, void(uint8_t link, uint8_t* data, uint16_t size));
49
50 void send_data(uint8_t link, const uint8_t* data, uint16_t size) { std::copy(data, data + size, std::back_inserter(sent_data)); }
51 std::vector<uint8_t> sent_data;
52
53 static ByteStuffer* Instance;
54};
55
56ByteStuffer* ByteStuffer::Instance = nullptr;
57
58extern "C" {
59void validator_recv_frame(uint8_t link, uint8_t* data, uint16_t size) { ByteStuffer::Instance->validator_recv_frame(link, data, size); }
60
61void send_data(uint8_t link, const uint8_t* data, uint16_t size) { ByteStuffer::Instance->send_data(link, data, size); }
62}
63
64TEST_F(ByteStuffer, receives_no_frame_for_a_single_zero_byte) {
65 EXPECT_CALL(*this, validator_recv_frame(_, _, _)).Times(0);
66 byte_stuffer_recv_byte(0, 0);
67}
68
69TEST_F(ByteStuffer, receives_no_frame_for_a_single_FF_byte) {
70 EXPECT_CALL(*this, validator_recv_frame(_, _, _)).Times(0);
71 byte_stuffer_recv_byte(0, 0xFF);
72}
73
74TEST_F(ByteStuffer, receives_no_frame_for_a_single_random_byte) {
75 EXPECT_CALL(*this, validator_recv_frame(_, _, _)).Times(0);
76 byte_stuffer_recv_byte(0, 0x4A);
77}
78
79TEST_F(ByteStuffer, receives_no_frame_for_a_zero_length_frame) {
80 EXPECT_CALL(*this, validator_recv_frame(_, _, _)).Times(0);
81 byte_stuffer_recv_byte(0, 1);
82 byte_stuffer_recv_byte(0, 0);
83}
84
85TEST_F(ByteStuffer, receives_single_byte_valid_frame) {
86 uint8_t expected[] = {0x37};
87 EXPECT_CALL(*this, validator_recv_frame(_, _, _)).With(Args<1, 2>(ElementsAreArray(expected)));
88 byte_stuffer_recv_byte(0, 2);
89 byte_stuffer_recv_byte(0, 0x37);
90 byte_stuffer_recv_byte(0, 0);
91}
92TEST_F(ByteStuffer, receives_three_bytes_valid_frame) {
93 uint8_t expected[] = {0x37, 0x99, 0xFF};
94 EXPECT_CALL(*this, validator_recv_frame(_, _, _)).With(Args<1, 2>(ElementsAreArray(expected)));
95 byte_stuffer_recv_byte(0, 4);
96 byte_stuffer_recv_byte(0, 0x37);
97 byte_stuffer_recv_byte(0, 0x99);
98 byte_stuffer_recv_byte(0, 0xFF);
99 byte_stuffer_recv_byte(0, 0);
100}
101
102TEST_F(ByteStuffer, receives_single_zero_valid_frame) {
103 uint8_t expected[] = {0};
104 EXPECT_CALL(*this, validator_recv_frame(_, _, _)).With(Args<1, 2>(ElementsAreArray(expected)));
105 byte_stuffer_recv_byte(0, 1);
106 byte_stuffer_recv_byte(0, 1);
107 byte_stuffer_recv_byte(0, 0);
108}
109
110TEST_F(ByteStuffer, receives_valid_frame_with_zeroes) {
111 uint8_t expected[] = {5, 0, 3, 0};
112 EXPECT_CALL(*this, validator_recv_frame(_, _, _)).With(Args<1, 2>(ElementsAreArray(expected)));
113 byte_stuffer_recv_byte(0, 2);
114 byte_stuffer_recv_byte(0, 5);
115 byte_stuffer_recv_byte(0, 2);
116 byte_stuffer_recv_byte(0, 3);
117 byte_stuffer_recv_byte(0, 1);
118 byte_stuffer_recv_byte(0, 0);
119}
120
121TEST_F(ByteStuffer, receives_two_valid_frames) {
122 uint8_t expected1[] = {5, 0};
123 uint8_t expected2[] = {3};
124 EXPECT_CALL(*this, validator_recv_frame(_, _, _)).With(Args<1, 2>(ElementsAreArray(expected1)));
125 EXPECT_CALL(*this, validator_recv_frame(_, _, _)).With(Args<1, 2>(ElementsAreArray(expected2)));
126 byte_stuffer_recv_byte(1, 2);
127 byte_stuffer_recv_byte(1, 5);
128 byte_stuffer_recv_byte(1, 1);
129 byte_stuffer_recv_byte(1, 0);
130 byte_stuffer_recv_byte(1, 2);
131 byte_stuffer_recv_byte(1, 3);
132 byte_stuffer_recv_byte(1, 0);
133}
134
135TEST_F(ByteStuffer, receives_valid_frame_after_unexpected_zero) {
136 uint8_t expected[] = {5, 7};
137 EXPECT_CALL(*this, validator_recv_frame(_, _, _)).With(Args<1, 2>(ElementsAreArray(expected)));
138 byte_stuffer_recv_byte(1, 3);
139 byte_stuffer_recv_byte(1, 1);
140 byte_stuffer_recv_byte(1, 0);
141 byte_stuffer_recv_byte(1, 3);
142 byte_stuffer_recv_byte(1, 5);
143 byte_stuffer_recv_byte(1, 7);
144 byte_stuffer_recv_byte(1, 0);
145}
146
147TEST_F(ByteStuffer, receives_valid_frame_after_unexpected_non_zero) {
148 uint8_t expected[] = {5, 7};
149 EXPECT_CALL(*this, validator_recv_frame(_, _, _)).With(Args<1, 2>(ElementsAreArray(expected)));
150 byte_stuffer_recv_byte(0, 2);
151 byte_stuffer_recv_byte(0, 9);
152 byte_stuffer_recv_byte(0, 4); // This should have been zero
153 byte_stuffer_recv_byte(0, 0);
154 byte_stuffer_recv_byte(0, 3);
155 byte_stuffer_recv_byte(0, 5);
156 byte_stuffer_recv_byte(0, 7);
157 byte_stuffer_recv_byte(0, 0);
158}
159
160TEST_F(ByteStuffer, receives_a_valid_frame_with_over254_non_zeroes_and_then_end_of_frame) {
161 uint8_t expected[254];
162 int i;
163 for (i = 0; i < 254; i++) {
164 expected[i] = i + 1;
165 }
166 EXPECT_CALL(*this, validator_recv_frame(_, _, _)).With(Args<1, 2>(ElementsAreArray(expected)));
167 byte_stuffer_recv_byte(0, 0xFF);
168 for (i = 0; i < 254; i++) {
169 byte_stuffer_recv_byte(0, i + 1);
170 }
171 byte_stuffer_recv_byte(0, 0);
172}
173
174TEST_F(ByteStuffer, receives_a_valid_frame_with_over254_non_zeroes_next_byte_is_non_zero) {
175 uint8_t expected[255];
176 int i;
177 for (i = 0; i < 254; i++) {
178 expected[i] = i + 1;
179 }
180 expected[254] = 7;
181 EXPECT_CALL(*this, validator_recv_frame(_, _, _)).With(Args<1, 2>(ElementsAreArray(expected)));
182 byte_stuffer_recv_byte(0, 0xFF);
183 for (i = 0; i < 254; i++) {
184 byte_stuffer_recv_byte(0, i + 1);
185 }
186 byte_stuffer_recv_byte(0, 2);
187 byte_stuffer_recv_byte(0, 7);
188 byte_stuffer_recv_byte(0, 0);
189}
190
191TEST_F(ByteStuffer, receives_a_valid_frame_with_over254_non_zeroes_next_byte_is_zero) {
192 uint8_t expected[255];
193 int i;
194 for (i = 0; i < 254; i++) {
195 expected[i] = i + 1;
196 }
197 expected[254] = 0;
198 EXPECT_CALL(*this, validator_recv_frame(_, _, _)).With(Args<1, 2>(ElementsAreArray(expected)));
199 byte_stuffer_recv_byte(0, 0xFF);
200 for (i = 0; i < 254; i++) {
201 byte_stuffer_recv_byte(0, i + 1);
202 }
203 byte_stuffer_recv_byte(0, 1);
204 byte_stuffer_recv_byte(0, 1);
205 byte_stuffer_recv_byte(0, 0);
206}
207
208TEST_F(ByteStuffer, receives_two_long_frames_and_some_more) {
209 uint8_t expected[515];
210 int i;
211 int j;
212 for (j = 0; j < 2; j++) {
213 for (i = 0; i < 254; i++) {
214 expected[i + 254 * j] = i + 1;
215 }
216 }
217 for (i = 0; i < 7; i++) {
218 expected[254 * 2 + i] = i + 1;
219 }
220 EXPECT_CALL(*this, validator_recv_frame(_, _, _)).With(Args<1, 2>(ElementsAreArray(expected)));
221 byte_stuffer_recv_byte(0, 0xFF);
222 for (i = 0; i < 254; i++) {
223 byte_stuffer_recv_byte(0, i + 1);
224 }
225 byte_stuffer_recv_byte(0, 0xFF);
226 for (i = 0; i < 254; i++) {
227 byte_stuffer_recv_byte(0, i + 1);
228 }
229 byte_stuffer_recv_byte(0, 8);
230 byte_stuffer_recv_byte(0, 1);
231 byte_stuffer_recv_byte(0, 2);
232 byte_stuffer_recv_byte(0, 3);
233 byte_stuffer_recv_byte(0, 4);
234 byte_stuffer_recv_byte(0, 5);
235 byte_stuffer_recv_byte(0, 6);
236 byte_stuffer_recv_byte(0, 7);
237 byte_stuffer_recv_byte(0, 0);
238}
239
240TEST_F(ByteStuffer, receives_an_all_zeros_frame_that_is_maximum_size) {
241 uint8_t expected[MAX_FRAME_SIZE] = {};
242 EXPECT_CALL(*this, validator_recv_frame(_, _, _)).With(Args<1, 2>(ElementsAreArray(expected)));
243 int i;
244 byte_stuffer_recv_byte(0, 1);
245 for (i = 0; i < MAX_FRAME_SIZE; i++) {
246 byte_stuffer_recv_byte(0, 1);
247 }
248 byte_stuffer_recv_byte(0, 0);
249}
250
251TEST_F(ByteStuffer, doesnt_recv_a_frame_thats_too_long_all_zeroes) {
252 uint8_t expected[1] = {0};
253 EXPECT_CALL(*this, validator_recv_frame(_, _, _)).Times(0);
254 int i;
255 byte_stuffer_recv_byte(0, 1);
256 for (i = 0; i < MAX_FRAME_SIZE; i++) {
257 byte_stuffer_recv_byte(0, 1);
258 }
259 byte_stuffer_recv_byte(0, 1);
260 byte_stuffer_recv_byte(0, 0);
261}
262
263TEST_F(ByteStuffer, received_frame_is_aborted_when_its_too_long) {
264 uint8_t expected[1] = {1};
265 EXPECT_CALL(*this, validator_recv_frame(_, _, _)).With(Args<1, 2>(ElementsAreArray(expected)));
266 int i;
267 byte_stuffer_recv_byte(0, 1);
268 for (i = 0; i < MAX_FRAME_SIZE; i++) {
269 byte_stuffer_recv_byte(0, 1);
270 }
271 byte_stuffer_recv_byte(0, 2);
272 byte_stuffer_recv_byte(0, 1);
273 byte_stuffer_recv_byte(0, 0);
274}
275
276TEST_F(ByteStuffer, does_nothing_when_sending_zero_size_frame) {
277 EXPECT_EQ(sent_data.size(), 0);
278 byte_stuffer_send_frame(0, NULL, 0);
279}
280
281TEST_F(ByteStuffer, send_one_byte_frame) {
282 uint8_t data[] = {5};
283 byte_stuffer_send_frame(1, data, 1);
284 uint8_t expected[] = {2, 5, 0};
285 EXPECT_THAT(sent_data, ElementsAreArray(expected));
286}
287
288TEST_F(ByteStuffer, sends_two_byte_frame) {
289 uint8_t data[] = {5, 0x77};
290 byte_stuffer_send_frame(0, data, 2);
291 uint8_t expected[] = {3, 5, 0x77, 0};
292 EXPECT_THAT(sent_data, ElementsAreArray(expected));
293}
294
295TEST_F(ByteStuffer, sends_one_byte_frame_with_zero) {
296 uint8_t data[] = {0};
297 byte_stuffer_send_frame(0, data, 1);
298 uint8_t expected[] = {1, 1, 0};
299 EXPECT_THAT(sent_data, ElementsAreArray(expected));
300}
301
302TEST_F(ByteStuffer, sends_two_byte_frame_starting_with_zero) {
303 uint8_t data[] = {0, 9};
304 byte_stuffer_send_frame(1, data, 2);
305 uint8_t expected[] = {1, 2, 9, 0};
306 EXPECT_THAT(sent_data, ElementsAreArray(expected));
307}
308
309TEST_F(ByteStuffer, sends_two_byte_frame_starting_with_non_zero) {
310 uint8_t data[] = {9, 0};
311 byte_stuffer_send_frame(1, data, 2);
312 uint8_t expected[] = {2, 9, 1, 0};
313 EXPECT_THAT(sent_data, ElementsAreArray(expected));
314}
315
316TEST_F(ByteStuffer, sends_three_byte_frame_zero_in_the_middle) {
317 uint8_t data[] = {9, 0, 0x68};
318 byte_stuffer_send_frame(0, data, 3);
319 uint8_t expected[] = {2, 9, 2, 0x68, 0};
320 EXPECT_THAT(sent_data, ElementsAreArray(expected));
321}
322
323TEST_F(ByteStuffer, sends_three_byte_frame_data_in_the_middle) {
324 uint8_t data[] = {0, 0x55, 0};
325 byte_stuffer_send_frame(0, data, 3);
326 uint8_t expected[] = {1, 2, 0x55, 1, 0};
327 EXPECT_THAT(sent_data, ElementsAreArray(expected));
328}
329
330TEST_F(ByteStuffer, sends_three_byte_frame_with_all_zeroes) {
331 uint8_t data[] = {0, 0, 0};
332 byte_stuffer_send_frame(0, data, 3);
333 uint8_t expected[] = {1, 1, 1, 1, 0};
334 EXPECT_THAT(sent_data, ElementsAreArray(expected));
335}
336
337TEST_F(ByteStuffer, sends_frame_with_254_non_zeroes) {
338 uint8_t data[254];
339 int i;
340 for (i = 0; i < 254; i++) {
341 data[i] = i + 1;
342 }
343 byte_stuffer_send_frame(0, data, 254);
344 uint8_t expected[256];
345 expected[0] = 0xFF;
346 for (i = 1; i < 255; i++) {
347 expected[i] = i;
348 }
349 expected[255] = 0;
350 EXPECT_THAT(sent_data, ElementsAreArray(expected));
351}
352
353TEST_F(ByteStuffer, sends_frame_with_255_non_zeroes) {
354 uint8_t data[255];
355 int i;
356 for (i = 0; i < 255; i++) {
357 data[i] = i + 1;
358 }
359 byte_stuffer_send_frame(0, data, 255);
360 uint8_t expected[258];
361 expected[0] = 0xFF;
362 for (i = 1; i < 255; i++) {
363 expected[i] = i;
364 }
365 expected[255] = 2;
366 expected[256] = 255;
367 expected[257] = 0;
368 EXPECT_THAT(sent_data, ElementsAreArray(expected));
369}
370
371TEST_F(ByteStuffer, sends_frame_with_254_non_zeroes_followed_by_zero) {
372 uint8_t data[255];
373 int i;
374 for (i = 0; i < 254; i++) {
375 data[i] = i + 1;
376 }
377 data[254] = 0;
378 byte_stuffer_send_frame(0, data, 255);
379 uint8_t expected[258];
380 expected[0] = 0xFF;
381 for (i = 1; i < 255; i++) {
382 expected[i] = i;
383 }
384 expected[255] = 1;
385 expected[256] = 1;
386 expected[257] = 0;
387 EXPECT_THAT(sent_data, ElementsAreArray(expected));
388}
389
390TEST_F(ByteStuffer, sends_and_receives_full_roundtrip_small_packet) {
391 uint8_t original_data[] = {1, 2, 3};
392 byte_stuffer_send_frame(0, original_data, sizeof(original_data));
393 EXPECT_CALL(*this, validator_recv_frame(_, _, _)).With(Args<1, 2>(ElementsAreArray(original_data)));
394 int i;
395 for (auto& d : sent_data) {
396 byte_stuffer_recv_byte(1, d);
397 }
398}
399
400TEST_F(ByteStuffer, sends_and_receives_full_roundtrip_small_packet_with_zeros) {
401 uint8_t original_data[] = {1, 0, 3, 0, 0, 9};
402 byte_stuffer_send_frame(1, original_data, sizeof(original_data));
403 EXPECT_CALL(*this, validator_recv_frame(_, _, _)).With(Args<1, 2>(ElementsAreArray(original_data)));
404 int i;
405 for (auto& d : sent_data) {
406 byte_stuffer_recv_byte(1, d);
407 }
408}
409
410TEST_F(ByteStuffer, sends_and_receives_full_roundtrip_254_bytes) {
411 uint8_t original_data[254];
412 int i;
413 for (i = 0; i < 254; i++) {
414 original_data[i] = i + 1;
415 }
416 byte_stuffer_send_frame(0, original_data, sizeof(original_data));
417 EXPECT_CALL(*this, validator_recv_frame(_, _, _)).With(Args<1, 2>(ElementsAreArray(original_data)));
418 for (auto& d : sent_data) {
419 byte_stuffer_recv_byte(1, d);
420 }
421}
422
423TEST_F(ByteStuffer, sends_and_receives_full_roundtrip_256_bytes) {
424 uint8_t original_data[256];
425 int i;
426 for (i = 0; i < 254; i++) {
427 original_data[i] = i + 1;
428 }
429 original_data[254] = 22;
430 original_data[255] = 23;
431 byte_stuffer_send_frame(0, original_data, sizeof(original_data));
432 EXPECT_CALL(*this, validator_recv_frame(_, _, _)).With(Args<1, 2>(ElementsAreArray(original_data)));
433 for (auto& d : sent_data) {
434 byte_stuffer_recv_byte(1, d);
435 }
436}
437
438TEST_F(ByteStuffer, sends_and_receives_full_roundtrip_254_bytes_and_then_zero) {
439 uint8_t original_data[255];
440 int i;
441 for (i = 0; i < 254; i++) {
442 original_data[i] = i + 1;
443 }
444 original_data[254] = 0;
445 byte_stuffer_send_frame(0, original_data, sizeof(original_data));
446 EXPECT_CALL(*this, validator_recv_frame(_, _, _)).With(Args<1, 2>(ElementsAreArray(original_data)));
447 for (auto& d : sent_data) {
448 byte_stuffer_recv_byte(1, d);
449 }
450}
diff --git a/quantum/serial_link/tests/frame_router_tests.cpp b/quantum/serial_link/tests/frame_router_tests.cpp
deleted file mode 100644
index f76dfb33d..000000000
--- a/quantum/serial_link/tests/frame_router_tests.cpp
+++ /dev/null
@@ -1,204 +0,0 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#include "gtest/gtest.h"
26#include "gmock/gmock.h"
27#include <array>
28extern "C" {
29#include "serial_link/protocol/transport.h"
30#include "serial_link/protocol/byte_stuffer.h"
31#include "serial_link/protocol/frame_router.h"
32}
33
34using testing::_;
35using testing::Args;
36using testing::ElementsAreArray;
37
38class FrameRouter : public testing::Test {
39 public:
40 FrameRouter() : current_router_buffer(nullptr) {
41 Instance = this;
42 init_byte_stuffer();
43 }
44
45 ~FrameRouter() { Instance = nullptr; }
46
47 void send_data(uint8_t link, const uint8_t* data, uint16_t size) {
48 auto& buffer = current_router_buffer->send_buffers[link];
49 std::copy(data, data + size, std::back_inserter(buffer));
50 }
51
52 void receive_data(uint8_t link, uint8_t* data, uint16_t size) {
53 int i;
54 for (i = 0; i < size; i++) {
55 byte_stuffer_recv_byte(link, data[i]);
56 }
57 }
58
59 void activate_router(uint8_t num) {
60 current_router_buffer = router_buffers + num;
61 router_set_master(num == 0);
62 }
63
64 void simulate_transport(uint8_t from, uint8_t to) {
65 activate_router(to);
66 if (from > to) {
67 receive_data(DOWN_LINK, router_buffers[from].send_buffers[UP_LINK].data(), router_buffers[from].send_buffers[UP_LINK].size());
68 } else if (to > from) {
69 receive_data(UP_LINK, router_buffers[from].send_buffers[DOWN_LINK].data(), router_buffers[from].send_buffers[DOWN_LINK].size());
70 }
71 }
72
73 MOCK_METHOD3(transport_recv_frame, void(uint8_t from, uint8_t* data, uint16_t size));
74
75 std::vector<uint8_t> received_data;
76
77 struct router_buffer {
78 std::vector<uint8_t> send_buffers[2];
79 };
80
81 router_buffer router_buffers[8];
82 router_buffer* current_router_buffer;
83
84 static FrameRouter* Instance;
85};
86
87FrameRouter* FrameRouter::Instance = nullptr;
88
89typedef struct {
90 std::array<uint8_t, 4> data;
91 uint8_t extra[16];
92} frame_buffer_t;
93
94extern "C" {
95void send_data(uint8_t link, const uint8_t* data, uint16_t size) { FrameRouter::Instance->send_data(link, data, size); }
96
97void transport_recv_frame(uint8_t from, uint8_t* data, uint16_t size) { FrameRouter::Instance->transport_recv_frame(from, data, size); }
98}
99
100TEST_F(FrameRouter, master_broadcast_is_received_by_everyone) {
101 frame_buffer_t data;
102 data.data = {0xAB, 0x70, 0x55, 0xBB};
103 activate_router(0);
104 router_send_frame(0xFF, (uint8_t*)&data, 4);
105 EXPECT_GT(router_buffers[0].send_buffers[DOWN_LINK].size(), 0);
106 EXPECT_EQ(router_buffers[0].send_buffers[UP_LINK].size(), 0);
107 EXPECT_CALL(*this, transport_recv_frame(0, _, _)).With(Args<1, 2>(ElementsAreArray(data.data)));
108 simulate_transport(0, 1);
109 EXPECT_GT(router_buffers[1].send_buffers[DOWN_LINK].size(), 0);
110 EXPECT_EQ(router_buffers[1].send_buffers[UP_LINK].size(), 0);
111
112 EXPECT_CALL(*this, transport_recv_frame(0, _, _)).With(Args<1, 2>(ElementsAreArray(data.data)));
113 simulate_transport(1, 2);
114 EXPECT_GT(router_buffers[2].send_buffers[DOWN_LINK].size(), 0);
115 EXPECT_EQ(router_buffers[2].send_buffers[UP_LINK].size(), 0);
116}
117
118TEST_F(FrameRouter, master_send_is_received_by_targets) {
119 frame_buffer_t data;
120 data.data = {0xAB, 0x70, 0x55, 0xBB};
121 activate_router(0);
122 router_send_frame((1 << 1) | (1 << 2), (uint8_t*)&data, 4);
123 EXPECT_GT(router_buffers[0].send_buffers[DOWN_LINK].size(), 0);
124 EXPECT_EQ(router_buffers[0].send_buffers[UP_LINK].size(), 0);
125
126 simulate_transport(0, 1);
127 EXPECT_GT(router_buffers[1].send_buffers[DOWN_LINK].size(), 0);
128 EXPECT_EQ(router_buffers[1].send_buffers[UP_LINK].size(), 0);
129
130 EXPECT_CALL(*this, transport_recv_frame(0, _, _)).With(Args<1, 2>(ElementsAreArray(data.data)));
131 simulate_transport(1, 2);
132 EXPECT_GT(router_buffers[2].send_buffers[DOWN_LINK].size(), 0);
133 EXPECT_EQ(router_buffers[2].send_buffers[UP_LINK].size(), 0);
134
135 EXPECT_CALL(*this, transport_recv_frame(0, _, _)).With(Args<1, 2>(ElementsAreArray(data.data)));
136 simulate_transport(2, 3);
137 EXPECT_GT(router_buffers[3].send_buffers[DOWN_LINK].size(), 0);
138 EXPECT_EQ(router_buffers[3].send_buffers[UP_LINK].size(), 0);
139}
140
141TEST_F(FrameRouter, first_link_sends_to_master) {
142 frame_buffer_t data;
143 data.data = {0xAB, 0x70, 0x55, 0xBB};
144 activate_router(1);
145 router_send_frame(0, (uint8_t*)&data, 4);
146 EXPECT_GT(router_buffers[1].send_buffers[UP_LINK].size(), 0);
147 EXPECT_EQ(router_buffers[1].send_buffers[DOWN_LINK].size(), 0);
148
149 EXPECT_CALL(*this, transport_recv_frame(1, _, _)).With(Args<1, 2>(ElementsAreArray(data.data)));
150 simulate_transport(1, 0);
151 EXPECT_EQ(router_buffers[0].send_buffers[DOWN_LINK].size(), 0);
152 EXPECT_EQ(router_buffers[0].send_buffers[UP_LINK].size(), 0);
153}
154
155TEST_F(FrameRouter, second_link_sends_to_master) {
156 frame_buffer_t data;
157 data.data = {0xAB, 0x70, 0x55, 0xBB};
158 activate_router(2);
159 router_send_frame(0, (uint8_t*)&data, 4);
160 EXPECT_GT(router_buffers[2].send_buffers[UP_LINK].size(), 0);
161 EXPECT_EQ(router_buffers[2].send_buffers[DOWN_LINK].size(), 0);
162
163 simulate_transport(2, 1);
164 EXPECT_GT(router_buffers[1].send_buffers[UP_LINK].size(), 0);
165 EXPECT_EQ(router_buffers[1].send_buffers[DOWN_LINK].size(), 0);
166
167 EXPECT_CALL(*this, transport_recv_frame(2, _, _)).With(Args<1, 2>(ElementsAreArray(data.data)));
168 simulate_transport(1, 0);
169 EXPECT_EQ(router_buffers[0].send_buffers[DOWN_LINK].size(), 0);
170 EXPECT_EQ(router_buffers[0].send_buffers[UP_LINK].size(), 0);
171}
172
173TEST_F(FrameRouter, master_sends_to_master_does_nothing) {
174 frame_buffer_t data;
175 data.data = {0xAB, 0x70, 0x55, 0xBB};
176 activate_router(0);
177 router_send_frame(0, (uint8_t*)&data, 4);
178 EXPECT_EQ(router_buffers[0].send_buffers[UP_LINK].size(), 0);
179 EXPECT_EQ(router_buffers[0].send_buffers[DOWN_LINK].size(), 0);
180}
181
182TEST_F(FrameRouter, link_sends_to_other_link_does_nothing) {
183 frame_buffer_t data;
184 data.data = {0xAB, 0x70, 0x55, 0xBB};
185 activate_router(1);
186 router_send_frame(2, (uint8_t*)&data, 4);
187 EXPECT_EQ(router_buffers[1].send_buffers[UP_LINK].size(), 0);
188 EXPECT_EQ(router_buffers[1].send_buffers[DOWN_LINK].size(), 0);
189}
190
191TEST_F(FrameRouter, master_receives_on_uplink_does_nothing) {
192 frame_buffer_t data;
193 data.data = {0xAB, 0x70, 0x55, 0xBB};
194 activate_router(1);
195 router_send_frame(0, (uint8_t*)&data, 4);
196 EXPECT_GT(router_buffers[1].send_buffers[UP_LINK].size(), 0);
197 EXPECT_EQ(router_buffers[1].send_buffers[DOWN_LINK].size(), 0);
198
199 EXPECT_CALL(*this, transport_recv_frame(_, _, _)).Times(0);
200 activate_router(0);
201 receive_data(UP_LINK, router_buffers[1].send_buffers[UP_LINK].data(), router_buffers[1].send_buffers[UP_LINK].size());
202 EXPECT_EQ(router_buffers[0].send_buffers[UP_LINK].size(), 0);
203 EXPECT_EQ(router_buffers[0].send_buffers[DOWN_LINK].size(), 0);
204}
diff --git a/quantum/serial_link/tests/frame_validator_tests.cpp b/quantum/serial_link/tests/frame_validator_tests.cpp
deleted file mode 100644
index 43dc57b63..000000000
--- a/quantum/serial_link/tests/frame_validator_tests.cpp
+++ /dev/null
@@ -1,100 +0,0 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#include "gtest/gtest.h"
26#include "gmock/gmock.h"
27extern "C" {
28#include "serial_link/protocol/frame_validator.h"
29}
30
31using testing::_;
32using testing::Args;
33using testing::ElementsAreArray;
34
35class FrameValidator : public testing::Test {
36 public:
37 FrameValidator() { Instance = this; }
38
39 ~FrameValidator() { Instance = nullptr; }
40
41 MOCK_METHOD3(route_incoming_frame, void(uint8_t link, uint8_t* data, uint16_t size));
42 MOCK_METHOD3(byte_stuffer_send_frame, void(uint8_t link, uint8_t* data, uint16_t size));
43
44 static FrameValidator* Instance;
45};
46
47FrameValidator* FrameValidator::Instance = nullptr;
48
49extern "C" {
50void route_incoming_frame(uint8_t link, uint8_t* data, uint16_t size) { FrameValidator::Instance->route_incoming_frame(link, data, size); }
51
52void byte_stuffer_send_frame(uint8_t link, uint8_t* data, uint16_t size) { FrameValidator::Instance->byte_stuffer_send_frame(link, data, size); }
53}
54
55TEST_F(FrameValidator, doesnt_validate_frames_under_5_bytes) {
56 EXPECT_CALL(*this, route_incoming_frame(_, _, _)).Times(0);
57 uint8_t data[] = {1, 2};
58 validator_recv_frame(0, 0, 1);
59 validator_recv_frame(0, data, 2);
60 validator_recv_frame(0, data, 3);
61 validator_recv_frame(0, data, 4);
62}
63
64TEST_F(FrameValidator, validates_one_byte_frame_with_correct_crc) {
65 uint8_t data[] = {0x44, 0x04, 0x6A, 0xB3, 0xA3};
66 EXPECT_CALL(*this, route_incoming_frame(_, _, _)).With(Args<1, 2>(ElementsAreArray(data, 1)));
67 validator_recv_frame(0, data, 5);
68}
69
70TEST_F(FrameValidator, does_not_validate_one_byte_frame_with_incorrect_crc) {
71 uint8_t data[] = {0x44, 0, 0, 0, 0};
72 EXPECT_CALL(*this, route_incoming_frame(_, _, _)).Times(0);
73 validator_recv_frame(1, data, 5);
74}
75
76TEST_F(FrameValidator, validates_four_byte_frame_with_correct_crc) {
77 uint8_t data[] = {0x44, 0x10, 0xFF, 0x00, 0x74, 0x4E, 0x30, 0xBA};
78 EXPECT_CALL(*this, route_incoming_frame(_, _, _)).With(Args<1, 2>(ElementsAreArray(data, 4)));
79 validator_recv_frame(1, data, 8);
80}
81
82TEST_F(FrameValidator, validates_five_byte_frame_with_correct_crc) {
83 uint8_t data[] = {1, 2, 3, 4, 5, 0xF4, 0x99, 0x0B, 0x47};
84 EXPECT_CALL(*this, route_incoming_frame(_, _, _)).With(Args<1, 2>(ElementsAreArray(data, 5)));
85 validator_recv_frame(0, data, 9);
86}
87
88TEST_F(FrameValidator, sends_one_byte_with_correct_crc) {
89 uint8_t original[] = {0x44, 0, 0, 0, 0};
90 uint8_t expected[] = {0x44, 0x04, 0x6A, 0xB3, 0xA3};
91 EXPECT_CALL(*this, byte_stuffer_send_frame(_, _, _)).With(Args<1, 2>(ElementsAreArray(expected)));
92 validator_send_frame(0, original, 1);
93}
94
95TEST_F(FrameValidator, sends_five_bytes_with_correct_crc) {
96 uint8_t original[] = {1, 2, 3, 4, 5, 0, 0, 0, 0};
97 uint8_t expected[] = {1, 2, 3, 4, 5, 0xF4, 0x99, 0x0B, 0x47};
98 EXPECT_CALL(*this, byte_stuffer_send_frame(_, _, _)).With(Args<1, 2>(ElementsAreArray(expected)));
99 validator_send_frame(0, original, 5);
100}
diff --git a/quantum/serial_link/tests/rules.mk b/quantum/serial_link/tests/rules.mk
deleted file mode 100644
index b81515bc5..000000000
--- a/quantum/serial_link/tests/rules.mk
+++ /dev/null
@@ -1,22 +0,0 @@
1serial_link_byte_stuffer_SRC :=\
2 $(SERIAL_PATH)/tests/byte_stuffer_tests.cpp \
3 $(SERIAL_PATH)/protocol/byte_stuffer.c
4
5serial_link_frame_validator_SRC := \
6 $(SERIAL_PATH)/tests/frame_validator_tests.cpp \
7 $(SERIAL_PATH)/protocol/frame_validator.c
8
9serial_link_frame_router_SRC := \
10 $(SERIAL_PATH)/tests/frame_router_tests.cpp \
11 $(SERIAL_PATH)/protocol/byte_stuffer.c \
12 $(SERIAL_PATH)/protocol/frame_validator.c \
13 $(SERIAL_PATH)/protocol/frame_router.c
14
15serial_link_triple_buffered_object_SRC := \
16 $(SERIAL_PATH)/tests/triple_buffered_object_tests.cpp \
17 $(SERIAL_PATH)/protocol/triple_buffered_object.c
18
19serial_link_transport_SRC := \
20 $(SERIAL_PATH)/tests/transport_tests.cpp \
21 $(SERIAL_PATH)/protocol/transport.c \
22 $(SERIAL_PATH)/protocol/triple_buffered_object.c
diff --git a/quantum/serial_link/tests/testlist.mk b/quantum/serial_link/tests/testlist.mk
deleted file mode 100644
index c5edaf478..000000000
--- a/quantum/serial_link/tests/testlist.mk
+++ /dev/null
@@ -1,6 +0,0 @@
1TEST_LIST +=\
2 serial_link_byte_stuffer\
3 serial_link_frame_validator\
4 serial_link_frame_router\
5 serial_link_triple_buffered_object\
6 serial_link_transport
diff --git a/quantum/serial_link/tests/transport_tests.cpp b/quantum/serial_link/tests/transport_tests.cpp
deleted file mode 100644
index cfd111046..000000000
--- a/quantum/serial_link/tests/transport_tests.cpp
+++ /dev/null
@@ -1,184 +0,0 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#include "gtest/gtest.h"
26#include "gmock/gmock.h"
27
28using testing::_;
29using testing::Args;
30using testing::ElementsAreArray;
31
32extern "C" {
33#include "serial_link/protocol/transport.h"
34}
35
36struct test_object1 {
37 uint32_t test;
38};
39
40struct test_object2 {
41 uint32_t test1;
42 uint32_t test2;
43};
44
45MASTER_TO_ALL_SLAVES_OBJECT(master_to_slave, test_object1);
46MASTER_TO_SINGLE_SLAVE_OBJECT(master_to_single_slave, test_object1);
47SLAVE_TO_MASTER_OBJECT(slave_to_master, test_object1);
48
49static remote_object_t* test_remote_objects[] = {
50 REMOTE_OBJECT(master_to_slave),
51 REMOTE_OBJECT(master_to_single_slave),
52 REMOTE_OBJECT(slave_to_master),
53};
54
55class Transport : public testing::Test {
56 public:
57 Transport() {
58 Instance = this;
59 add_remote_objects(test_remote_objects, sizeof(test_remote_objects) / sizeof(remote_object_t*));
60 }
61
62 ~Transport() {
63 Instance = nullptr;
64 reinitialize_serial_link_transport();
65 }
66
67 MOCK_METHOD0(signal_data_written, void());
68 MOCK_METHOD1(router_send_frame, void(uint8_t destination));
69
70 void router_send_frame(uint8_t destination, uint8_t* data, uint16_t size) {
71 router_send_frame(destination);
72 std::copy(data, data + size, std::back_inserter(sent_data));
73 }
74
75 static Transport* Instance;
76
77 std::vector<uint8_t> sent_data;
78};
79
80Transport* Transport::Instance = nullptr;
81
82extern "C" {
83void signal_data_written(void) { Transport::Instance->signal_data_written(); }
84
85void router_send_frame(uint8_t destination, uint8_t* data, uint16_t size) { Transport::Instance->router_send_frame(destination, data, size); }
86}
87
88TEST_F(Transport, write_to_local_signals_an_event) {
89 begin_write_master_to_slave();
90 EXPECT_CALL(*this, signal_data_written());
91 end_write_master_to_slave();
92 begin_write_slave_to_master();
93 EXPECT_CALL(*this, signal_data_written());
94 end_write_slave_to_master();
95 begin_write_master_to_single_slave(1);
96 EXPECT_CALL(*this, signal_data_written());
97 end_write_master_to_single_slave(1);
98}
99
100TEST_F(Transport, writes_from_master_to_all_slaves) {
101 update_transport();
102 test_object1* obj = begin_write_master_to_slave();
103 obj->test = 5;
104 EXPECT_CALL(*this, signal_data_written());
105 end_write_master_to_slave();
106 EXPECT_CALL(*this, router_send_frame(0xFF));
107 update_transport();
108 transport_recv_frame(0, sent_data.data(), sent_data.size());
109 test_object1* obj2 = read_master_to_slave();
110 EXPECT_NE(obj2, nullptr);
111 EXPECT_EQ(obj2->test, 5);
112}
113
114TEST_F(Transport, writes_from_slave_to_master) {
115 update_transport();
116 test_object1* obj = begin_write_slave_to_master();
117 obj->test = 7;
118 EXPECT_CALL(*this, signal_data_written());
119 end_write_slave_to_master();
120 EXPECT_CALL(*this, router_send_frame(0));
121 update_transport();
122 transport_recv_frame(3, sent_data.data(), sent_data.size());
123 test_object1* obj2 = read_slave_to_master(2);
124 EXPECT_EQ(read_slave_to_master(0), nullptr);
125 EXPECT_NE(obj2, nullptr);
126 EXPECT_EQ(obj2->test, 7);
127}
128
129TEST_F(Transport, writes_from_master_to_single_slave) {
130 update_transport();
131 test_object1* obj = begin_write_master_to_single_slave(3);
132 obj->test = 7;
133 EXPECT_CALL(*this, signal_data_written());
134 end_write_master_to_single_slave(3);
135 EXPECT_CALL(*this, router_send_frame(4));
136 update_transport();
137 transport_recv_frame(0, sent_data.data(), sent_data.size());
138 test_object1* obj2 = read_master_to_single_slave();
139 EXPECT_NE(obj2, nullptr);
140 EXPECT_EQ(obj2->test, 7);
141}
142
143TEST_F(Transport, ignores_object_with_invalid_id) {
144 update_transport();
145 test_object1* obj = begin_write_master_to_single_slave(3);
146 obj->test = 7;
147 EXPECT_CALL(*this, signal_data_written());
148 end_write_master_to_single_slave(3);
149 EXPECT_CALL(*this, router_send_frame(4));
150 update_transport();
151 sent_data[sent_data.size() - 1] = 44;
152 transport_recv_frame(0, sent_data.data(), sent_data.size());
153 test_object1* obj2 = read_master_to_single_slave();
154 EXPECT_EQ(obj2, nullptr);
155}
156
157TEST_F(Transport, ignores_object_with_size_too_small) {
158 update_transport();
159 test_object1* obj = begin_write_master_to_slave();
160 obj->test = 7;
161 EXPECT_CALL(*this, signal_data_written());
162 end_write_master_to_slave();
163 EXPECT_CALL(*this, router_send_frame(_));
164 update_transport();
165 sent_data[sent_data.size() - 2] = 0;
166 transport_recv_frame(0, sent_data.data(), sent_data.size() - 1);
167 test_object1* obj2 = read_master_to_slave();
168 EXPECT_EQ(obj2, nullptr);
169}
170
171TEST_F(Transport, ignores_object_with_size_too_big) {
172 update_transport();
173 test_object1* obj = begin_write_master_to_slave();
174 obj->test = 7;
175 EXPECT_CALL(*this, signal_data_written());
176 end_write_master_to_slave();
177 EXPECT_CALL(*this, router_send_frame(_));
178 update_transport();
179 sent_data.resize(sent_data.size() + 22);
180 sent_data[sent_data.size() - 1] = 0;
181 transport_recv_frame(0, sent_data.data(), sent_data.size());
182 test_object1* obj2 = read_master_to_slave();
183 EXPECT_EQ(obj2, nullptr);
184}
diff --git a/quantum/serial_link/tests/triple_buffered_object_tests.cpp b/quantum/serial_link/tests/triple_buffered_object_tests.cpp
deleted file mode 100644
index 8de9bfdeb..000000000
--- a/quantum/serial_link/tests/triple_buffered_object_tests.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#include "gtest/gtest.h"
26extern "C" {
27#include "serial_link/protocol/triple_buffered_object.h"
28}
29
30struct test_object {
31 uint8_t state;
32 uint32_t buffer[3];
33};
34
35test_object test_object;
36
37class TripleBufferedObject : public testing::Test {
38 public:
39 TripleBufferedObject() { triple_buffer_init((triple_buffer_object_t*)&test_object); }
40};
41
42TEST_F(TripleBufferedObject, writes_and_reads_object) {
43 *triple_buffer_begin_write(&test_object) = 0x3456ABCC;
44 triple_buffer_end_write(&test_object);
45 EXPECT_EQ(*triple_buffer_read(&test_object), 0x3456ABCC);
46}
47
48TEST_F(TripleBufferedObject, does_not_read_empty) { EXPECT_EQ(triple_buffer_read(&test_object), nullptr); }
49
50TEST_F(TripleBufferedObject, writes_twice_and_reads_object) {
51 *triple_buffer_begin_write(&test_object) = 0x3456ABCC;
52 triple_buffer_end_write(&test_object);
53 *triple_buffer_begin_write(&test_object) = 0x44778899;
54 triple_buffer_end_write(&test_object);
55 EXPECT_EQ(*triple_buffer_read(&test_object), 0x44778899);
56}
57
58TEST_F(TripleBufferedObject, performs_another_write_in_the_middle_of_read) {
59 *triple_buffer_begin_write(&test_object) = 1;
60 triple_buffer_end_write(&test_object);
61 uint32_t* read = triple_buffer_read(&test_object);
62 *triple_buffer_begin_write(&test_object) = 2;
63 triple_buffer_end_write(&test_object);
64 EXPECT_EQ(*read, 1);
65 EXPECT_EQ(*triple_buffer_read(&test_object), 2);
66 EXPECT_EQ(triple_buffer_read(&test_object), nullptr);
67}
68
69TEST_F(TripleBufferedObject, performs_two_writes_in_the_middle_of_read) {
70 *triple_buffer_begin_write(&test_object) = 1;
71 triple_buffer_end_write(&test_object);
72 uint32_t* read = triple_buffer_read(&test_object);
73 *triple_buffer_begin_write(&test_object) = 2;
74 triple_buffer_end_write(&test_object);
75 *triple_buffer_begin_write(&test_object) = 3;
76 triple_buffer_end_write(&test_object);
77 EXPECT_EQ(*read, 1);
78 EXPECT_EQ(*triple_buffer_read(&test_object), 3);
79 EXPECT_EQ(triple_buffer_read(&test_object), nullptr);
80}
diff --git a/quantum/usb_device_state.c b/quantum/usb_device_state.c
new file mode 100644
index 000000000..5ccd309ec
--- /dev/null
+++ b/quantum/usb_device_state.c
@@ -0,0 +1,51 @@
1/*
2 * Copyright 2021 Andrei Purdea <andrei@purdea.ro>
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "usb_device_state.h"
19
20enum usb_device_state usb_device_state = USB_DEVICE_STATE_NO_INIT;
21
22__attribute__((weak)) void notify_usb_device_state_change_kb(enum usb_device_state usb_device_state) { notify_usb_device_state_change_user(usb_device_state); }
23
24__attribute__((weak)) void notify_usb_device_state_change_user(enum usb_device_state usb_device_state) {}
25
26static void notify_usb_device_state_change(enum usb_device_state usb_device_state) { notify_usb_device_state_change_kb(usb_device_state); }
27
28void usb_device_state_set_configuration(bool isConfigured, uint8_t configurationNumber) {
29 usb_device_state = isConfigured ? USB_DEVICE_STATE_CONFIGURED : USB_DEVICE_STATE_INIT;
30 notify_usb_device_state_change(usb_device_state);
31}
32
33void usb_device_state_set_suspend(bool isConfigured, uint8_t configurationNumber) {
34 usb_device_state = USB_DEVICE_STATE_SUSPEND;
35 notify_usb_device_state_change(usb_device_state);
36}
37
38void usb_device_state_set_resume(bool isConfigured, uint8_t configurationNumber) {
39 usb_device_state = isConfigured ? USB_DEVICE_STATE_CONFIGURED : USB_DEVICE_STATE_INIT;
40 notify_usb_device_state_change(usb_device_state);
41}
42
43void usb_device_state_set_reset(void) {
44 usb_device_state = USB_DEVICE_STATE_INIT;
45 notify_usb_device_state_change(usb_device_state);
46}
47
48void usb_device_state_init(void) {
49 usb_device_state = USB_DEVICE_STATE_INIT;
50 notify_usb_device_state_change(usb_device_state);
51}
diff --git a/quantum/usb_device_state.h b/quantum/usb_device_state.h
new file mode 100644
index 000000000..c229311d4
--- /dev/null
+++ b/quantum/usb_device_state.h
@@ -0,0 +1,39 @@
1/*
2 * Copyright 2021 Andrei Purdea <andrei@purdea.ro>
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#pragma once
19
20#include <stdbool.h>
21#include <stdint.h>
22
23void usb_device_state_set_configuration(bool isConfigured, uint8_t configurationNumber);
24void usb_device_state_set_suspend(bool isConfigured, uint8_t configurationNumber);
25void usb_device_state_set_resume(bool isConfigured, uint8_t configurationNumber);
26void usb_device_state_set_reset(void);
27void usb_device_state_init(void);
28
29enum usb_device_state {
30 USB_DEVICE_STATE_NO_INIT = 0, // We're in this state before calling usb_device_state_init()
31 USB_DEVICE_STATE_INIT = 1, // Can consume up to 100mA
32 USB_DEVICE_STATE_CONFIGURED = 2, // Can consume up to what is specified in configuration descriptor, typically 500mA
33 USB_DEVICE_STATE_SUSPEND = 3 // Can consume only suspend current
34};
35
36extern enum usb_device_state usb_device_state;
37
38void notify_usb_device_state_change_kb(enum usb_device_state usb_device_state);
39void notify_usb_device_state_change_user(enum usb_device_state usb_device_state);
diff --git a/quantum/visualizer/LICENSE.md b/quantum/visualizer/LICENSE.md
deleted file mode 100644
index 22d4c3f08..000000000
--- a/quantum/visualizer/LICENSE.md
+++ /dev/null
@@ -1,29 +0,0 @@
1The files in this project are licensed under the MIT license
2It uses the following libraries
3uGFX - with it's own license, see the license.html file in the uGFX subfolder for more information
4tmk_core - is indirectly used and not included in the repository. It's licensed under the GPLv2 license
5Chibios - which is used by tmk_core is licensed under GPLv3.
6
7Therefore the effective license for any project using the library is GPLv3
8
9The MIT License (MIT)
10
11Copyright (c) 2016 Fred Sundvik
12
13Permission is hereby granted, free of charge, to any person obtaining a copy
14of this software and associated documentation files (the "Software"), to deal
15in the Software without restriction, including without limitation the rights
16to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17copies of the Software, and to permit persons to whom the Software is
18furnished to do so, subject to the following conditions:
19
20The above copyright notice and this permission notice shall be included in all
21copies or substantial portions of the Software.
22
23THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29SOFTWARE.
diff --git a/quantum/visualizer/common_gfxconf.h b/quantum/visualizer/common_gfxconf.h
deleted file mode 100644
index e0735b37d..000000000
--- a/quantum/visualizer/common_gfxconf.h
+++ /dev/null
@@ -1,354 +0,0 @@
1/**
2 * This file has a different license to the rest of the uGFX system.
3 * You can copy, modify and distribute this file as you see fit.
4 * You do not need to publish your source modifications to this file.
5 * The only thing you are not permitted to do is to relicense it
6 * under a different license.
7 */
8
9/**
10 * Copy this file into your project directory and rename it as gfxconf.h
11 * Edit your copy to turn on the uGFX features you want to use.
12 * The values below are the defaults.
13 *
14 * Only remove the comments from lines where you want to change the
15 * default value. This allows definitions to be included from
16 * driver makefiles when required and provides the best future
17 * compatibility for your project.
18 *
19 * Please use spaces instead of tabs in this file.
20 */
21
22#pragma once
23
24///////////////////////////////////////////////////////////////////////////
25// GFX - Compatibility options //
26///////////////////////////////////////////////////////////////////////////
27//#define GFX_COMPAT_V2 GFXON
28//#define GFX_COMPAT_OLDCOLORS GFXON
29
30///////////////////////////////////////////////////////////////////////////
31// GOS - One of these must be defined, preferably in your Makefile //
32///////////////////////////////////////////////////////////////////////////
33//#define GFX_USE_OS_CHIBIOS GFXOFF
34//#define GFX_USE_OS_FREERTOS GFXOFF
35// #define GFX_FREERTOS_USE_TRACE GFXOFF
36//#define GFX_USE_OS_WIN32 GFXOFF
37//#define GFX_USE_OS_LINUX GFXOFF
38//#define GFX_USE_OS_OSX GFXOFF
39//#define GFX_USE_OS_ECOS GFXOFF
40//#define GFX_USE_OS_RAWRTOS GFXOFF
41//#define GFX_USE_OS_ARDUINO GFXOFF
42//#define GFX_USE_OS_KEIL GFXOFF
43//#define GFX_USE_OS_RTX5 GFXOFF
44//#define GFX_USE_OS_CMSIS GFXOFF
45//#define GFX_USE_OS_CMSIS2 GFXOFF
46//#define GFX_USE_OS_RAW32 GFXOFF
47//#define GFX_USE_OS_ZEPHYR GFXOFF
48//#define GFX_USE_OS_NIOS GFXOFF
49//#define GFX_USE_OS_QT GFXOFF
50// #define INTERRUPTS_OFF() optional_code
51// #define INTERRUPTS_ON() optional_code
52
53// Options that (should where relevant) apply to all operating systems
54#define GFX_NO_INLINE GFXON
55// #define GFX_COMPILER GFX_COMPILER_UNKNOWN
56// #define GFX_SHOW_COMPILER GFXOFF
57// #define GFX_CPU GFX_CPU_UNKNOWN
58// #define GFX_CPU_NO_ALIGNMENT_FAULTS GFXOFF
59// #define GFX_CPU_ENDIAN GFX_CPU_ENDIAN_UNKNOWN
60// #define GFX_OS_HEAP_SIZE 0
61// #define GFX_OS_NO_INIT GFXOFF
62// #define GFX_OS_INIT_NO_WARNING GFXOFF
63// #define GFX_OS_PRE_INIT_FUNCTION myHardwareInitRoutine
64// #define GFX_OS_EXTRA_INIT_FUNCTION myOSInitRoutine
65// #define GFX_OS_EXTRA_DEINIT_FUNCTION myOSDeInitRoutine
66// #define GFX_OS_CALL_UGFXMAIN GFXOFF
67// #define GFX_OS_UGFXMAIN_STACKSIZE 0
68// #define GFX_EMULATE_MALLOC GFXOFF
69// #define GFX_MEM_LT64K GFXOFF
70
71///////////////////////////////////////////////////////////////////////////
72// GDISP //
73///////////////////////////////////////////////////////////////////////////
74#define GFX_USE_GDISP GFXON
75
76//#define GDISP_NEED_AUTOFLUSH GFXOFF
77//#define GDISP_NEED_TIMERFLUSH GFXOFF
78//#define GDISP_NEED_VALIDATION GFXON
79//#define GDISP_NEED_CLIP GFXON
80#define GDISP_NEED_CIRCLE GFXON
81//#define GDISP_NEED_DUALCIRCLE GFXOFF
82#define GDISP_NEED_ELLIPSE GFXON
83#define GDISP_NEED_ARC GFXON
84#define GDISP_NEED_ARCSECTORS GFXON
85#define GDISP_NEED_CONVEX_POLYGON GFXON
86//#define GDISP_NEED_SCROLL GFXOFF
87#define GDISP_NEED_PIXELREAD GFXON
88#define GDISP_NEED_CONTROL GFXON
89//#define GDISP_NEED_QUERY GFXOFF
90//#define GDISP_NEED_MULTITHREAD GFXOFF
91//#define GDISP_NEED_STREAMING GFXOFF
92#define GDISP_NEED_TEXT GFXON
93// #define GDISP_NEED_TEXT_WORDWRAP GFXOFF
94// #define GDISP_NEED_TEXT_BOXPADLR 1
95// #define GDISP_NEED_TEXT_BOXPADTB 1
96// #define GDISP_NEED_ANTIALIAS GFXOFF
97// #define GDISP_NEED_UTF8 GFXOFF
98#define GDISP_NEED_TEXT_KERNING GFXON
99// #define GDISP_INCLUDE_FONT_UI1 GFXOFF
100// #define GDISP_INCLUDE_FONT_UI2 GFXOFF // The smallest preferred font.
101// #define GDISP_INCLUDE_FONT_LARGENUMBERS GFXOFF
102// #define GDISP_INCLUDE_FONT_DEJAVUSANS10 GFXOFF
103// #define GDISP_INCLUDE_FONT_DEJAVUSANS12 GFXOFF
104// #define GDISP_INCLUDE_FONT_DEJAVUSANS16 GFXOFF
105// #define GDISP_INCLUDE_FONT_DEJAVUSANS20 GFXOFF
106// #define GDISP_INCLUDE_FONT_DEJAVUSANS24 GFXOFF
107// #define GDISP_INCLUDE_FONT_DEJAVUSANS32 GFXOFF
108#define GDISP_INCLUDE_FONT_DEJAVUSANSBOLD12 GFXON
109// #define GDISP_INCLUDE_FONT_FIXED_10X20 GFXOFF
110// #define GDISP_INCLUDE_FONT_FIXED_7X14 GFXOFF
111#define GDISP_INCLUDE_FONT_FIXED_5X8 GFXON
112// #define GDISP_INCLUDE_FONT_DEJAVUSANS12_AA GFXOFF
113// #define GDISP_INCLUDE_FONT_DEJAVUSANS16_AA GFXOFF
114// #define GDISP_INCLUDE_FONT_DEJAVUSANS20_AA GFXOFF
115// #define GDISP_INCLUDE_FONT_DEJAVUSANS24_AA GFXOFF
116// #define GDISP_INCLUDE_FONT_DEJAVUSANS32_AA GFXOFF
117// #define GDISP_INCLUDE_FONT_DEJAVUSANSBOLD12_AA GFXOFF
118// #define GDISP_INCLUDE_USER_FONTS GFXOFF
119
120//#define GDISP_NEED_IMAGE GFXOFF
121// #define GDISP_NEED_IMAGE_NATIVE GFXOFF
122// #define GDISP_NEED_IMAGE_GIF GFXOFF
123// #define GDISP_IMAGE_GIF_BLIT_BUFFER_SIZE 32
124// #define GDISP_NEED_IMAGE_BMP GFXOFF
125// #define GDISP_NEED_IMAGE_BMP_1 GFXON
126// #define GDISP_NEED_IMAGE_BMP_4 GFXON
127// #define GDISP_NEED_IMAGE_BMP_4_RLE GFXON
128// #define GDISP_NEED_IMAGE_BMP_8 GFXON
129// #define GDISP_NEED_IMAGE_BMP_8_RLE GFXON
130// #define GDISP_NEED_IMAGE_BMP_16 GFXON
131// #define GDISP_NEED_IMAGE_BMP_24 GFXON
132// #define GDISP_NEED_IMAGE_BMP_32 GFXON
133// #define GDISP_IMAGE_BMP_BLIT_BUFFER_SIZE 32
134// #define GDISP_NEED_IMAGE_JPG GFXOFF
135// #define GDISP_NEED_IMAGE_PNG GFXOFF
136// #define GDISP_NEED_IMAGE_PNG_INTERLACED GFXOFF
137// #define GDISP_NEED_IMAGE_PNG_TRANSPARENCY GFXON
138// #define GDISP_NEED_IMAGE_PNG_BACKGROUND GFXON
139// #define GDISP_NEED_IMAGE_PNG_ALPHACLIFF 32
140// #define GDISP_NEED_IMAGE_PNG_PALETTE_124 GFXON
141// #define GDISP_NEED_IMAGE_PNG_PALETTE_8 GFXON
142// #define GDISP_NEED_IMAGE_PNG_GRAYSCALE_124 GFXON
143// #define GDISP_NEED_IMAGE_PNG_GRAYSCALE_8 GFXON
144// #define GDISP_NEED_IMAGE_PNG_GRAYSCALE_16 GFXON
145// #define GDISP_NEED_IMAGE_PNG_GRAYALPHA_8 GFXON
146// #define GDISP_NEED_IMAGE_PNG_GRAYALPHA_16 GFXON
147// #define GDISP_NEED_IMAGE_PNG_RGB_8 GFXON
148// #define GDISP_NEED_IMAGE_PNG_RGB_16 GFXON
149// #define GDISP_NEED_IMAGE_PNG_RGBALPHA_8 GFXON
150// #define GDISP_NEED_IMAGE_PNG_RGBALPHA_16 GFXON
151// #define GDISP_IMAGE_PNG_BLIT_BUFFER_SIZE 32
152// #define GDISP_IMAGE_PNG_FILE_BUFFER_SIZE 8
153// #define GDISP_IMAGE_PNG_Z_BUFFER_SIZE 32768
154// #define GDISP_NEED_IMAGE_ACCOUNTING GFXOFF
155
156//#define GDISP_NEED_PIXMAP GFXOFF
157// #define GDISP_NEED_PIXMAP_IMAGE GFXOFF
158
159//#define GDISP_DEFAULT_ORIENTATION gOrientationLandscape // If not defined the native hardware orientation is used.
160//#define GDISP_LINEBUF_SIZE 128
161//#define GDISP_STARTUP_COLOR GFX_BLACK
162#define GDISP_NEED_STARTUP_LOGO GFXOFF
163
164//#define GDISP_TOTAL_DISPLAYS 1
165
166//#define GDISP_DRIVER_LIST GDISPVMT_Win32, GDISPVMT_Win32
167#ifdef GDISP_DRIVER_LIST
168// // For code and speed optimization define as GFXON or GFXOFF if all controllers have the same capability
169# define GDISP_HARDWARE_STREAM_WRITE GFXOFF
170# define GDISP_HARDWARE_STREAM_READ GFXOFF
171# define GDISP_HARDWARE_STREAM_POS GFXOFF
172# define GDISP_HARDWARE_DRAWPIXEL GFXON
173# define GDISP_HARDWARE_CLEARS GFXOFF
174# define GDISP_HARDWARE_FILLS GFXOFF
175//#define GDISP_HARDWARE_BITFILLS GFXOFF
176# define GDISP_HARDWARE_SCROLL GFXOFF
177# define GDISP_HARDWARE_PIXELREAD GFXON
178# define GDISP_HARDWARE_CONTROL GFXON
179# define GDISP_HARDWARE_QUERY GFXOFF
180# define GDISP_HARDWARE_CLIP GFXOFF
181
182# define GDISP_PIXELFORMAT GDISP_PIXELFORMAT_RGB888
183#endif
184
185#define GDISP_USE_GFXNET GFXOFF
186// #define GDISP_GFXNET_PORT 13001
187// #define GDISP_GFXNET_CUSTOM_LWIP_STARTUP GFXOFF
188// #define GDISP_DONT_WAIT_FOR_NET_DISPLAY GFXOFF
189// #define GDISP_GFXNET_UNSAFE_SOCKETS GFXOFF
190
191///////////////////////////////////////////////////////////////////////////
192// GWIN //
193///////////////////////////////////////////////////////////////////////////
194#define GFX_USE_GWIN GFXOFF
195
196//#define GWIN_NEED_WINDOWMANAGER GFXOFF
197// #define GWIN_REDRAW_IMMEDIATE GFXOFF
198// #define GWIN_REDRAW_SINGLEOP GFXOFF
199// #define GWIN_NEED_FLASHING GFXOFF
200// #define GWIN_FLASHING_PERIOD 250
201
202//#define GWIN_NEED_CONSOLE GFXOFF
203// #define GWIN_CONSOLE_USE_HISTORY GFXOFF
204// #define GWIN_CONSOLE_HISTORY_AVERAGING GFXOFF
205// #define GWIN_CONSOLE_HISTORY_ATCREATE GFXOFF
206// #define GWIN_CONSOLE_ESCSEQ GFXOFF
207// #define GWIN_CONSOLE_USE_BASESTREAM GFXOFF
208// #define GWIN_CONSOLE_USE_FLOAT GFXOFF
209//#define GWIN_NEED_GRAPH GFXOFF
210//#define GWIN_NEED_GL3D GFXOFF
211
212//#define GWIN_NEED_WIDGET GFXOFF
213//#define GWIN_FOCUS_HIGHLIGHT_WIDTH 1
214// #define GWIN_NEED_LABEL GFXOFF
215// #define GWIN_LABEL_ATTRIBUTE GFXOFF
216// #define GWIN_NEED_BUTTON GFXOFF
217// #define GWIN_BUTTON_LAZY_RELEASE GFXOFF
218// #define GWIN_NEED_SLIDER GFXOFF
219// #define GWIN_SLIDER_NOSNAP GFXOFF
220// #define GWIN_SLIDER_DEAD_BAND 5
221// #define GWIN_SLIDER_TOGGLE_INC 20
222// #define GWIN_NEED_CHECKBOX GFXOFF
223// #define GWIN_NEED_IMAGE GFXOFF
224// #define GWIN_NEED_IMAGE_ANIMATION GFXOFF
225// #define GWIN_NEED_RADIO GFXOFF
226// #define GWIN_NEED_LIST GFXOFF
227// #define GWIN_NEED_LIST_IMAGES GFXOFF
228// #define GWIN_NEED_PROGRESSBAR GFXOFF
229// #define GWIN_PROGRESSBAR_AUTO GFXOFF
230// #define GWIN_NEED_KEYBOARD GFXOFF
231// #define GWIN_KEYBOARD_DEFAULT_LAYOUT VirtualKeyboard_English1
232// #define GWIN_NEED_KEYBOARD_ENGLISH1 GFXON
233// #define GWIN_NEED_TEXTEDIT GFXOFF
234// #define GWIN_FLAT_STYLING GFXOFF
235// #define GWIN_WIDGET_TAGS GFXOFF
236
237//#define GWIN_NEED_CONTAINERS GFXOFF
238// #define GWIN_NEED_CONTAINER GFXOFF
239// #define GWIN_NEED_FRAME GFXOFF
240// #define GWIN_NEED_TABSET GFXOFF
241// #define GWIN_TABSET_TABHEIGHT 18
242
243///////////////////////////////////////////////////////////////////////////
244// GTRANS //
245///////////////////////////////////////////////////////////////////////////
246//#define GFX_USE_GTRANS GFXOFF
247
248///////////////////////////////////////////////////////////////////////////
249// GEVENT //
250///////////////////////////////////////////////////////////////////////////
251#define GFX_USE_GEVENT GFXON
252
253//#define GEVENT_ASSERT_NO_RESOURCE GFXOFF
254//#define GEVENT_MAXIMUM_SIZE 32
255//#define GEVENT_MAX_SOURCE_LISTENERS 32
256
257///////////////////////////////////////////////////////////////////////////
258// GTIMER //
259///////////////////////////////////////////////////////////////////////////
260#define GFX_USE_GTIMER GFXOFF
261
262//#define GTIMER_THREAD_PRIORITY gThreadpriorityHigh
263//#define GTIMER_THREAD_WORKAREA_SIZE 2048
264
265///////////////////////////////////////////////////////////////////////////
266// GQUEUE //
267///////////////////////////////////////////////////////////////////////////
268#define GFX_USE_GQUEUE GFXOFF
269
270//#define GQUEUE_NEED_ASYNC GFXOFF
271//#define GQUEUE_NEED_GSYNC GFXOFF
272//#define GQUEUE_NEED_FSYNC GFXOFF
273//#define GQUEUE_NEED_BUFFERS GFXOFF
274
275///////////////////////////////////////////////////////////////////////////
276// GINPUT //
277///////////////////////////////////////////////////////////////////////////
278#define GFX_USE_GINPUT GFXOFF
279
280//#define GINPUT_NEED_MOUSE GFXOFF
281// #define GINPUT_TOUCH_STARTRAW GFXOFF
282// #define GINPUT_TOUCH_NOTOUCH GFXOFF
283// #define GINPUT_TOUCH_NOCALIBRATE GFXOFF
284// #define GINPUT_TOUCH_NOCALIBRATE_GUI GFXOFF
285// #define GINPUT_MOUSE_POLL_PERIOD 25
286// #define GINPUT_MOUSE_CLICK_TIME 300
287// #define GINPUT_TOUCH_CXTCLICK_TIME 700
288// #define GINPUT_TOUCH_USER_CALIBRATION_LOAD GFXOFF
289// #define GINPUT_TOUCH_USER_CALIBRATION_SAVE GFXOFF
290// #define GMOUSE_DRIVER_LIST GMOUSEVMT_Win32, GMOUSEVMT_Win32
291// #define GINPUT_TOUCH_CALIBRATION_FONT1 "* Double"
292// #define GINPUT_TOUCH_CALIBRATION_FONT2 "* Narrow"
293// #define GINPUT_TOUCH_CALIBRATION_TITLE "Calibration"
294// #define GINPUT_TOUCH_CALIBRATION_ERROR "Calibration Failed!"
295//#define GINPUT_NEED_KEYBOARD GFXOFF
296// #define GINPUT_KEYBOARD_POLL_PERIOD 200
297// #define GKEYBOARD_DRIVER_LIST GKEYBOARDVMT_Win32, GKEYBOARDVMT_Win32
298// #define GKEYBOARD_LAYOUT_OFF GFXOFF
299// #define GKEYBOARD_LAYOUT_SCANCODE2_US GFXOFF
300//#define GINPUT_NEED_TOGGLE GFXOFF
301//#define GINPUT_NEED_DIAL GFXOFF
302
303///////////////////////////////////////////////////////////////////////////
304// GFILE //
305///////////////////////////////////////////////////////////////////////////
306#define GFX_USE_GFILE GFXOFF
307
308//#define GFILE_NEED_PRINTG GFXOFF
309//#define GFILE_NEED_SCANG GFXOFF
310//#define GFILE_NEED_STRINGS GFXOFF
311//#define GFILE_NEED_FILELISTS GFXOFF
312//#define GFILE_NEED_STDIO GFXOFF
313//#define GFILE_NEED_NOAUTOMOUNT GFXOFF
314//#define GFILE_NEED_NOAUTOSYNC GFXOFF
315
316//#define GFILE_NEED_MEMFS GFXOFF
317//#define GFILE_NEED_ROMFS GFXOFF
318//#define GFILE_NEED_RAMFS GFXOFF
319//#define GFILE_NEED_FATFS GFXOFF
320//#define GFILE_NEED_NATIVEFS GFXOFF
321//#define GFILE_NEED_CHBIOSFS GFXOFF
322//#define GFILE_NEED_USERFS GFXOFF
323
324//#define GFILE_ALLOW_FLOATS GFXOFF
325//#define GFILE_ALLOW_DEVICESPECIFIC GFXOFF
326//#define GFILE_MAX_GFILES 3
327
328///////////////////////////////////////////////////////////////////////////
329// GADC //
330///////////////////////////////////////////////////////////////////////////
331#define GFX_USE_GADC GFXOFF
332// #define GADC_MAX_LOWSPEED_DEVICES 4
333
334///////////////////////////////////////////////////////////////////////////
335// GAUDIO //
336///////////////////////////////////////////////////////////////////////////
337#define GFX_USE_GAUDIO GFXOFF
338// #define GAUDIO_NEED_PLAY GFXOFF
339// #define GAUDIO_NEED_RECORD GFXOFF
340
341///////////////////////////////////////////////////////////////////////////
342// GMISC //
343///////////////////////////////////////////////////////////////////////////
344#define GFX_USE_GMISC GFXON
345
346//#define GMISC_NEED_ARRAYOPS GFXOFF
347//#define GMISC_NEED_FASTTRIG GFXOFF
348//#define GMISC_NEED_FIXEDTRIG GFXOFF
349//#define GMISC_NEED_INVSQRT GFXOFF
350// #define GMISC_INVSQRT_MIXED_ENDIAN GFXOFF
351// #define GMISC_INVSQRT_REAL_SLOW GFXOFF
352#define GMISC_NEED_MATRIXFLOAT2D GFXON
353#define GMISC_NEED_MATRIXFIXED2D GFXOFF
354//#define GMISC_NEED_HITTEST_POLY GFXOFF
diff --git a/quantum/visualizer/default_animations.c b/quantum/visualizer/default_animations.c
deleted file mode 100644
index 2f43c67cc..000000000
--- a/quantum/visualizer/default_animations.c
+++ /dev/null
@@ -1,177 +0,0 @@
1/* Copyright 2017 Fred Sundvik
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#if defined(VISUALIZER_ENABLE)
18
19# include "default_animations.h"
20# include "visualizer.h"
21# ifdef LCD_ENABLE
22# include "lcd_keyframes.h"
23# endif
24# ifdef LCD_BACKLIGHT_ENABLE
25# include "lcd_backlight_keyframes.h"
26# endif
27
28# ifdef BACKLIGHT_ENABLE
29# include "led_backlight_keyframes.h"
30# endif
31
32# include "visualizer_keyframes.h"
33
34# if defined(LCD_ENABLE) || defined(LCD_BACKLIGHT_ENABLE) || defined(BACKLIGHT_ENABLE)
35
36static bool keyframe_enable(keyframe_animation_t* animation, visualizer_state_t* state) {
37# ifdef LCD_ENABLE
38 lcd_keyframe_enable(animation, state);
39# endif
40# ifdef LCD_BACKLIGHT_ENABLE
41 lcd_backlight_keyframe_enable(animation, state);
42# endif
43# ifdef BACKLIGHT_ENABLE
44 led_backlight_keyframe_enable(animation, state);
45# endif
46 return false;
47}
48
49static bool keyframe_disable(keyframe_animation_t* animation, visualizer_state_t* state) {
50# ifdef LCD_ENABLE
51 lcd_keyframe_disable(animation, state);
52# endif
53# ifdef LCD_BACKLIGHT_ENABLE
54 lcd_backlight_keyframe_disable(animation, state);
55# endif
56# ifdef BACKLIGHT_ENABLE
57 led_backlight_keyframe_disable(animation, state);
58# endif
59 return false;
60}
61
62static bool keyframe_fade_in(keyframe_animation_t* animation, visualizer_state_t* state) {
63 bool ret = false;
64# ifdef LCD_BACKLIGHT_ENABLE
65 ret |= lcd_backlight_keyframe_animate_color(animation, state);
66# endif
67# ifdef BACKLIGHT_ENABLE
68 ret |= led_backlight_keyframe_fade_in_all(animation, state);
69# endif
70 return ret;
71}
72
73static bool keyframe_fade_out(keyframe_animation_t* animation, visualizer_state_t* state) {
74 bool ret = false;
75# ifdef LCD_BACKLIGHT_ENABLE
76 ret |= lcd_backlight_keyframe_animate_color(animation, state);
77# endif
78# ifdef BACKLIGHT_ENABLE
79 ret |= led_backlight_keyframe_fade_out_all(animation, state);
80# endif
81 return ret;
82}
83
84// Don't worry, if the startup animation is long, you can use the keyboard like normal
85// during that time
86keyframe_animation_t default_startup_animation = {
87# if LCD_ENABLE
88 .num_frames = 3,
89# else
90 .num_frames = 2,
91# endif
92 .loop = false,
93 .frame_lengths = {0,
94# if LCD_ENABLE
95 0,
96# endif
97 gfxMillisecondsToTicks(5000)},
98 .frame_functions =
99 {
100 keyframe_enable,
101# if LCD_ENABLE
102 lcd_keyframe_draw_logo,
103# endif
104 keyframe_fade_in,
105 },
106};
107
108keyframe_animation_t default_suspend_animation = {
109# if LCD_ENABLE
110 .num_frames = 3,
111# else
112 .num_frames = 2,
113# endif
114 .loop = false,
115 .frame_lengths =
116 {
117# if LCD_ENABLE
118 0,
119# endif
120 gfxMillisecondsToTicks(1000), 0},
121 .frame_functions =
122 {
123# if LCD_ENABLE
124 lcd_keyframe_display_layer_text,
125# endif
126 keyframe_fade_out,
127 keyframe_disable,
128 },
129};
130# endif
131
132# if defined(BACKLIGHT_ENABLE)
133# define CROSSFADE_TIME 1000
134# define GRADIENT_TIME 3000
135
136keyframe_animation_t led_test_animation = {
137 .num_frames = 14,
138 .loop = true,
139 .frame_lengths =
140 {
141 gfxMillisecondsToTicks(1000), // fade in
142 gfxMillisecondsToTicks(1000), // no op (leds on)
143 gfxMillisecondsToTicks(1000), // fade out
144 gfxMillisecondsToTicks(CROSSFADE_TIME), // crossfade
145 gfxMillisecondsToTicks(GRADIENT_TIME), // left to rigt (outside in)
146 gfxMillisecondsToTicks(CROSSFADE_TIME), // crossfade
147 gfxMillisecondsToTicks(GRADIENT_TIME), // top_to_bottom
148 0, // mirror leds
149 gfxMillisecondsToTicks(CROSSFADE_TIME), // crossfade
150 gfxMillisecondsToTicks(GRADIENT_TIME), // left_to_right (mirrored, so inside out)
151 gfxMillisecondsToTicks(CROSSFADE_TIME), // crossfade
152 gfxMillisecondsToTicks(GRADIENT_TIME), // top_to_bottom
153 0, // normal leds
154 gfxMillisecondsToTicks(CROSSFADE_TIME), // crossfade
155
156 },
157 .frame_functions =
158 {
159 led_backlight_keyframe_fade_in_all,
160 keyframe_no_operation,
161 led_backlight_keyframe_fade_out_all,
162 led_backlight_keyframe_crossfade,
163 led_backlight_keyframe_left_to_right_gradient,
164 led_backlight_keyframe_crossfade,
165 led_backlight_keyframe_top_to_bottom_gradient,
166 led_backlight_keyframe_mirror_orientation,
167 led_backlight_keyframe_crossfade,
168 led_backlight_keyframe_left_to_right_gradient,
169 led_backlight_keyframe_crossfade,
170 led_backlight_keyframe_top_to_bottom_gradient,
171 led_backlight_keyframe_normal_orientation,
172 led_backlight_keyframe_crossfade,
173 },
174};
175# endif
176
177#endif
diff --git a/quantum/visualizer/default_animations.h b/quantum/visualizer/default_animations.h
deleted file mode 100644
index 9accd8977..000000000
--- a/quantum/visualizer/default_animations.h
+++ /dev/null
@@ -1,27 +0,0 @@
1/* Copyright 2017 Fred Sundvik
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include "visualizer.h"
20
21// You can use these default animations, but of course you can also write your own custom ones instead
22extern keyframe_animation_t default_startup_animation;
23extern keyframe_animation_t default_suspend_animation;
24
25// An animation for testing and demonstrating the led support, should probably not be used for real world
26// cases
27extern keyframe_animation_t led_test_animation;
diff --git a/quantum/visualizer/lcd_backlight.c b/quantum/visualizer/lcd_backlight.c
deleted file mode 100644
index 23978974e..000000000
--- a/quantum/visualizer/lcd_backlight.c
+++ /dev/null
@@ -1,87 +0,0 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#include "lcd_backlight.h"
26#include <math.h>
27
28static uint8_t current_hue = 0;
29static uint8_t current_saturation = 0;
30static uint8_t current_intensity = 0;
31static uint8_t current_brightness = 0;
32
33void lcd_backlight_init(void) {
34 lcd_backlight_hal_init();
35 lcd_backlight_color(current_hue, current_saturation, current_intensity);
36}
37
38// This code is based on Brian Neltner's blogpost and example code
39// "Why every LED light should be using HSI colorspace".
40// http://blog.saikoled.com/post/43693602826/why-every-led-light-should-be-using-hsi
41static void hsi_to_rgb(float h, float s, float i, uint16_t* r_out, uint16_t* g_out, uint16_t* b_out) {
42 unsigned int r, g, b;
43 h = fmodf(h, 360.0f); // cycle h around to 0-360 degrees
44 h = 3.14159f * h / 180.0f; // Convert to radians.
45 s = s > 0.0f ? (s < 1.0f ? s : 1.0f) : 0.0f; // clamp s and i to interval [0,1]
46 i = i > 0.0f ? (i < 1.0f ? i : 1.0f) : 0.0f;
47
48 // Math! Thanks in part to Kyle Miller.
49 if (h < 2.09439f) {
50 r = 65535.0f * i / 3.0f * (1.0f + s * cos(h) / cosf(1.047196667f - h));
51 g = 65535.0f * i / 3.0f * (1.0f + s * (1.0f - cosf(h) / cos(1.047196667f - h)));
52 b = 65535.0f * i / 3.0f * (1.0f - s);
53 } else if (h < 4.188787) {
54 h = h - 2.09439;
55 g = 65535.0f * i / 3.0f * (1.0f + s * cosf(h) / cosf(1.047196667f - h));
56 b = 65535.0f * i / 3.0f * (1.0f + s * (1.0f - cosf(h) / cosf(1.047196667f - h)));
57 r = 65535.0f * i / 3.0f * (1.0f - s);
58 } else {
59 h = h - 4.188787;
60 b = 65535.0f * i / 3.0f * (1.0f + s * cosf(h) / cosf(1.047196667f - h));
61 r = 65535.0f * i / 3.0f * (1.0f + s * (1.0f - cosf(h) / cosf(1.047196667f - h)));
62 g = 65535.0f * i / 3.0f * (1.0f - s);
63 }
64 *r_out = r > 65535 ? 65535 : r;
65 *g_out = g > 65535 ? 65535 : g;
66 *b_out = b > 65535 ? 65535 : b;
67}
68
69void lcd_backlight_color(uint8_t hue, uint8_t saturation, uint8_t intensity) {
70 uint16_t r, g, b;
71 float hue_f = 360.0f * (float)hue / 255.0f;
72 float saturation_f = (float)saturation / 255.0f;
73 float intensity_f = (float)intensity / 255.0f;
74 intensity_f *= (float)current_brightness / 255.0f;
75 hsi_to_rgb(hue_f, saturation_f, intensity_f, &r, &g, &b);
76 current_hue = hue;
77 current_saturation = saturation;
78 current_intensity = intensity;
79 lcd_backlight_hal_color(r, g, b);
80}
81
82void lcd_backlight_brightness(uint8_t b) {
83 current_brightness = b;
84 lcd_backlight_color(current_hue, current_saturation, current_intensity);
85}
86
87uint8_t lcd_get_backlight_brightness(void) { return current_brightness; }
diff --git a/quantum/visualizer/lcd_backlight.h b/quantum/visualizer/lcd_backlight.h
deleted file mode 100644
index 4ea5b1463..000000000
--- a/quantum/visualizer/lcd_backlight.h
+++ /dev/null
@@ -1,43 +0,0 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#pragma once
26
27#include <stdint.h>
28
29// Helper macros for storing hue, staturation and intensity as unsigned integers
30#define LCD_COLOR(hue, saturation, intensity) (hue << 16 | saturation << 8 | intensity)
31#define LCD_HUE(color) ((color >> 16) & 0xFF)
32#define LCD_SAT(color) ((color >> 8) & 0xFF)
33#define LCD_INT(color) (color & 0xFF)
34
35static inline uint32_t change_lcd_color_intensity(uint32_t color, uint8_t new_intensity) { return (color & 0xFFFFFF00) | new_intensity; }
36
37void lcd_backlight_init(void);
38void lcd_backlight_color(uint8_t hue, uint8_t saturation, uint8_t intensity);
39void lcd_backlight_brightness(uint8_t b);
40uint8_t lcd_get_backlight_brightness(void);
41
42void lcd_backlight_hal_init(void);
43void lcd_backlight_hal_color(uint16_t r, uint16_t g, uint16_t b);
diff --git a/quantum/visualizer/lcd_backlight_keyframes.c b/quantum/visualizer/lcd_backlight_keyframes.c
deleted file mode 100644
index c13cce311..000000000
--- a/quantum/visualizer/lcd_backlight_keyframes.c
+++ /dev/null
@@ -1,69 +0,0 @@
1/* Copyright 2017 Fred Sundvik
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "lcd_backlight_keyframes.h"
18
19bool lcd_backlight_keyframe_animate_color(keyframe_animation_t* animation, visualizer_state_t* state) {
20 int frame_length = animation->frame_lengths[animation->current_frame];
21 int current_pos = frame_length - animation->time_left_in_frame;
22 uint8_t t_h = LCD_HUE(state->target_lcd_color);
23 uint8_t t_s = LCD_SAT(state->target_lcd_color);
24 uint8_t t_i = LCD_INT(state->target_lcd_color);
25 uint8_t p_h = LCD_HUE(state->prev_lcd_color);
26 uint8_t p_s = LCD_SAT(state->prev_lcd_color);
27 uint8_t p_i = LCD_INT(state->prev_lcd_color);
28
29 uint8_t d_h1 = t_h - p_h; // Modulo arithmetic since we want to wrap around
30 int d_h2 = t_h - p_h;
31 // Chose the shortest way around
32 int d_h = abs(d_h2) < d_h1 ? d_h2 : d_h1;
33 int d_s = t_s - p_s;
34 int d_i = t_i - p_i;
35
36 int hue = (d_h * current_pos) / frame_length;
37 int sat = (d_s * current_pos) / frame_length;
38 int intensity = (d_i * current_pos) / frame_length;
39 // dprintf("%X -> %X = %X\n", p_h, t_h, hue);
40 hue += p_h;
41 sat += p_s;
42 intensity += p_i;
43 state->current_lcd_color = LCD_COLOR(hue, sat, intensity);
44 lcd_backlight_color(LCD_HUE(state->current_lcd_color), LCD_SAT(state->current_lcd_color), LCD_INT(state->current_lcd_color));
45
46 return true;
47}
48
49bool lcd_backlight_keyframe_set_color(keyframe_animation_t* animation, visualizer_state_t* state) {
50 (void)animation;
51 state->prev_lcd_color = state->target_lcd_color;
52 state->current_lcd_color = state->target_lcd_color;
53 lcd_backlight_color(LCD_HUE(state->current_lcd_color), LCD_SAT(state->current_lcd_color), LCD_INT(state->current_lcd_color));
54 return false;
55}
56
57bool lcd_backlight_keyframe_disable(keyframe_animation_t* animation, visualizer_state_t* state) {
58 (void)animation;
59 (void)state;
60 lcd_backlight_hal_color(0, 0, 0);
61 return false;
62}
63
64bool lcd_backlight_keyframe_enable(keyframe_animation_t* animation, visualizer_state_t* state) {
65 (void)animation;
66 (void)state;
67 lcd_backlight_color(LCD_HUE(state->current_lcd_color), LCD_SAT(state->current_lcd_color), LCD_INT(state->current_lcd_color));
68 return false;
69}
diff --git a/quantum/visualizer/lcd_backlight_keyframes.h b/quantum/visualizer/lcd_backlight_keyframes.h
deleted file mode 100644
index 88768dd4a..000000000
--- a/quantum/visualizer/lcd_backlight_keyframes.h
+++ /dev/null
@@ -1,27 +0,0 @@
1/* Copyright 2017 Fred Sundvik
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include "visualizer.h"
20
21// Animates the LCD backlight color between the current color and the target color (of the state)
22bool lcd_backlight_keyframe_animate_color(keyframe_animation_t* animation, visualizer_state_t* state);
23// Sets the backlight color to the target color
24bool lcd_backlight_keyframe_set_color(keyframe_animation_t* animation, visualizer_state_t* state);
25
26bool lcd_backlight_keyframe_disable(keyframe_animation_t* animation, visualizer_state_t* state);
27bool lcd_backlight_keyframe_enable(keyframe_animation_t* animation, visualizer_state_t* state);
diff --git a/quantum/visualizer/lcd_keyframes.c b/quantum/visualizer/lcd_keyframes.c
deleted file mode 100644
index 1d6f3dca1..000000000
--- a/quantum/visualizer/lcd_keyframes.c
+++ /dev/null
@@ -1,184 +0,0 @@
1/* Copyright 2017 Fred Sundvik
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "lcd_keyframes.h"
18#include <string.h>
19#include "action_util.h"
20#include "led.h"
21#include "resources/resources.h"
22
23bool lcd_keyframe_display_layer_text(keyframe_animation_t* animation, visualizer_state_t* state) {
24 (void)animation;
25 gdispClear(White);
26 gdispDrawString(0, 10, state->layer_text, state->font_dejavusansbold12, Black);
27 return false;
28}
29
30static void format_layer_bitmap_string(uint16_t default_layer, uint16_t layer, char* buffer) {
31 for (int i = 0; i < 16; i++) {
32 uint32_t mask = (1u << i);
33 if (default_layer & mask) {
34 if (layer & mask) {
35 *buffer = 'B';
36 } else {
37 *buffer = 'D';
38 }
39 } else if (layer & mask) {
40 *buffer = '1';
41 } else {
42 *buffer = '0';
43 }
44 ++buffer;
45
46 if (i == 3 || i == 7 || i == 11) {
47 *buffer = ' ';
48 ++buffer;
49 }
50 }
51 *buffer = 0;
52}
53
54bool lcd_keyframe_display_layer_bitmap(keyframe_animation_t* animation, visualizer_state_t* state) {
55 (void)animation;
56 const char* layer_help = "1=On D=Default B=Both";
57 char layer_buffer[16 + 4]; // 3 spaces and one null terminator
58 gdispClear(White);
59 gdispDrawString(0, 0, layer_help, state->font_fixed5x8, Black);
60 format_layer_bitmap_string(state->status.default_layer, state->status.layer, layer_buffer);
61 gdispDrawString(0, 10, layer_buffer, state->font_fixed5x8, Black);
62 format_layer_bitmap_string(state->status.default_layer >> 16, state->status.layer >> 16, layer_buffer);
63 gdispDrawString(0, 20, layer_buffer, state->font_fixed5x8, Black);
64 return false;
65}
66
67static void format_mods_bitmap_string(uint8_t mods, char* buffer) {
68 *buffer = ' ';
69 ++buffer;
70
71 for (int i = 0; i < 8; i++) {
72 uint32_t mask = (1u << i);
73 if (mods & mask) {
74 *buffer = '1';
75 } else {
76 *buffer = '0';
77 }
78 ++buffer;
79
80 if (i == 3) {
81 *buffer = ' ';
82 ++buffer;
83 }
84 }
85 *buffer = 0;
86}
87
88bool lcd_keyframe_display_mods_bitmap(keyframe_animation_t* animation, visualizer_state_t* state) {
89 (void)animation;
90
91 const char* title = "Modifier states";
92 const char* mods_header = " CSAG CSAG ";
93 char status_buffer[12];
94
95 gdispClear(White);
96 gdispDrawString(0, 0, title, state->font_fixed5x8, Black);
97 gdispDrawString(0, 10, mods_header, state->font_fixed5x8, Black);
98 format_mods_bitmap_string(state->status.mods, status_buffer);
99 gdispDrawString(0, 20, status_buffer, state->font_fixed5x8, Black);
100
101 return false;
102}
103
104#define LED_STATE_STRING_SIZE sizeof("NUM CAPS SCRL COMP KANA")
105
106static void get_led_state_string(char* output, visualizer_state_t* state) {
107 uint8_t pos = 0;
108
109 if (state->status.leds & (1u << USB_LED_NUM_LOCK)) {
110 memcpy(output + pos, "NUM ", 4);
111 pos += 4;
112 }
113 if (state->status.leds & (1u << USB_LED_CAPS_LOCK)) {
114 memcpy(output + pos, "CAPS ", 5);
115 pos += 5;
116 }
117 if (state->status.leds & (1u << USB_LED_SCROLL_LOCK)) {
118 memcpy(output + pos, "SCRL ", 5);
119 pos += 5;
120 }
121 if (state->status.leds & (1u << USB_LED_COMPOSE)) {
122 memcpy(output + pos, "COMP ", 5);
123 pos += 5;
124 }
125 if (state->status.leds & (1u << USB_LED_KANA)) {
126 memcpy(output + pos, "KANA", 4);
127 pos += 4;
128 }
129 output[pos] = 0;
130}
131
132bool lcd_keyframe_display_led_states(keyframe_animation_t* animation, visualizer_state_t* state) {
133 (void)animation;
134 char output[LED_STATE_STRING_SIZE];
135 get_led_state_string(output, state);
136 gdispClear(White);
137 gdispDrawString(0, 10, output, state->font_dejavusansbold12, Black);
138 return false;
139}
140
141bool lcd_keyframe_display_layer_and_led_states(keyframe_animation_t* animation, visualizer_state_t* state) {
142 (void)animation;
143 gdispClear(White);
144 uint8_t y = 10;
145 if (state->status.leds) {
146 char output[LED_STATE_STRING_SIZE];
147 get_led_state_string(output, state);
148 gdispDrawString(0, 1, output, state->font_dejavusansbold12, Black);
149 y = 17;
150 }
151 gdispDrawString(0, y, state->layer_text, state->font_dejavusansbold12, Black);
152 return false;
153}
154
155bool lcd_keyframe_draw_logo(keyframe_animation_t* animation, visualizer_state_t* state) {
156 (void)state;
157 (void)animation;
158 // Read the uGFX documentation for information how to use the displays
159 // http://wiki.ugfx.org/index.php/Main_Page
160 gdispClear(Black);
161
162 // You can use static variables for things that can't be found in the animation
163 // or state structs, here we use the image
164
165 // gdispGBlitArea is a tricky function to use since it supports blitting part of the image
166 // if you have full screen image, then just use LCD_WIDTH and LCD_HEIGHT for both source and target dimensions
167 gdispGBlitArea(GDISP, 0, 0, 128, 32, 0, 0, LCD_WIDTH, (pixel_t*)resource_lcd_logo);
168
169 return false;
170}
171
172bool lcd_keyframe_disable(keyframe_animation_t* animation, visualizer_state_t* state) {
173 (void)animation;
174 (void)state;
175 gdispSetPowerMode(powerOff);
176 return false;
177}
178
179bool lcd_keyframe_enable(keyframe_animation_t* animation, visualizer_state_t* state) {
180 (void)animation;
181 (void)state;
182 gdispSetPowerMode(powerOn);
183 return false;
184}
diff --git a/quantum/visualizer/lcd_keyframes.h b/quantum/visualizer/lcd_keyframes.h
deleted file mode 100644
index b7125e832..000000000
--- a/quantum/visualizer/lcd_keyframes.h
+++ /dev/null
@@ -1,35 +0,0 @@
1/* Copyright 2017 Fred Sundvik
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include "visualizer.h"
20
21// Displays the layer text centered vertically on the screen
22bool lcd_keyframe_display_layer_text(keyframe_animation_t* animation, visualizer_state_t* state);
23// Displays a bitmap (0/1) of all the currently active layers
24bool lcd_keyframe_display_layer_bitmap(keyframe_animation_t* animation, visualizer_state_t* state);
25// Displays a bitmap (0/1) of all the currently active mods
26bool lcd_keyframe_display_mods_bitmap(keyframe_animation_t* animation, visualizer_state_t* state);
27// Displays the keyboard led states (CAPS (Caps lock), NUM (Num lock), SCRL (Scroll lock), COMP (Compose), KANA)
28bool lcd_keyframe_display_led_states(keyframe_animation_t* animation, visualizer_state_t* state);
29// Displays both the layer text and the led states
30bool lcd_keyframe_display_layer_and_led_states(keyframe_animation_t* animation, visualizer_state_t* state);
31// Displays the QMK logo on the LCD screen
32bool lcd_keyframe_draw_logo(keyframe_animation_t* animation, visualizer_state_t* state);
33
34bool lcd_keyframe_disable(keyframe_animation_t* animation, visualizer_state_t* state);
35bool lcd_keyframe_enable(keyframe_animation_t* animation, visualizer_state_t* state);
diff --git a/quantum/visualizer/led_backlight_keyframes.c b/quantum/visualizer/led_backlight_keyframes.c
deleted file mode 100644
index 338ada522..000000000
--- a/quantum/visualizer/led_backlight_keyframes.c
+++ /dev/null
@@ -1,143 +0,0 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24#include "gfx.h"
25#include <math.h>
26#include "led_backlight_keyframes.h"
27
28static uint8_t fade_led_color(keyframe_animation_t* animation, int from, int to) {
29 int frame_length = animation->frame_lengths[animation->current_frame];
30 int current_pos = frame_length - animation->time_left_in_frame;
31 int delta = to - from;
32 int luma = (delta * current_pos) / frame_length;
33 luma += from;
34 return luma;
35}
36
37static void keyframe_fade_all_leds_from_to(keyframe_animation_t* animation, uint8_t from, uint8_t to) {
38 uint8_t luma = fade_led_color(animation, from, to);
39 color_t color = LUMA2COLOR(luma);
40 gdispGClear(LED_DISPLAY, color);
41}
42
43// TODO: Should be customizable per keyboard
44#define NUM_ROWS LED_HEIGHT
45#define NUM_COLS LED_WIDTH
46
47static uint8_t crossfade_start_frame[NUM_ROWS][NUM_COLS];
48static uint8_t crossfade_end_frame[NUM_ROWS][NUM_COLS];
49
50static uint8_t compute_gradient_color(float t, float index, float num) {
51 const float two_pi = M_PI * 2.0f;
52 float normalized_index = (1.0f - index / (num - 1.0f)) * two_pi;
53 float x = t * two_pi + normalized_index;
54 float v = 0.5 * (cosf(x) + 1.0f);
55 return (uint8_t)(255.0f * v);
56}
57
58bool led_backlight_keyframe_fade_in_all(keyframe_animation_t* animation, visualizer_state_t* state) {
59 (void)state;
60 keyframe_fade_all_leds_from_to(animation, 0, 255);
61 return true;
62}
63
64bool led_backlight_keyframe_fade_out_all(keyframe_animation_t* animation, visualizer_state_t* state) {
65 (void)state;
66 keyframe_fade_all_leds_from_to(animation, 255, 0);
67 return true;
68}
69
70bool led_backlight_keyframe_left_to_right_gradient(keyframe_animation_t* animation, visualizer_state_t* state) {
71 (void)state;
72 float frame_length = animation->frame_lengths[animation->current_frame];
73 float current_pos = frame_length - animation->time_left_in_frame;
74 float t = current_pos / frame_length;
75 for (int i = 0; i < NUM_COLS; i++) {
76 uint8_t color = compute_gradient_color(t, i, NUM_COLS);
77 gdispGDrawLine(LED_DISPLAY, i, 0, i, NUM_ROWS - 1, LUMA2COLOR(color));
78 }
79 return true;
80}
81
82bool led_backlight_keyframe_top_to_bottom_gradient(keyframe_animation_t* animation, visualizer_state_t* state) {
83 (void)state;
84 float frame_length = animation->frame_lengths[animation->current_frame];
85 float current_pos = frame_length - animation->time_left_in_frame;
86 float t = current_pos / frame_length;
87 for (int i = 0; i < NUM_ROWS; i++) {
88 uint8_t color = compute_gradient_color(t, i, NUM_ROWS);
89 gdispGDrawLine(LED_DISPLAY, 0, i, NUM_COLS - 1, i, LUMA2COLOR(color));
90 }
91 return true;
92}
93
94static void copy_current_led_state(uint8_t* dest) {
95 for (int i = 0; i < NUM_ROWS; i++) {
96 for (int j = 0; j < NUM_COLS; j++) {
97 dest[i * NUM_COLS + j] = gdispGGetPixelColor(LED_DISPLAY, j, i);
98 }
99 }
100}
101bool led_backlight_keyframe_crossfade(keyframe_animation_t* animation, visualizer_state_t* state) {
102 (void)state;
103 if (animation->first_update_of_frame) {
104 copy_current_led_state(&crossfade_start_frame[0][0]);
105 run_next_keyframe(animation, state);
106 copy_current_led_state(&crossfade_end_frame[0][0]);
107 }
108 for (int i = 0; i < NUM_ROWS; i++) {
109 for (int j = 0; j < NUM_COLS; j++) {
110 color_t color = LUMA2COLOR(fade_led_color(animation, crossfade_start_frame[i][j], crossfade_end_frame[i][j]));
111 gdispGDrawPixel(LED_DISPLAY, j, i, color);
112 }
113 }
114 return true;
115}
116
117bool led_backlight_keyframe_mirror_orientation(keyframe_animation_t* animation, visualizer_state_t* state) {
118 (void)state;
119 (void)animation;
120 gdispGSetOrientation(LED_DISPLAY, GDISP_ROTATE_180);
121 return false;
122}
123
124bool led_backlight_keyframe_normal_orientation(keyframe_animation_t* animation, visualizer_state_t* state) {
125 (void)state;
126 (void)animation;
127 gdispGSetOrientation(LED_DISPLAY, GDISP_ROTATE_0);
128 return false;
129}
130
131bool led_backlight_keyframe_disable(keyframe_animation_t* animation, visualizer_state_t* state) {
132 (void)state;
133 (void)animation;
134 gdispGSetPowerMode(LED_DISPLAY, powerOff);
135 return false;
136}
137
138bool led_backlight_keyframe_enable(keyframe_animation_t* animation, visualizer_state_t* state) {
139 (void)state;
140 (void)animation;
141 gdispGSetPowerMode(LED_DISPLAY, powerOn);
142 return false;
143}
diff --git a/quantum/visualizer/led_backlight_keyframes.h b/quantum/visualizer/led_backlight_keyframes.h
deleted file mode 100644
index 90153be5e..000000000
--- a/quantum/visualizer/led_backlight_keyframes.h
+++ /dev/null
@@ -1,40 +0,0 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#pragma once
26
27#include "visualizer.h"
28
29bool led_backlight_keyframe_fade_in_all(keyframe_animation_t* animation, visualizer_state_t* state);
30bool led_backlight_keyframe_fade_out_all(keyframe_animation_t* animation, visualizer_state_t* state);
31bool led_backlight_keyframe_left_to_right_gradient(keyframe_animation_t* animation, visualizer_state_t* state);
32bool led_backlight_keyframe_top_to_bottom_gradient(keyframe_animation_t* animation, visualizer_state_t* state);
33bool led_backlight_keyframe_crossfade(keyframe_animation_t* animation, visualizer_state_t* state);
34bool led_backlight_keyframe_mirror_orientation(keyframe_animation_t* animation, visualizer_state_t* state);
35bool led_backlight_keyframe_normal_orientation(keyframe_animation_t* animation, visualizer_state_t* state);
36
37bool led_backlight_keyframe_disable(keyframe_animation_t* animation, visualizer_state_t* state);
38bool led_backlight_keyframe_enable(keyframe_animation_t* animation, visualizer_state_t* state);
39
40extern keyframe_animation_t led_test_animation;
diff --git a/quantum/visualizer/readme.md b/quantum/visualizer/readme.md
deleted file mode 100644
index 298efb742..000000000
--- a/quantum/visualizer/readme.md
+++ /dev/null
@@ -1,18 +0,0 @@
1# A visualization library for the TMK keyboard firmware
2
3This library is designed to work together with the [TMK keyboard firmware](https://github.com/tmk/tmk_keyboard). Currently it only works for [Chibios](http://www.chibios.org/)
4 flavors, but it would be possible to add support for other configurations as well. The LCD display functionality is provided by the [uGFX library](https://ugfx.io/).
5
6## To use this library as a user
7You can and should modify the visualizer\_user.c file. Check the comments in the file for more information.
8
9## To add this library to custom keyboard projects
10
111. Add tmk_visualizer as a submodule to your project
121. Set VISUALIZER_DIR in the main keyboard project makefile to point to the submodule
131. Define LCD\_ENABLE and/or LCD\_BACKLIGHT\_ENABLE, to enable support
141. Include the visualizer.mk make file
151. Copy the files in the example\_integration folder to your keyboard project
161. All other files than the callback.c file are included automatically, so you will need to add callback.c to your makefile manually. If you already have a similar file in your project, you can just copy the functions instead of the whole file.
171. Edit the files to match your hardware. You might might want to read the Chibios and UGfx documentation, for more information.
181. If you enable LCD support you might also have to write a custom uGFX display driver, check the uGFX documentation for that. You probably also want to enable SPI support in your Chibios configuration.
diff --git a/quantum/visualizer/resources/lcd_logo.c b/quantum/visualizer/resources/lcd_logo.c
deleted file mode 100644
index 13bf734cb..000000000
--- a/quantum/visualizer/resources/lcd_logo.c
+++ /dev/null
@@ -1,45 +0,0 @@
1/* Copyright 2017 Fred Sundvik
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "resources.h"
18
19// clang-format off
20
21// To generate an image array like this
22// Ensure the image is 128 x 32 or smaller
23// Convert the bitmap to a C array using a program like http://www.riuson.com/lcd-image-converter/
24// Ensure the the conversion process produces a monochrome format array - 1 bit/pixel, left to right, top to bottom
25// Update array in the source code with the C array produced by the conversion program
26
27// The image below is generated from lcd_logo.png
28__attribute__((weak)) const uint8_t resource_lcd_logo[512] = {
29 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
30 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
31 0x00, 0x02, 0x92, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x92, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
32 0x00, 0x02, 0x92, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
33 0x00, 0x1F, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
34 0x00, 0xFE, 0xEE, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0xEE, 0xF0, 0x01, 0xC6, 0x0D, 0x8C, 0x1F, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
35 0x00, 0xFE, 0xEE, 0xFE, 0x03, 0xE7, 0x1D, 0x9C, 0x1F, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0xEE, 0xF0, 0x06, 0x37, 0x1D, 0xB8, 0x18, 0x0B, 0x59, 0xC8, 0x09, 0xE5, 0x9E, 0x00,
36 0x00, 0x1E, 0xEE, 0xF0, 0x06, 0x37, 0xBD, 0xF0, 0x18, 0x6F, 0x7F, 0xEC, 0x9B, 0x37, 0xB3, 0x00, 0x00, 0xFE, 0xEE, 0xFE, 0x06, 0x37, 0xBD, 0xE0, 0x1F, 0x6C, 0x66, 0x6D, 0xD8, 0x36, 0x33, 0x00,
37 0x00, 0x1E, 0xEE, 0xF0, 0x06, 0x36, 0xED, 0xF0, 0x1F, 0x6C, 0x66, 0x6D, 0x59, 0xF6, 0x3E, 0x00, 0x00, 0x1F, 0x6D, 0xF0, 0x06, 0x36, 0xED, 0xB8, 0x18, 0x6C, 0x66, 0x67, 0x73, 0x36, 0x30, 0x00,
38 0x00, 0xFF, 0x83, 0xFE, 0x03, 0xE6, 0x4D, 0x9C, 0x18, 0x6C, 0x66, 0x67, 0x73, 0x36, 0x1F, 0x00, 0x00, 0x1F, 0xEF, 0xF0, 0x01, 0xC6, 0x0D, 0x8C, 0x18, 0x6C, 0x66, 0x62, 0x21, 0xD6, 0x0E, 0x00,
39 0x00, 0xFF, 0xEF, 0xFE, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xF0, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
40 0x00, 0x1F, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
41 0x00, 0x02, 0x92, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x92, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
42 0x00, 0x02, 0x92, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
43 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
44 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
45};
diff --git a/quantum/visualizer/resources/lcd_logo.png b/quantum/visualizer/resources/lcd_logo.png
deleted file mode 100644
index 178ef65f1..000000000
--- a/quantum/visualizer/resources/lcd_logo.png
+++ /dev/null
Binary files differ
diff --git a/quantum/visualizer/resources/resources.h b/quantum/visualizer/resources/resources.h
deleted file mode 100644
index 5178fbe55..000000000
--- a/quantum/visualizer/resources/resources.h
+++ /dev/null
@@ -1,23 +0,0 @@
1/* Copyright 2017 Fred Sundvik
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include <stdint.h>
20
21#ifdef LCD_ENABLE
22extern const uint8_t resource_lcd_logo[];
23#endif
diff --git a/quantum/visualizer/visualizer.c b/quantum/visualizer/visualizer.c
deleted file mode 100644
index 709affbb7..000000000
--- a/quantum/visualizer/visualizer.c
+++ /dev/null
@@ -1,483 +0,0 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#include "config.h"
26#include "visualizer.h"
27#include <string.h>
28#ifdef PROTOCOL_CHIBIOS
29# include <ch.h>
30#endif
31
32#include "gfx.h"
33
34#ifdef LCD_BACKLIGHT_ENABLE
35# include "lcd_backlight.h"
36#endif
37
38//#define DEBUG_VISUALIZER
39
40#ifdef DEBUG_VISUALIZER
41# include "debug.h"
42#else
43# include "nodebug.h"
44#endif
45
46#ifdef SERIAL_LINK_ENABLE
47# include "serial_link/protocol/transport.h"
48# include "serial_link/system/serial_link.h"
49#endif
50
51#include "action_util.h"
52
53// Define this in config.h
54#ifndef VISUALIZER_THREAD_PRIORITY
55// The visualizer needs gfx thread priorities
56# define VISUALIZER_THREAD_PRIORITY (NORMAL_PRIORITY - 2)
57#endif
58
59static visualizer_keyboard_status_t current_status = {.layer = 0xFFFFFFFF,
60 .default_layer = 0xFFFFFFFF,
61 .leds = 0xFFFFFFFF,
62#ifdef BACKLIGHT_ENABLE
63 .backlight_level = 0,
64#endif
65 .mods = 0xFF,
66 .suspended = false,
67#ifdef VISUALIZER_USER_DATA_SIZE
68 .user_data = {0}
69#endif
70};
71
72static bool same_status(visualizer_keyboard_status_t* status1, visualizer_keyboard_status_t* status2) {
73 return status1->layer == status2->layer && status1->default_layer == status2->default_layer && status1->mods == status2->mods && status1->leds == status2->leds && status1->suspended == status2->suspended
74#ifdef BACKLIGHT_ENABLE
75 && status1->backlight_level == status2->backlight_level
76#endif
77#ifdef VISUALIZER_USER_DATA_SIZE
78 && memcmp(status1->user_data, status2->user_data, VISUALIZER_USER_DATA_SIZE) == 0
79#endif
80 ;
81}
82
83static bool visualizer_enabled = false;
84
85#ifdef VISUALIZER_USER_DATA_SIZE
86static uint8_t user_data[VISUALIZER_USER_DATA_SIZE];
87#endif
88
89#define MAX_SIMULTANEOUS_ANIMATIONS 4
90static keyframe_animation_t* animations[MAX_SIMULTANEOUS_ANIMATIONS] = {};
91
92#ifdef SERIAL_LINK_ENABLE
93MASTER_TO_ALL_SLAVES_OBJECT(current_status, visualizer_keyboard_status_t);
94
95static remote_object_t* remote_objects[] = {
96 REMOTE_OBJECT(current_status),
97};
98
99#endif
100
101GDisplay* LCD_DISPLAY = 0;
102GDisplay* LED_DISPLAY = 0;
103
104#ifdef LCD_DISPLAY_NUMBER
105__attribute__((weak)) GDisplay* get_lcd_display(void) { return gdispGetDisplay(LCD_DISPLAY_NUMBER); }
106#endif
107
108#ifdef LED_DISPLAY_NUMBER
109__attribute__((weak)) GDisplay* get_led_display(void) { return gdispGetDisplay(LED_DISPLAY_NUMBER); }
110#endif
111
112void start_keyframe_animation(keyframe_animation_t* animation) {
113 animation->current_frame = -1;
114 animation->time_left_in_frame = 0;
115 animation->need_update = true;
116 int free_index = -1;
117 for (int i = 0; i < MAX_SIMULTANEOUS_ANIMATIONS; i++) {
118 if (animations[i] == animation) {
119 return;
120 }
121 if (free_index == -1 && animations[i] == NULL) {
122 free_index = i;
123 }
124 }
125 if (free_index != -1) {
126 animations[free_index] = animation;
127 }
128}
129
130void stop_keyframe_animation(keyframe_animation_t* animation) {
131 animation->current_frame = animation->num_frames;
132 animation->time_left_in_frame = 0;
133 animation->need_update = true;
134 animation->first_update_of_frame = false;
135 animation->last_update_of_frame = false;
136 for (int i = 0; i < MAX_SIMULTANEOUS_ANIMATIONS; i++) {
137 if (animations[i] == animation) {
138 animations[i] = NULL;
139 return;
140 }
141 }
142}
143
144void stop_all_keyframe_animations(void) {
145 for (int i = 0; i < MAX_SIMULTANEOUS_ANIMATIONS; i++) {
146 if (animations[i]) {
147 animations[i]->current_frame = animations[i]->num_frames;
148 animations[i]->time_left_in_frame = 0;
149 animations[i]->need_update = true;
150 animations[i]->first_update_of_frame = false;
151 animations[i]->last_update_of_frame = false;
152 animations[i] = NULL;
153 }
154 }
155}
156
157static uint8_t get_num_running_animations(void) {
158 uint8_t count = 0;
159 for (int i = 0; i < MAX_SIMULTANEOUS_ANIMATIONS; i++) {
160 count += animations[i] ? 1 : 0;
161 }
162 return count;
163}
164
165static bool update_keyframe_animation(keyframe_animation_t* animation, visualizer_state_t* state, systemticks_t delta, systemticks_t* sleep_time) {
166 // TODO: Clean up this messy code
167 dprintf("Animation frame%d, left %d, delta %d\n", animation->current_frame, animation->time_left_in_frame, delta);
168 if (animation->current_frame == animation->num_frames) {
169 animation->need_update = false;
170 return false;
171 }
172 if (animation->current_frame == -1) {
173 animation->current_frame = 0;
174 animation->time_left_in_frame = animation->frame_lengths[0];
175 animation->need_update = true;
176 animation->first_update_of_frame = true;
177 } else {
178 animation->time_left_in_frame -= delta;
179 while (animation->time_left_in_frame <= 0) {
180 int left = animation->time_left_in_frame;
181 if (animation->need_update) {
182 animation->time_left_in_frame = 0;
183 animation->last_update_of_frame = true;
184 (*animation->frame_functions[animation->current_frame])(animation, state);
185 animation->last_update_of_frame = false;
186 }
187 animation->current_frame++;
188 animation->need_update = true;
189 animation->first_update_of_frame = true;
190 if (animation->current_frame == animation->num_frames) {
191 if (animation->loop) {
192 animation->current_frame = 0;
193 } else {
194 stop_keyframe_animation(animation);
195 return false;
196 }
197 }
198 delta = -left;
199 animation->time_left_in_frame = animation->frame_lengths[animation->current_frame];
200 animation->time_left_in_frame -= delta;
201 }
202 }
203 if (animation->need_update) {
204 animation->need_update = (*animation->frame_functions[animation->current_frame])(animation, state);
205 animation->first_update_of_frame = false;
206 }
207
208 systemticks_t wanted_sleep = animation->need_update ? gfxMillisecondsToTicks(10) : (unsigned)animation->time_left_in_frame;
209 if (wanted_sleep < *sleep_time) {
210 *sleep_time = wanted_sleep;
211 }
212
213 return true;
214}
215
216void run_next_keyframe(keyframe_animation_t* animation, visualizer_state_t* state) {
217 int next_frame = animation->current_frame + 1;
218 if (next_frame == animation->num_frames) {
219 next_frame = 0;
220 }
221 keyframe_animation_t temp_animation = *animation;
222 temp_animation.current_frame = next_frame;
223 temp_animation.time_left_in_frame = animation->frame_lengths[next_frame];
224 temp_animation.first_update_of_frame = true;
225 temp_animation.last_update_of_frame = false;
226 temp_animation.need_update = false;
227 visualizer_state_t temp_state = *state;
228 (*temp_animation.frame_functions[next_frame])(&temp_animation, &temp_state);
229}
230
231// TODO: Optimize the stack size, this is probably way too big
232static DECLARE_THREAD_STACK(visualizerThreadStack, 1024);
233static DECLARE_THREAD_FUNCTION(visualizerThread, arg) {
234 (void)arg;
235
236 GListener event_listener;
237 geventListenerInit(&event_listener);
238 geventAttachSource(&event_listener, (GSourceHandle)&current_status, 0);
239
240 visualizer_keyboard_status_t initial_status = {
241 .default_layer = 0xFFFFFFFF,
242 .layer = 0xFFFFFFFF,
243 .mods = 0xFF,
244 .leds = 0xFFFFFFFF,
245 .suspended = false,
246#ifdef BACKLIGHT_ENABLE
247 .backlight_level = 0,
248#endif
249#ifdef VISUALIZER_USER_DATA_SIZE
250 .user_data = {0},
251#endif
252 };
253
254 visualizer_state_t state = {.status = initial_status,
255 .current_lcd_color = 0,
256#ifdef LCD_ENABLE
257 .font_fixed5x8 = gdispOpenFont("fixed_5x8"),
258 .font_dejavusansbold12 = gdispOpenFont("DejaVuSansBold12")
259#endif
260 };
261 initialize_user_visualizer(&state);
262 state.prev_lcd_color = state.current_lcd_color;
263
264#ifdef LCD_BACKLIGHT_ENABLE
265 lcd_backlight_color(LCD_HUE(state.current_lcd_color), LCD_SAT(state.current_lcd_color), LCD_INT(state.current_lcd_color));
266#endif
267
268 systemticks_t sleep_time = TIME_INFINITE;
269 systemticks_t current_time = gfxSystemTicks();
270 bool force_update = true;
271
272 while (true) {
273 systemticks_t new_time = gfxSystemTicks();
274 systemticks_t delta = new_time - current_time;
275 current_time = new_time;
276 bool enabled = visualizer_enabled;
277 if (force_update || !same_status(&state.status, &current_status)) {
278 force_update = false;
279#if BACKLIGHT_ENABLE
280 if (current_status.backlight_level != state.status.backlight_level) {
281 if (current_status.backlight_level != 0) {
282 gdispGSetPowerMode(LED_DISPLAY, powerOn);
283 uint16_t percent = (uint16_t)current_status.backlight_level * 100 / BACKLIGHT_LEVELS;
284 gdispGSetBacklight(LED_DISPLAY, percent);
285 } else {
286 gdispGSetPowerMode(LED_DISPLAY, powerOff);
287 }
288 state.status.backlight_level = current_status.backlight_level;
289 }
290#endif
291 if (visualizer_enabled) {
292 if (current_status.suspended) {
293 stop_all_keyframe_animations();
294 visualizer_enabled = false;
295 state.status = current_status;
296 user_visualizer_suspend(&state);
297 } else {
298 visualizer_keyboard_status_t prev_status = state.status;
299 state.status = current_status;
300 update_user_visualizer_state(&state, &prev_status);
301 }
302 state.prev_lcd_color = state.current_lcd_color;
303 }
304 }
305 if (!enabled && state.status.suspended && current_status.suspended == false) {
306 // Setting the status to the initial status will force an update
307 // when the visualizer is enabled again
308 state.status = initial_status;
309 state.status.suspended = false;
310 stop_all_keyframe_animations();
311 user_visualizer_resume(&state);
312 state.prev_lcd_color = state.current_lcd_color;
313 }
314 sleep_time = TIME_INFINITE;
315 for (int i = 0; i < MAX_SIMULTANEOUS_ANIMATIONS; i++) {
316 if (animations[i]) {
317 update_keyframe_animation(animations[i], &state, delta, &sleep_time);
318 }
319 }
320#ifdef BACKLIGHT_ENABLE
321 gdispGFlush(LED_DISPLAY);
322#endif
323
324#ifdef LCD_ENABLE
325 gdispGFlush(LCD_DISPLAY);
326#endif
327
328#ifdef EMULATOR
329 draw_emulator();
330#endif
331 // Enable the visualizer when the startup or the suspend animation has finished
332 if (!visualizer_enabled && state.status.suspended == false && get_num_running_animations() == 0) {
333 visualizer_enabled = true;
334 force_update = true;
335 sleep_time = 0;
336 }
337
338 systemticks_t after_update = gfxSystemTicks();
339 unsigned update_delta = after_update - current_time;
340 if (sleep_time != TIME_INFINITE) {
341 if (sleep_time > update_delta) {
342 sleep_time -= update_delta;
343 } else {
344 sleep_time = 0;
345 }
346 }
347 dprintf("Update took %d, last delta %d, sleep_time %d\n", update_delta, delta, sleep_time);
348#ifdef PROTOCOL_CHIBIOS
349 // The gEventWait function really takes milliseconds, even if the documentation says ticks.
350 // Unfortunately there's no generic ugfx conversion from system time to milliseconds,
351 // so let's do it in a platform dependent way.
352
353 // On windows the system ticks is the same as milliseconds anyway
354 if (sleep_time != TIME_INFINITE) {
355 sleep_time = TIME_I2MS(sleep_time);
356 }
357#endif
358 geventEventWait(&event_listener, sleep_time);
359 }
360#ifdef LCD_ENABLE
361 gdispCloseFont(state.font_fixed5x8);
362 gdispCloseFont(state.font_dejavusansbold12);
363#endif
364
365 return 0;
366}
367
368void visualizer_init(void) {
369 gfxInit();
370
371#ifdef LCD_BACKLIGHT_ENABLE
372 lcd_backlight_init();
373#endif
374
375#ifdef SERIAL_LINK_ENABLE
376 add_remote_objects(remote_objects, sizeof(remote_objects) / sizeof(remote_object_t*));
377#endif
378
379#ifdef LCD_ENABLE
380 LCD_DISPLAY = get_lcd_display();
381#endif
382
383#ifdef BACKLIGHT_ENABLE
384 LED_DISPLAY = get_led_display();
385#endif
386
387 // We are using a low priority thread, the idea is to have it run only
388 // when the main thread is sleeping during the matrix scanning
389 gfxThreadCreate(visualizerThreadStack, sizeof(visualizerThreadStack), VISUALIZER_THREAD_PRIORITY, visualizerThread, NULL);
390}
391
392void update_status(bool changed) {
393 if (changed) {
394 GSourceListener* listener = geventGetSourceListener((GSourceHandle)&current_status, NULL);
395 if (listener) {
396 geventSendEvent(listener);
397 }
398 }
399#ifdef SERIAL_LINK_ENABLE
400 static systime_t last_update = 0;
401 systime_t current_update = chVTGetSystemTimeX();
402 systime_t delta = current_update - last_update;
403 if (changed || delta > TIME_MS2I(10)) {
404 last_update = current_update;
405 visualizer_keyboard_status_t* r = begin_write_current_status();
406 *r = current_status;
407 end_write_current_status();
408 }
409#endif
410}
411
412uint8_t visualizer_get_mods() {
413 uint8_t mods = get_mods();
414
415#ifndef NO_ACTION_ONESHOT
416 if (!has_oneshot_mods_timed_out()) {
417 mods |= get_oneshot_mods();
418 }
419#endif
420 return mods;
421}
422
423#ifdef VISUALIZER_USER_DATA_SIZE
424void visualizer_set_user_data(void* u) { memcpy(user_data, u, VISUALIZER_USER_DATA_SIZE); }
425#endif
426
427void visualizer_update(layer_state_t default_state, layer_state_t state, uint8_t mods, uint32_t leds) {
428 // Note that there's a small race condition here, the thread could read
429 // a state where one of these are set but not the other. But this should
430 // not really matter as it will be fixed during the next loop step.
431 // Alternatively a mutex could be used instead of the volatile variables
432
433 bool changed = false;
434#ifdef SERIAL_LINK_ENABLE
435 if (is_serial_link_connected()) {
436 visualizer_keyboard_status_t* new_status = read_current_status();
437 if (new_status) {
438 if (!same_status(&current_status, new_status)) {
439 changed = true;
440 current_status = *new_status;
441 }
442 }
443 } else {
444#else
445 {
446#endif
447 visualizer_keyboard_status_t new_status = {
448 .layer = state,
449 .default_layer = default_state,
450 .mods = mods,
451 .leds = leds,
452#ifdef BACKLIGHT_ENABLE
453 .backlight_level = current_status.backlight_level,
454#endif
455 .suspended = current_status.suspended,
456 };
457#ifdef VISUALIZER_USER_DATA_SIZE
458 memcpy(new_status.user_data, user_data, VISUALIZER_USER_DATA_SIZE);
459#endif
460 if (!same_status(&current_status, &new_status)) {
461 changed = true;
462 current_status = new_status;
463 }
464 }
465 update_status(changed);
466}
467
468void visualizer_suspend(void) {
469 current_status.suspended = true;
470 update_status(true);
471}
472
473void visualizer_resume(void) {
474 current_status.suspended = false;
475 update_status(true);
476}
477
478#ifdef BACKLIGHT_ENABLE
479void backlight_set(uint8_t level) {
480 current_status.backlight_level = level;
481 update_status(true);
482}
483#endif
diff --git a/quantum/visualizer/visualizer.h b/quantum/visualizer/visualizer.h
deleted file mode 100644
index 627c80a30..000000000
--- a/quantum/visualizer/visualizer.h
+++ /dev/null
@@ -1,154 +0,0 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#pragma once
26
27#include <stdlib.h>
28#include <stdint.h>
29#include <stdbool.h>
30
31#include "config.h"
32#include "gfx.h"
33#include "action_layer.h"
34
35#ifdef LCD_BACKLIGHT_ENABLE
36# include "lcd_backlight.h"
37#endif
38
39#ifdef BACKLIGHT_ENABLE
40# include "backlight.h"
41#endif
42
43// use this function to merge both real_mods and oneshot_mods in a uint16_t
44uint8_t visualizer_get_mods(void);
45
46// This need to be called once at the start
47void visualizer_init(void);
48// This should be called at every matrix scan
49void visualizer_update(layer_state_t default_state, layer_state_t state, uint8_t mods, uint32_t leds);
50
51// This should be called when the keyboard goes to suspend state
52void visualizer_suspend(void);
53// This should be called when the keyboard wakes up from suspend state
54void visualizer_resume(void);
55
56// These functions are week, so they can be overridden by the keyboard
57// if needed
58GDisplay* get_lcd_display(void);
59GDisplay* get_led_display(void);
60
61// For emulator builds, this function need to be implemented
62#ifdef EMULATOR
63void draw_emulator(void);
64#endif
65
66// If you need support for more than 16 keyframes per animation, you can change this
67#define MAX_VISUALIZER_KEY_FRAMES 16
68
69struct keyframe_animation_t;
70
71typedef struct {
72 layer_state_t layer;
73 layer_state_t default_layer;
74 uint32_t leds; // See led.h for available statuses
75 uint8_t mods;
76 bool suspended;
77#ifdef BACKLIGHT_ENABLE
78 uint8_t backlight_level;
79#endif
80#ifdef VISUALIZER_USER_DATA_SIZE
81 uint8_t user_data[VISUALIZER_USER_DATA_SIZE];
82#endif
83} visualizer_keyboard_status_t;
84
85// The state struct is used by the various keyframe functions
86// It's also used for setting the LCD color and layer text
87// from the user customized code
88typedef struct visualizer_state_t {
89 // The user code should primarily be modifying these
90 uint32_t target_lcd_color;
91 const char* layer_text;
92
93 // The user visualizer(and animation functions) can read these
94 visualizer_keyboard_status_t status;
95
96 // These are used by the animation functions
97 uint32_t current_lcd_color;
98 uint32_t prev_lcd_color;
99#ifdef LCD_ENABLE
100 gFont font_fixed5x8;
101 gFont font_dejavusansbold12;
102#endif
103} visualizer_state_t;
104
105// Any custom keyframe function should have this signature
106// return true to get continuous updates, otherwise you will only get one
107// update per frame
108typedef bool (*frame_func)(struct keyframe_animation_t*, visualizer_state_t*);
109
110// Represents a keyframe animation, so fields are internal to the system
111// while others are meant to be initialized by the user code
112typedef struct keyframe_animation_t {
113 // These should be initialized
114 int num_frames;
115 bool loop;
116 int frame_lengths[MAX_VISUALIZER_KEY_FRAMES];
117 frame_func frame_functions[MAX_VISUALIZER_KEY_FRAMES];
118
119 // Used internally by the system, and can also be read by
120 // keyframe update functions
121 int current_frame;
122 int time_left_in_frame;
123 bool first_update_of_frame;
124 bool last_update_of_frame;
125 bool need_update;
126
127} keyframe_animation_t;
128
129extern GDisplay* LCD_DISPLAY;
130extern GDisplay* LED_DISPLAY;
131
132void start_keyframe_animation(keyframe_animation_t* animation);
133void stop_keyframe_animation(keyframe_animation_t* animation);
134// This runs the next keyframe, but does not update the animation state
135// Useful for crossfades for example
136void run_next_keyframe(keyframe_animation_t* animation, visualizer_state_t* state);
137
138// The master can set userdata which will be transferred to the slave
139#ifdef VISUALIZER_USER_DATA_SIZE
140void visualizer_set_user_data(void* user_data);
141#endif
142
143// These functions have to be implemented by the user
144// Called regularly each time the state has changed (but not every scan loop)
145void update_user_visualizer_state(visualizer_state_t* state, visualizer_keyboard_status_t* prev_status);
146// Called when the computer goes to suspend, will also stop calling update_user_visualizer_state
147void user_visualizer_suspend(visualizer_state_t* state);
148// You have to start at least one animation as a response to the following two functions
149// When the animation has finished the visualizer will resume normal operation and start calling the
150// update_user_visualizer_state again
151// Called when the keyboard boots up
152void initialize_user_visualizer(visualizer_state_t* state);
153// Called when the computer resumes from a suspend
154void user_visualizer_resume(visualizer_state_t* state);
diff --git a/quantum/visualizer/visualizer.mk b/quantum/visualizer/visualizer.mk
deleted file mode 100644
index 4c961ac59..000000000
--- a/quantum/visualizer/visualizer.mk
+++ /dev/null
@@ -1,123 +0,0 @@
1# The MIT License (MIT)
2#
3# Copyright (c) 2016 Fred Sundvik
4#
5# Permission is hereby granted, free of charge, to any person obtaining a copy
6# of this software and associated documentation files (the "Software"), to deal
7# in the Software without restriction, including without limitation the rights
8# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9# copies of the Software, and to permit persons to whom the Software is
10# furnished to do so, subject to the following conditions:
11#
12# The above copyright notice and this permission notice shall be included in all
13# copies or substantial portions of the Software.
14#
15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21# SOFTWARE.
22
23define ADD_DRIVER
24 $(1)_DRIVER:=$(strip $($(1)_DRIVER))
25 $(1)_WIDTH:=$(strip $($(1)_WIDTH))
26 $(1)_HEIGHT:=$(strip $($(1)_HEIGHT))
27 ifeq ($($(1)_DRIVER),)
28 $$(error $(1)_DRIVER is not defined)
29 endif
30 ifeq ($($(1)_WIDTH),)
31 $$(error $(1)_WIDTH is not defined)
32 endif
33 ifeq ($($(1)_HEIGHT),)
34 $$(error $(1)_HEIGHT is not defined)
35 endif
36 OPT_DEFS+=-D$(1)_WIDTH=$($(1)_WIDTH)
37 OPT_DEFS+=-D$(1)_HEIGHT=$($(1)_HEIGHT)
38 GFXDEFS+=-D$(1)_WIDTH=$($(1)_WIDTH)
39 GFXDEFS+=-D$(1)_HEIGHT=$($(1)_HEIGHT)
40 $(1)_DISPLAY_NUMBER:=$$(words $$(GDISP_DRIVER_LIST))
41 OPT_DEFS+=-D$(1)_DISPLAY_NUMBER=$$($(1)_DISPLAY_NUMBER)
42 include $(TOP_DIR)/drivers/ugfx/gdisp/$($(1)_DRIVER)/driver.mk
43endef
44
45GDISP_DRIVER_LIST:=
46
47SRC += $(VISUALIZER_DIR)/visualizer.c \
48 $(VISUALIZER_DIR)/visualizer_keyframes.c
49EXTRAINCDIRS += $(GFXINC) $(VISUALIZER_DIR)
50GFXLIB = $(LIB_PATH)/ugfx
51VPATH += $(VISUALIZER_PATH)
52
53OPT_DEFS += -DVISUALIZER_ENABLE
54
55ifdef LCD_ENABLE
56OPT_DEFS += -DLCD_ENABLE
57ULIBS += -lm
58endif
59
60ifeq ($(strip $(LCD_ENABLE)), yes)
61 SRC += $(VISUALIZER_DIR)/lcd_keyframes.c
62 ifeq ($(strip $(LCD_BACKLIGHT_ENABLE)), yes)
63 OPT_DEFS += -DLCD_BACKLIGHT_ENABLE
64 SRC += $(VISUALIZER_DIR)/lcd_backlight.c
65 SRC += $(VISUALIZER_DIR)/lcd_backlight_keyframes.c
66 endif
67# Note, that the linker will strip out any resources that are not actually in use
68SRC += $(VISUALIZER_DIR)/resources/lcd_logo.c
69$(eval $(call ADD_DRIVER,LCD))
70endif
71
72ifeq ($(strip $(BACKLIGHT_ENABLE)), yes)
73SRC += $(VISUALIZER_DIR)/led_backlight_keyframes.c
74$(eval $(call ADD_DRIVER,LED))
75endif
76
77SRC += $(VISUALIZER_DIR)/default_animations.c
78
79include $(GFXLIB)/gfx.mk
80# For the common_gfxconf.h
81GFXINC += quantum/visualizer
82
83GFXSRC := $(patsubst $(TOP_DIR)/%,%,$(GFXSRC))
84GFXDEFS := $(patsubst %,-D%,$(patsubst -D%,%,$(GFXDEFS)))
85
86GDISP_LIST_COMMA=,
87GDISP_LIST_EMPTY=
88GDISP_LIST_SPACE=$(GDISP_LIST_EMPTY) $(GDISP_LIST_EMPTY)
89
90GDISP_DRIVER_LIST := $(strip $(GDISP_DRIVER_LIST))
91GDISP_DRIVER_LIST := $(subst $(GDISP_LIST_SPACE),$(GDISP_LIST_COMMA),$(GDISP_DRIVER_LIST))
92
93GFXDEFS +=-DGDISP_DRIVER_LIST="$(GDISP_DRIVER_LIST)"
94
95ifneq ("$(wildcard $(KEYMAP_PATH)/visualizer.c)","")
96 SRC += $(KEYMAP_PATH)/visualizer.c
97else
98 VISUALIZER_1 := $(KEYBOARD_PATH_1)/visualizer.c
99 VISUALIZER_2 := $(KEYBOARD_PATH_2)/visualizer.c
100 VISUALIZER_3 := $(KEYBOARD_PATH_3)/visualizer.c
101 VISUALIZER_4 := $(KEYBOARD_PATH_4)/visualizer.c
102 VISUALIZER_5 := $(KEYBOARD_PATH_5)/visualizer.c
103
104 ifneq ("$(wildcard $(VISUALIZER_5))","")
105 SRC += $(VISUALIZER_5)
106 endif
107 ifneq ("$(wildcard $(VISUALIZER_4))","")
108 SRC += $(VISUALIZER_4)
109 endif
110 ifneq ("$(wildcard $(VISUALIZER_3))","")
111 SRC += $(VISUALIZER_3)
112 endif
113 ifneq ("$(wildcard $(VISUALIZER_2))","")
114 SRC += $(VISUALIZER_2)
115 endif
116 ifneq ("$(wildcard $(VISUALIZER_1))","")
117 SRC += $(VISUALIZER_1)
118 endif
119endif
120
121ifdef EMULATOR
122UINCDIR += $(TMK_DIR)/common
123endif
diff --git a/quantum/visualizer/visualizer_keyframes.c b/quantum/visualizer/visualizer_keyframes.c
deleted file mode 100644
index 8f6a7e15a..000000000
--- a/quantum/visualizer/visualizer_keyframes.c
+++ /dev/null
@@ -1,23 +0,0 @@
1/* Copyright 2017 Fred Sundvik
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "visualizer_keyframes.h"
18
19bool keyframe_no_operation(keyframe_animation_t* animation, visualizer_state_t* state) {
20 (void)animation;
21 (void)state;
22 return false;
23}
diff --git a/quantum/visualizer/visualizer_keyframes.h b/quantum/visualizer/visualizer_keyframes.h
deleted file mode 100644
index c92ff1611..000000000
--- a/quantum/visualizer/visualizer_keyframes.h
+++ /dev/null
@@ -1,23 +0,0 @@
1/* Copyright 2017 Fred Sundvik
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include "visualizer.h"
20
21// Some predefined keyframe functions that can be used by the user code
22// Does nothing, useful for adding delays
23bool keyframe_no_operation(keyframe_animation_t* animation, visualizer_state_t* state);