audio_dac_additive.c (20130B)
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 "gpio.h" 20 #include <math.h> 21 #include "util.h" 22 23 // Need to disable GCC's "tautological-compare" warning for this file, as it causes issues when running `KEEP_INTERMEDIATES=yes`. Corresponding pop at the end of the file. 24 #pragma GCC diagnostic push 25 #pragma GCC diagnostic ignored "-Wtautological-compare" 26 27 /* 28 Audio Driver: DAC 29 30 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 31 32 it is also possible to have a custom sample-LUT by implementing/overriding 'dac_value_generate' 33 34 this driver allows for multiple simultaneous tones to be played through one single channel by doing additive wave-synthesis 35 */ 36 37 #if !defined(AUDIO_PIN) 38 # error "Audio feature enabled, but no suitable pin selected as AUDIO_PIN - see docs/feature_audio under 'ARM (DAC additive)' for available options." 39 #endif 40 #if defined(AUDIO_PIN_ALT) && !defined(AUDIO_PIN_ALT_AS_NEGATIVE) 41 # 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." 42 #endif 43 44 #if !defined(AUDIO_PIN_ALT) 45 // no ALT pin defined is valid, but the c-ifs below need some value set 46 # define AUDIO_PIN_ALT PAL_NOLINE 47 #endif 48 49 #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) 50 # define AUDIO_DAC_SAMPLE_WAVEFORM_SINE 51 #endif 52 53 #ifdef AUDIO_DAC_SAMPLE_WAVEFORM_SINE 54 /* one full sine wave over [0,2*pi], but shifted up one amplitude and left pi/4; for the samples to start at 0 55 */ 56 static const dacsample_t dac_buffer_sine[] = { 57 // 256 values, max 4095 58 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, 59 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, 60 }; 61 #endif // AUDIO_DAC_SAMPLE_WAVEFORM_SINE 62 #ifdef AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE 63 static const dacsample_t dac_buffer_triangle[] = { 64 // 256 values, max 4095 65 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, 66 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, 67 }; 68 #endif // AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE 69 #ifdef AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE 70 static const dacsample_t dac_buffer_square[] = { 71 AUDIO_DAC_OFF_VALUE, // first and 72 AUDIO_DAC_SAMPLE_MAX, // second steps 73 }; 74 #endif // AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE 75 /* 76 // four steps: 0, 1/3, 2/3 and 1 77 static const dacsample_t dac_buffer_staircase[] = { 78 0, 79 AUDIO_DAC_SAMPLE_MAX / 3, 80 2 * AUDIO_DAC_SAMPLE_MAX / 3, 81 AUDIO_DAC_SAMPLE_MAX, 82 } 83 */ 84 #ifdef AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID 85 static const dacsample_t dac_buffer_trapezoid[] = { 86 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, 87 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, 88 }; 89 #endif // AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID 90 91 static dacsample_t dac_buffer[AUDIO_DAC_BUFFER_SIZE]; 92 93 /* keep track of the sample position for for each frequency */ 94 static float dac_if[AUDIO_MAX_SIMULTANEOUS_TONES] = {0.0}; 95 96 static float active_tones_snapshot[AUDIO_MAX_SIMULTANEOUS_TONES] = {0}; 97 static uint8_t active_tones_snapshot_length = 0; 98 99 typedef enum { 100 OUTPUT_SHOULD_START, 101 OUTPUT_RUN_NORMALLY, 102 // path 1: wait for zero, then change/update active tones 103 OUTPUT_TONES_CHANGED, 104 OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE, 105 // path 2: hardware should stop, wait for zero then turn output off = stop the timer 106 OUTPUT_SHOULD_STOP, 107 OUTPUT_REACHED_ZERO_BEFORE_OFF, 108 OUTPUT_OFF, 109 OUTPUT_OFF_1, 110 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 111 number_of_output_states 112 } output_states_t; 113 output_states_t state = OUTPUT_OFF_2; 114 115 /** 116 * Generation of the waveform being passed to the callback. Declared weak so users 117 * can override it with their own wave-forms/noises. 118 */ 119 __attribute__((weak)) uint16_t dac_value_generate(void) { 120 // DAC is running/asking for values but snapshot length is zero -> must be playing a pause 121 if (active_tones_snapshot_length == 0) { 122 return AUDIO_DAC_OFF_VALUE; 123 } 124 125 /* doing additive wave synthesis over all currently playing tones = adding up 126 * sine-wave-samples for each frequency, scaled by the number of active tones 127 */ 128 uint_fast16_t value = 0; 129 float frequency = 0.0f; 130 131 #if defined(AUDIO_DAC_SAMPLE_WAVEFORM_SINE) 132 const size_t wavetable_length = ARRAY_SIZE(dac_buffer_sine); 133 #elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE) 134 const size_t wavetable_length = ARRAY_SIZE(dac_buffer_triangle); 135 #elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID) 136 const size_t wavetable_length = ARRAY_SIZE(dac_buffer_trapezoid); 137 #elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE) 138 const size_t wavetable_length = ARRAY_SIZE(dac_buffer_square); 139 #endif 140 141 for (size_t i = 0; i < active_tones_snapshot_length; i++) { 142 /* Note: a user implementation does not have to rely on the active_tones_snapshot, but 143 * could directly query the active frequencies through audio_get_processed_frequency */ 144 frequency = active_tones_snapshot[i]; 145 146 float new_dac_if = dac_if[i]; 147 new_dac_if += frequency * ((float)wavetable_length / AUDIO_DAC_SAMPLE_RATE * 2.0f / 3.0f); 148 /*Note: the 2/3 are necessary to get the correct frequencies on the 149 * DAC output (as measured with an oscilloscope), since the gpt 150 * timer runs with 3*AUDIO_DAC_SAMPLE_RATE; and the DAC callback 151 * is called twice per conversion.*/ 152 153 while (new_dac_if >= wavetable_length) 154 new_dac_if -= wavetable_length; 155 dac_if[i] = new_dac_if; 156 157 // Wavetable generation/lookup 158 size_t dac_i = (size_t)new_dac_if; 159 160 #if defined(AUDIO_DAC_SAMPLE_WAVEFORM_SINE) 161 value += dac_buffer_sine[dac_i] / active_tones_snapshot_length; 162 #elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE) 163 value += dac_buffer_triangle[dac_i] / active_tones_snapshot_length; 164 #elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID) 165 value += dac_buffer_trapezoid[dac_i] / active_tones_snapshot_length; 166 #elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE) 167 value += dac_buffer_square[dac_i] / active_tones_snapshot_length; 168 #endif 169 /* 170 // SINE 171 value += dac_buffer_sine[dac_i] / active_tones_snapshot_length / 3; 172 // TRIANGLE 173 value += dac_buffer_triangle[dac_i] / active_tones_snapshot_length / 3; 174 // SQUARE 175 value += dac_buffer_square[dac_i] / active_tones_snapshot_length / 3; 176 //NOTE: combination of these three wave-forms is more exemplary - and doesn't sound particularly good :-P 177 */ 178 179 // STAIRS (mostly usefully as test-pattern) 180 // value_avg = dac_buffer_staircase[dac_i] / active_tones_snapshot_length; 181 } 182 183 return value; 184 } 185 186 /** 187 * DAC streaming callback. Does all of the main computing for playing songs. 188 * 189 * Note: chibios calls this CB twice: during the 'half buffer event', and the 'full buffer event'. 190 */ 191 static void dac_end(DACDriver *dacp) { 192 dacsample_t *sample_p = (dacp)->samples; 193 194 // work on the other half of the buffer 195 if (dacIsBufferComplete(dacp)) { 196 sample_p += AUDIO_DAC_BUFFER_SIZE / 2; // 'half_index' 197 } 198 199 for (uint8_t s = 0; s < AUDIO_DAC_BUFFER_SIZE / 2; s++) { 200 if (OUTPUT_OFF <= state) { 201 sample_p[s] = AUDIO_DAC_OFF_VALUE; 202 continue; 203 } else { 204 sample_p[s] = dac_value_generate(); 205 } 206 207 /* zero crossing (or approach, whereas zero == DAC_OFF_VALUE, which can be configured to anything from 0 to DAC_SAMPLE_MAX) 208 * ============================*=*========================== AUDIO_DAC_SAMPLE_MAX 209 * * * 210 * * * 211 * --------------------------------------------------------- 212 * * * } AUDIO_DAC_SAMPLE_MAX/100 213 * --------------------------------------------------------- AUDIO_DAC_OFF_VALUE 214 * * * } AUDIO_DAC_SAMPLE_MAX/100 215 * --------------------------------------------------------- 216 * * 217 * * * 218 * * * 219 * =====*=*================================================= 0x0 220 */ 221 if (((sample_p[s] + (AUDIO_DAC_SAMPLE_MAX / 100)) > AUDIO_DAC_OFF_VALUE) && // value approaches from below 222 (sample_p[s] < (AUDIO_DAC_OFF_VALUE + (AUDIO_DAC_SAMPLE_MAX / 100))) // or above 223 ) { 224 if ((OUTPUT_SHOULD_START == state) && (active_tones_snapshot_length > 0)) { 225 state = OUTPUT_RUN_NORMALLY; 226 } else if (OUTPUT_TONES_CHANGED == state) { 227 state = OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE; 228 } else if (OUTPUT_SHOULD_STOP == state) { 229 state = OUTPUT_REACHED_ZERO_BEFORE_OFF; 230 } 231 } 232 233 // still 'ramping up', reset the output to OFF_VALUE until the generated values reach that value, to do a smooth handover 234 if (OUTPUT_SHOULD_START == state) { 235 sample_p[s] = AUDIO_DAC_OFF_VALUE; 236 } 237 238 if ((OUTPUT_SHOULD_START == state) || (OUTPUT_REACHED_ZERO_BEFORE_OFF == state) || (OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE == state)) { 239 uint8_t active_tones = MIN(AUDIO_MAX_SIMULTANEOUS_TONES, audio_get_number_of_active_tones()); 240 active_tones_snapshot_length = 0; 241 // update the snapshot - once, and only on occasion that something changed; 242 // -> saves cpu cycles (?) 243 for (uint8_t i = 0; i < active_tones; i++) { 244 float freq = audio_get_processed_frequency(i); 245 if (freq > 0) { // disregard 'rest' notes, with valid frequency 0.0f; which would only lower the resulting waveform volume during the additive synthesis step 246 active_tones_snapshot[active_tones_snapshot_length++] = freq; 247 } 248 } 249 250 if ((0 == active_tones_snapshot_length) && (OUTPUT_REACHED_ZERO_BEFORE_OFF == state)) { 251 state = OUTPUT_OFF; 252 } 253 if (OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE == state) { 254 state = OUTPUT_RUN_NORMALLY; 255 } 256 } 257 } 258 259 // update audio internal state (note position, current_note, ...) 260 if (audio_update_state()) { 261 if (OUTPUT_SHOULD_STOP != state) { 262 state = OUTPUT_TONES_CHANGED; 263 } 264 } 265 266 if (OUTPUT_OFF <= state) { 267 if (OUTPUT_OFF_2 == state) { 268 // stopping timer6 = stopping the DAC at whatever value it is currently pushing to the output = AUDIO_DAC_OFF_VALUE 269 gptStopTimer(&GPTD6); 270 } else { 271 state++; 272 } 273 } 274 } 275 276 static void dac_error(DACDriver *dacp, dacerror_t err) { 277 (void)dacp; 278 (void)err; 279 280 chSysHalt("DAC failure. halp"); 281 } 282 283 static const GPTConfig gpt6cfg1 = {.frequency = AUDIO_DAC_SAMPLE_RATE * 3, 284 .callback = NULL, 285 .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */ 286 .dier = 0U}; 287 288 static const DACConfig dac_conf = {.init = AUDIO_DAC_OFF_VALUE, .datamode = DAC_DHRM_12BIT_RIGHT}; 289 290 /** 291 * @note The DAC_TRG(0) here selects the Timer 6 TRGO event, which is triggered 292 * on the rising edge after 3 APB1 clock cycles, causing our gpt6cfg1.frequency 293 * to be a third of what we expect. 294 * 295 * Here are all the values for DAC_TRG (TSEL in the ref manual) 296 * TIM15_TRGO 0b011 297 * TIM2_TRGO 0b100 298 * TIM3_TRGO 0b001 299 * TIM6_TRGO 0b000 300 * TIM7_TRGO 0b010 301 * EXTI9 0b110 302 * SWTRIG 0b111 303 */ 304 static const DACConversionGroup dac_conv_cfg = {.num_channels = 1U, .end_cb = dac_end, .error_cb = dac_error, .trigger = DAC_TRG(0b000)}; 305 306 void audio_driver_initialize_impl(void) { 307 if ((AUDIO_PIN == A4) || (AUDIO_PIN_ALT == A4)) { 308 palSetLineMode(A4, PAL_MODE_INPUT_ANALOG); 309 dacStart(&DACD1, &dac_conf); 310 } 311 if ((AUDIO_PIN == A5) || (AUDIO_PIN_ALT == A5)) { 312 palSetLineMode(A5, PAL_MODE_INPUT_ANALOG); 313 dacStart(&DACD2, &dac_conf); 314 } 315 316 /* enable the output buffer, to directly drive external loads with no additional circuitry 317 * 318 * see: AN4566 Application note: Extending the DAC performance of STM32 microcontrollers 319 * Note: Buffer-Off bit -> has to be set 0 to enable the output buffer 320 * Note: enabling the output buffer imparts an additional dc-offset of a couple mV 321 * 322 * this is done here, reaching directly into the stm32 registers since chibios has not implemented BOFF handling yet 323 * (see: chibios/os/hal/ports/STM32/todo.txt '- BOFF handling in DACv1.' 324 */ 325 DACD1.params->dac->CR &= ~DAC_CR_BOFF1; 326 DACD2.params->dac->CR &= ~DAC_CR_BOFF2; 327 328 /* Start the DAC output with all off values. This buffer will then get fed 329 * with samples from dac_end, which will play notes. 330 */ 331 for (size_t i = 0; i < AUDIO_DAC_BUFFER_SIZE; i++) { 332 dac_buffer[i] = AUDIO_DAC_OFF_VALUE; 333 } 334 335 if (AUDIO_PIN == A4) { 336 dacStartConversion(&DACD1, &dac_conv_cfg, dac_buffer, AUDIO_DAC_BUFFER_SIZE); 337 } else if (AUDIO_PIN == A5) { 338 dacStartConversion(&DACD2, &dac_conv_cfg, dac_buffer, AUDIO_DAC_BUFFER_SIZE); 339 } 340 341 // no inverted/out-of-phase waveform (yet?), only pulling AUDIO_PIN_ALT to AUDIO_DAC_OFF_VALUE 342 #if defined(AUDIO_PIN_ALT_AS_NEGATIVE) 343 if (AUDIO_PIN_ALT == A4) { 344 dacPutChannelX(&DACD1, 0, AUDIO_DAC_OFF_VALUE); 345 } else if (AUDIO_PIN_ALT == A5) { 346 dacPutChannelX(&DACD2, 0, AUDIO_DAC_OFF_VALUE); 347 } 348 #endif 349 350 gptStart(&GPTD6, &gpt6cfg1); 351 } 352 353 void audio_driver_stop_impl(void) { 354 state = OUTPUT_SHOULD_STOP; 355 } 356 357 void audio_driver_start_impl(void) { 358 gptStartContinuous(&GPTD6, 2U); 359 360 for (uint8_t i = 0; i < AUDIO_MAX_SIMULTANEOUS_TONES; i++) { 361 dac_if[i] = 0.0f; 362 active_tones_snapshot[i] = 0.0f; 363 } 364 active_tones_snapshot_length = 0; 365 state = OUTPUT_SHOULD_START; 366 } 367 368 #pragma GCC diagnostic pop