aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael L. Walker <michael.l.walker@intel.com>2018-04-12 08:16:35 -0700
committerJack Humbert <jack.humb@gmail.com>2018-04-12 13:14:48 -0400
commit22215a0e920a237faabdfa1b8826cd110ab20c3b (patch)
tree4681e0cb65cbc69f3bed16165de620dc6511d653
parent5319667c5504ab28b5bdb6f81ae22ecfec5e4a46 (diff)
downloadqmk_firmware-22215a0e920a237faabdfa1b8826cd110ab20c3b.tar.gz
qmk_firmware-22215a0e920a237faabdfa1b8826cd110ab20c3b.zip
Added support for audio using pins C4, C5, B6, B7
-rw-r--r--docs/config_options.md10
-rw-r--r--docs/feature_audio.md13
-rw-r--r--quantum/audio/audio.c188
3 files changed, 135 insertions, 76 deletions
diff --git a/docs/config_options.md b/docs/config_options.md
index af8602c3f..d45592f82 100644
--- a/docs/config_options.md
+++ b/docs/config_options.md
@@ -61,10 +61,18 @@ This is a C header file that is one of the first things included, and will persi
61 * COL2ROW or ROW2COL - how your matrix is configured. COL2ROW means the black mark on your diode is facing to the rows, and between the switch and the rows. 61 * COL2ROW or ROW2COL - how your matrix is configured. COL2ROW means the black mark on your diode is facing to the rows, and between the switch and the rows.
62* `#define AUDIO_VOICES` 62* `#define AUDIO_VOICES`
63 * turns on the alternate audio voices (to cycle through) 63 * turns on the alternate audio voices (to cycle through)
64* `#define C4_AUDIO`
65 * enables audio on pin C4
66* `#define C5_AUDIO`
67 * enables audio on pin C5
64* `#define C6_AUDIO` 68* `#define C6_AUDIO`
65 * enables audio on pin C6 69 * enables audio on pin C6
66* `#define B5_AUDIO` 70* `#define B5_AUDIO`
67 * enables audio on pin B5 (duophony is enable if both are enabled) 71 * enables audio on pin B5 (duophony is enables if one of B[5-7]_AUDIO is enabled along with one of C[4-6]_AUDIO)
72* `#define B6_AUDIO`
73 * enables audio on pin B6 (duophony is enables if one of B[5-7]_AUDIO is enabled along with one of C[4-6]_AUDIO)
74* `#define B7_AUDIO`
75 * enables audio on pin B7 (duophony is enables if one of B[5-7]_AUDIO is enabled along with one of C[4-6]_AUDIO)
68* `#define BACKLIGHT_PIN B7` 76* `#define BACKLIGHT_PIN B7`
69 * pin of the backlight - B5, B6, B7 use PWM, others use softPWM 77 * pin of the backlight - B5, B6, B7 use PWM, others use softPWM
70* `#define BACKLIGHT_LEVELS 3` 78* `#define BACKLIGHT_LEVELS 3`
diff --git a/docs/feature_audio.md b/docs/feature_audio.md
index 5b11aa3ab..eaaa2fe51 100644
--- a/docs/feature_audio.md
+++ b/docs/feature_audio.md
@@ -1,6 +1,17 @@
1# Audio 1# Audio
2 2
3Your keyboard can make sounds! If you've got a Planck, Preonic, or basically any AVR keyboard that allows access to the C6 or B5 port (`#define C6_AUDIO` and/or `#define B5_AUDIO`), you can hook up a simple speaker and make it beep. You can use those beeps to indicate layer transitions, modifiers, special keys, or just to play some funky 8bit tunes. 3Your keyboard can make sounds! If you've got a Planck, Preonic, or basically any AVR keyboard that allows access to certain PWM-capable pins, you can hook up a simple speaker and make it beep. You can use those beeps to indicate layer transitions, modifiers, special keys, or just to play some funky 8bit tunes.
4
5Up to two simultaneous audio voices are supported, one driven by timer 1 and another driven by timer 3. The following pins can be defined as audio outputs in config.h:
6Timer 1:
7`#define B5_AUDIO`
8`#define B6_AUDIO`
9`#define B7_AUDIO`
10
11Timer 3:
12`#define C4_AUDIO`
13`#define C5_AUDIO`
14`#define C6_AUDIO`
4 15
5If you add `AUDIO_ENABLE = yes` to your `rules.mk`, there's a couple different sounds that will automatically be enabled without any other configuration: 16If you add `AUDIO_ENABLE = yes` to your `rules.mk`, there's a couple different sounds that will automatically be enabled without any other configuration:
6 17
diff --git a/quantum/audio/audio.c b/quantum/audio/audio.c
index 85220e163..3c6d18c4f 100644
--- a/quantum/audio/audio.c
+++ b/quantum/audio/audio.c
@@ -35,44 +35,81 @@
35// Timer Abstractions 35// Timer Abstractions
36// ----------------------------------------------------------------------------- 36// -----------------------------------------------------------------------------
37 37
38// TIMSK3 - Timer/Counter #3 Interrupt Mask Register 38//Currently we support timers 1 and 3 used at the sime time, channels A-C,
39// Turn on/off 3A interputs, stopping/enabling the ISR calls 39//pins PB5, PB6, PB7, PC4, PC5, and PC6
40#ifdef C6_AUDIO 40#if defined(C6_AUDIO)
41 #define CPIN_AUDIO
42 #define CPIN_SET_DIRECTION DDRC |= _BV(PORTC6);
43 #define INIT_AUDIO_COUNTER_3 TCCR3A = (0 << COM3A1) | (0 << COM3A0) | (1 << WGM31) | (0 << WGM30);
41 #define ENABLE_AUDIO_COUNTER_3_ISR TIMSK3 |= _BV(OCIE3A) 44 #define ENABLE_AUDIO_COUNTER_3_ISR TIMSK3 |= _BV(OCIE3A)
42 #define DISABLE_AUDIO_COUNTER_3_ISR TIMSK3 &= ~_BV(OCIE3A) 45 #define DISABLE_AUDIO_COUNTER_3_ISR TIMSK3 &= ~_BV(OCIE3A)
43#endif
44
45#ifdef B5_AUDIO
46 #define ENABLE_AUDIO_COUNTER_1_ISR TIMSK1 |= _BV(OCIE1A)
47 #define DISABLE_AUDIO_COUNTER_1_ISR TIMSK1 &= ~_BV(OCIE1A)
48#endif
49
50// TCCR3A: Timer/Counter #3 Control Register
51// Compare Output Mode (COM3An) = 0b00 = Normal port operation, OC3A disconnected from PC6
52
53#ifdef C6_AUDIO
54 #define ENABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A |= _BV(COM3A1); 46 #define ENABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A |= _BV(COM3A1);
55 #define DISABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A &= ~(_BV(COM3A1) | _BV(COM3A0)); 47 #define DISABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A &= ~(_BV(COM3A1) | _BV(COM3A0));
48 #define TIMER_3_PERIOD ICR3
49 #define TIMER_3_DUTY_CYCLE OCR3A
50 #define TIMER3_AUDIO_vect TIMER3_COMPA_vect
56#endif 51#endif
57 52#if defined(C5_AUDIO)
58#ifdef B5_AUDIO 53 #define CPIN_AUDIO
59 #define ENABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A |= _BV(COM1A1); 54 #define CPIN_SET_DIRECTION DDRC |= _BV(PORTC5);
60 #define DISABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A &= ~(_BV(COM1A1) | _BV(COM1A0)); 55 #define INIT_AUDIO_COUNTER_3 TCCR3A = (0 << COM3B1) | (0 << COM3B0) | (1 << WGM31) | (0 << WGM30);
56 #define ENABLE_AUDIO_COUNTER_3_ISR TIMSK3 |= _BV(OCIE3B)
57 #define DISABLE_AUDIO_COUNTER_3_ISR TIMSK3 &= ~_BV(OCIE3B)
58 #define ENABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A |= _BV(COM3B1);
59 #define DISABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A &= ~(_BV(COM3B1) | _BV(COM3B0));
60 #define TIMER_3_PERIOD ICR3
61 #define TIMER_3_DUTY_CYCLE OCR3B
62 #define TIMER3_AUDIO_vect TIMER3_COMPB_vect
61#endif 63#endif
62 64#if defined(C4_AUDIO)
63// Fast PWM Mode Controls 65 #define CPIN_AUDIO
64 66 #define CPIN_SET_DIRECTION DDRC |= _BV(PORTC4);
65#ifdef C6_AUDIO 67 #define INIT_AUDIO_COUNTER_3 TCCR3A = (0 << COM3C1) | (0 << COM3C0) | (1 << WGM31) | (0 << WGM30);
68 #define ENABLE_AUDIO_COUNTER_3_ISR TIMSK3 |= _BV(OCIE3C)
69 #define DISABLE_AUDIO_COUNTER_3_ISR TIMSK3 &= ~_BV(OCIE3C)
70 #define ENABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A |= _BV(COM3C1);
71 #define DISABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A &= ~(_BV(COM3C1) | _BV(COM3C0));
66 #define TIMER_3_PERIOD ICR3 72 #define TIMER_3_PERIOD ICR3
67 #define TIMER_3_DUTY_CYCLE OCR3A 73 #define TIMER_3_DUTY_CYCLE OCR3C
74 #define TIMER3_AUDIO_vect TIMER3_COMPC_vect
68#endif 75#endif
69 76
70#ifdef B5_AUDIO 77#if defined(B5_AUDIO)
78 #define BPIN_AUDIO
79 #define BPIN_SET_DIRECTION DDRC |= _BV(PORTB5);
80 #define INIT_AUDIO_COUNTER_1 TCCR1A = (0 << COM1A1) | (0 << COM1A0) | (1 << WGM11) | (0 << WGM10);
81 #define ENABLE_AUDIO_COUNTER_1_ISR TIMSK1 |= _BV(OCIE1A)
82 #define DISABLE_AUDIO_COUNTER_1_ISR TIMSK1 &= ~_BV(OCIE1A)
83 #define ENABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A |= _BV(COM1A1);
84 #define DISABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A &= ~(_BV(COM1A1) | _BV(COM1A0));
71 #define TIMER_1_PERIOD ICR1 85 #define TIMER_1_PERIOD ICR1
72 #define TIMER_1_DUTY_CYCLE OCR1A 86 #define TIMER_1_DUTY_CYCLE OCR1A
87 #define TIMER1_AUDIO_vect TIMER1_COMPA_vect
88#endif
89#if defined(B6_AUDIO)
90 #define BPIN_AUDIO
91 #define BPIN_SET_DIRECTION DDRC |= _BV(PORTB6);
92 #define INIT_AUDIO_COUNTER_1 TCCR1A = (0 << COM1B1) | (0 << COM1B0) | (1 << WGM11) | (0 << WGM10);
93 #define ENABLE_AUDIO_COUNTER_1_ISR TIMSK1 |= _BV(OCIE1B)
94 #define DISABLE_AUDIO_COUNTER_1_ISR TIMSK1 &= ~_BV(OCIE1B)
95 #define ENABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A |= _BV(COM1B1);
96 #define DISABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A &= ~(_BV(COM1B1) | _BV(COM1B0));
97 #define TIMER_1_PERIOD ICR1
98 #define TIMER_1_DUTY_CYCLE OCR1B
99 #define TIMER1_AUDIO_vect TIMER1_COMPB_vect
100#endif
101#if defined(B7_AUDIO)
102 #define BPIN_AUDIO
103 #define BPIN_SET_DIRECTION DDRC |= _BV(PORTB7);
104 #define INIT_AUDIO_COUNTER_1 TCCR1A = (0 << COM1C1) | (0 << COM1C0) | (1 << WGM11) | (0 << WGM10);
105 #define ENABLE_AUDIO_COUNTER_1_ISR TIMSK1 |= _BV(OCIE1C)
106 #define DISABLE_AUDIO_COUNTER_1_ISR TIMSK1 &= ~_BV(OCIE1C)
107 #define ENABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A |= _BV(COM1C1);
108 #define DISABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A &= ~(_BV(COM1C1) | _BV(COM1C0));
109 #define TIMER_1_PERIOD ICR1
110 #define TIMER_1_DUTY_CYCLE OCR1C
111 #define TIMER1_AUDIO_vect TIMER1_COMPC_vect
73#endif 112#endif
74
75
76// ----------------------------------------------------------------------------- 113// -----------------------------------------------------------------------------
77 114
78 115
@@ -147,47 +184,51 @@ void audio_init()
147 184
148 if (!audio_initialized) { 185 if (!audio_initialized) {
149 186
150 // Set port PC6 (OC3A and /OC4A) as output 187 // Set audio ports as output
151 188 #ifdef CPIN_AUDIO
152 #ifdef C6_AUDIO 189 CPIN_SET_DIRECTION
153 DDRC |= _BV(PORTC6);
154 //#else
155 // DDRC |= _BV(PORTC6); // Why is PC6 being set as output low, if C6_audio isn't defined?
156 // PORTC &= ~_BV(PORTC6);
157 #endif 190 #endif
158 191 #ifdef BPIN_AUDIO
159 #ifdef B5_AUDIO 192 BPIN_SET_DIRECTION
160 DDRB |= _BV(PORTB5);
161 //#else
162 // DDRB |= _BV(PORTB5); // Same as with PC6
163 // PORTB &= ~_BV(PORTB5);
164 #endif 193 #endif
165 194
166 #ifdef C6_AUDIO 195 #ifdef CPIN_AUDIO
167 DISABLE_AUDIO_COUNTER_3_ISR; 196 DISABLE_AUDIO_COUNTER_3_ISR;
168 #endif 197 #endif
169 198 #ifdef BPIN_AUDIO
170 #ifdef B5_AUDIO
171 DISABLE_AUDIO_COUNTER_1_ISR; 199 DISABLE_AUDIO_COUNTER_1_ISR;
172 #endif 200 #endif
173 201
174 // TCCR3A / TCCR3B: Timer/Counter #3 Control Registers 202 // TCCR3A / TCCR3B: Timer/Counter #3 Control Registers TCCR3A/TCCR3B, TCCR1A/TCCR1B
175 // Compare Output Mode (COM3An) = 0b00 = Normal port operation, OC3A disconnected from PC6 203 // Compare Output Mode (COM3An and COM1An) = 0b00 = Normal port operation
176 // Waveform Generation Mode (WGM3n) = 0b1110 = Fast PWM Mode 14 (Period = ICR3, Duty Cycle = OCR3A) 204 // OC3A -- PC6
177 // Clock Select (CS3n) = 0b010 = Clock / 8 205 // OC3B -- PC5
206 // OC3C -- PC4
207 // OC1A -- PB5
208 // OC1B -- PB6
209 // OC1C -- PB7
210
211 // Waveform Generation Mode (WGM3n) = 0b1110 = Fast PWM Mode 14. Period = ICR3, Duty Cycle OCR3A)
212 // OCR3A - PC6
213 // OCR3B - PC5
214 // OCR3C - PC4
215 // OCR1A - PB5
216 // OCR1B - PB6
217 // OCR1C - PB7
178 218
179 #ifdef C6_AUDIO 219 // Clock Select (CS3n) = 0b010 = Clock / 8
180 TCCR3A = (0 << COM3A1) | (0 << COM3A0) | (1 << WGM31) | (0 << WGM30); 220 #ifdef CPIN_AUDIO
221 INIT_AUDIO_COUNTER_3
181 TCCR3B = (1 << WGM33) | (1 << WGM32) | (0 << CS32) | (1 << CS31) | (0 << CS30); 222 TCCR3B = (1 << WGM33) | (1 << WGM32) | (0 << CS32) | (1 << CS31) | (0 << CS30);
223 TIMER_3_PERIOD = (uint16_t)(((float)F_CPU) / (440 * CPU_PRESCALER));
224 TIMER_3_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (440 * CPU_PRESCALER)) * note_timbre);
182 #endif 225 #endif
183 226 #ifdef BPIN_AUDIO
184 #ifdef B5_AUDIO 227 INIT_AUDIO_COUNTER_1
185 TCCR1A = (0 << COM1A1) | (0 << COM1A0) | (1 << WGM11) | (0 << WGM10);
186 TCCR1B = (1 << WGM13) | (1 << WGM12) | (0 << CS12) | (1 << CS11) | (0 << CS10); 228 TCCR1B = (1 << WGM13) | (1 << WGM12) | (0 << CS12) | (1 << CS11) | (0 << CS10);
187
188 TIMER_1_PERIOD = (uint16_t)(((float)F_CPU) / (440 * CPU_PRESCALER)); 229 TIMER_1_PERIOD = (uint16_t)(((float)F_CPU) / (440 * CPU_PRESCALER));
189 TIMER_1_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (440 * CPU_PRESCALER)) * note_timbre); 230 TIMER_1_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (440 * CPU_PRESCALER)) * note_timbre);
190 #endif 231 #endif
191 232
192 audio_initialized = true; 233 audio_initialized = true;
193 } 234 }
@@ -207,13 +248,12 @@ void stop_all_notes()
207 } 248 }
208 voices = 0; 249 voices = 0;
209 250
210 251 #ifdef CPIN_AUDIO
211 #ifdef C6_AUDIO
212 DISABLE_AUDIO_COUNTER_3_ISR; 252 DISABLE_AUDIO_COUNTER_3_ISR;
213 DISABLE_AUDIO_COUNTER_3_OUTPUT; 253 DISABLE_AUDIO_COUNTER_3_OUTPUT;
214 #endif 254 #endif
215 255
216 #ifdef B5_AUDIO 256 #ifdef BPIN_AUDIO
217 DISABLE_AUDIO_COUNTER_1_ISR; 257 DISABLE_AUDIO_COUNTER_1_ISR;
218 DISABLE_AUDIO_COUNTER_1_OUTPUT; 258 DISABLE_AUDIO_COUNTER_1_OUTPUT;
219 #endif 259 #endif
@@ -259,11 +299,11 @@ void stop_note(float freq)
259 voice_place = 0; 299 voice_place = 0;
260 } 300 }
261 if (voices == 0) { 301 if (voices == 0) {
262 #ifdef C6_AUDIO 302 #ifdef CPIN_AUDIO
263 DISABLE_AUDIO_COUNTER_3_ISR; 303 DISABLE_AUDIO_COUNTER_3_ISR;
264 DISABLE_AUDIO_COUNTER_3_OUTPUT; 304 DISABLE_AUDIO_COUNTER_3_OUTPUT;
265 #endif 305 #endif
266 #ifdef B5_AUDIO 306 #ifdef BPIN_AUDIO
267 DISABLE_AUDIO_COUNTER_1_ISR; 307 DISABLE_AUDIO_COUNTER_1_ISR;
268 DISABLE_AUDIO_COUNTER_1_OUTPUT; 308 DISABLE_AUDIO_COUNTER_1_OUTPUT;
269 #endif 309 #endif
@@ -295,15 +335,15 @@ float vibrato(float average_freq) {
295 335
296#endif 336#endif
297 337
298#ifdef C6_AUDIO 338#ifdef CPIN_AUDIO
299ISR(TIMER3_COMPA_vect) 339ISR(TIMER3_AUDIO_vect)
300{ 340{
301 float freq; 341 float freq;
302 342
303 if (playing_note) { 343 if (playing_note) {
304 if (voices > 0) { 344 if (voices > 0) {
305 345
306 #ifdef B5_AUDIO 346 #ifdef BPIN_AUDIO
307 float freq_alt = 0; 347 float freq_alt = 0;
308 if (voices > 1) { 348 if (voices > 1) {
309 if (polyphony_rate == 0) { 349 if (polyphony_rate == 0) {
@@ -477,10 +517,10 @@ ISR(TIMER3_COMPA_vect)
477} 517}
478#endif 518#endif
479 519
480#ifdef B5_AUDIO 520#ifdef BPIN_AUDIO
481ISR(TIMER1_COMPA_vect) 521ISR(TIMER1_AUDIO_vect)
482{ 522{
483 #if defined(B5_AUDIO) && !defined(C6_AUDIO) 523 #if defined(BPIN_AUDIO) && !defined(CPIN_AUDIO)
484 float freq = 0; 524 float freq = 0;
485 525
486 if (playing_note) { 526 if (playing_note) {
@@ -627,10 +667,10 @@ void play_note(float freq, int vol) {
627 } 667 }
628 668
629 if (audio_config.enable && voices < 8) { 669 if (audio_config.enable && voices < 8) {
630 #ifdef C6_AUDIO 670 #ifdef CPIN_AUDIO
631 DISABLE_AUDIO_COUNTER_3_ISR; 671 DISABLE_AUDIO_COUNTER_3_ISR;
632 #endif 672 #endif
633 #ifdef B5_AUDIO 673 #ifdef BPIN_AUDIO
634 DISABLE_AUDIO_COUNTER_1_ISR; 674 DISABLE_AUDIO_COUNTER_1_ISR;
635 #endif 675 #endif
636 676
@@ -648,12 +688,12 @@ void play_note(float freq, int vol) {
648 voices++; 688 voices++;
649 } 689 }
650 690
651 #ifdef C6_AUDIO 691 #ifdef CPIN_AUDIO
652 ENABLE_AUDIO_COUNTER_3_ISR; 692 ENABLE_AUDIO_COUNTER_3_ISR;
653 ENABLE_AUDIO_COUNTER_3_OUTPUT; 693 ENABLE_AUDIO_COUNTER_3_OUTPUT;
654 #endif 694 #endif
655 #ifdef B5_AUDIO 695 #ifdef BPIN_AUDIO
656 #ifdef C6_AUDIO 696 #ifdef CPIN_AUDIO
657 if (voices > 1) { 697 if (voices > 1) {
658 ENABLE_AUDIO_COUNTER_1_ISR; 698 ENABLE_AUDIO_COUNTER_1_ISR;
659 ENABLE_AUDIO_COUNTER_1_OUTPUT; 699 ENABLE_AUDIO_COUNTER_1_OUTPUT;
@@ -676,10 +716,10 @@ void play_notes(float (*np)[][2], uint16_t n_count, bool n_repeat)
676 716
677 if (audio_config.enable) { 717 if (audio_config.enable) {
678 718
679 #ifdef C6_AUDIO 719 #ifdef CPIN_AUDIO
680 DISABLE_AUDIO_COUNTER_3_ISR; 720 DISABLE_AUDIO_COUNTER_3_ISR;
681 #endif 721 #endif
682 #ifdef B5_AUDIO 722 #ifdef BPIN_AUDIO
683 DISABLE_AUDIO_COUNTER_1_ISR; 723 DISABLE_AUDIO_COUNTER_1_ISR;
684 #endif 724 #endif
685 725
@@ -701,12 +741,12 @@ void play_notes(float (*np)[][2], uint16_t n_count, bool n_repeat)
701 note_position = 0; 741 note_position = 0;
702 742
703 743
704 #ifdef C6_AUDIO 744 #ifdef CPIN_AUDIO
705 ENABLE_AUDIO_COUNTER_3_ISR; 745 ENABLE_AUDIO_COUNTER_3_ISR;
706 ENABLE_AUDIO_COUNTER_3_OUTPUT; 746 ENABLE_AUDIO_COUNTER_3_OUTPUT;
707 #endif 747 #endif
708 #ifdef B5_AUDIO 748 #ifdef BPIN_AUDIO
709 #ifndef C6_AUDIO 749 #ifndef CPIN_AUDIO
710 ENABLE_AUDIO_COUNTER_1_ISR; 750 ENABLE_AUDIO_COUNTER_1_ISR;
711 ENABLE_AUDIO_COUNTER_1_OUTPUT; 751 ENABLE_AUDIO_COUNTER_1_OUTPUT;
712 #endif 752 #endif