【STM32】贪吃蛇 [阶段 3] 增强模块结构(架构优化)
这篇博客是 承接:【项目思维】贪吃蛇(嵌入式进阶方向)中 聚焦于 🧱 阶段 3:增强模块结构(架构优化) 中的 菜单系统(Menu System),这部分的结构优化可以学到的内容包含:工程项目架构设计、模块划分、状态管理、事件响应、接口抽象、可扩展性(预留) 等方面。接下来,我将对这些内容进行详细说明。
🧱 阶段 3:菜单系统的架构优化(模块化 + 状态机)
🎯 目标:
构建一个可维护、可扩展、可复用的菜单系统框架,为后续游戏 / 应用系统提供通用架构。
一、总体架构图
Main
├── MenuManager(菜单框架)
│ ├── 状态管理(enum GameState)
│ ├── 菜单项结构体(MenuItem)
│ └── 菜单事件派发器(事件响应)
├── UI_Display(接口抽象)
│ ├── OLED 显示实现
│ └── TFT 显示实现
├── InputManager(输入管理)
│ ├── 按键扫描
│ └── 输入事件封装
├── Assets(资源)
│ ├── 字体 / 图标
│ └── 菜单配置
📦 二、文件结构 + 模块划分(示例)
此处的模块化划分仅供参考:模块化的具体情况要根据具体项目进行合理化分解。
📁 Core/
│ ├── main.c
│ ├── app.c / app.h // 主程序调度
│ └── game_state.c / .h // 状态控制
📁 Menu/
│ ├── menu.c / menu.h // 菜单控制逻辑
│ └── menu_items.c / .h // 菜单项配置
📁 Drivers/
│ ├── oled.c / .h // OLED 显示驱动
│ ├── tft.c / .h // TFT 驱动
│ └── input.c / .h // 输入处理(按键)
📁 Assets/
│ └── font.c / icon.c // 字体、图标资源
📦 三、状态管理(GameState)
typedef enum {STATE_MENU,STATE_PLAY,STATE_PAUSE,STATE_GAME_OVER
} GameState;static GameState current_state = STATE_MENU;void switch_state(GameState new_state) {current_state = new_state;// 可在此执行 state entry 动作
}
🔖 四、菜单项结构体定义
typedef struct MenuItem {const char* name;void (*on_select)(void); // 选中时执行的回调
} MenuItem;MenuItem main_menu[] = {{"Start Game", start_game},{"Settings", open_settings},{"About", show_about},{"Exit", system_exit}
};
🎮 五、菜单控制逻辑(MenuManager)
1. 菜单状态结构:
typedef struct {int current_index;MenuItem* items;int item_count;
} MenuState;MenuState menu = {.current_index = 0,.items = main_menu,.item_count = sizeof(main_menu)/sizeof(MenuItem)
};
2. 菜单事件响应:
void menu_handle_event(InputEvent evt) {switch (evt) {case EVENT_UP:menu.current_index = (menu.current_index - 1 + menu.item_count) % menu.item_count;break;case EVENT_DOWN:menu.current_index = (menu.current_index + 1) % menu.item_count;break;case EVENT_OK:if (menu.items[menu.current_index].on_select)menu.items[menu.current_index].on_select();break;default:break;}
}
🖥️ 六、显示层抽象(支持 OLED / TFT)
1. 显示接口定义(display_interface.h
):
typedef struct {void (*clear)(void);void (*draw_text)(int x, int y, const char* text, bool selected);void (*refresh)(void);
} DisplayInterface;extern DisplayInterface* display; // 指向当前使用的显示驱动
2. OLED 实现:
DisplayInterface oled_display = {.clear = oled_clear,.draw_text = oled_draw_text,.refresh = oled_refresh
};
使用方式:
void render_menu(MenuState* m) {display->clear();for (int i = 0; i < m->item_count; ++i) {display->draw_text(0, i*10, m->items[i].name, i == m->current_index);}display->refresh();
}
🕹️ 七、事件机制(按键输入与事件派发)
1. 输入事件定义:
typedef enum {EVENT_NONE,EVENT_UP,EVENT_DOWN,EVENT_OK
} InputEvent;
2. 按键扫描模块输入处理:
InputEvent read_input_event() {if (key_press(KEY_UP)) return EVENT_UP;if (key_press(KEY_DOWN)) return EVENT_DOWN;if (key_press(KEY_OK)) return EVENT_OK;return EVENT_NONE;
}
此处可以加入 事件队列机制,支持多个模块监听同一事件。
🛠️ 八、主函数(main.c
)
int main(void) {hardware_init();display = &oled_display; // 或 tft_displaywhile (1) {InputEvent evt = read_input_event();switch (current_state) {case STATE_MENU:menu_handle_event(evt);render_menu(&menu);break;case STATE_PLAY:game_run();break;// 更多状态...}delay_ms(50);}
}
菜单系统设计原则
模块解耦:菜单逻辑、UI 显示、输入处理独立开发。
状态明确:使用 GAME_STATE 管理程序流程。
接口抽象:显示接口统一支持不同设备。
事件驱动:菜单响应按键事件,支持外部事件扩展。
可扩展性:菜单项可动态增加 / 嵌套子菜单。
这个菜单后续还可以扩展更多的功能:
功能 实现方式
菜单嵌套 每个菜单项跳转到子菜单(子 MenuItem 数组)
动画效果 菜单切换时加入滑动、淡入淡出
声音反馈 按键 / 选中时播放 beep 声
存档支持 菜单设置保存到 Flash
多语言支持 使用字符串资源表切换语言
以上,欢迎有从事同行业的电子信息工程、互联网通信、嵌入式开发的朋友共同探讨与提问,我可以提供实战演示或模板库。希望内容能够对你产生帮助!