Compare commits

...

3 Commits

Author SHA1 Message Date
Razvalyaev
71fc59d34d Рефакторинг меню и добавление кучи всего:
- иерархическая и универсальная структура меню, которую относительно удобно расширять и добавлять
- заготовик меню для таймера и секундомера
- работающие игры (выбить 1.00 сек, тест реакции, кликер)
2026-04-21 18:29:44 +03:00
Razvalyaev
7b7ab6f11d рефакторинг мелодий и добавление звуковых эффектов (не проверены) 2026-04-21 14:02:32 +03:00
Razvalyaev
ad9b951bdb Сделан хедер с мелодиями 2026-04-18 17:06:55 +03:00
12 changed files with 1391 additions and 619 deletions

View File

@@ -1,6 +1,8 @@
#include "melody.h" #include "melody.h"
#define FIXED_ARR 10 // фиксированный период таймера MelodyHandle melody;
#define FIXED_ARR 1000 // фиксированный период таймера
#define FIXED_VOLUME 2 // фиксированный период таймера
// Конвертирует длительность ноты в миллисекунды // Конвертирует длительность ноты в миллисекунды
static uint32_t _duration_to_ms(MelodyHandle* mh, float duration) { static uint32_t _duration_to_ms(MelodyHandle* mh, float duration) {
@@ -31,15 +33,14 @@ static void _set_freq(MelodyHandle* mh, uint32_t freq) {
// Фиксированный период // Фиксированный период
__HAL_TIM_SET_AUTORELOAD(mh->htim, FIXED_ARR); __HAL_TIM_SET_AUTORELOAD(mh->htim, FIXED_ARR);
// Скважность 50% для чистого тона // Скважность 50% для чистого тона
__HAL_TIM_SET_COMPARE(mh->htim, mh->channel, FIXED_ARR / 2); __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) { void Melody_Init(MelodyHandle* mh, TIM_HandleTypeDef* htim, uint32_t channel, uint32_t timer_clock_hz) {
mh->htim = htim; mh->htim = htim;
mh->channel = channel; mh->channel = channel;
mh->timer_clock_hz = timer_clock_hz; mh->timer_clock_hz = timer_clock_hz;
mh->sequence = NULL; mh->melody = NULL;
mh->length = 0;
mh->current_index = 0; mh->current_index = 0;
mh->note_start_time = 0; mh->note_start_time = 0;
mh->is_playing = 0; mh->is_playing = 0;
@@ -56,20 +57,19 @@ void Melody_SetBPM(MelodyHandle* mh, uint16_t bpm) {
mh->bpm = bpm; mh->bpm = bpm;
} }
void Melody_Play(MelodyHandle* mh, const Note* sequence, uint16_t length, uint16_t bpm) { void Melody_Play(MelodyHandle* mh, Melody_t *melody, uint16_t bpm) {
if (mh->is_playing) { if (mh->is_playing) {
_set_freq(mh, 0); _set_freq(mh, 0);
} }
mh->sequence = sequence; mh->melody = melody;
mh->length = length;
mh->current_index = 0; mh->current_index = 0;
mh->is_playing = 1; mh->is_playing = 1;
mh->note_start_time = HAL_GetTick(); mh->note_start_time = HAL_GetTick();
mh->bpm = bpm; mh->bpm = bpm;
if (length > 0 && sequence[0].freq != 0) { if (melody->length > 0 && melody->sequence[0].freq != 0) {
_set_freq(mh, sequence[0].freq); _set_freq(mh, melody->sequence[0].freq);
} }
} }
@@ -82,22 +82,22 @@ void Melody_Update(MelodyHandle* mh) {
if (!mh->is_playing) return; if (!mh->is_playing) return;
uint32_t now = HAL_GetTick(); uint32_t now = HAL_GetTick();
uint32_t dur_ms = _duration_to_ms(mh, mh->sequence[mh->current_index].duration); uint32_t dur_ms = _duration_to_ms(mh, mh->melody->sequence[mh->current_index].duration);
if (now - mh->note_start_time >= dur_ms) { if (now - mh->note_start_time >= dur_ms) {
mh->current_index++; mh->current_index++;
if (mh->current_index >= mh->length) { if (mh->current_index >= mh->melody->length) {
Melody_Stop(mh); Melody_Stop(mh);
return; return;
} }
mh->note_start_time = now; mh->note_start_time = now;
if (mh->sequence[mh->current_index].freq == NOTE_REST) { if (mh->melody->sequence[mh->current_index].freq == NOTE_REST) {
__HAL_TIM_SET_COMPARE(mh->htim, mh->channel, 0); __HAL_TIM_SET_COMPARE(mh->htim, mh->channel, 0);
} else { } else {
_set_freq(mh, mh->sequence[mh->current_index].freq); _set_freq(mh, mh->melody->sequence[mh->current_index].freq);
} }
} }
} }

View File

@@ -2,174 +2,26 @@
#define MELODY_H #define MELODY_H
#include "stm32f1xx_hal.h" #include "stm32f1xx_hal.h"
#include "songs.h"
typedef struct { #include "sounds.h"
uint16_t freq; // частота в Гц или NOTE_REST
float duration; // длительность в долях (0.25 = четверть)
} Note;
typedef struct { typedef struct {
TIM_HandleTypeDef* htim; TIM_HandleTypeDef* htim;
uint32_t channel; uint32_t channel;
uint32_t timer_clock_hz; // частота тактирования таймера в Гц uint32_t timer_clock_hz; // частота тактирования таймера в Гц
const Note* sequence; Melody_t *melody;
uint16_t length;
uint16_t current_index; uint16_t current_index;
uint32_t note_start_time; uint32_t note_start_time;
uint8_t is_playing; uint8_t is_playing;
uint16_t bpm; uint16_t bpm;
} MelodyHandle; } MelodyHandle;
extern MelodyHandle melody;
void Melody_Init(MelodyHandle* mh, TIM_HandleTypeDef* htim, uint32_t channel, uint32_t timer_clock_hz); void Melody_Init(MelodyHandle* mh, TIM_HandleTypeDef* htim, uint32_t channel, uint32_t timer_clock_hz);
void Melody_SetBPM(MelodyHandle* mh, uint16_t bpm); void Melody_SetBPM(MelodyHandle* mh, uint16_t bpm);
void Melody_Play(MelodyHandle* mh, const Note* sequence, uint16_t length, uint16_t bpm); void Melody_Play(MelodyHandle* mh, Melody_t* melody, uint16_t bpm);
void Melody_Stop(MelodyHandle* mh); void Melody_Stop(MelodyHandle* mh);
void Melody_Update(MelodyHandle* mh); void Melody_Update(MelodyHandle* mh);
uint8_t Melody_IsPlaying(MelodyHandle* mh); uint8_t Melody_IsPlaying(MelodyHandle* mh);
// Длительности (в долях от целой ноты)
#define NOTE_WHOLE 1.0
#define NOTE_HALF 0.5
#define NOTE_QUARTER 0.25
#define NOTE_EIGHTH 0.125
#define NOTE_SIXTEENTH 0.0625
#define NOTE_THIRTYSECOND 0.03125
#define NOTE_WHOLE_DOT 1.5
#define NOTE_HALF_DOT 0.75
#define NOTE_QUARTER_DOT 0.375
#define NOTE_EIGHTH_DOT 0.1875
#define NOTE_SIXTEENTH_DOT 0.09375
// Ноты (частота, Гц) - диапазон 700-8000 Гц для SCS-17-S
// Октавы смещены: старая 4-я = новая 0-я, старая 5-я = новая 1-я и т.д.
// 0-я октава (бывшая 4-я)
#define NOTE_B0 494
#define NOTE_C1 523
#define NOTE_CS1 554
#define NOTE_D1 587
#define NOTE_DS1 622
#define NOTE_E1 659
#define NOTE_F1 698
#define NOTE_FS1 740
#define NOTE_G1 784
#define NOTE_GS1 831
#define NOTE_A1 880
#define NOTE_AS1 932
#define NOTE_B1 988
// 2-я октава (бывшая 6-я)
#define NOTE_C2 1047
#define NOTE_CS2 1109
#define NOTE_D2 1175
#define NOTE_DS2 1245
#define NOTE_E2 1319
#define NOTE_F2 1397
#define NOTE_FS2 1480
#define NOTE_G2 1568
#define NOTE_GS2 1661
#define NOTE_A2 1760
#define NOTE_AS2 1865
#define NOTE_B2 1976
// 3-я октава (бывшая 7-я)
#define NOTE_C3 2093
#define NOTE_CS3 2217
#define NOTE_D3 2349
#define NOTE_DS3 2489
#define NOTE_E3 2637
#define NOTE_F3 2794
#define NOTE_FS3 2960
#define NOTE_G3 3136
#define NOTE_GS3 3322
#define NOTE_A3 3520
#define NOTE_AS3 3729
#define NOTE_B3 3951
// 4-я октава (бывшая 8-я)
#define NOTE_C4 4186
#define NOTE_CS4 4435
#define NOTE_D4 4699
#define NOTE_DS4 4978
#define NOTE_E4 5274
#define NOTE_F4 5588
#define NOTE_FS4 5920
#define NOTE_G4 6272
#define NOTE_GS4 6645
#define NOTE_A4 7040
#define NOTE_AS4 7459
#define NOTE_B4 7902
// Сольфеджио с новыми октавами
// 0-я октава
#define SI0 NOTE_B0 // 494 Hz
// 1-я октава
#define DO1 NOTE_C1 // 523 Hz
#define RE1 NOTE_D1 // 587 Hz
#define MI1 NOTE_E1 // 659 Hz
#define FA1 NOTE_F1 // 698 Hz
#define SOL1 NOTE_G1 // 784 Hz
#define LA1 NOTE_A1 // 880 Hz
#define SI1 NOTE_B1 // 988 Hz
// 2-я октава
#define DO2 NOTE_C2 // 1047 Hz
#define RE2 NOTE_D2 // 1175 Hz
#define MI2 NOTE_E2 // 1319 Hz
#define FA2 NOTE_F2 // 1397 Hz
#define SOL2 NOTE_G2 // 1568 Hz
#define LA2 NOTE_A2 // 1760 Hz
#define SI2 NOTE_B2 // 1976 Hz
// 3-я октава
#define DO3 NOTE_C3 // 2093 Hz
#define RE3 NOTE_D3 // 2349 Hz
#define MI3 NOTE_E3 // 2637 Hz
#define FA3 NOTE_F3 // 2794 Hz
#define SOL3 NOTE_G3 // 3136 Hz
#define LA3 NOTE_A3 // 3520 Hz
#define SI3 NOTE_B3 // 3951 Hz
// 4-я октава
#define DO4 NOTE_C4 // 4186 Hz
#define RE4 NOTE_D4 // 4699 Hz
#define MI4 NOTE_E4 // 5274 Hz
#define FA4 NOTE_F4 // 5588 Hz
#define SOL4 NOTE_G4 // 6272 Hz
#define LA4 NOTE_A4 // 7040 Hz
#define SI4 NOTE_B4 // 7902 Hz
// Диезы для 1-й октавы
#define DO1s NOTE_CS1 // 554 Hz
#define RE1s NOTE_DS1 // 622 Hz
#define FA1s NOTE_FS1 // 740 Hz
#define SOL1s NOTE_GS1 // 831 Hz
#define LA1s NOTE_AS1 // 932 Hz
// Диезы для 2-й октавы
#define DO2s NOTE_CS2 // 1109 Hz
#define RE2s NOTE_DS2 // 1245 Hz
#define FA2s NOTE_FS2 // 1480 Hz
#define SOL2s NOTE_GS2 // 1661 Hz
#define LA2s NOTE_AS2 // 1865 Hz
// Диезы для 3-й октавы
#define DO3s NOTE_CS3 // 2217 Hz
#define RE3s NOTE_DS3 // 2489 Hz
#define FA3s NOTE_FS3 // 2960 Hz
#define SOL3s NOTE_GS3 // 3322 Hz
#define LA3s NOTE_AS3 // 3729 Hz
// Диезы для 4-й октавы
#define DO4s NOTE_CS4 // 4435 Hz
#define RE4s NOTE_DS4 // 4978 Hz
#define FA4s NOTE_FS4 // 5920 Hz
#define SOL4s NOTE_GS4 // 6645 Hz
#define LA4s NOTE_AS4 // 7459 Hz
#define NOTE_REST 0
#endif #endif

View File

@@ -1,426 +1,195 @@
#include "menu.h" #include "menu.h"
#include "segment.h" #include "segment.h"
#include "clock_manager.h" #include <string.h>
#include <stdio.h>
// Время удержания кнопки (мс), после которого нажатие считается "длинным"
// Используется для входа в меню из режима часов
#define LONG_PRESS_MS 500 #define LONG_PRESS_MS 500
// Задержка перед началом автоповтора (мс) MenuContext g_ctx;
// При удержании кнопки первые 500 мс ничего не происходит uint8_t g_sound_enabled = 1;
#define REPEAT_DELAY_MS 500
// Интервал между автоповторами (мс) extern bool ReadButton(Button_Type btn);
// После начала повтора каждые 100 мс кнопка срабатывает заново extern MenuNode* Menu_GetRootMenu(void);
#define REPEAT_INTERVAL_MS 100
// Интервал мигания редактируемого разряда (мс) void Menu_Init(MenuNode* startNode) {
// Моргает каждые 500 мс (0.5 сек вкл / 0.5 сек выкл) g_ctx.currentNode = startNode;
#define BLINK_INTERVAL_MS 500 g_ctx.needsRedraw = true;
g_ctx.lastTick = 0;
// Задержка антидребезга (мс) if (startNode && startNode->onEnter) {
// Изменение состояния кнопки фиксируется только если оно стабильно в течение 30 мс startNode->onEnter();
#define DEBOUNCE_MS 30
typedef enum {
MAIN_MENU_SET_TIME,
MAIN_MENU_SET_DUTY,
MAIN_MENU_RESET,
MAIN_MENU_COUNT
} MainMenuItem;
typedef struct {
// Состояние
SystemState state;
MainMenuItem selectedMenuItem;
// Для SET_TIME
time_t originalTime;
time_t editTime;
uint8_t editStep;
bool blinkState;
uint32_t lastBlinkTime;
// Для SET_DUTY
uint8_t originalDuty;
uint8_t editDuty;
// Для кнопок
bool buttonPrevState[BUTTON_COUNT];
uint32_t buttonPressTime[BUTTON_COUNT];
bool longPressSent[BUTTON_COUNT];
bool repeatActive[BUTTON_COUNT];
uint32_t lastRepeatTime[BUTTON_COUNT];
uint32_t lastDebounceTime[BUTTON_COUNT];
bool buttonStableState[BUTTON_COUNT];
} MenuContext;
MenuContext menu;
// ==================== Отображение ====================
static void FormatTime(char* buf, const time_t* t) {
buf[0] = '0' + t->hour / 10;
buf[1] = '0' + t->hour % 10;
buf[2] = '0' + t->min / 10;
buf[3] = '0' + t->min % 10;
buf[4] = '0' + t->sec / 10;
buf[5] = '0' + t->sec % 10;
}
static void UpdateDisplay(void) {
switch (menu.state) {
case STATE_CLOCK: {
time_t now = ClockManager_GetTime(1);
char buf[7];
FormatTime(buf, &now);
Segment_SetString(buf);
break;
}
case STATE_MAIN_MENU: {
switch (menu.selectedMenuItem) {
case MAIN_MENU_SET_TIME:
Segment_SetString("SET T ");
break;
case MAIN_MENU_SET_DUTY:
Segment_SetString("SET D ");
break;
case MAIN_MENU_RESET:
Segment_SetString("RESET ");
break;
default:
break;
}
break;
}
case STATE_SET_TIME: {
char buf[7];
FormatTime(buf, &menu.editTime);
if (menu.blinkState && menu.editStep < 6) {
buf[menu.editStep] = ' ';
}
Segment_SetString(buf);
break;
}
case STATE_SET_DUTY: {
char buf[6] = {'D', 'U', 'T', 'Y', ' ' , ' '};
if (menu.editDuty == 10) {
buf[4] = '1';
buf[5] = '0';
} else {
buf[5] = '0' + menu.editDuty;
}
Segment_SetString(buf);
break;
}
case STATE_RESET_CONFIRM:
Segment_SetString("RESET ");
break;
} }
} }
// ==================== Логика SET_TIME ==================== // НОВАЯ ФУНКЦИЯ - открыть меню
static void IncreaseTimeDigit(void) { void Menu_OpenMenu(MenuNode* menu) {
uint8_t tens, units; if (!menu) return;
switch (menu.editStep) { g_ctx.currentNode = menu;
case 0: // десятки часов (0-2) g_ctx.needsRedraw = true;
tens = menu.editTime.hour / 10; if (menu->onEnter) {
units = menu.editTime.hour % 10; menu->onEnter();
tens++;
if (tens > 2) tens = 0;
menu.editTime.hour = tens * 10 + units;
break;
case 1: // единицы часов (0-9, но с учетом десятков)
tens = menu.editTime.hour / 10;
units = menu.editTime.hour % 10;
units++;
if (tens == 2 && units > 3) units = 0; // 23 → 20
if (units > 9) units = 0;
menu.editTime.hour = tens * 10 + units;
break;
case 2: // десятки минут (0-5)
tens = menu.editTime.min / 10;
units = menu.editTime.min % 10;
tens = (tens + 1) % 6;
menu.editTime.min = tens * 10 + units;
break;
case 3: // единицы минут (0-9)
units = (menu.editTime.min % 10 + 1) % 10;
menu.editTime.min = (menu.editTime.min / 10) * 10 + units;
break;
case 4: // десятки секунд (0-5)
tens = menu.editTime.sec / 10;
units = menu.editTime.sec % 10;
tens = (tens + 1) % 6;
menu.editTime.sec = tens * 10 + units;
break;
case 5: // единицы секунд (0-9)
units = (menu.editTime.sec % 10 + 1) % 10;
menu.editTime.sec = (menu.editTime.sec / 10) * 10 + units;
break;
} }
UpdateDisplay();
} }
static void DecreaseTimeDigit(void) { void Menu_HandleButton(Button_Type btn, bool longPress) {
uint8_t tens, units; if (!g_ctx.currentNode) return;
switch (menu.editStep) {
case 0: // десятки часов (0-2)
tens = menu.editTime.hour / 10;
units = menu.editTime.hour % 10;
if (tens == 0) tens = 2;
else tens--;
menu.editTime.hour = tens * 10 + units;
break;
case 1: // единицы часов // Длинное нажатие SELECT на корневом уровне (часы) - вход в меню
tens = menu.editTime.hour / 10; if (longPress && btn == BUTTON_SELECT && g_ctx.currentNode->parent == NULL) {
units = menu.editTime.hour % 10; SOUND_DOUBLE;
if (units == 0) { MenuNode* rootMenu = Menu_GetRootMenu();
units = (tens == 2) ? 3 : 9; if (rootMenu) {
} else { Menu_OpenMenu(rootMenu);
units--;
} }
menu.editTime.hour = tens * 10 + units;
break;
case 2: // десятки минут (0-5)
tens = menu.editTime.min / 10;
units = menu.editTime.min % 10;
tens = (tens == 0) ? 5 : tens - 1;
menu.editTime.min = tens * 10 + units;
break;
case 3: // единицы минут
units = (menu.editTime.min % 10 == 0) ? 9 : (menu.editTime.min % 10) - 1;
menu.editTime.min = (menu.editTime.min / 10) * 10 + units;
break;
case 4: // десятки секунд (0-5)
tens = menu.editTime.sec / 10;
units = menu.editTime.sec % 10;
tens = (tens == 0) ? 5 : tens - 1;
menu.editTime.sec = tens * 10 + units;
break;
case 5: // единицы секунд
units = (menu.editTime.sec % 10 == 0) ? 9 : (menu.editTime.sec % 10) - 1;
menu.editTime.sec = (menu.editTime.sec / 10) * 10 + units;
break;
}
UpdateDisplay();
}
// ==================== Обработка кнопок ====================
static void ProcessButton(Button_Type btn, bool longPress) {
// Длинное нажатие SELECT в режиме часов - вход в меню
if (menu.state == STATE_CLOCK && longPress && btn == BUTTON_SELECT) {
menu.state = STATE_MAIN_MENU;
menu.selectedMenuItem = MAIN_MENU_SET_TIME;
UpdateDisplay();
return; return;
} }
switch (menu.state) { if (g_ctx.currentNode->onButton) {
case STATE_MAIN_MENU: if(btn == BUTTON_SELECT)
if (btn == BUTTON_UP) { {
menu.selectedMenuItem++; SOUND_DOUBLE;
if (menu.selectedMenuItem >= MAIN_MENU_COUNT) {
menu.selectedMenuItem = 0;
} }
UpdateDisplay(); else
{
SOUND_CLICK;
} }
else if (btn == BUTTON_DOWN) {
if (menu.selectedMenuItem == 0) { g_ctx.currentNode->onButton(btn, longPress);
menu.selectedMenuItem = MAIN_MENU_COUNT - 1; return;
} else {
menu.selectedMenuItem--;
} }
UpdateDisplay();
} if (g_ctx.currentNode->parent != NULL) {
else if (btn == BUTTON_SELECT && !longPress) { switch (btn) {
switch (menu.selectedMenuItem) { case BUTTON_UP:
case MAIN_MENU_SET_TIME: SOUND_CLICK;
menu.state = STATE_SET_TIME; if (g_ctx.currentNode->selectedChild > 0) {
menu.originalTime = ClockManager_GetTime(0); g_ctx.currentNode->selectedChild--;
menu.editTime = menu.originalTime; g_ctx.needsRedraw = true;
menu.editStep = 0;
menu.blinkState = true;
menu.lastBlinkTime = HAL_GetTick();
break;
case MAIN_MENU_SET_DUTY:
menu.state = STATE_SET_DUTY;
menu.originalDuty = ClockManager_GetDuty();
menu.editDuty = menu.originalDuty;
break;
case MAIN_MENU_RESET:
menu.state = STATE_RESET_CONFIRM;
break;
default: break;
}
UpdateDisplay();
}
else if (btn == BUTTON_BACK) {
menu.state = STATE_CLOCK;
UpdateDisplay();
} }
break; break;
case STATE_SET_TIME: case BUTTON_DOWN:
if (btn == BUTTON_UP) { SOUND_CLICK;
IncreaseTimeDigit(); if (g_ctx.currentNode->selectedChild < g_ctx.currentNode->childCount - 1) {
} g_ctx.currentNode->selectedChild++;
else if (btn == BUTTON_DOWN) { g_ctx.needsRedraw = true;
DecreaseTimeDigit();
}
else if (btn == BUTTON_SELECT) {
menu.editStep++;
if (menu.editStep >= 6) {
ClockManager_SetTime(menu.editTime.hour, menu.editTime.min, menu.editTime.sec);
menu.state = STATE_CLOCK;
}
UpdateDisplay();
}
else if (btn == BUTTON_BACK) {
menu.state = STATE_CLOCK;
UpdateDisplay();
} }
break; break;
case STATE_SET_DUTY: case BUTTON_SELECT: {
if (btn == BUTTON_UP && menu.editDuty < 10) { SOUND_DOUBLE;
menu.editDuty++; if (g_ctx.currentNode->children) {
Segment_SetBrightness(menu.editDuty * 10); MenuNode* selected = g_ctx.currentNode->children[g_ctx.currentNode->selectedChild];
UpdateDisplay(); if (selected) {
g_ctx.currentNode = selected;
g_ctx.needsRedraw = true;
if (selected->onEnter) {
selected->onEnter();
} }
else if (btn == BUTTON_DOWN && menu.editDuty > 0) {
menu.editDuty--;
Segment_SetBrightness(menu.editDuty * 10);
UpdateDisplay();
} }
else if (btn == BUTTON_SELECT) {
ClockManager_SetDuty(menu.editDuty);
menu.state = STATE_CLOCK;
UpdateDisplay();
}
else if (btn == BUTTON_BACK) {
menu.state = STATE_CLOCK;
Segment_SetBrightness(menu.originalDuty * 10);
UpdateDisplay();
} }
break; break;
}
case STATE_RESET_CONFIRM: case BUTTON_BACK:
if (btn == BUTTON_SELECT) { SOUND_CLICK;
ClockManager_ResetTime(); Menu_GoBack();
ClockManager_SetDuty(8);
menu.state = STATE_CLOCK;
UpdateDisplay();
}
else if (btn == BUTTON_BACK) {
menu.state = STATE_CLOCK;
UpdateDisplay();
}
break; break;
default: default:
break; break;
} }
} }
// ==================== Публичные функции ====================
void Menu_Init(void) {
menu.state = STATE_CLOCK;
menu.selectedMenuItem = MAIN_MENU_SET_TIME;
menu.editStep = 0;
menu.blinkState = false;
menu.lastBlinkTime = 0;
for (int i = 0; i < BUTTON_COUNT; i++) {
menu.buttonPrevState[i] = false;
menu.buttonPressTime[i] = 0;
menu.longPressSent[i] = false;
menu.repeatActive[i] = false;
menu.lastDebounceTime[i] = 0;
menu.buttonStableState[i] = false;
}
} }
void Menu_Process(void) { void Menu_Process(void) {
uint32_t tick = HAL_GetTick(); uint32_t tick = HAL_GetTick();
if (menu.state == STATE_SET_TIME) { // ОБРАБОТКА КНОПОК С ДЕБАУНСОМ
if (tick - menu.lastBlinkTime >= BLINK_INTERVAL_MS) {
menu.lastBlinkTime = tick;
menu.blinkState = !menu.blinkState;
UpdateDisplay();
}
}
for (int i = 0; i < BUTTON_COUNT; i++) { for (int i = 0; i < BUTTON_COUNT; i++) {
bool rawPressed = ReadButton(i); bool reading = ReadButton((Button_Type)i);
bool pressed;
// Антидребезг if (reading != g_ctx.lastButtonState[i]) {
if (rawPressed != menu.buttonStableState[i]) { g_ctx.lastDebounceTime[i] = tick;
menu.lastDebounceTime[i] = tick;
menu.buttonStableState[i] = rawPressed;
} }
if ((tick - menu.lastDebounceTime[i]) >= DEBOUNCE_MS) { if ((tick - g_ctx.lastDebounceTime[i]) > 30) { // DEBOUNCE_MS = 30
pressed = menu.buttonStableState[i]; if (reading != g_ctx.buttonState[i]) {
g_ctx.buttonState[i] = reading;
if (g_ctx.buttonState[i]) {
g_ctx.pressStartTime[i] = tick;
g_ctx.longPressTriggered[i] = false;
} else { } else {
pressed = menu.buttonPrevState[i]; // старое значение пока не стабилизировалось if (!g_ctx.longPressTriggered[i]) {
} Menu_HandleButton((Button_Type)i, false);
bool wasPressed = menu.buttonPrevState[i];
if (pressed && !wasPressed) {
menu.buttonPressTime[i] = tick;
menu.longPressSent[i] = false;
menu.repeatActive[i] = false;
}
else if (pressed && wasPressed) {
if (!menu.longPressSent[i] && (tick - menu.buttonPressTime[i] >= LONG_PRESS_MS)) {
menu.longPressSent[i] = true;
ProcessButton((Button_Type)i, true);
}
else if (menu.longPressSent[i] && !menu.repeatActive[i] &&
(tick - menu.buttonPressTime[i] >= REPEAT_DELAY_MS)) {
menu.repeatActive[i] = true;
menu.lastRepeatTime[i] = tick;
ProcessButton((Button_Type)i, true);
}
else if (menu.repeatActive[i] && (tick - menu.lastRepeatTime[i] >= REPEAT_INTERVAL_MS)) {
menu.lastRepeatTime[i] = tick;
ProcessButton((Button_Type)i, true);
} }
} }
else if (!pressed && wasPressed) {
if (!menu.longPressSent[i]) {
ProcessButton((Button_Type)i, false);
}
menu.repeatActive[i] = false;
}
menu.buttonPrevState[i] = pressed;
}
static uint32_t lastClockUpdate = 0;
if (menu.state == STATE_CLOCK && (tick - lastClockUpdate >= 200)) {
lastClockUpdate = tick;
UpdateDisplay();
} }
} }
SystemState Menu_GetState(void) { if (g_ctx.buttonState[i] && !g_ctx.longPressTriggered[i]) {
return menu.state; if ((tick - g_ctx.pressStartTime[i]) >= LONG_PRESS_MS) {
g_ctx.longPressTriggered[i] = true;
Menu_HandleButton((Button_Type)i, true);
}
}
g_ctx.lastButtonState[i] = reading;
}
if (g_ctx.currentNode && g_ctx.currentNode->onUpdate) {
g_ctx.currentNode->onUpdate();
}
if (tick - g_ctx.lastTick >= 200) {
g_ctx.lastTick = tick;
g_ctx.needsRedraw = true;
}
if ((g_ctx.needsRedraw && g_ctx.currentNode && g_ctx.currentNode->display) ||
(g_ctx.currentNode && g_ctx.currentNode->display && g_ctx.currentNode->needsRedraw)) {
g_ctx.currentNode->display();
g_ctx.needsRedraw = false;
}
}
void Menu_GoBack(void) {
if (g_ctx.currentNode && g_ctx.currentNode->parent) {
g_ctx.currentNode = g_ctx.currentNode->parent;
g_ctx.needsRedraw = true;
if (g_ctx.currentNode->onEnter) {
g_ctx.currentNode->onEnter();
}
}
}
void Menu_Refresh(void) {
g_ctx.needsRedraw = true;
}
MenuNode* Menu_GetCurrentNode(void) {
return g_ctx.currentNode;
}
void Menu_Sound_On(void) {
g_sound_enabled = 1;
SOUND_SUCCESS;
}
void Menu_Sound_Off(void) {
g_sound_enabled = 0;
}
void Menu_Sound_Toggle(void) {
if (g_sound_enabled) {
Menu_Sound_Off();
} else {
Menu_Sound_On();
}
}
uint8_t Menu_Sound_IsEnabled(void) {
return g_sound_enabled;
} }

View File

@@ -4,6 +4,14 @@
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include "main.h" #include "main.h"
#include "melody.h"
// Макросы звуковых эффектов
#define SFX_BPM 480
#define SOUND_CLICK if(g_sound_enabled) Melody_Play(&melody, &SFX_Click, SFX_BPM)
#define SOUND_DOUBLE if(g_sound_enabled) Melody_Play(&melody, &SFX_DoubleBeep, SFX_BPM)
#define SOUND_ERROR if(g_sound_enabled) Melody_Play(&melody, &SFX_Error, SFX_BPM)
#define SOUND_SUCCESS if(g_sound_enabled) Melody_Play(&melody, &SFX_Success, SFX_BPM)
typedef enum { typedef enum {
BUTTON_UP, BUTTON_UP,
@@ -13,18 +21,50 @@ typedef enum {
BUTTON_COUNT BUTTON_COUNT
} Button_Type; } Button_Type;
typedef enum { typedef struct MenuNode {
STATE_CLOCK, // режим часов const char* name;
STATE_MAIN_MENU, // главное меню (выбор пункта) struct MenuNode* parent;
STATE_SET_TIME, // настройка времени struct MenuNode** children;
STATE_SET_DUTY, // настройка яркости uint8_t childCount;
STATE_RESET_CONFIRM // подтверждение сброса uint8_t selectedChild;
} SystemState; uint8_t needsRedraw;
// Чтение кнопки (реализуется в main.c или hal) void (*display)(void);
int ReadButton(int btn); void (*onEnter)(void);
void (*onUpdate)(void);
void (*onButton)(Button_Type btn, bool longPress);
void Menu_Init(void); void* data;
} MenuNode;
typedef struct {
MenuNode* currentNode;
uint32_t lastTick;
bool needsRedraw;
uint32_t lastDebounceTime[BUTTON_COUNT];
uint8_t lastButtonState[BUTTON_COUNT];
uint8_t buttonState[BUTTON_COUNT];
uint32_t pressStartTime[BUTTON_COUNT];
uint8_t longPressTriggered[BUTTON_COUNT];
} MenuContext;
// Глобальные переменные
extern uint8_t g_sound_enabled;
extern MenuContext g_ctx;
// Функции
void Menu_Init(MenuNode* startNode);
void Menu_Process(void); void Menu_Process(void);
void Menu_HandleButton(Button_Type btn, bool longPress);
void Menu_GoBack(void);
void Menu_Refresh(void);
MenuNode* Menu_GetCurrentNode(void);
void Menu_OpenMenu(MenuNode* menu);
void Menu_Sound_On(void);
void Menu_Sound_Off(void);
void Menu_Sound_Toggle(void);
uint8_t Menu_Sound_IsEnabled(void);
#endif #endif

695
Core/Clock/menu_items.c Normal file
View File

@@ -0,0 +1,695 @@
#include "menu_items.h"
#include "segment.h"
#include "clock_manager.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
static MenuNode g_rootMenu;
// Сначала объявляем узлы активностей
MenuNode g_clockNode;
MenuNode g_timerNode;
MenuNode g_stopwatchNode;
MenuNode g_ZeroMillisNode;
MenuNode g_ReactionTimeNode;
MenuNode g_ClickerTimeNode;
// Потом объявляем узлы меню
MenuNode g_gamesNode;
MenuNode g_settingsNode;
MenuNode g_timeEditNode;
MenuNode g_dutyEditNode;
MenuNode g_LEDEditNode;
MenuNode g_MenuSoundNode;
MenuNode g_PowerOnSongNode;
MenuNode g_SongNode;
MenuNode g_resetNode;
// ==================== Данные ====================
typedef struct {
time_t editTime;
uint8_t editStep;
bool blinkState;
uint32_t lastBlink;
} TimeEditData;
typedef struct {
uint32_t startTime;
uint32_t pressTime;
bool celebrating;
bool result;
int32_t diff_ms;
uint8_t state;
} Game1SecData;
typedef struct {
uint32_t waitStart;
uint32_t ledOnTime;
uint32_t reactionTime;
uint8_t state;
} GameReactionData;
typedef struct {
uint32_t endTime;
uint16_t clicks;
bool active;
bool finished;
} GameClickerData;
static TimeEditData g_timeData;
static uint8_t g_originalDuty;
static uint8_t g_editDuty;
static Game1SecData g_game1sec;
static GameReactionData g_gameReaction;
static GameClickerData g_gameClicker;
// Текущая активность в корне
static MenuNode* g_currentActivity = NULL;
// ==================== Функции отображения активностей ====================
static void Display_Clock(void) {
time_t now = ClockManager_GetTime(1);
char buf[7];
sprintf(buf, "%02d%02d%02d", now.hour, now.min, now.sec);
Segment_SetString(buf);
}
static void Display_Timer(void) {
Segment_SetString("TIMER ");
}
static void Display_Stopwatch(void) {
Segment_SetString("SECOND ");
}
static void Display_ZeroMillis(void) {
char buf[7];
switch (g_game1sec.state) {
case 0:
g_game1sec.celebrating = 0;
sprintf(buf, "START"); break;
case 1:
g_game1sec.diff_ms = (HAL_GetTick() - g_game1sec.startTime);
case 2:
sprintf(buf, " %4d", g_game1sec.diff_ms/10);
break;
default: sprintf(buf, "ERROR"); break;
}
if(g_game1sec.state == 2)
{
if((g_game1sec.diff_ms/10)%100 == 0)
{
if(!g_game1sec.celebrating)
{
g_game1sec.celebrating = 1;
Melody_Play(&melody, &Polyphia_PlayingGod, 134);
}
}
}
Segment_SetString(buf);
}
static void Display_Reaction(void) {
char buf[7];
switch (g_gameReaction.state) {
case 0: sprintf(buf, "START"); break;
case 1: sprintf(buf, " "); break;
case 2: sprintf(buf, "888888"); break;
case 3: sprintf(buf, "%6d", g_gameReaction.reactionTime); break;
default: sprintf(buf, "ERROR"); break;
}
Segment_SetString(buf);
}
static void Display_Clicker(void) {
char buf[7];
if (!g_gameClicker.active && !g_gameClicker.finished) {
sprintf(buf, "START");
} else if (g_gameClicker.active) {
uint32_t remaining = (g_gameClicker.endTime - HAL_GetTick()) / 1000;
sprintf(buf, "%2d %3d", remaining, g_gameClicker.clicks);
} else {
sprintf(buf, " %3d", g_gameClicker.clicks);
}
Segment_SetString(buf);
}
static void Display_MenuItem(void) {
MenuNode* current = Menu_GetCurrentNode();
if (current && current->selectedChild < current->childCount) {
const char* name = current->children[current->selectedChild]->name;
char buf[7];
sprintf(buf, "%-6.6s", name);
Segment_SetString(buf);
}
}
static void Display_TimeEdit(void) {
char buf[7];
sprintf(buf, "%02d%02d%02d",
g_timeData.editTime.hour,
g_timeData.editTime.min,
g_timeData.editTime.sec);
if (g_timeData.blinkState && g_timeData.editStep < 6) {
buf[g_timeData.editStep] = ' ';
}
Segment_SetString(buf);
}
static void Display_DutyEdit(void) {
char buf[7] = "DUTY ";
if (g_editDuty == 10) {
buf[4] = '1';
buf[5] = '0';
} else {
buf[5] = '0' + g_editDuty;
}
Segment_SetString(buf);
}
static void Display_Reset(void) {
Segment_SetString("RESET ");
}
// ==================== Вход в активности ====================
static void OnEnter_Clock(void) {
g_currentActivity = &g_clockNode;
g_rootMenu.parent = &g_clockNode;
}
static void OnEnter_Timer(void) {
g_currentActivity = &g_timerNode;
g_rootMenu.parent = &g_timerNode;
}
static void OnEnter_Stopwatch(void) {
g_currentActivity = &g_stopwatchNode;
g_rootMenu.parent = &g_stopwatchNode;
}
static void OnEnter_ZeroMillis(void) {
g_game1sec.state = 0;
Menu_Refresh();
}
static void OnEnter_Reaction(void) {
g_gameReaction.state = 0;
Menu_Refresh();
}
static void OnEnter_Clicker(void) {
g_gameClicker.active = false;
g_gameClicker.finished = false;
g_gameClicker.clicks = 0;
Menu_Refresh();
}
static void OnEnter_TimeEdit(void) {
g_timeData.editTime = ClockManager_GetTime(0);
g_timeData.editStep = 0;
g_timeData.blinkState = true;
g_timeData.lastBlink = HAL_GetTick();
}
static void OnEnter_DutyEdit(void) {
g_originalDuty = ClockManager_GetDuty();
g_editDuty = g_originalDuty;
Segment_SetBrightness(g_editDuty * 10);
}
static void OnEnter_Reset(void) {}
// ==================== Обновления ====================
static void OnUpdate_TimeEdit(void) {
uint32_t tick = HAL_GetTick();
if (tick - g_timeData.lastBlink >= 500) {
g_timeData.lastBlink = tick;
g_timeData.blinkState = !g_timeData.blinkState;
Menu_Refresh();
}
}
static void OnUpdate_Reaction(void) {
uint32_t tick = HAL_GetTick();
if (g_gameReaction.state == 1) {
if (tick - g_gameReaction.waitStart >= g_gameReaction.ledOnTime) {
g_gameReaction.state = 2;
g_gameReaction.waitStart = tick;
Menu_Refresh();
}
}
else if (g_gameReaction.state == 2) {
if (tick - g_gameReaction.waitStart >= 2000) {
g_gameReaction.state = 3;
g_gameReaction.reactionTime = 999;
Menu_Refresh();
}
}
}
static void OnUpdate_Clicker(void) {
if (g_gameClicker.active && HAL_GetTick() >= g_gameClicker.endTime) {
g_gameClicker.active = false;
g_gameClicker.finished = true;
Menu_Refresh();
}
}
// ==================== Обработчики кнопок активностей ====================
static void Clock_OnButton(Button_Type btn, bool longPress) {
if (longPress && btn == BUTTON_SELECT) {
Menu_OpenMenu(&g_rootMenu);
}
}
static void Timer_OnButton(Button_Type btn, bool longPress) {
if (longPress && btn == BUTTON_SELECT) {
Menu_OpenMenu(&g_rootMenu);
}
}
static void Stopwatch_OnButton(Button_Type btn, bool longPress) {
if (longPress && btn == BUTTON_SELECT) {
Menu_OpenMenu(&g_rootMenu);
}
}
static void ZeroMillis_OnButton(Button_Type btn, bool longPress) {
if (longPress && btn == BUTTON_SELECT) {
Menu_OpenMenu(&g_rootMenu);
return;
}
if (btn == BUTTON_BACK) {
Menu_GoBack();
}
uint32_t tick = HAL_GetTick();
if (g_game1sec.state == 0 && btn == BUTTON_SELECT) {
g_game1sec.state = 1;
g_game1sec.startTime = tick;
Menu_Refresh();
}
else if ((btn == BUTTON_SELECT) && (g_game1sec.state == 1)) {
g_game1sec.state = 2;
g_game1sec.pressTime = HAL_GetTick();
g_game1sec.diff_ms = (g_game1sec.pressTime - g_game1sec.startTime);
if (g_game1sec.diff_ms < 0) g_game1sec.diff_ms = -g_game1sec.diff_ms;
Menu_Refresh();
}
else if ((g_game1sec.state == 2) && (btn == BUTTON_SELECT))
{
g_game1sec.state = 0;
}
}
static void Reaction_OnButton(Button_Type btn, bool longPress) {
if (longPress && btn == BUTTON_SELECT) {
Menu_OpenMenu(&g_rootMenu);
return;
}
if (btn == BUTTON_BACK) {
Menu_GoBack();
}
uint32_t tick = HAL_GetTick();
if (g_gameReaction.state == 0 && btn == BUTTON_SELECT) {
g_gameReaction.state = 1;
g_gameReaction.waitStart = tick;
g_gameReaction.ledOnTime = 1000 + (rand() % 4000);
Menu_Refresh();
}
else if (g_gameReaction.state == 2 && btn == BUTTON_SELECT) {
g_gameReaction.reactionTime = tick - g_gameReaction.waitStart;
g_gameReaction.state = 3;
Menu_Refresh();
}
else if ((g_gameReaction.state == 3) && (btn == BUTTON_SELECT))
{
g_gameReaction.state = 0;
}
}
static void Clicker_OnButton(Button_Type btn, bool longPress) {
if (longPress && btn == BUTTON_SELECT) {
Menu_OpenMenu(&g_rootMenu);
return;
}
if (btn == BUTTON_BACK) {
Menu_GoBack();
}
if (!g_gameClicker.active && !g_gameClicker.finished && btn == BUTTON_SELECT) {
g_gameClicker.active = true;
g_gameClicker.endTime = HAL_GetTick() + 10000;
g_gameClicker.clicks = 0;
Menu_Refresh();
}
else if (g_gameClicker.active && btn == BUTTON_UP) {
g_gameClicker.clicks++;
Menu_Refresh();
}
else if (g_gameClicker.finished && (btn == BUTTON_SELECT))
{
g_gameClicker.finished = 0;
g_gameClicker.active = 0;
}
}
static void TimeEdit_OnButton(Button_Type btn, bool longPress) {
(void)longPress;
switch (btn) {
case BUTTON_UP: {
uint8_t tens, units;
switch (g_timeData.editStep) {
case 0:
tens = g_timeData.editTime.hour / 10;
units = g_timeData.editTime.hour % 10;
tens = (tens + 1) % 3;
g_timeData.editTime.hour = tens * 10 + units;
break;
case 1:
tens = g_timeData.editTime.hour / 10;
units = (g_timeData.editTime.hour % 10 + 1) % 10;
if (tens == 2 && units > 3) units = 0;
g_timeData.editTime.hour = tens * 10 + units;
break;
case 2:
tens = (g_timeData.editTime.min / 10 + 1) % 6;
g_timeData.editTime.min = tens * 10 + (g_timeData.editTime.min % 10);
break;
case 3:
units = (g_timeData.editTime.min % 10 + 1) % 10;
g_timeData.editTime.min = (g_timeData.editTime.min / 10) * 10 + units;
break;
case 4:
tens = (g_timeData.editTime.sec / 10 + 1) % 6;
g_timeData.editTime.sec = tens * 10 + (g_timeData.editTime.sec % 10);
break;
case 5:
units = (g_timeData.editTime.sec % 10 + 1) % 10;
g_timeData.editTime.sec = (g_timeData.editTime.sec / 10) * 10 + units;
break;
}
Menu_Refresh();
break;
}
case BUTTON_DOWN: {
uint8_t tens, units;
switch (g_timeData.editStep) {
case 0:
tens = g_timeData.editTime.hour / 10;
units = g_timeData.editTime.hour % 10;
tens = (tens == 0) ? 2 : tens - 1;
g_timeData.editTime.hour = tens * 10 + units;
break;
case 1:
tens = g_timeData.editTime.hour / 10;
units = g_timeData.editTime.hour % 10;
if (units == 0) units = (tens == 2) ? 3 : 9;
else units--;
g_timeData.editTime.hour = tens * 10 + units;
break;
case 2:
tens = g_timeData.editTime.min / 10;
tens = (tens == 0) ? 5 : tens - 1;
g_timeData.editTime.min = tens * 10 + (g_timeData.editTime.min % 10);
break;
case 3:
units = g_timeData.editTime.min % 10;
units = (units == 0) ? 9 : units - 1;
g_timeData.editTime.min = (g_timeData.editTime.min / 10) * 10 + units;
break;
case 4:
tens = g_timeData.editTime.sec / 10;
tens = (tens == 0) ? 5 : tens - 1;
g_timeData.editTime.sec = tens * 10 + (g_timeData.editTime.sec % 10);
break;
case 5:
units = g_timeData.editTime.sec % 10;
units = (units == 0) ? 9 : units - 1;
g_timeData.editTime.sec = (g_timeData.editTime.sec / 10) * 10 + units;
break;
}
Menu_Refresh();
break;
}
case BUTTON_SELECT:
g_timeData.editStep++;
if (g_timeData.editStep >= 6) {
ClockManager_SetTime(g_timeData.editTime.hour,
g_timeData.editTime.min,
g_timeData.editTime.sec);
Menu_GoBack();
}
Menu_Refresh();
break;
case BUTTON_BACK:
Menu_GoBack();
default:
break;
}
}
static void DutyEdit_OnButton(Button_Type btn, bool longPress) {
(void)longPress;
switch (btn) {
case BUTTON_UP:
if (g_editDuty < 10) {
g_editDuty++;
Segment_SetBrightness(g_editDuty * 10);
Menu_Refresh();
}
break;
case BUTTON_DOWN:
if (g_editDuty > 0) {
g_editDuty--;
Segment_SetBrightness(g_editDuty * 10);
Menu_Refresh();
}
break;
case BUTTON_SELECT:
ClockManager_SetDuty(g_editDuty);
Menu_GoBack();
break;
case BUTTON_BACK:
ClockManager_SetDuty(g_originalDuty);
Menu_GoBack();
default:
break;
}
}
static void Reset_OnButton(Button_Type btn, bool longPress) {
if ((btn == BUTTON_SELECT) && longPress) {
ClockManager_ResetTime();
ClockManager_SetDuty(5);
Menu_GoBack();
}
}
// ==================== Узлы активностей (корень) ====================
MenuNode g_clockNode = {
.name = "CLOC",
.parent = NULL,
.children = NULL,
.childCount = 0,
.selectedChild = 0,
.display = Display_Clock,
.onEnter = OnEnter_Clock,
.onUpdate = NULL,
.onButton = Clock_OnButton,
.data = NULL
};
MenuNode g_timerNode = {
.name = "TIMER",
.parent = NULL,
.children = NULL,
.childCount = 0,
.selectedChild = 0,
.display = Display_Timer,
.onEnter = OnEnter_Timer,
.onUpdate = NULL,
.onButton = Timer_OnButton,
.data = NULL
};
MenuNode g_stopwatchNode = {
.name = "SECOND",
.parent = NULL,
.children = NULL,
.childCount = 0,
.selectedChild = 0,
.display = Display_Stopwatch,
.onEnter = OnEnter_Stopwatch,
.onUpdate = NULL,
.onButton = Stopwatch_OnButton,
.data = NULL
};
MenuNode g_ZeroMillisNode = {
.name = "00 SEC",
.parent = &g_gamesNode,
.children = NULL,
.childCount = 0,
.selectedChild = 0,
.needsRedraw = 1,
.display = Display_ZeroMillis,
.onEnter = OnEnter_ZeroMillis,
.onUpdate = NULL,
.onButton = ZeroMillis_OnButton,
.data = &g_game1sec
};
MenuNode g_ReactionTimeNode = {
.name = "CSTEST",
.parent = &g_gamesNode,
.children = NULL,
.childCount = 0,
.selectedChild = 0,
.display = Display_Reaction,
.onEnter = OnEnter_Reaction,
.onUpdate = OnUpdate_Reaction,
.onButton = Reaction_OnButton,
.data = &g_gameReaction
};
MenuNode g_ClickerTimeNode = {
.name = "CLICER",
.parent = &g_gamesNode,
.children = NULL,
.childCount = 0,
.selectedChild = 0,
.display = Display_Clicker,
.onEnter = OnEnter_Clicker,
.onUpdate = OnUpdate_Clicker,
.onButton = Clicker_OnButton,
.data = &g_gameClicker
};
// ==================== Узлы меню ====================
MenuNode g_gamesNode;
MenuNode g_settingsNode;
MenuNode g_timeEditNode;
MenuNode g_dutyEditNode;
MenuNode g_LEDEditNode;
MenuNode g_MenuSoundNode;
MenuNode g_PowerOnSongNode;
MenuNode g_SongNode;
MenuNode g_resetNode;
static MenuNode* g_gamesChildren[] = {
&g_ZeroMillisNode,
&g_ReactionTimeNode,
&g_ClickerTimeNode
};
static MenuNode* g_settingsChildren[] = {
&g_timeEditNode,
&g_dutyEditNode,
&g_LEDEditNode,
&g_MenuSoundNode,
&g_PowerOnSongNode,
&g_SongNode,
&g_resetNode
};
static MenuNode* g_mainMenuChildren[] = {
&g_clockNode,
&g_timerNode,
&g_stopwatchNode,
&g_gamesNode,
&g_settingsNode
};
static MenuNode g_rootMenu = {
.name = "MAIN",
.parent = NULL,
.children = g_mainMenuChildren,
.childCount = 5,
.selectedChild = 0,
.display = Display_MenuItem,
.onEnter = NULL,
.onUpdate = NULL,
.onButton = NULL,
.data = NULL
};
void MenuItems_Init(void) {
g_gamesNode = (MenuNode){
.name = "PLAY",
.parent = &g_rootMenu,
.children = g_gamesChildren,
.childCount = 3,
.selectedChild = 0,
.display = Display_MenuItem,
.onEnter = NULL,
.onUpdate = NULL,
.onButton = NULL,
.data = NULL
};
g_settingsNode = (MenuNode){
.name = "SETUP",
.parent = &g_rootMenu,
.children = g_settingsChildren,
.childCount = 7,
.selectedChild = 0,
.display = Display_MenuItem,
.onEnter = NULL,
.onUpdate = NULL,
.onButton = NULL,
.data = NULL
};
g_timeEditNode = (MenuNode){
.name = "SET T",
.parent = &g_settingsNode,
.children = NULL,
.childCount = 0,
.selectedChild = 0,
.display = Display_TimeEdit,
.onEnter = OnEnter_TimeEdit,
.onUpdate = OnUpdate_TimeEdit,
.onButton = TimeEdit_OnButton,
.data = &g_timeData
};
g_dutyEditNode = (MenuNode){
.name = "SET D",
.parent = &g_settingsNode,
.children = NULL,
.childCount = 0,
.selectedChild = 0,
.display = Display_DutyEdit,
.onEnter = OnEnter_DutyEdit,
.onUpdate = NULL,
.onButton = DutyEdit_OnButton,
.data = &g_editDuty
};
g_resetNode = (MenuNode){
.name = "RESET",
.parent = &g_settingsNode,
.children = NULL,
.childCount = 0,
.selectedChild = 0,
.display = Display_Reset,
.onEnter = OnEnter_Reset,
.onUpdate = NULL,
.onButton = Reset_OnButton,
.data = NULL
};
}
MenuNode* Menu_GetRootMenu(void) {
return &g_rootMenu;
}
MenuNode* Menu_GetCurrentActivity(void) {
return g_currentActivity;
}

19
Core/Clock/menu_items.h Normal file
View File

@@ -0,0 +1,19 @@
#ifndef MENU_ITEMS_H
#define MENU_ITEMS_H
#include "menu.h"
// Глобальные узлы меню (для доступа из main.c)
extern MenuNode g_clockNode;
extern MenuNode g_timerNode;
extern MenuNode g_stopwatchNode;
extern MenuNode g_gamesNode;
extern MenuNode g_settingsNode;
extern MenuNode g_timeEditNode;
extern MenuNode g_dutyEditNode;
extern MenuNode g_resetNode;
// Инициализация всех пунктов меню
void MenuItems_Init(void);
#endif

161
Core/Clock/notes.h Normal file
View File

@@ -0,0 +1,161 @@
#ifndef NOTES_H
#define NOTES_H
#include "stm32f1xx_hal.h"
typedef struct {
uint16_t freq; // частота в Гц или NOTE_REST
float duration; // длительность в долях (0.25 = четверть)
} Note_t;
typedef struct {
float length; // длительность мелодии в количестве нот
const Note_t *sequence; // последовательность нот
} Melody_t;
// Длительности (в долях от целой ноты)
#define NOTE_WHOLE 1.0
#define NOTE_HALF 0.5
#define NOTE_QUARTER 0.25
#define NOTE_EIGHTH 0.125
#define NOTE_SIXTEENTH 0.0625
#define NOTE_THIRTYSECOND 0.03125
#define NOTE_WHOLE_DOT 1.5
#define NOTE_HALF_DOT 0.75
#define NOTE_QUARTER_DOT 0.375
#define NOTE_EIGHTH_DOT 0.1875
#define NOTE_SIXTEENTH_DOT 0.09375
// Ноты (частота, Гц) - диапазон 700-8000 Гц для SCS-17-S
// Октавы смещены: старая 4-я = новая 0-я, старая 5-я = новая 1-я и т.д.
// 0-я октава (бывшая 4-я)
#define NOTE_B0 494
#define NOTE_C1 523
#define NOTE_CS1 554
#define NOTE_D1 587
#define NOTE_DS1 622
#define NOTE_E1 659
#define NOTE_F1 698
#define NOTE_FS1 740
#define NOTE_G1 784
#define NOTE_GS1 831
#define NOTE_A1 880
#define NOTE_AS1 932
#define NOTE_B1 988
// 2-я октава (бывшая 6-я)
#define NOTE_C2 1047
#define NOTE_CS2 1109
#define NOTE_D2 1175
#define NOTE_DS2 1245
#define NOTE_E2 1319
#define NOTE_F2 1397
#define NOTE_FS2 1480
#define NOTE_G2 1568
#define NOTE_GS2 1661
#define NOTE_A2 1760
#define NOTE_AS2 1865
#define NOTE_B2 1976
// 3-я октава (бывшая 7-я)
#define NOTE_C3 2093
#define NOTE_CS3 2217
#define NOTE_D3 2349
#define NOTE_DS3 2489
#define NOTE_E3 2637
#define NOTE_F3 2794
#define NOTE_FS3 2960
#define NOTE_G3 3136
#define NOTE_GS3 3322
#define NOTE_A3 3520
#define NOTE_AS3 3729
#define NOTE_B3 3951
// 4-я октава (бывшая 8-я)
#define NOTE_C4 4186
#define NOTE_CS4 4435
#define NOTE_D4 4699
#define NOTE_DS4 4978
#define NOTE_E4 5274
#define NOTE_F4 5588
#define NOTE_FS4 5920
#define NOTE_G4 6272
#define NOTE_GS4 6645
#define NOTE_A4 7040
#define NOTE_AS4 7459
#define NOTE_B4 7902
// Сольфеджио с новыми октавами
// 0-я октава
#define SI0 NOTE_B0 // 494 Hz
// 1-я октава
#define DO1 NOTE_C1 // 523 Hz
#define RE1 NOTE_D1 // 587 Hz
#define MI1 NOTE_E1 // 659 Hz
#define FA1 NOTE_F1 // 698 Hz
#define SOL1 NOTE_G1 // 784 Hz
#define LA1 NOTE_A1 // 880 Hz
#define SI1 NOTE_B1 // 988 Hz
// 2-я октава
#define DO2 NOTE_C2 // 1047 Hz
#define RE2 NOTE_D2 // 1175 Hz
#define MI2 NOTE_E2 // 1319 Hz
#define FA2 NOTE_F2 // 1397 Hz
#define SOL2 NOTE_G2 // 1568 Hz
#define LA2 NOTE_A2 // 1760 Hz
#define SI2 NOTE_B2 // 1976 Hz
// 3-я октава
#define DO3 NOTE_C3 // 2093 Hz
#define RE3 NOTE_D3 // 2349 Hz
#define MI3 NOTE_E3 // 2637 Hz
#define FA3 NOTE_F3 // 2794 Hz
#define SOL3 NOTE_G3 // 3136 Hz
#define LA3 NOTE_A3 // 3520 Hz
#define SI3 NOTE_B3 // 3951 Hz
// 4-я октава
#define DO4 NOTE_C4 // 4186 Hz
#define RE4 NOTE_D4 // 4699 Hz
#define MI4 NOTE_E4 // 5274 Hz
#define FA4 NOTE_F4 // 5588 Hz
#define SOL4 NOTE_G4 // 6272 Hz
#define LA4 NOTE_A4 // 7040 Hz
#define SI4 NOTE_B4 // 7902 Hz
// Диезы для 1-й октавы
#define DO1s NOTE_CS1 // 554 Hz
#define RE1s NOTE_DS1 // 622 Hz
#define FA1s NOTE_FS1 // 740 Hz
#define SOL1s NOTE_GS1 // 831 Hz
#define LA1s NOTE_AS1 // 932 Hz
// Диезы для 2-й октавы
#define DO2s NOTE_CS2 // 1109 Hz
#define RE2s NOTE_DS2 // 1245 Hz
#define FA2s NOTE_FS2 // 1480 Hz
#define SOL2s NOTE_GS2 // 1661 Hz
#define LA2s NOTE_AS2 // 1865 Hz
// Диезы для 3-й октавы
#define DO3s NOTE_CS3 // 2217 Hz
#define RE3s NOTE_DS3 // 2489 Hz
#define FA3s NOTE_FS3 // 2960 Hz
#define SOL3s NOTE_GS3 // 3322 Hz
#define LA3s NOTE_AS3 // 3729 Hz
// Диезы для 4-й октавы
#define DO4s NOTE_CS4 // 4435 Hz
#define RE4s NOTE_DS4 // 4978 Hz
#define FA4s NOTE_FS4 // 5920 Hz
#define SOL4s NOTE_GS4 // 6645 Hz
#define LA4s NOTE_AS4 // 7459 Hz
#define NOTE_REST 0
#endif

126
Core/Clock/songs.h Normal file
View File

@@ -0,0 +1,126 @@
#pragma once
#include "notes.h"
#define song_length(song_arr) sizeof(song_arr)/sizeof(song_arr[0])
static const Note_t Polyphia_OD_Notes[] = {
//1 ТАКТ
{FA1s, NOTE_SIXTEENTH},
{LA1, NOTE_SIXTEENTH},
{DO2s, NOTE_SIXTEENTH},
{FA2s, NOTE_SIXTEENTH},
{NOTE_REST, NOTE_QUARTER},
{FA1s, NOTE_SIXTEENTH},
{LA1, NOTE_SIXTEENTH},
{DO2s, NOTE_SIXTEENTH},
{RE2, NOTE_SIXTEENTH},
{FA1, NOTE_SIXTEENTH},
{SOL1s, NOTE_SIXTEENTH},
{SI1, NOTE_SIXTEENTH},
{FA2s, NOTE_EIGHTH},
//2 ТАКТ
{SOL2s, NOTE_SIXTEENTH},
{RE2, NOTE_SIXTEENTH},
{SI1, NOTE_SIXTEENTH},
{DO1s, NOTE_SIXTEENTH},
{FA1, NOTE_SIXTEENTH},
{SOL1s, NOTE_SIXTEENTH},
{DO2s, NOTE_SIXTEENTH},
{FA2, NOTE_THIRTYSECOND},
{FA2s, NOTE_THIRTYSECOND},
{FA2, NOTE_SIXTEENTH},
{DO2s, NOTE_SIXTEENTH},
{SI1, NOTE_SIXTEENTH},
{SOL1s, NOTE_SIXTEENTH},
{FA1, NOTE_SIXTEENTH},
{DO3s, NOTE_SIXTEENTH},
//3 ТАКТ
{FA1s, NOTE_SIXTEENTH},
{LA1, NOTE_SIXTEENTH},
{DO2s, NOTE_SIXTEENTH},
{FA2s, NOTE_SIXTEENTH},
{NOTE_REST, NOTE_QUARTER},
{FA1s, NOTE_SIXTEENTH},
{LA1, NOTE_SIXTEENTH},
{DO2s, NOTE_SIXTEENTH},
{RE2, NOTE_SIXTEENTH},
{FA1, NOTE_SIXTEENTH},
{SOL1s, NOTE_SIXTEENTH},
{SI1, NOTE_SIXTEENTH},
{FA2s, NOTE_EIGHTH},
//4 ТАКТ
{SOL2s, NOTE_SIXTEENTH},
{RE2, NOTE_SIXTEENTH},
{SI1, NOTE_SIXTEENTH},
{LA1, NOTE_SIXTEENTH},
{DO2s, NOTE_SIXTEENTH},
{FA2s, NOTE_SIXTEENTH},
{LA2, NOTE_SIXTEENTH},
{SI1, NOTE_SIXTEENTH},
{SI0, NOTE_SIXTEENTH},
{RE2, NOTE_SIXTEENTH},
{SOL2s, NOTE_SIXTEENTH},
{LA1, NOTE_SIXTEENTH},
{LA2, NOTE_SIXTEENTH},
{FA2s, NOTE_SIXTEENTH},
//5 ТАКТ
{SI1, NOTE_SIXTEENTH},
{RE2, NOTE_SIXTEENTH},
{FA2s, NOTE_SIXTEENTH},
{SI2, NOTE_SIXTEENTH}
};
static Melody_t Polyphia_OD = {song_length(Polyphia_OD_Notes), Polyphia_OD_Notes};
static const Note_t Polyphia_PlayingGod_Notes[] = {
//1 ТАКТ
{SOL2, NOTE_QUARTER},
{MI3, NOTE_EIGHTH},
{SI3, NOTE_EIGHTH},
{MI4, NOTE_EIGHTH},
{SI3, NOTE_EIGHTH},
{SOL3, NOTE_EIGHTH},
{SI3, NOTE_EIGHTH},
{DO4, NOTE_EIGHTH_DOT},
{LA3, NOTE_SIXTEENTH},
{SI3, NOTE_EIGHTH},
{SI2, NOTE_EIGHTH_DOT},
{DO2, NOTE_SIXTEENTH},
{SI3, NOTE_EIGHTH},
{MI3, NOTE_QUARTER},
//2 ТАКТ
{RE3s, NOTE_QUARTER},
{SI2, NOTE_EIGHTH},
{LA2, NOTE_EIGHTH},
{LA3, NOTE_EIGHTH},
{SI3, NOTE_SIXTEENTH},
{LA3, NOTE_EIGHTH},
{RE3, NOTE_SIXTEENTH},
{FA4s, NOTE_SIXTEENTH},
{LA4, NOTE_SIXTEENTH},
{RE3s, NOTE_EIGHTH},
{LA3, NOTE_SIXTEENTH},
{RE3s, NOTE_EIGHTH},
{SI4, NOTE_SIXTEENTH},
{FA4s, NOTE_SIXTEENTH},
{RE3s, NOTE_THIRTYSECOND},
{NOTE_REST, NOTE_THIRTYSECOND},
{RE3s, NOTE_EIGHTH},
{MI3, NOTE_EIGHTH},
{DO3, NOTE_SIXTEENTH},
{SI2, NOTE_SIXTEENTH},
{MI3, NOTE_SIXTEENTH},
};
static Melody_t Polyphia_PlayingGod = {song_length(Polyphia_PlayingGod_Notes), Polyphia_PlayingGod_Notes};

54
Core/Clock/sounds.h Normal file
View File

@@ -0,0 +1,54 @@
#pragma once
#include "notes.h"
#define song_length(song_arr) sizeof(song_arr)/sizeof(song_arr[0])
#define SFX_BPM 480 // 480 BPM для звуковых эффектов
// ==================== ЗВУКОВЫЕ ЭФФЕКТЫ ====================
// Короткий писк
static const Note_t SFX_Beep_Notes[] = {
{DO4, NOTE_SIXTEENTH}
};
static Melody_t SFX_Beep = {song_length(SFX_Beep_Notes), SFX_Beep_Notes};
// Двойной писк
static const Note_t SFX_DoubleBeep_Notes[] = {
{DO4, NOTE_SIXTEENTH},
{NOTE_REST, NOTE_SIXTEENTH},
{DO4, NOTE_SIXTEENTH}
};
static Melody_t SFX_DoubleBeep = {song_length(SFX_DoubleBeep_Notes), SFX_DoubleBeep_Notes};
// Ошибка
static const Note_t SFX_Error_Notes[] = {
{DO4, NOTE_EIGHTH},
{NOTE_REST, NOTE_EIGHTH},
{DO4, NOTE_EIGHTH}
};
static Melody_t SFX_Error = {song_length(SFX_Error_Notes), SFX_Error_Notes};
// Успех
static const Note_t SFX_Success_Notes[] = {
{DO4, NOTE_SIXTEENTH},
{MI4, NOTE_SIXTEENTH},
{SOL4, NOTE_QUARTER}
};
static Melody_t SFX_Success = {song_length(SFX_Success_Notes), SFX_Success_Notes};
// Нажатие кнопки
static const Note_t SFX_Click_Notes[] = {
{DO4, NOTE_THIRTYSECOND}
};
static Melody_t SFX_Click = {song_length(SFX_Click_Notes), SFX_Click_Notes};
// Тревога
static const Note_t SFX_Alarm_Notes[] = {
{LA4, NOTE_EIGHTH},
{NOTE_REST, NOTE_EIGHTH},
{LA4, NOTE_EIGHTH},
{NOTE_REST, NOTE_EIGHTH},
{LA4, NOTE_EIGHTH}
};
static Melody_t SFX_Alarm = {song_length(SFX_Alarm_Notes), SFX_Alarm_Notes};

View File

@@ -25,9 +25,10 @@
/* Private includes ----------------------------------------------------------*/ /* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */ /* USER CODE BEGIN Includes */
#include "clock_manager.h" #include "clock_manager.h"
#include "menu.h" #include "menu_items.h"
#include "segment.h" #include "segment.h"
#include "melody.h" #include "melody.h"
#include "songs.h"
/* USER CODE END Includes */ /* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/ /* Private typedef -----------------------------------------------------------*/
@@ -79,49 +80,9 @@ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
Segment_Process(); Segment_Process();
} }
} }
MelodyHandle melody;
const Note mySong[] = {
// //1 ÒÀÊÒ
// {SOL2, NOTE_QUARTER},
// {MI3, NOTE_EIGHTH},
// {SI3, NOTE_EIGHTH},
// {MI4, NOTE_EIGHTH},
// {SI3, NOTE_EIGHTH},
// {SOL3, NOTE_EIGHTH},
// {SI3, NOTE_EIGHTH},
// {DO4, NOTE_EIGHTH_DOT},
// {LA3, NOTE_SIXTEENTH},
// {SI3, NOTE_EIGHTH},
// {SI2, NOTE_EIGHTH_DOT},
// {DO2, NOTE_SIXTEENTH},
// {SI3, NOTE_EIGHTH},
// {MI3, NOTE_QUARTER},
//2 ÒÀÊÒ #define curr_song Polyphia_OD
{RE3s, NOTE_QUARTER}, Melody_t *mySong = &curr_song;
{SI2, NOTE_EIGHTH},
{LA2, NOTE_EIGHTH},
{LA3, NOTE_EIGHTH},
{SI3, NOTE_SIXTEENTH},
{LA3, NOTE_EIGHTH},
{RE3, NOTE_SIXTEENTH},
{FA4s, NOTE_SIXTEENTH},
{LA4, NOTE_SIXTEENTH},
{RE3s, NOTE_EIGHTH},
{LA3, NOTE_SIXTEENTH},
{RE3s, NOTE_EIGHTH},
{SI4, NOTE_SIXTEENTH},
{FA4s, NOTE_SIXTEENTH},
{RE3s, NOTE_THIRTYSECOND},
{NOTE_REST, NOTE_THIRTYSECOND},
{RE3s, NOTE_EIGHTH},
{MI3, NOTE_EIGHTH},
{DO3, NOTE_SIXTEENTH},
{SI2, NOTE_SIXTEENTH},
{MI3, NOTE_SIXTEENTH},
};
/* USER CODE END 0 */ /* USER CODE END 0 */
@@ -165,13 +126,13 @@ int main(void)
/* USER CODE BEGIN WHILE */ /* USER CODE BEGIN WHILE */
Segment_Init(); Segment_Init();
ClockManager_Init(); ClockManager_Init();
Menu_Init(); Menu_Init(&g_clockNode);
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
HAL_TIM_Base_Start_IT(&htim2); HAL_TIM_Base_Start_IT(&htim2);
// Èíèöèàëèçàöèÿ ñ BPM = 120 MenuItems_Init();
Melody_Init(&melody, &htim1, TIM_CHANNEL_1, 72000000); Melody_Init(&melody, &htim1, TIM_CHANNEL_1, 72000000);
Melody_Play(&melody, mySong, sizeof(mySong)/sizeof(mySong[0]), 80); // Melody_Play(&melody, mySong, 134);
while (1) while (1)
{ {
Menu_Process(); Menu_Process();

View File

@@ -170,6 +170,16 @@
<WinNumber>1</WinNumber> <WinNumber>1</WinNumber>
<ItemText>htim1.Instance,0x0A</ItemText> <ItemText>htim1.Instance,0x0A</ItemText>
</Ww> </Ww>
<Ww>
<count>4</count>
<WinNumber>1</WinNumber>
<ItemText>g_ctx</ItemText>
</Ww>
<Ww>
<count>5</count>
<WinNumber>1</WinNumber>
<ItemText>g_game1sec</ItemText>
</Ww>
</WatchWindow1> </WatchWindow1>
<Tracepoint> <Tracepoint>
<THDelay>0</THDelay> <THDelay>0</THDelay>
@@ -598,6 +608,30 @@
<tvExp>0</tvExp> <tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg> <tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2> <bDave2>0</bDave2>
<PathWithFileName>..\Core\Clock\menu_items.c</PathWithFileName>
<FilenameWithoutPath>menu_items.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>28</FileNumber>
<FileType>5</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\Core\Clock\menu_items.h</PathWithFileName>
<FilenameWithoutPath>menu_items.h</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>29</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\Core\Clock\menu.c</PathWithFileName> <PathWithFileName>..\Core\Clock\menu.c</PathWithFileName>
<FilenameWithoutPath>menu.c</FilenameWithoutPath> <FilenameWithoutPath>menu.c</FilenameWithoutPath>
<RteFlg>0</RteFlg> <RteFlg>0</RteFlg>
@@ -605,7 +639,7 @@
</File> </File>
<File> <File>
<GroupNumber>5</GroupNumber> <GroupNumber>5</GroupNumber>
<FileNumber>28</FileNumber> <FileNumber>30</FileNumber>
<FileType>5</FileType> <FileType>5</FileType>
<tvExp>0</tvExp> <tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg> <tvExpOptDlg>0</tvExpOptDlg>
@@ -617,7 +651,7 @@
</File> </File>
<File> <File>
<GroupNumber>5</GroupNumber> <GroupNumber>5</GroupNumber>
<FileNumber>29</FileNumber> <FileNumber>31</FileNumber>
<FileType>1</FileType> <FileType>1</FileType>
<tvExp>0</tvExp> <tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg> <tvExpOptDlg>0</tvExpOptDlg>
@@ -629,7 +663,7 @@
</File> </File>
<File> <File>
<GroupNumber>5</GroupNumber> <GroupNumber>5</GroupNumber>
<FileNumber>30</FileNumber> <FileNumber>32</FileNumber>
<FileType>5</FileType> <FileType>5</FileType>
<tvExp>0</tvExp> <tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg> <tvExpOptDlg>0</tvExpOptDlg>
@@ -641,7 +675,7 @@
</File> </File>
<File> <File>
<GroupNumber>5</GroupNumber> <GroupNumber>5</GroupNumber>
<FileNumber>31</FileNumber> <FileNumber>33</FileNumber>
<FileType>1</FileType> <FileType>1</FileType>
<tvExp>0</tvExp> <tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg> <tvExpOptDlg>0</tvExpOptDlg>
@@ -653,7 +687,7 @@
</File> </File>
<File> <File>
<GroupNumber>5</GroupNumber> <GroupNumber>5</GroupNumber>
<FileNumber>32</FileNumber> <FileNumber>34</FileNumber>
<FileType>5</FileType> <FileType>5</FileType>
<tvExp>0</tvExp> <tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg> <tvExpOptDlg>0</tvExpOptDlg>
@@ -663,6 +697,42 @@
<RteFlg>0</RteFlg> <RteFlg>0</RteFlg>
<bShared>0</bShared> <bShared>0</bShared>
</File> </File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>35</FileNumber>
<FileType>5</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\Core\Clock\songs.h</PathWithFileName>
<FilenameWithoutPath>songs.h</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>36</FileNumber>
<FileType>5</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\Core\Clock\sounds.h</PathWithFileName>
<FilenameWithoutPath>sounds.h</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>37</FileNumber>
<FileType>5</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\Core\Clock\notes.h</PathWithFileName>
<FilenameWithoutPath>notes.h</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
</Group> </Group>
<Group> <Group>

View File

@@ -777,6 +777,16 @@
<FileType>5</FileType> <FileType>5</FileType>
<FilePath>..\Core\Clock\clock_manager.h</FilePath> <FilePath>..\Core\Clock\clock_manager.h</FilePath>
</File> </File>
<File>
<FileName>menu_items.c</FileName>
<FileType>1</FileType>
<FilePath>..\Core\Clock\menu_items.c</FilePath>
</File>
<File>
<FileName>menu_items.h</FileName>
<FileType>5</FileType>
<FilePath>..\Core\Clock\menu_items.h</FilePath>
</File>
<File> <File>
<FileName>menu.c</FileName> <FileName>menu.c</FileName>
<FileType>1</FileType> <FileType>1</FileType>
@@ -807,6 +817,21 @@
<FileType>5</FileType> <FileType>5</FileType>
<FilePath>..\Core\Clock\melody.h</FilePath> <FilePath>..\Core\Clock\melody.h</FilePath>
</File> </File>
<File>
<FileName>songs.h</FileName>
<FileType>5</FileType>
<FilePath>..\Core\Clock\songs.h</FilePath>
</File>
<File>
<FileName>sounds.h</FileName>
<FileType>5</FileType>
<FilePath>..\Core\Clock\sounds.h</FilePath>
</File>
<File>
<FileName>notes.h</FileName>
<FileType>5</FileType>
<FilePath>..\Core\Clock\notes.h</FilePath>
</File>
</Files> </Files>
</Group> </Group>
<Group> <Group>