#include "audio.h"
#include "ch.h"
#include "hal.h"
#if !defined(AUDIO_PIN)
# error "Audio feature enabled, but no pin selected - see docs/feature_audio under the ARM PWM settings"
#endif
extern bool playing_note;
extern bool playing_melody;
extern uint8_t note_timbre;
static void pwm_audio_period_callback(PWMDriver *pwmp);
static void pwm_audio_channel_interrupt_callback(PWMDriver *pwmp);
static PWMConfig pwmCFG = {
.frequency = 100000,
.period = 2,
.callback = pwm_audio_period_callback,
.channels =
{
{PWM_OUTPUT_ACTIVE_HIGH, pwm_audio_channel_interrupt_callback},
{PWM_OUTPUT_DISABLED, NULL},
{PWM_OUTPUT_DISABLED, NULL},
{PWM_OUTPUT_DISABLED, NULL}
},
};
static float channel_1_frequency = 0.0f;
void channel_1_set_frequency(float freq) {
channel_1_frequency = freq;
if (freq <= 0.0) return;
pwmcnt_t period = (pwmCFG.frequency / freq);
pwmChangePeriod(&AUDIO_PWM_DRIVER, period);
pwmEnableChannel(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1,
PWM_PERCENTAGE_TO_WIDTH(&AUDIO_PWM_DRIVER, (100 - note_timbre) * 100));
}
float channel_1_get_frequency(void) { return channel_1_frequency; }
void channel_1_start(void) {
pwmStop(&AUDIO_PWM_DRIVER);
pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG);
pwmEnablePeriodicNotification(&AUDIO_PWM_DRIVER);
pwmEnableChannelNotification(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1);
}
void channel_1_stop(void) {
pwmStop(&AUDIO_PWM_DRIVER);
palClearLine(AUDIO_PIN);
#if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE)
palClearLine(AUDIO_PIN_ALT); #endif
}
static void pwm_audio_period_callback(PWMDriver *pwmp) {
(void)pwmp;
palClearLine(AUDIO_PIN);
#if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE)
palSetLine(AUDIO_PIN_ALT);
#endif
}
static void pwm_audio_channel_interrupt_callback(PWMDriver *pwmp) {
(void)pwmp;
if (channel_1_frequency > 0) {
palSetLine(AUDIO_PIN); #if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE)
palClearLine(AUDIO_PIN_ALT);
#endif
}
}
static void gpt_callback(GPTDriver *gptp);
GPTConfig gptCFG = {
.frequency = 60 * 64,
.callback = gpt_callback,
};
void audio_driver_initialize(void) {
pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG);
palSetLineMode(AUDIO_PIN, PAL_MODE_OUTPUT_PUSHPULL);
palClearLine(AUDIO_PIN);
#if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE)
palSetLineMode(AUDIO_PIN_ALT, PAL_MODE_OUTPUT_PUSHPULL);
palClearLine(AUDIO_PIN_ALT);
#endif
pwmEnablePeriodicNotification(&AUDIO_PWM_DRIVER); pwmEnableChannelNotification(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1);
gptStart(&AUDIO_STATE_TIMER, &gptCFG);
}
void audio_driver_start(void) {
channel_1_stop();
channel_1_start();
if (playing_note || playing_melody) {
gptStartContinuous(&AUDIO_STATE_TIMER, 64);
}
}
void audio_driver_stop(void) {
channel_1_stop();
gptStopTimer(&AUDIO_STATE_TIMER);
}
static void gpt_callback(GPTDriver *gptp) {
float freq;
if (audio_update_state()) {
freq = audio_get_processed_frequency(0); channel_1_set_frequency(freq);
}
}