diff options
Diffstat (limited to 'quantum/audio/driver_chibios_pwm_software.c')
-rw-r--r-- | quantum/audio/driver_chibios_pwm_software.c | 164 |
1 files changed, 0 insertions, 164 deletions
diff --git a/quantum/audio/driver_chibios_pwm_software.c b/quantum/audio/driver_chibios_pwm_software.c deleted file mode 100644 index 15c3e98b6..000000000 --- a/quantum/audio/driver_chibios_pwm_software.c +++ /dev/null | |||
@@ -1,164 +0,0 @@ | |||
1 | /* Copyright 2020 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 | /* | ||
19 | Audio Driver: PWM | ||
20 | |||
21 | the duty-cycle is always kept at 50%, and the pwm-period is adjusted to match the frequency of a note to be played back. | ||
22 | |||
23 | this driver uses the chibios-PWM system to produce a square-wave on any given output pin in software | ||
24 | - a pwm callback is used to set/clear the configured pin. | ||
25 | |||
26 | */ | ||
27 | #include "audio.h" | ||
28 | #include "ch.h" | ||
29 | #include "hal.h" | ||
30 | |||
31 | #if !defined(AUDIO_PIN) | ||
32 | # error "Audio feature enabled, but no pin selected - see docs/feature_audio under the ARM PWM settings" | ||
33 | #endif | ||
34 | extern bool playing_note; | ||
35 | extern bool playing_melody; | ||
36 | extern uint8_t note_timbre; | ||
37 | |||
38 | static void pwm_audio_period_callback(PWMDriver *pwmp); | ||
39 | static void pwm_audio_channel_interrupt_callback(PWMDriver *pwmp); | ||
40 | |||
41 | static PWMConfig pwmCFG = { | ||
42 | .frequency = 100000, /* PWM clock frequency */ | ||
43 | // CHIBIOS-BUG? can't set the initial period to <2, or the pwm (hard or software) takes ~130ms with .frequency=500000 for a pwmChangePeriod to take effect; with no output=silence in the meantime | ||
44 | .period = 2, /* initial PWM period (in ticks) 1S (1/10kHz=0.1mS 0.1ms*10000 ticks=1S) */ | ||
45 | .callback = pwm_audio_period_callback, | ||
46 | .channels = | ||
47 | { | ||
48 | // software-PWM just needs another callback on any channel | ||
49 | {PWM_OUTPUT_ACTIVE_HIGH, pwm_audio_channel_interrupt_callback}, /* channel 0 -> TIMx_CH1 */ | ||
50 | {PWM_OUTPUT_DISABLED, NULL}, /* channel 1 -> TIMx_CH2 */ | ||
51 | {PWM_OUTPUT_DISABLED, NULL}, /* channel 2 -> TIMx_CH3 */ | ||
52 | {PWM_OUTPUT_DISABLED, NULL} /* channel 3 -> TIMx_CH4 */ | ||
53 | }, | ||
54 | }; | ||
55 | |||
56 | static float channel_1_frequency = 0.0f; | ||
57 | void channel_1_set_frequency(float freq) { | ||
58 | channel_1_frequency = freq; | ||
59 | |||
60 | if (freq <= 0.0) // a pause/rest has freq=0 | ||
61 | return; | ||
62 | |||
63 | pwmcnt_t period = (pwmCFG.frequency / freq); | ||
64 | pwmChangePeriod(&AUDIO_PWM_DRIVER, period); | ||
65 | |||
66 | pwmEnableChannel(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1, | ||
67 | // adjust the duty-cycle so that the output is for 'note_timbre' duration HIGH | ||
68 | PWM_PERCENTAGE_TO_WIDTH(&AUDIO_PWM_DRIVER, (100 - note_timbre) * 100)); | ||
69 | } | ||
70 | |||
71 | float channel_1_get_frequency(void) { return channel_1_frequency; } | ||
72 | |||
73 | void channel_1_start(void) { | ||
74 | pwmStop(&AUDIO_PWM_DRIVER); | ||
75 | pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG); | ||
76 | |||
77 | pwmEnablePeriodicNotification(&AUDIO_PWM_DRIVER); | ||
78 | pwmEnableChannelNotification(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1); | ||
79 | } | ||
80 | |||
81 | void channel_1_stop(void) { | ||
82 | pwmStop(&AUDIO_PWM_DRIVER); | ||
83 | |||
84 | palClearLine(AUDIO_PIN); // leave the line low, after last note was played | ||
85 | |||
86 | #if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE) | ||
87 | palClearLine(AUDIO_PIN_ALT); // leave the line low, after last note was played | ||
88 | #endif | ||
89 | } | ||
90 | |||
91 | // generate a PWM signal on any pin, not necessarily the one connected to the timer | ||
92 | static void pwm_audio_period_callback(PWMDriver *pwmp) { | ||
93 | (void)pwmp; | ||
94 | palClearLine(AUDIO_PIN); | ||
95 | |||
96 | #if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE) | ||
97 | palSetLine(AUDIO_PIN_ALT); | ||
98 | #endif | ||
99 | } | ||
100 | static void pwm_audio_channel_interrupt_callback(PWMDriver *pwmp) { | ||
101 | (void)pwmp; | ||
102 | if (channel_1_frequency > 0) { | ||
103 | palSetLine(AUDIO_PIN); // generate a PWM signal on any pin, not necessarily the one connected to the timer | ||
104 | #if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE) | ||
105 | palClearLine(AUDIO_PIN_ALT); | ||
106 | #endif | ||
107 | } | ||
108 | } | ||
109 | |||
110 | static void gpt_callback(GPTDriver *gptp); | ||
111 | GPTConfig gptCFG = { | ||
112 | /* a whole note is one beat, which is - per definition in musical_notes.h - set to 64 | ||
113 | the longest note is BREAVE_DOT=128+64=192, the shortest SIXTEENTH=4 | ||
114 | the tempo (which might vary!) is in bpm (beats per minute) | ||
115 | therefore: if the timer ticks away at .frequency = (60*64)Hz, | ||
116 | and the .interval counts from 64 downwards - audio_update_state is | ||
117 | called just often enough to not miss anything | ||
118 | */ | ||
119 | .frequency = 60 * 64, | ||
120 | .callback = gpt_callback, | ||
121 | }; | ||
122 | |||
123 | void audio_driver_initialize(void) { | ||
124 | pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG); | ||
125 | |||
126 | palSetLineMode(AUDIO_PIN, PAL_MODE_OUTPUT_PUSHPULL); | ||
127 | palClearLine(AUDIO_PIN); | ||
128 | |||
129 | #if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE) | ||
130 | palSetLineMode(AUDIO_PIN_ALT, PAL_MODE_OUTPUT_PUSHPULL); | ||
131 | palClearLine(AUDIO_PIN_ALT); | ||
132 | #endif | ||
133 | |||
134 | pwmEnablePeriodicNotification(&AUDIO_PWM_DRIVER); // enable pwm callbacks | ||
135 | pwmEnableChannelNotification(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1); | ||
136 | |||
137 | gptStart(&AUDIO_STATE_TIMER, &gptCFG); | ||
138 | } | ||
139 | |||
140 | void audio_driver_start(void) { | ||
141 | channel_1_stop(); | ||
142 | channel_1_start(); | ||
143 | |||
144 | if (playing_note || playing_melody) { | ||
145 | gptStartContinuous(&AUDIO_STATE_TIMER, 64); | ||
146 | } | ||
147 | } | ||
148 | |||
149 | void audio_driver_stop(void) { | ||
150 | channel_1_stop(); | ||
151 | gptStopTimer(&AUDIO_STATE_TIMER); | ||
152 | } | ||
153 | |||
154 | /* a regular timer task, that checks the note to be currently played | ||
155 | * and updates the pwm to output that frequency | ||
156 | */ | ||
157 | static void gpt_callback(GPTDriver *gptp) { | ||
158 | float freq; // TODO: freq_alt | ||
159 | |||
160 | if (audio_update_state()) { | ||
161 | freq = audio_get_processed_frequency(0); // freq_alt would be index=1 | ||
162 | channel_1_set_frequency(freq); | ||
163 | } | ||
164 | } | ||