Follow+Star Public Account Number, don’t miss out on exciting content
Source | Rooftop Sparrow Studio
As the performance of MCUs continues to improve, using MCUs for GUI has become increasingly popular. Today, I will share a menu framework created for monochrome screens.Open source address:
https://github.com/wujique/stm32f407/tree/sw_arch
1. Overview
The menu referred to here is designed for small screens like<span>128*64</span>, such as the one shown below, and is not a GUI for color screens.

2. Menu Framework Design
As a low-level driver engineer, once the driver is completed, a hardware testing program needs to be written. This testing program is generally used by the testing department/hardware engineers to test the hardware and is also used for testing semi-finished products on the factory production line.
Initially, I was lazy and didn’t want to go straight to a one-second implementation, so I made all menus in a layered manner.
void test_main(void)
{
while(1)
{
get_key(&key);
switch(key)
{
case1:
test_key();
break;
case2:
test_lcd();
break;
....
}
}
}
As the number of menus increased, it became cumbersome. This way of writing is inconvenient for maintenance, looks unattractive, and wastes program space.
As a programmer who reads “The Beauty of Programming“, I decided to change the situation. After searching for a long time on search engines, I found two references: “Design of Multi-layer LCD Menu Interface Based on Binary Tree” and “Design Method and Implementation of General Tree Menu Based on Node Number.pdf”. Following their design methods, I tinkered with a version that works well, but it still felt cumbersome.
This is because they used a tree data structure. While this is excellent for program execution and efficiency, for me, the menu code is one-time, but the menu content changes frequently. Maintaining a tree with dozens or hundreds of menus in my head is not easy.
After thinking it over, what exactly is wrong with these menus? Why are they not user-friendly for me? I came to the following conclusions:
-
Too Broad Management. For menus, you only need to manage the menu switching; once you reach the lowest level, which is the actual testing function, you shouldn’t manage it anymore. Menu switching is similar, but the actual tests are different. For example, in the menu, pressing key 1 enters the first menu. However, in testing, key 1 has different functions. If the menu has to manage this as well, the same action has too many functions, making it difficult to achieve unified abstraction and modularization.
-
Different Starting Points. The menus mentioned above are all designed with the goal of creating a good menu data structure for fast and efficient program execution. What I want is an easily maintainable menu structure. It doesn’t matter how messy or complicated the menu code is, and even if there are hundreds or thousands of menus, polling them would only take a few hundred microseconds, which is acceptable.
3. Improved Menu
Based on the requirements, I redesigned a menu structure.
/**
* @brief Menu Object
*/
typedef struct _strMenu
{
MenuLel l; ///< Menu Level
char cha[MENU_LANG_BUF_SIZE]; /// Chinese
char eng[MENU_LANG_BUF_SIZE]; /// English
MenuType type; /// Menu Type
s32 (*fun)(void); /// Test Function
}MENU;
Yes, it’s that simple. Each menu is this structure. By filling a list with this structure, we have our menu.
const MENU EMenuListTest[]=
{
MENU_L_0,// Menu Level
"测试程序",// Chinese
"test", // English
MENU_TYPE_LIST,// Menu Type
NULL,// Menu Function, only functional menus will execute, those with submenus will not execute
MENU_L_1,// Menu Level
"LCD",// Chinese
"LCD", // English
MENU_TYPE_LIST,// Menu Type
NULL,// Menu Function, only functional menus will execute, those with submenus will not execute
MENU_L_2,// Menu Level
"VSPI OLED",// Chinese
"VSPI OLED", // English
MENU_TYPE_FUN,// Menu Type
test_oled,// Menu Function, only functional menus will execute, those with submenus will not execute
MENU_L_2,// Menu Level
"I2C OLED",// Chinese
"I2C OLED", // English
MENU_TYPE_FUN,// Menu Type
test_i2coled,// Menu Function, only functional menus will execute, those with submenus will not execute
MENU_L_1,// Menu Level
"声音",// Chinese
"sound", // English
MENU_TYPE_LIST,// Menu Type
NULL,// Menu Function, only functional menus will execute, those with submenus will not execute
MENU_L_2,// Menu Level
"蜂鸣器",// Chinese
"buzzer", // English
MENU_TYPE_FUN,// Menu Type
test_test,// Menu Function, only functional menus will execute, those with submenus will not execute
MENU_L_2,// Menu Level
"DAC音乐",// Chinese
"DAC music", // English
MENU_TYPE_FUN,// Menu Type
test_test,// Menu Function, only functional menus will execute, those with submenus will not execute
MENU_L_2,// Menu Level
"收音",// Chinese
"FM", // English
MENU_TYPE_FUN,// Menu Type
test_test,// Menu Function, only functional menus will execute, those with submenus will not execute
MENU_L_1,// Menu Level
"触摸屏",// Chinese
"tp", // English
MENU_TYPE_LIST,// Menu Type
NULL,// Menu Function, only functional menus will execute, those with submenus will not execute
MENU_L_2,// Menu Level
"校准",// Chinese
"calibrate", // English
MENU_TYPE_FUN,// Menu Type
test_cal,// Menu Function, only functional menus will execute, those with submenus will not execute
MENU_L_2,// Menu Level
"测试",// Chinese
"test", // English
MENU_TYPE_FUN,// Menu Type
test_tp,// Menu Function, only functional menus will execute, those with submenus will not execute
MENU_L_1,// Menu Level
"按键",// Chinese
"KEY", // English
MENU_TYPE_FUN,// Menu Type
test_key,// Menu Function, only functional menus will execute, those with submenus will not execute
/* The last menu is an end menu, which is meaningless */
MENU_L_0,// Menu Level
"END",// Chinese
"END", // English
MENU_TYPE_NULL,// Menu Type
NULL,// Menu Function, only functional menus will execute, those with submenus will not execute
};
What are the characteristics and requirements of this menu list? 1 It needs a root node and an end node 2 Child nodes must follow the parent node, similar to the structure below.
-----------------------------------------------
Root Node
First Level Menu 1
First Submenu 1
First Submenu 2
First Submenu 3
Second Level Menu 1
First Submenu 1
First Grandchild Menu 1
Second Grandchild Menu 2
Second Submenu 2
Third Submenu 3
Third Level Menu 1
Fourth Level Menu 1
Fifth Level Menu 1
End Node
------------------------------------------------
The second first-level menu has three submenus, where the first submenu has two grandchildren (third-level menus).Maintaining the menu means maintaining this list, adding, deleting, and modifying is very easy. What about the menu program? Who cares? After defining the menu, run it through the following function:
emenu_run(WJQTestLcd, (MENU *)&WJQTestList[0], sizeof(WJQTestList)/sizeof(MENU), FONT_SONGTI_1616, 2);
- The first parameter is which LCD to display the menu on,
- The second is the menu list,
- The third is the menu length,
- The fourth is the font,
- The fifth is the line spacing
Note: Running this menu requires an RTOS because the menu code is in a while(1) loop, which will trap it indefinitely. Other threads (TASK) are needed to maintain the system, such as key scanning.
If you need to quickly evaluate Cortex-A processors, you can check out the evaluation board from Chuanglong Technology, now with free shipping for only 99 yuan (original price 299 yuan).
⬇⬇⬇⬇⬇

4. Menu Implementation Effects
Related files: emenu.c, emenu.h, emenu_test.c
Current code:
1 Implements a dual-column menu, allowing selection of the next layer using number keys. A maximum of 8 menus can be displayed per page (using keys 1-8 on a 4*4 keyboard)
2 Implements a single-column menu, allowing scrolling through the menu with up and down keys, and the confirmation key to enter the menu. 3 The top menu has not been implemented; anyone interested can add it.
3 Based on the LCD driver architecture, this simple menu adapts to various LCDs.
The effects are as follows; feel free to take it if you need it, no thanks necessary.
5. Display Effects






6. Conclusion
This concludes the article. Interested readers can refer to this method to write their own menu framework.
Address:
https://github.com/wujique/stm32f407/tree/sw_arch
Disclaimer: The materials in this article are sourced from the internet, and the copyright belongs to the original author. If there are any copyright issues, please contact me for removal.