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