aboutsummaryrefslogtreecommitdiff
path: root/quantum/audio/driver_avr_pwm_hardware.c
diff options
context:
space:
mode:
Diffstat (limited to 'quantum/audio/driver_avr_pwm_hardware.c')
-rw-r--r--quantum/audio/driver_avr_pwm_hardware.c332
1 files changed, 0 insertions, 332 deletions
diff --git a/quantum/audio/driver_avr_pwm_hardware.c b/quantum/audio/driver_avr_pwm_hardware.c
deleted file mode 100644
index df03a4558..000000000
--- a/quantum/audio/driver_avr_pwm_hardware.c
+++ /dev/null
@@ -1,332 +0,0 @@
1/* Copyright 2016 Jack Humbert
2 * Copyright 2020 JohSchneider
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#if defined(__AVR__)
19# include <avr/pgmspace.h>
20# include <avr/interrupt.h>
21# include <avr/io.h>
22#endif
23
24#include "audio.h"
25
26extern bool playing_note;
27extern bool playing_melody;
28extern uint8_t note_timbre;
29
30#define CPU_PRESCALER 8
31
32/*
33 Audio Driver: PWM
34
35 drive up to two speakers through the AVR PWM hardware-peripheral, using timer1 and/or timer3 on Atmega32U4.
36
37 the primary channel_1 can be connected to either pin PC4 PC5 or PC6 (the later being used by most AVR based keyboards) with a PMW signal generated by timer3
38 and an optional secondary channel_2 on either pin PB5, PB6 or PB7, with a PWM signal from timer1
39
40 alternatively, the PWM pins on PORTB can be used as only/primary speaker
41*/
42
43#if defined(AUDIO_PIN) && (AUDIO_PIN != C4) && (AUDIO_PIN != C5) && (AUDIO_PIN != C6) && (AUDIO_PIN != B5) && (AUDIO_PIN != B6) && (AUDIO_PIN != B7) && (AUDIO_PIN != D5)
44# error "Audio feature enabled, but no suitable pin selected as AUDIO_PIN - see docs/feature_audio under the AVR settings for available options."
45#endif
46
47#if (AUDIO_PIN == C4) || (AUDIO_PIN == C5) || (AUDIO_PIN == C6)
48# define AUDIO1_PIN_SET
49# define AUDIO1_TIMSKx TIMSK3
50# define AUDIO1_TCCRxA TCCR3A
51# define AUDIO1_TCCRxB TCCR3B
52# define AUDIO1_ICRx ICR3
53# define AUDIO1_WGMx0 WGM30
54# define AUDIO1_WGMx1 WGM31
55# define AUDIO1_WGMx2 WGM32
56# define AUDIO1_WGMx3 WGM33
57# define AUDIO1_CSx0 CS30
58# define AUDIO1_CSx1 CS31
59# define AUDIO1_CSx2 CS32
60
61# if (AUDIO_PIN == C6)
62# define AUDIO1_COMxy0 COM3A0
63# define AUDIO1_COMxy1 COM3A1
64# define AUDIO1_OCIExy OCIE3A
65# define AUDIO1_OCRxy OCR3A
66# define AUDIO1_PIN C6
67# define AUDIO1_TIMERx_COMPy_vect TIMER3_COMPA_vect
68# elif (AUDIO_PIN == C5)
69# define AUDIO1_COMxy0 COM3B0
70# define AUDIO1_COMxy1 COM3B1
71# define AUDIO1_OCIExy OCIE3B
72# define AUDIO1_OCRxy OCR3B
73# define AUDIO1_PIN C5
74# define AUDIO1_TIMERx_COMPy_vect TIMER3_COMPB_vect
75# elif (AUDIO_PIN == C4)
76# define AUDIO1_COMxy0 COM3C0
77# define AUDIO1_COMxy1 COM3C1
78# define AUDIO1_OCIExy OCIE3C
79# define AUDIO1_OCRxy OCR3C
80# define AUDIO1_PIN C4
81# define AUDIO1_TIMERx_COMPy_vect TIMER3_COMPC_vect
82# endif
83#endif
84
85#if defined(AUDIO_PIN) && defined(AUDIO_PIN_ALT) && (AUDIO_PIN == AUDIO_PIN_ALT)
86# error "Audio feature: AUDIO_PIN and AUDIO_PIN_ALT on the same pin makes no sense."
87#endif
88
89#if ((AUDIO_PIN == B5) && ((AUDIO_PIN_ALT == B6) || (AUDIO_PIN_ALT == B7))) || ((AUDIO_PIN == B6) && ((AUDIO_PIN_ALT == B5) || (AUDIO_PIN_ALT == B7))) || ((AUDIO_PIN == B7) && ((AUDIO_PIN_ALT == B5) || (AUDIO_PIN_ALT == B6)))
90# error "Audio feature: PORTB as AUDIO_PIN and AUDIO_PIN_ALT at the same time is not supported."
91#endif
92
93#if defined(AUDIO_PIN_ALT) && (AUDIO_PIN_ALT != B5) && (AUDIO_PIN_ALT != B6) && (AUDIO_PIN_ALT != B7)
94# error "Audio feature: the pin selected as AUDIO_PIN_ALT is not supported."
95#endif
96
97#if (AUDIO_PIN == B5) || (AUDIO_PIN == B6) || (AUDIO_PIN == B7) || (AUDIO_PIN_ALT == B5) || (AUDIO_PIN_ALT == B6) || (AUDIO_PIN_ALT == B7) || (AUDIO_PIN == D5)
98# define AUDIO2_PIN_SET
99# define AUDIO2_TIMSKx TIMSK1
100# define AUDIO2_TCCRxA TCCR1A
101# define AUDIO2_TCCRxB TCCR1B
102# define AUDIO2_ICRx ICR1
103# define AUDIO2_WGMx0 WGM10
104# define AUDIO2_WGMx1 WGM11
105# define AUDIO2_WGMx2 WGM12
106# define AUDIO2_WGMx3 WGM13
107# define AUDIO2_CSx0 CS10
108# define AUDIO2_CSx1 CS11
109# define AUDIO2_CSx2 CS12
110
111# if (AUDIO_PIN == B5) || (AUDIO_PIN_ALT == B5)
112# define AUDIO2_COMxy0 COM1A0
113# define AUDIO2_COMxy1 COM1A1
114# define AUDIO2_OCIExy OCIE1A
115# define AUDIO2_OCRxy OCR1A
116# define AUDIO2_PIN B5
117# define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPA_vect
118# elif (AUDIO_PIN == B6) || (AUDIO_PIN_ALT == B6)
119# define AUDIO2_COMxy0 COM1B0
120# define AUDIO2_COMxy1 COM1B1
121# define AUDIO2_OCIExy OCIE1B
122# define AUDIO2_OCRxy OCR1B
123# define AUDIO2_PIN B6
124# define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPB_vect
125# elif (AUDIO_PIN == B7) || (AUDIO_PIN_ALT == B7)
126# define AUDIO2_COMxy0 COM1C0
127# define AUDIO2_COMxy1 COM1C1
128# define AUDIO2_OCIExy OCIE1C
129# define AUDIO2_OCRxy OCR1C
130# define AUDIO2_PIN B7
131# define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPC_vect
132# elif (AUDIO_PIN == D5) && defined(__AVR_ATmega32A__)
133# pragma message "Audio support for ATmega32A is experimental and can cause crashes."
134# undef AUDIO2_TIMSKx
135# define AUDIO2_TIMSKx TIMSK
136# define AUDIO2_COMxy0 COM1A0
137# define AUDIO2_COMxy1 COM1A1
138# define AUDIO2_OCIExy OCIE1A
139# define AUDIO2_OCRxy OCR1A
140# define AUDIO2_PIN D5
141# define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPA_vect
142# endif
143#endif
144
145// C6 seems to be the assumed default by many existing keyboard - but sill warn the user
146#if !defined(AUDIO1_PIN_SET) && !defined(AUDIO2_PIN_SET)
147# pragma message "Audio feature enabled, but no suitable pin selected - see docs/feature_audio under the AVR settings for available options. Don't expect to hear anything... :-)"
148// TODO: make this an error - go through the breaking-change-process and change all keyboards to the new define
149#endif
150// -----------------------------------------------------------------------------
151
152#ifdef AUDIO1_PIN_SET
153static float channel_1_frequency = 0.0f;
154void channel_1_set_frequency(float freq) {
155 if (freq == 0.0f) // a pause/rest is a valid "note" with freq=0
156 {
157 // disable the output, but keep the pwm-ISR going (with the previous
158 // frequency) so the audio-state keeps getting updated
159 // Note: setting the duty-cycle 0 is not possible on non-inverting PWM mode - see the AVR data-sheet
160 AUDIO1_TCCRxA &= ~(_BV(AUDIO1_COMxy1) | _BV(AUDIO1_COMxy0));
161 return;
162 } else {
163 AUDIO1_TCCRxA |= _BV(AUDIO1_COMxy1); // enable output, PWM mode
164 }
165
166 channel_1_frequency = freq;
167
168 // set pwm period
169 AUDIO1_ICRx = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER));
170 // and duty cycle
171 AUDIO1_OCRxy = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre / 100);
172}
173
174void channel_1_start(void) {
175 // enable timer-counter ISR
176 AUDIO1_TIMSKx |= _BV(AUDIO1_OCIExy);
177 // enable timer-counter output
178 AUDIO1_TCCRxA |= _BV(AUDIO1_COMxy1);
179}
180
181void channel_1_stop(void) {
182 // disable timer-counter ISR
183 AUDIO1_TIMSKx &= ~_BV(AUDIO1_OCIExy);
184 // disable timer-counter output
185 AUDIO1_TCCRxA &= ~(_BV(AUDIO1_COMxy1) | _BV(AUDIO1_COMxy0));
186}
187#endif
188
189#ifdef AUDIO2_PIN_SET
190static float channel_2_frequency = 0.0f;
191void channel_2_set_frequency(float freq) {
192 if (freq == 0.0f) {
193 AUDIO2_TCCRxA &= ~(_BV(AUDIO2_COMxy1) | _BV(AUDIO2_COMxy0));
194 return;
195 } else {
196 AUDIO2_TCCRxA |= _BV(AUDIO2_COMxy1);
197 }
198
199 channel_2_frequency = freq;
200
201 AUDIO2_ICRx = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER));
202 AUDIO2_OCRxy = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre / 100);
203}
204
205float channel_2_get_frequency(void) { return channel_2_frequency; }
206
207void channel_2_start(void) {
208 AUDIO2_TIMSKx |= _BV(AUDIO2_OCIExy);
209 AUDIO2_TCCRxA |= _BV(AUDIO2_COMxy1);
210}
211
212void channel_2_stop(void) {
213 AUDIO2_TIMSKx &= ~_BV(AUDIO2_OCIExy);
214 AUDIO2_TCCRxA &= ~(_BV(AUDIO2_COMxy1) | _BV(AUDIO2_COMxy0));
215}
216#endif
217
218void audio_driver_initialize() {
219#ifdef AUDIO1_PIN_SET
220 channel_1_stop();
221 setPinOutput(AUDIO1_PIN);
222#endif
223
224#ifdef AUDIO2_PIN_SET
225 channel_2_stop();
226 setPinOutput(AUDIO2_PIN);
227#endif
228
229 // TCCR3A / TCCR3B: Timer/Counter #3 Control Registers TCCR3A/TCCR3B, TCCR1A/TCCR1B
230 // Compare Output Mode (COM3An and COM1An) = 0b00 = Normal port operation
231 // OC3A -- PC6
232 // OC3B -- PC5
233 // OC3C -- PC4
234 // OC1A -- PB5
235 // OC1B -- PB6
236 // OC1C -- PB7
237
238 // Waveform Generation Mode (WGM3n) = 0b1110 = Fast PWM Mode 14. Period = ICR3, Duty Cycle OCR3A)
239 // OCR3A - PC6
240 // OCR3B - PC5
241 // OCR3C - PC4
242 // OCR1A - PB5
243 // OCR1B - PB6
244 // OCR1C - PB7
245
246 // Clock Select (CS3n) = 0b010 = Clock / 8
247#ifdef AUDIO1_PIN_SET
248 // initialize timer-counter
249 AUDIO1_TCCRxA = (0 << AUDIO1_COMxy1) | (0 << AUDIO1_COMxy0) | (1 << AUDIO1_WGMx1) | (0 << AUDIO1_WGMx0);
250 AUDIO1_TCCRxB = (1 << AUDIO1_WGMx3) | (1 << AUDIO1_WGMx2) | (0 << AUDIO1_CSx2) | (1 << AUDIO1_CSx1) | (0 << AUDIO1_CSx0);
251#endif
252
253#ifdef AUDIO2_PIN_SET
254 AUDIO2_TCCRxA = (0 << AUDIO2_COMxy1) | (0 << AUDIO2_COMxy0) | (1 << AUDIO2_WGMx1) | (0 << AUDIO2_WGMx0);
255 AUDIO2_TCCRxB = (1 << AUDIO2_WGMx3) | (1 << AUDIO2_WGMx2) | (0 << AUDIO2_CSx2) | (1 << AUDIO2_CSx1) | (0 << AUDIO2_CSx0);
256#endif
257}
258
259void audio_driver_stop() {
260#ifdef AUDIO1_PIN_SET
261 channel_1_stop();
262#endif
263
264#ifdef AUDIO2_PIN_SET
265 channel_2_stop();
266#endif
267}
268
269void audio_driver_start(void) {
270#ifdef AUDIO1_PIN_SET
271 channel_1_start();
272 if (playing_note) {
273 channel_1_set_frequency(audio_get_processed_frequency(0));
274 }
275#endif
276
277#if !defined(AUDIO1_PIN_SET) && defined(AUDIO2_PIN_SET)
278 channel_2_start();
279 if (playing_note) {
280 channel_2_set_frequency(audio_get_processed_frequency(0));
281 }
282#endif
283}
284
285static volatile uint32_t isr_counter = 0;
286#ifdef AUDIO1_PIN_SET
287ISR(AUDIO1_TIMERx_COMPy_vect) {
288 isr_counter++;
289 if (isr_counter < channel_1_frequency / (CPU_PRESCALER * 8)) return;
290
291 isr_counter = 0;
292 bool state_changed = audio_update_state();
293
294 if (!playing_note && !playing_melody) {
295 channel_1_stop();
296# ifdef AUDIO2_PIN_SET
297 channel_2_stop();
298# endif
299 return;
300 }
301
302 if (state_changed) {
303 channel_1_set_frequency(audio_get_processed_frequency(0));
304# ifdef AUDIO2_PIN_SET
305 if (audio_get_number_of_active_tones() > 1) {
306 channel_2_set_frequency(audio_get_processed_frequency(1));
307 } else {
308 channel_2_stop();
309 }
310# endif
311 }
312}
313#endif
314
315#if !defined(AUDIO1_PIN_SET) && defined(AUDIO2_PIN_SET)
316ISR(AUDIO2_TIMERx_COMPy_vect) {
317 isr_counter++;
318 if (isr_counter < channel_2_frequency / (CPU_PRESCALER * 8)) return;
319
320 isr_counter = 0;
321 bool state_changed = audio_update_state();
322
323 if (!playing_note && !playing_melody) {
324 channel_2_stop();
325 return;
326 }
327
328 if (state_changed) {
329 channel_2_set_frequency(audio_get_processed_frequency(0));
330 }
331}
332#endif