Рефакторинг меню и добавление кучи всего:
- иерархическая и универсальная структура меню, которую относительно удобно расширять и добавлять - заготовик меню для таймера и секундомера - работающие игры (выбить 1.00 сек, тест реакции, кликер)
This commit is contained in:
@@ -1,426 +1,195 @@
|
||||
#include "menu.h"
|
||||
#include "segment.h"
|
||||
#include "clock_manager.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// Время удержания кнопки (мс), после которого нажатие считается "длинным"
|
||||
// Используется для входа в меню из режима часов
|
||||
#define LONG_PRESS_MS 500
|
||||
#define LONG_PRESS_MS 500
|
||||
|
||||
// Задержка перед началом автоповтора (мс)
|
||||
// При удержании кнопки первые 500 мс ничего не происходит
|
||||
#define REPEAT_DELAY_MS 500
|
||||
MenuContext g_ctx;
|
||||
uint8_t g_sound_enabled = 1;
|
||||
|
||||
// Интервал между автоповторами (мс)
|
||||
// После начала повтора каждые 100 мс кнопка срабатывает заново
|
||||
#define REPEAT_INTERVAL_MS 100
|
||||
extern bool ReadButton(Button_Type btn);
|
||||
extern MenuNode* Menu_GetRootMenu(void);
|
||||
|
||||
// Интервал мигания редактируемого разряда (мс)
|
||||
// Моргает каждые 500 мс (0.5 сек вкл / 0.5 сек выкл)
|
||||
#define BLINK_INTERVAL_MS 500
|
||||
|
||||
// Задержка антидребезга (мс)
|
||||
// Изменение состояния кнопки фиксируется только если оно стабильно в течение 30 мс
|
||||
#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;
|
||||
void Menu_Init(MenuNode* startNode) {
|
||||
g_ctx.currentNode = startNode;
|
||||
g_ctx.needsRedraw = true;
|
||||
g_ctx.lastTick = 0;
|
||||
|
||||
// Для 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;
|
||||
if (startNode && startNode->onEnter) {
|
||||
startNode->onEnter();
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== Логика SET_TIME ====================
|
||||
static void IncreaseTimeDigit(void) {
|
||||
uint8_t tens, units;
|
||||
switch (menu.editStep) {
|
||||
case 0: // десятки часов (0-2)
|
||||
tens = menu.editTime.hour / 10;
|
||||
units = menu.editTime.hour % 10;
|
||||
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;
|
||||
// НОВАЯ ФУНКЦИЯ - открыть меню
|
||||
void Menu_OpenMenu(MenuNode* menu) {
|
||||
if (!menu) return;
|
||||
g_ctx.currentNode = menu;
|
||||
g_ctx.needsRedraw = true;
|
||||
if (menu->onEnter) {
|
||||
menu->onEnter();
|
||||
}
|
||||
UpdateDisplay();
|
||||
}
|
||||
|
||||
static void DecreaseTimeDigit(void) {
|
||||
uint8_t tens, units;
|
||||
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: // единицы часов
|
||||
tens = menu.editTime.hour / 10;
|
||||
units = menu.editTime.hour % 10;
|
||||
if (units == 0) {
|
||||
units = (tens == 2) ? 3 : 9;
|
||||
} else {
|
||||
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();
|
||||
void Menu_HandleButton(Button_Type btn, bool longPress) {
|
||||
if (!g_ctx.currentNode) return;
|
||||
|
||||
// Длинное нажатие SELECT на корневом уровне (часы) - вход в меню
|
||||
if (longPress && btn == BUTTON_SELECT && g_ctx.currentNode->parent == NULL) {
|
||||
SOUND_DOUBLE;
|
||||
MenuNode* rootMenu = Menu_GetRootMenu();
|
||||
if (rootMenu) {
|
||||
Menu_OpenMenu(rootMenu);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
switch (menu.state) {
|
||||
case STATE_MAIN_MENU:
|
||||
if (btn == BUTTON_UP) {
|
||||
menu.selectedMenuItem++;
|
||||
if (menu.selectedMenuItem >= MAIN_MENU_COUNT) {
|
||||
menu.selectedMenuItem = 0;
|
||||
}
|
||||
UpdateDisplay();
|
||||
}
|
||||
else if (btn == BUTTON_DOWN) {
|
||||
if (menu.selectedMenuItem == 0) {
|
||||
menu.selectedMenuItem = MAIN_MENU_COUNT - 1;
|
||||
} else {
|
||||
menu.selectedMenuItem--;
|
||||
}
|
||||
UpdateDisplay();
|
||||
}
|
||||
else if (btn == BUTTON_SELECT && !longPress) {
|
||||
switch (menu.selectedMenuItem) {
|
||||
case MAIN_MENU_SET_TIME:
|
||||
menu.state = STATE_SET_TIME;
|
||||
menu.originalTime = ClockManager_GetTime(0);
|
||||
menu.editTime = menu.originalTime;
|
||||
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;
|
||||
|
||||
case STATE_SET_TIME:
|
||||
if (btn == BUTTON_UP) {
|
||||
IncreaseTimeDigit();
|
||||
}
|
||||
else if (btn == BUTTON_DOWN) {
|
||||
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;
|
||||
|
||||
case STATE_SET_DUTY:
|
||||
if (btn == BUTTON_UP && menu.editDuty < 10) {
|
||||
menu.editDuty++;
|
||||
Segment_SetBrightness(menu.editDuty * 10);
|
||||
UpdateDisplay();
|
||||
}
|
||||
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;
|
||||
|
||||
case STATE_RESET_CONFIRM:
|
||||
if (btn == BUTTON_SELECT) {
|
||||
ClockManager_ResetTime();
|
||||
ClockManager_SetDuty(8);
|
||||
menu.state = STATE_CLOCK;
|
||||
UpdateDisplay();
|
||||
}
|
||||
else if (btn == BUTTON_BACK) {
|
||||
menu.state = STATE_CLOCK;
|
||||
UpdateDisplay();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
if (g_ctx.currentNode->onButton) {
|
||||
if(btn == BUTTON_SELECT)
|
||||
{
|
||||
SOUND_DOUBLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
SOUND_CLICK;
|
||||
}
|
||||
|
||||
g_ctx.currentNode->onButton(btn, longPress);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== Публичные функции ====================
|
||||
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;
|
||||
if (g_ctx.currentNode->parent != NULL) {
|
||||
switch (btn) {
|
||||
case BUTTON_UP:
|
||||
SOUND_CLICK;
|
||||
if (g_ctx.currentNode->selectedChild > 0) {
|
||||
g_ctx.currentNode->selectedChild--;
|
||||
g_ctx.needsRedraw = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case BUTTON_DOWN:
|
||||
SOUND_CLICK;
|
||||
if (g_ctx.currentNode->selectedChild < g_ctx.currentNode->childCount - 1) {
|
||||
g_ctx.currentNode->selectedChild++;
|
||||
g_ctx.needsRedraw = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case BUTTON_SELECT: {
|
||||
SOUND_DOUBLE;
|
||||
if (g_ctx.currentNode->children) {
|
||||
MenuNode* selected = g_ctx.currentNode->children[g_ctx.currentNode->selectedChild];
|
||||
if (selected) {
|
||||
g_ctx.currentNode = selected;
|
||||
g_ctx.needsRedraw = true;
|
||||
if (selected->onEnter) {
|
||||
selected->onEnter();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case BUTTON_BACK:
|
||||
SOUND_CLICK;
|
||||
Menu_GoBack();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Menu_Process(void) {
|
||||
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++) {
|
||||
bool rawPressed = ReadButton(i);
|
||||
bool pressed;
|
||||
|
||||
// Антидребезг
|
||||
if (rawPressed != menu.buttonStableState[i]) {
|
||||
menu.lastDebounceTime[i] = tick;
|
||||
menu.buttonStableState[i] = rawPressed;
|
||||
}
|
||||
|
||||
if ((tick - menu.lastDebounceTime[i]) >= DEBOUNCE_MS) {
|
||||
pressed = menu.buttonStableState[i];
|
||||
} else {
|
||||
pressed = menu.buttonPrevState[i]; // старое значение пока не стабилизировалось
|
||||
}
|
||||
bool wasPressed = menu.buttonPrevState[i];
|
||||
bool reading = ReadButton((Button_Type)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;
|
||||
if (reading != g_ctx.lastButtonState[i]) {
|
||||
g_ctx.lastDebounceTime[i] = tick;
|
||||
}
|
||||
|
||||
menu.buttonPrevState[i] = pressed;
|
||||
if ((tick - g_ctx.lastDebounceTime[i]) > 30) { // DEBOUNCE_MS = 30
|
||||
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 {
|
||||
if (!g_ctx.longPressTriggered[i]) {
|
||||
Menu_HandleButton((Button_Type)i, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (g_ctx.buttonState[i] && !g_ctx.longPressTriggered[i]) {
|
||||
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;
|
||||
}
|
||||
|
||||
static uint32_t lastClockUpdate = 0;
|
||||
if (menu.state == STATE_CLOCK && (tick - lastClockUpdate >= 200)) {
|
||||
lastClockUpdate = tick;
|
||||
UpdateDisplay();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
SystemState Menu_GetState(void) {
|
||||
return menu.state;
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user