這篇博客是 承接:【項目思維】貪吃蛇(嵌入式進階方向)中 聚焦于 🧱 階段 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
多語言支持 使用字符串資源表切換語言
以上,歡迎有從事同行業的電子信息工程、互聯網通信、嵌入式開發的朋友共同探討與提問,我可以提供實戰演示或模板庫。希望內容能夠對你產生幫助!