qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

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