Files
Lamps/Core/Clock/melody.c
Razvalyaev 71fc59d34d Рефакторинг меню и добавление кучи всего:
- иерархическая и универсальная структура меню, которую относительно удобно расширять и добавлять
- заготовик меню для таймера и секундомера
- работающие игры (выбить 1.00 сек, тест реакции, кликер)
2026-04-21 18:29:44 +03:00

107 lines
3.5 KiB
C

#include "melody.h"
MelodyHandle melody;
#define FIXED_ARR 1000 // фиксированный период таймера
#define FIXED_VOLUME 2 // фиксированный период таймера
// Конвертирует длительность ноты в миллисекунды
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_VOLUME);
}
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->melody = NULL;
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, Melody_t *melody, uint16_t bpm) {
if (mh->is_playing) {
_set_freq(mh, 0);
}
mh->melody = melody;
mh->current_index = 0;
mh->is_playing = 1;
mh->note_start_time = HAL_GetTick();
mh->bpm = bpm;
if (melody->length > 0 && melody->sequence[0].freq != 0) {
_set_freq(mh, melody->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->melody->sequence[mh->current_index].duration);
if (now - mh->note_start_time >= dur_ms) {
mh->current_index++;
if (mh->current_index >= mh->melody->length) {
Melody_Stop(mh);
return;
}
mh->note_start_time = now;
if (mh->melody->sequence[mh->current_index].freq == NOTE_REST) {
__HAL_TIM_SET_COMPARE(mh->htim, mh->channel, 0);
} else {
_set_freq(mh, mh->melody->sequence[mh->current_index].freq);
}
}
}
uint8_t Melody_IsPlaying(MelodyHandle* mh) {
return mh->is_playing;
}