diff options
Diffstat (limited to 'quantum')
27 files changed, 493 insertions, 1464 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/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 | ||
42 | typedef union { | 37 | typedef 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 | |||
26 | extern bool playing_note; | ||
27 | extern bool playing_melody; | ||
28 | extern 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 | ||
153 | static float channel_1_frequency = 0.0f; | ||
154 | void 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 | |||
174 | void 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 | |||
181 | void 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 | ||
190 | static float channel_2_frequency = 0.0f; | ||
191 | void 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 | |||
205 | float channel_2_get_frequency(void) { return channel_2_frequency; } | ||
206 | |||
207 | void channel_2_start(void) { | ||
208 | AUDIO2_TIMSKx |= _BV(AUDIO2_OCIExy); | ||
209 | AUDIO2_TCCRxA |= _BV(AUDIO2_COMxy1); | ||
210 | } | ||
211 | |||
212 | void channel_2_stop(void) { | ||
213 | AUDIO2_TIMSKx &= ~_BV(AUDIO2_OCIExy); | ||
214 | AUDIO2_TCCRxA &= ~(_BV(AUDIO2_COMxy1) | _BV(AUDIO2_COMxy0)); | ||
215 | } | ||
216 | #endif | ||
217 | |||
218 | void 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 | |||
259 | void 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 | |||
269 | void 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 | |||
285 | static volatile uint32_t isr_counter = 0; | ||
286 | #ifdef AUDIO1_PIN_SET | ||
287 | ISR(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) | ||
316 | ISR(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 | */ | ||
126 | uint16_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 | */ | ||
51 | static 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 | ||
57 | static 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 | ||
63 | static 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 | ||
70 | static 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 | ||
78 | static const dacsample_t dac_buffer_trapezoid[AUDIO_DAC_BUFFER_SIZE] = {0x0, 0x1f, 0x7f, 0xdf, 0x13f, 0x19f, 0x1ff, 0x25f, 0x2bf, 0x31f, 0x37f, 0x3df, 0x43f, 0x49f, 0x4ff, 0x55f, 0x5bf, 0x61f, 0x67f, 0x6df, 0x73f, 0x79f, 0x7ff, 0x85f, 0x8bf, 0x91f, 0x97f, 0x9df, 0xa3f, 0xa9f, 0xaff, 0xb5f, 0xbbf, 0xc1f, 0xc7f, 0xcdf, 0xd3f, 0xd9f, 0xdff, 0xe5f, 0xebf, 0xf1f, 0xf7f, 0xfdf, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, | ||
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 | |||
82 | static 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 */ | ||
85 | static float dac_if[AUDIO_MAX_SIMULTANEOUS_TONES] = {0.0}; | ||
86 | |||
87 | static float active_tones_snapshot[AUDIO_MAX_SIMULTANEOUS_TONES] = {0, 0}; | ||
88 | static uint8_t active_tones_snapshot_length = 0; | ||
89 | |||
90 | typedef 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; | ||
104 | output_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 | */ | ||
169 | static 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 | |||
254 | static void dac_error(DACDriver *dacp, dacerror_t err) { | ||
255 | (void)dacp; | ||
256 | (void)err; | ||
257 | |||
258 | chSysHalt("DAC failure. halp"); | ||
259 | } | ||
260 | |||
261 | static 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 | |||
266 | static 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 | */ | ||
282 | static const DACConversionGroup dac_conv_cfg = {.num_channels = 1U, .end_cb = dac_end, .error_cb = dac_error, .trigger = DAC_TRG(0b000)}; | ||
283 | |||
284 | void 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 | |||
324 | void audio_driver_stop(void) { state = OUTPUT_SHOULD_STOP; } | ||
325 | |||
326 | void 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 | ||
53 | static 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 | ||
60 | static 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 | |||
66 | GPTConfig gpt6cfg1 = {.frequency = AUDIO_DAC_SAMPLE_RATE, | ||
67 | .callback = NULL, | ||
68 | .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */ | ||
69 | .dier = 0U}; | ||
70 | GPTConfig 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 | |||
75 | static void gpt_audio_state_cb(GPTDriver *gptp); | ||
76 | GPTConfig 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 | |||
81 | static const DACConfig dac_conf_ch1 = {.init = AUDIO_DAC_OFF_VALUE, .datamode = DAC_DHRM_12BIT_RIGHT}; | ||
82 | static 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 | */ | ||
98 | static const DACConversionGroup dac_conv_grp_ch1 = {.num_channels = 1U, .trigger = DAC_TRG(0b000)}; | ||
99 | static const DACConversionGroup dac_conv_grp_ch2 = {.num_channels = 1U, .trigger = DAC_TRG(0b010)}; | ||
100 | |||
101 | void channel_1_start(void) { | ||
102 | gptStart(&GPTD6, &gpt6cfg1); | ||
103 | gptStartContinuous(&GPTD6, 2U); | ||
104 | palSetPadMode(GPIOA, 4, PAL_MODE_INPUT_ANALOG); | ||
105 | } | ||
106 | |||
107 | void channel_1_stop(void) { | ||
108 | gptStopTimer(&GPTD6); | ||
109 | palSetPadMode(GPIOA, 4, PAL_MODE_OUTPUT_PUSHPULL); | ||
110 | palSetPad(GPIOA, 4); | ||
111 | } | ||
112 | |||
113 | static float channel_1_frequency = 0.0f; | ||
114 | void 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 | } | ||
124 | float channel_1_get_frequency(void) { return channel_1_frequency; } | ||
125 | |||
126 | void channel_2_start(void) { | ||
127 | gptStart(&GPTD7, &gpt7cfg1); | ||
128 | gptStartContinuous(&GPTD7, 2U); | ||
129 | palSetPadMode(GPIOA, 5, PAL_MODE_INPUT_ANALOG); | ||
130 | } | ||
131 | |||
132 | void channel_2_stop(void) { | ||
133 | gptStopTimer(&GPTD7); | ||
134 | palSetPadMode(GPIOA, 5, PAL_MODE_OUTPUT_PUSHPULL); | ||
135 | palSetPad(GPIOA, 5); | ||
136 | } | ||
137 | |||
138 | static float channel_2_frequency = 0.0f; | ||
139 | void 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 | } | ||
149 | float channel_2_get_frequency(void) { return channel_2_frequency; } | ||
150 | |||
151 | static 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 | |||
186 | void 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 | |||
219 | void 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 | |||
237 | void 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 | /* | ||
19 | Audio Driver: PWM | ||
20 | |||
21 | the duty-cycle is always kept at 50%, and the pwm-period is adjusted to match the frequency of a note to be played back. | ||
22 | |||
23 | this driver uses the chibios-PWM system to produce a square-wave on specific output pins that are connected to the PWM hardware. | ||
24 | The hardware directly toggles the pin via its alternate function. see your MCUs data-sheet for which pin can be driven by what timer - looking for TIMx_CHy and the corresponding alternate function. | ||
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 | |||
36 | extern bool playing_note; | ||
37 | extern bool playing_melody; | ||
38 | extern uint8_t note_timbre; | ||
39 | |||
40 | static 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 | |||
71 | static float channel_1_frequency = 0.0f; | ||
72 | void 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 | |||
85 | float channel_1_get_frequency(void) { return channel_1_frequency; } | ||
86 | |||
87 | void channel_1_start(void) { | ||
88 | pwmStop(&AUDIO_PWM_DRIVER); | ||
89 | pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG); | ||
90 | } | ||
91 | |||
92 | void channel_1_stop(void) { pwmStop(&AUDIO_PWM_DRIVER); } | ||
93 | |||
94 | static void gpt_callback(GPTDriver *gptp); | ||
95 | GPTConfig 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 | |||
107 | void 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 | |||
120 | void 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 | |||
129 | void 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 | */ | ||
137 | static 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 | /* | ||
19 | Audio Driver: PWM | ||
20 | |||
21 | the duty-cycle is always kept at 50%, and the pwm-period is adjusted to match the frequency of a note to be played back. | ||
22 | |||
23 | this 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 | ||
34 | extern bool playing_note; | ||
35 | extern bool playing_melody; | ||
36 | extern uint8_t note_timbre; | ||
37 | |||
38 | static void pwm_audio_period_callback(PWMDriver *pwmp); | ||
39 | static void pwm_audio_channel_interrupt_callback(PWMDriver *pwmp); | ||
40 | |||
41 | static 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 | |||
56 | static float channel_1_frequency = 0.0f; | ||
57 | void 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 | |||
71 | float channel_1_get_frequency(void) { return channel_1_frequency; } | ||
72 | |||
73 | void 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 | |||
81 | void 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 | ||
92 | static 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 | } | ||
100 | static 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 | |||
110 | static void gpt_callback(GPTDriver *gptp); | ||
111 | GPTConfig 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 | |||
123 | void 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 | |||
140 | void 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 | |||
149 | void 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 | */ | ||
157 | static 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 | ||
71 | void backlight_init_ports(void) { | 75 | void 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 | */ |
45 | void eeconfig_init_quantum(void) { | 40 | void 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 | */ |
113 | void eeconfig_disable(void) { | 105 | void 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/keyboard.c b/quantum/keyboard.c index b98fc64e4..6054faa03 100644 --- a/quantum/keyboard.c +++ b/quantum/keyboard.c | |||
@@ -76,6 +76,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
76 | #ifdef JOYSTICK_ENABLE | 76 | #ifdef JOYSTICK_ENABLE |
77 | # include "process_joystick.h" | 77 | # include "process_joystick.h" |
78 | #endif | 78 | #endif |
79 | #ifdef PROGRAMMABLE_BUTTON_ENABLE | ||
80 | # include "programmable_button.h" | ||
81 | #endif | ||
79 | #ifdef HD44780_ENABLE | 82 | #ifdef HD44780_ENABLE |
80 | # include "hd44780.h" | 83 | # include "hd44780.h" |
81 | #endif | 84 | #endif |
@@ -97,9 +100,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
97 | #ifdef DIP_SWITCH_ENABLE | 100 | #ifdef DIP_SWITCH_ENABLE |
98 | # include "dip_switch.h" | 101 | # include "dip_switch.h" |
99 | #endif | 102 | #endif |
100 | #ifdef STM32_EEPROM_ENABLE | ||
101 | # include "eeprom_stm32.h" | ||
102 | #endif | ||
103 | #ifdef EEPROM_DRIVER | 103 | #ifdef EEPROM_DRIVER |
104 | # include "eeprom_driver.h" | 104 | # include "eeprom_driver.h" |
105 | #endif | 105 | #endif |
@@ -246,9 +246,6 @@ void keyboard_setup(void) { | |||
246 | disable_jtag(); | 246 | disable_jtag(); |
247 | #endif | 247 | #endif |
248 | print_set_sendchar(sendchar); | 248 | print_set_sendchar(sendchar); |
249 | #ifdef STM32_EEPROM_ENABLE | ||
250 | EEPROM_Init(); | ||
251 | #endif | ||
252 | #ifdef EEPROM_DRIVER | 249 | #ifdef EEPROM_DRIVER |
253 | eeprom_driver_init(); | 250 | eeprom_driver_init(); |
254 | #endif | 251 | #endif |
@@ -548,6 +545,10 @@ MATRIX_LOOP_END: | |||
548 | digitizer_task(); | 545 | digitizer_task(); |
549 | #endif | 546 | #endif |
550 | 547 | ||
548 | #ifdef PROGRAMMABLE_BUTTON_ENABLE | ||
549 | programmable_button_send(); | ||
550 | #endif | ||
551 | |||
551 | // update LED | 552 | // update LED |
552 | if (led_status != host_keyboard_leds()) { | 553 | if (led_status != host_keyboard_leds()) { |
553 | led_status = host_keyboard_leds(); | 554 | led_status = host_keyboard_leds(); |
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 | |||
312 | bool matrix_post_scan(void) { | 310 | bool 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..92e3a7c92 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 |
82 | endif | 82 | endif |
83 | 83 | ||
84 | ifneq ($(findstring MK66F18, $(MCU)),) | 84 | ifneq ($(findstring MK66FX1M0, $(MCU)),) |
85 | # Cortex version | 85 | # Cortex version |
86 | MCU = cortex-m4 | 86 | MCU = cortex-m4 |
87 | 87 | ||
@@ -273,6 +273,38 @@ ifneq ($(findstring STM32F401, $(MCU)),) | |||
273 | UF2_FAMILY ?= STM32F4 | 273 | UF2_FAMILY ?= STM32F4 |
274 | endif | 274 | endif |
275 | 275 | ||
276 | ifneq ($(findstring STM32F405, $(MCU)),) | ||
277 | # Cortex version | ||
278 | MCU = cortex-m4 | ||
279 | |||
280 | # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7 | ||
281 | ARMV = 7 | ||
282 | |||
283 | ## chip/board settings | ||
284 | # - the next two should match the directories in | ||
285 | # <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES) | ||
286 | MCU_FAMILY = STM32 | ||
287 | MCU_SERIES = STM32F4xx | ||
288 | |||
289 | # Linker script to use | ||
290 | # - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/ | ||
291 | # or <keyboard_dir>/ld/ | ||
292 | MCU_LDSCRIPT ?= STM32F405xG | ||
293 | |||
294 | # Startup code to use | ||
295 | # - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/ | ||
296 | MCU_STARTUP ?= stm32f4xx | ||
297 | |||
298 | # Board: it should exist either in <chibios>/os/hal/boards/, | ||
299 | # <keyboard_dir>/boards/, or drivers/boards/ | ||
300 | BOARD ?= GENERIC_STM32_F405XG | ||
301 | |||
302 | USE_FPU ?= yes | ||
303 | |||
304 | # UF2 settings | ||
305 | UF2_FAMILY ?= STM32F4 | ||
306 | endif | ||
307 | |||
276 | ifneq ($(findstring STM32F407, $(MCU)),) | 308 | ifneq ($(findstring STM32F407, $(MCU)),) |
277 | # Cortex version | 309 | # Cortex version |
278 | MCU = cortex-m4 | 310 | MCU = cortex-m4 |
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 | /* | ||
2 | Copyright 2021 Thomas Weißschuh <thomas@t-8ch.de> | ||
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 "process_programmable_button.h" | ||
19 | #include "programmable_button.h" | ||
20 | |||
21 | bool 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 | /* | ||
2 | Copyright 2021 Thomas Weißschuh <thomas@t-8ch.de> | ||
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 <stdint.h> | ||
21 | #include "quantum.h" | ||
22 | |||
23 | bool 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 @@ | |||
22 | unicode_config_t unicode_config; | 22 | unicode_config_t unicode_config; |
23 | uint8_t unicode_saved_mods; | 23 | uint8_t unicode_saved_mods; |
24 | bool unicode_saved_caps_lock; | 24 | bool unicode_saved_caps_lock; |
25 | bool unicode_saved_num_lock; | ||
25 | 26 | ||
26 | #if UNICODE_SELECTED_MODES != -1 | 27 | #if UNICODE_SELECTED_MODES != -1 |
27 | static uint8_t selected[] = {UNICODE_SELECTED_MODES}; | 28 | static 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 | |||
172 | static 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 | |||
158 | void register_hex(uint16_t hex) { | 185 | void 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 | /* | ||
2 | Copyright 2021 Thomas Weißschuh <thomas@t-8ch.de> | ||
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 "programmable_button.h" | ||
19 | #include "host.h" | ||
20 | |||
21 | #define REPORT_BIT(index) (((uint32_t)1) << (index - 1)) | ||
22 | |||
23 | static uint32_t programmable_button_report = 0; | ||
24 | |||
25 | void programmable_button_clear(void) { programmable_button_report = 0; } | ||
26 | |||
27 | void programmable_button_send(void) { host_programmable_button_send(programmable_button_report); } | ||
28 | |||
29 | void programmable_button_on(uint8_t index) { programmable_button_report |= REPORT_BIT(index); } | ||
30 | |||
31 | void programmable_button_off(uint8_t index) { programmable_button_report &= ~REPORT_BIT(index); } | ||
32 | |||
33 | bool programmable_button_is_on(uint8_t index) { return !!(programmable_button_report & REPORT_BIT(index)); }; | ||
34 | |||
35 | uint32_t programmable_button_get_report(void) { return programmable_button_report; }; | ||
36 | |||
37 | void 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 | /* | ||
2 | Copyright 2021 Thomas Weißschuh <thomas@t-8ch.de> | ||
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 <stdint.h> | ||
21 | #include <stdbool.h> | ||
22 | #include "report.h" | ||
23 | |||
24 | void programmable_button_clear(void); | ||
25 | void programmable_button_send(void); | ||
26 | void programmable_button_on(uint8_t index); | ||
27 | void programmable_button_off(uint8_t index); | ||
28 | bool programmable_button_is_on(uint8_t index); | ||
29 | uint32_t programmable_button_get_report(void); | ||
30 | void programmable_button_set_report(uint32_t report); | ||
diff --git a/quantum/quantum.c b/quantum/quantum.c index e60378afe..326c8370b 100644 --- a/quantum/quantum.c +++ b/quantum/quantum.c | |||
@@ -296,6 +296,9 @@ bool process_record_quantum(keyrecord_t *record) { | |||
296 | #ifdef JOYSTICK_ENABLE | 296 | #ifdef JOYSTICK_ENABLE |
297 | process_joystick(keycode, record) && | 297 | process_joystick(keycode, record) && |
298 | #endif | 298 | #endif |
299 | #ifdef PROGRAMMABLE_BUTTON_ENABLE | ||
300 | process_programmable_button(keycode, record) && | ||
301 | #endif | ||
299 | true)) { | 302 | true)) { |
300 | return false; | 303 | return false; |
301 | } | 304 | } |
@@ -480,3 +483,99 @@ void api_send_unicode(uint32_t unicode) { | |||
480 | __attribute__((weak)) void startup_user() {} | 483 | __attribute__((weak)) void startup_user() {} |
481 | 484 | ||
482 | __attribute__((weak)) void shutdown_user() {} | 485 | __attribute__((weak)) void shutdown_user() {} |
486 | |||
487 | /** \brief Run keyboard level Power down | ||
488 | * | ||
489 | * FIXME: needs doc | ||
490 | */ | ||
491 | __attribute__((weak)) void suspend_power_down_user(void) {} | ||
492 | /** \brief Run keyboard level Power down | ||
493 | * | ||
494 | * FIXME: needs doc | ||
495 | */ | ||
496 | __attribute__((weak)) void suspend_power_down_kb(void) { suspend_power_down_user(); } | ||
497 | |||
498 | void suspend_power_down_quantum(void) { | ||
499 | #ifndef NO_SUSPEND_POWER_DOWN | ||
500 | // Turn off backlight | ||
501 | # ifdef BACKLIGHT_ENABLE | ||
502 | backlight_set(0); | ||
503 | # endif | ||
504 | |||
505 | # ifdef LED_MATRIX_ENABLE | ||
506 | led_matrix_task(); | ||
507 | # endif | ||
508 | # ifdef RGB_MATRIX_ENABLE | ||
509 | rgb_matrix_task(); | ||
510 | # endif | ||
511 | |||
512 | // Turn off LED indicators | ||
513 | uint8_t leds_off = 0; | ||
514 | # if defined(BACKLIGHT_CAPS_LOCK) && defined(BACKLIGHT_ENABLE) | ||
515 | if (is_backlight_enabled()) { | ||
516 | // Don't try to turn off Caps Lock indicator as it is backlight and backlight is already off | ||
517 | leds_off |= (1 << USB_LED_CAPS_LOCK); | ||
518 | } | ||
519 | # endif | ||
520 | led_set(leds_off); | ||
521 | |||
522 | // Turn off audio | ||
523 | # ifdef AUDIO_ENABLE | ||
524 | stop_all_notes(); | ||
525 | # endif | ||
526 | |||
527 | // Turn off underglow | ||
528 | # if defined(RGBLIGHT_SLEEP) && defined(RGBLIGHT_ENABLE) | ||
529 | rgblight_suspend(); | ||
530 | # endif | ||
531 | |||
532 | # if defined(LED_MATRIX_ENABLE) | ||
533 | led_matrix_set_suspend_state(true); | ||
534 | # endif | ||
535 | # if defined(RGB_MATRIX_ENABLE) | ||
536 | rgb_matrix_set_suspend_state(true); | ||
537 | # endif | ||
538 | |||
539 | # ifdef OLED_ENABLE | ||
540 | oled_off(); | ||
541 | # endif | ||
542 | # ifdef ST7565_ENABLE | ||
543 | st7565_off(); | ||
544 | # endif | ||
545 | #endif | ||
546 | } | ||
547 | |||
548 | /** \brief run user level code immediately after wakeup | ||
549 | * | ||
550 | * FIXME: needs doc | ||
551 | */ | ||
552 | __attribute__((weak)) void suspend_wakeup_init_user(void) {} | ||
553 | |||
554 | /** \brief run keyboard level code immediately after wakeup | ||
555 | * | ||
556 | * FIXME: needs doc | ||
557 | */ | ||
558 | __attribute__((weak)) void suspend_wakeup_init_kb(void) { suspend_wakeup_init_user(); } | ||
559 | |||
560 | __attribute__((weak)) void suspend_wakeup_init_quantum(void) { | ||
561 | // Turn on backlight | ||
562 | #ifdef BACKLIGHT_ENABLE | ||
563 | backlight_init(); | ||
564 | #endif | ||
565 | |||
566 | // Restore LED indicators | ||
567 | led_set(host_keyboard_leds()); | ||
568 | |||
569 | // Wake up underglow | ||
570 | #if defined(RGBLIGHT_SLEEP) && defined(RGBLIGHT_ENABLE) | ||
571 | rgblight_wakeup(); | ||
572 | #endif | ||
573 | |||
574 | #if defined(LED_MATRIX_ENABLE) | ||
575 | led_matrix_set_suspend_state(false); | ||
576 | #endif | ||
577 | #if defined(RGB_MATRIX_ENABLE) | ||
578 | rgb_matrix_set_suspend_state(false); | ||
579 | #endif | ||
580 | suspend_wakeup_init_kb(); | ||
581 | } | ||
diff --git a/quantum/quantum.h b/quantum/quantum.h index 86b717e44..5cbe84d0c 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 |
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/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 | |||
20 | enum 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 | |||
26 | static void notify_usb_device_state_change(enum usb_device_state usb_device_state) { notify_usb_device_state_change_kb(usb_device_state); } | ||
27 | |||
28 | void 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 | |||
33 | void 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 | |||
38 | void 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 | |||
43 | void 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 | |||
48 | void 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 | |||
23 | void usb_device_state_set_configuration(bool isConfigured, uint8_t configurationNumber); | ||
24 | void usb_device_state_set_suspend(bool isConfigured, uint8_t configurationNumber); | ||
25 | void usb_device_state_set_resume(bool isConfigured, uint8_t configurationNumber); | ||
26 | void usb_device_state_set_reset(void); | ||
27 | void usb_device_state_init(void); | ||
28 | |||
29 | enum 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 | |||
36 | extern enum usb_device_state usb_device_state; | ||
37 | |||
38 | void notify_usb_device_state_change_kb(enum usb_device_state usb_device_state); | ||
39 | void notify_usb_device_state_change_user(enum usb_device_state usb_device_state); | ||