заготовка для играния мелодий
This commit is contained in:
107
Core/Clock/melody.c
Normal file
107
Core/Clock/melody.c
Normal file
@@ -0,0 +1,107 @@
|
||||
#include "melody.h"
|
||||
|
||||
#define FIXED_ARR 10 // фиксированный период таймера
|
||||
|
||||
// Конвертирует длительность ноты в миллисекунды
|
||||
static uint32_t _duration_to_ms(MelodyHandle* mh, float duration) {
|
||||
float quarter_sec = 60.0f / mh->bpm; // четверть в секундах
|
||||
float whole_sec = quarter_sec * 4.0f; // целая нота в секундах
|
||||
float note_sec = whole_sec * duration; // нота в секундах
|
||||
return (uint32_t)(note_sec * 1000.0f); // в миллисекунды
|
||||
}
|
||||
|
||||
// Устанавливает частоту на выходе PWM
|
||||
static void _set_freq(MelodyHandle* mh, uint32_t freq) {
|
||||
if (freq == 0) {
|
||||
// Выключаем звук - просто CCR = 0
|
||||
__HAL_TIM_SET_COMPARE(mh->htim, mh->channel, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// Считаем предделитель: PSC = F_timer / freq / (ARR+1)
|
||||
// ARR+1 = 11, так как ARR = 10
|
||||
uint32_t psc = mh->timer_clock_hz / freq / (FIXED_ARR + 1);
|
||||
|
||||
// Ограничиваем диапазоном 1..65535
|
||||
if (psc < 1) psc = 1;
|
||||
if (psc > 65535) psc = 65535;
|
||||
|
||||
// Устанавливаем prescaler (вычитаем 1, т.к. счётчик от 0 до psc-1)
|
||||
__HAL_TIM_SET_PRESCALER(mh->htim, psc - 1);
|
||||
// Фиксированный период
|
||||
__HAL_TIM_SET_AUTORELOAD(mh->htim, FIXED_ARR);
|
||||
// Скважность 50% для чистого тона
|
||||
__HAL_TIM_SET_COMPARE(mh->htim, mh->channel, FIXED_ARR / 2);
|
||||
}
|
||||
|
||||
void Melody_Init(MelodyHandle* mh, TIM_HandleTypeDef* htim, uint32_t channel, uint32_t timer_clock_hz) {
|
||||
mh->htim = htim;
|
||||
mh->channel = channel;
|
||||
mh->timer_clock_hz = timer_clock_hz;
|
||||
mh->sequence = NULL;
|
||||
mh->length = 0;
|
||||
mh->current_index = 0;
|
||||
mh->note_start_time = 0;
|
||||
mh->is_playing = 0;
|
||||
|
||||
// Настраиваем таймер на фиксированный период
|
||||
__HAL_TIM_SET_AUTORELOAD(mh->htim, FIXED_ARR);
|
||||
__HAL_TIM_SET_COMPARE(mh->htim, mh->channel, 0);
|
||||
|
||||
// Запускаем PWM один раз
|
||||
HAL_TIM_PWM_Start(mh->htim, mh->channel);
|
||||
}
|
||||
|
||||
void Melody_SetBPM(MelodyHandle* mh, uint16_t bpm) {
|
||||
mh->bpm = bpm;
|
||||
}
|
||||
|
||||
void Melody_Play(MelodyHandle* mh, const Note* sequence, uint16_t length, uint16_t bpm) {
|
||||
if (mh->is_playing) {
|
||||
_set_freq(mh, 0);
|
||||
}
|
||||
|
||||
mh->sequence = sequence;
|
||||
mh->length = length;
|
||||
mh->current_index = 0;
|
||||
mh->is_playing = 1;
|
||||
mh->note_start_time = HAL_GetTick();
|
||||
mh->bpm = bpm;
|
||||
|
||||
if (length > 0 && sequence[0].freq != 0) {
|
||||
_set_freq(mh, sequence[0].freq);
|
||||
}
|
||||
}
|
||||
|
||||
void Melody_Stop(MelodyHandle* mh) {
|
||||
mh->is_playing = 0;
|
||||
_set_freq(mh, 0);
|
||||
}
|
||||
|
||||
void Melody_Update(MelodyHandle* mh) {
|
||||
if (!mh->is_playing) return;
|
||||
|
||||
uint32_t now = HAL_GetTick();
|
||||
uint32_t dur_ms = _duration_to_ms(mh, mh->sequence[mh->current_index].duration);
|
||||
|
||||
if (now - mh->note_start_time >= dur_ms) {
|
||||
mh->current_index++;
|
||||
|
||||
if (mh->current_index >= mh->length) {
|
||||
Melody_Stop(mh);
|
||||
return;
|
||||
}
|
||||
|
||||
mh->note_start_time = now;
|
||||
|
||||
if (mh->sequence[mh->current_index].freq == NOTE_REST) {
|
||||
__HAL_TIM_SET_COMPARE(mh->htim, mh->channel, 0);
|
||||
} else {
|
||||
_set_freq(mh, mh->sequence[mh->current_index].freq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t Melody_IsPlaying(MelodyHandle* mh) {
|
||||
return mh->is_playing;
|
||||
}
|
||||
Reference in New Issue
Block a user