完全由C编写,高度可移植,超级牛逼的菜单架构!
今天给大家推荐一个非常不错的菜单架构,好朋友宋工写的,他把他认为最能拿得出手的东西贡献了出来
,是一个挺不错的菜单架构,大家拿到源码,可以自己学习借鉴下。
框架,相对一套完整的程序来说,往往是最重要的,一个好的程序框架,不仅能使代码更加清晰明了,稳定运行,而且可以很方便的扩展功能,非常易于维护。很多大神的经典代码框架,大家都可以欣赏下,之后,完全领悟其中的精髓,融会贯通,用于实际的项目,为社会创造价值。之前分享过两篇,这里重新贴出来一下:
开个玩笑,接下来进入正题。
首先看一下菜单视频,这个作品是17年参加立创电子设计大赛的,得了个优秀奖。
这个菜单系统会根据定义自动构建菜单层级,超时自动退回主界面,菜单值预览等功能。
0主界面
1日期设置 (设置阳历,农历自动计算)
2时间设置
-----------2.1时间调整
-----------2.2 24小时制
-----------2.3 时间校准 (用于时间隔一段时间自动加减几秒,PS:DS3231的精度够了,完全不用校准)
3语音设置
-----------3.1 播报模式 (详细模式,简洁模式)
-----------3.2 播报时段 (设置几点到几点开启语音播报)
-----------3.3 音量
4温度选择 (选择显示的文档来自哪一个传感器,并且在每个传感器里面可以设置各自的补偿)
-----------4.1 DS3231
-----------4.2 DS18B20
-----------4.3 SHT30
5 显示效果 (目前使用的是渐变切换效果,感觉这个最入眼,什么滚动,百叶窗的个人不喜欢,留个接口后面慢慢加进去吧)
6亮度设置
------------6.1 亮度模式 (固定亮度或者自动亮度)
------------6.2 亮度标定 (设置自动亮度时,最高光线和最暗光线对应的亮度值)
------------6.3 环境亮度 (获取环境的最高和最暗光线值)
--------------------------6.3.1 自动学习 (24小时自动采集环境的最高和最暗光线值)
--------------------------6.3.2 手动采集 (手动采集环境的最高和最暗光线值)
--------------------------6.3.3 手动设置 (手动指定)
7 遥控设置
------------7.1遥控开关 (使用完遥控后可在此关闭遥控的响应,这样用遥控控制原设备的时候就不会影响到时钟了)
------------7.2 遥控学习
8 字体设置
------------8.1 字体选择 (选择时钟字体、菜单字体)
------------8.2 字体颜色 (选择时钟颜色、菜单颜色)
9 频谱 (暂时没做,接口留着,后面慢慢加)
-------------9.1颜色设置
-------------9.2 分频设置
-------------9.3 打开频谱
10 授时设置 (DS3231的精度足够,个人感觉不用浪费买个GPS或者网络模块进行授时,不过接口放在这里,可以在这里设置用什么模块授时,什么时候授时)
11 闹钟设置 (6组闹钟,可以单独设置周内的星期几响)
-------------11.1闹钟一
-------------11.2闹钟二
-------------11.3闹钟三
-------------11.4闹钟四
-------------11.5闹钟五
-------------11.6闹钟六
12 屏幕检测 (考虑到LED点阵屏幕会有坏点,所以加了个屏幕检测功能,红-绿-黄-三色循环显示)
13 还原设置
14 版本信息
这个菜单架构支持多级菜单,三级,四级,五级。。。接下来看一下菜单定义方式,汉字部分是菜单要显示的文字,数字部分表示菜单层级关系。0是主界面 ,1,2,3,4。是最顶层菜单,201,202,203是菜单2的下一级菜单,如果要给201时间调整下面再增加一级子菜单,只需要复制一行,把数字这里改成20101就可以了,要删除某一菜单也很简单,如果该菜单是最底层菜单直接删除该行定义就行了,如果有子菜单,可以通过改数字,把下级菜单提到上级。MENU_MODE_QUICKVIEW是我后来加的,意思是显示菜单列表的时候,是否在该行最右边显示菜单内的值,比如:
闹钟一。。。。关
闹钟二。。。。开
这样,就不用点ok键很快就可以看到总开关了,需要设置具体的值的时候再点ok进去设置。另外这个MenuNode结构体后面得再改造一下,现在的结构没有把常量与状态量分开存放,有些浪费RAM,后期改进。
typedef struct _MenuNode{ const unsigned char* Title; unsigned long int ID: 26; unsigned long int Mode: 1; unsigned long int Status: 1; unsigned long int CurrentItem: 4; char* Message_String; struct _MenuNode* BrotherNode_First; struct _MenuNode* BrotherNode_End; struct _MenuNode* BrotherNode_Prev; struct _MenuNode* BrotherNode_Next; struct _MenuNode* FatherNode; struct _MenuNode* SonNode_First; void (*SetPageRun) (void* Para);} MenuNode;MenuNode MenuNodes[MENU_NODES_COUNT] ={ { (const unsigned char*)'主界面', 0, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, &Page_Main}, { (const unsigned char*)'日期设置', 1, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, &Page_Set_Date}, { (const unsigned char*)'时间设置', 2, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU}, { (const unsigned char*)'时间调整', 201, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, &Page_Set_Time}, { (const unsigned char*)'24小时制', 202, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, &Page_Set24Time}, { (const unsigned char*)'时间校准', 203, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, &Page_SetTimeCalibration}, { (const unsigned char*)'语音设置', 3, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU}, { (const unsigned char*)'播报模式', 301, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, &Page_SetVoiceMode}, { (const unsigned char*)'播报时间', 302, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, &Page_SetVoiceStartEnd}, { (const unsigned char*)'音量', 303, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, &Page_SetVolume}, { (const unsigned char*)'温度选择', 4, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU}, { (const unsigned char*)'DS3231', 401, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, &Page_Senser_Ds3231}, { (const unsigned char*)'DS18B20', 402, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, &Page_Senser_Ds18b20}, { (const unsigned char*)'SHT30', 403, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, &Page_Senser_Sht30}, { (const unsigned char*)'显示效果', 5, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU}, { (const unsigned char*)'时间参数', 501, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, &Page_DisplayTimerSet}, { (const unsigned char*)'页面开关', 502, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, &Page_DisplayPageEnable}, { (const unsigned char*)'版式选择', 503, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, &Page_PageSwitch}, { (const unsigned char*)'亮度设置', 6, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU}, { (const unsigned char*)'亮度模式', 601, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, &Page_SetBrightnessMode}, { (const unsigned char*)'亮度标定', 602, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, &Page_SetBrightnessCelibration}, { (const unsigned char*)'环境亮度', 603, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU}, { (const unsigned char*)'自动采集', 60301, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, &Page_EnvBightNessLearnAuto}, { (const unsigned char*)'手动采集', 60302, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, &Page_BrightnessLearnManuel}, { (const unsigned char*)'手动设置', 60303, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, &Page_BrightnessLearnSet}, { (const unsigned char*)'灵敏度', 604, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, &Page_SetBrightness_Sensitivity}, { (const unsigned char*)'遥控设置', 7, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU}, { (const unsigned char*)'遥控开关', 701, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, &Page_SetRemoteKeyEnable}, { (const unsigned char*)'遥控学习', 702, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, &Page_RemoteKeyLearn}, { (const unsigned char*)'字体设置', 8, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU}, { (const unsigned char*)'字体选择', 801, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, &Page_SetFontType}, { (const unsigned char*)'字体颜色', 802, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, &Page_SetFontColor}, { (const unsigned char*)'频谱', 9, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU}, { (const unsigned char*)'颜色设置', 901, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU}, { (const unsigned char*)'分频设置', 902, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU}, { (const unsigned char*)'打开频谱', 903, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU}, { (const unsigned char*)'授时设置', 10, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, &Page_GUI_Test}, { (const unsigned char*)'闹钟设置', 11, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU}, { (const unsigned char*)'闹钟一', 1101, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, &Page_SetAlarmClock}, { (const unsigned char*)'闹钟二', 1102, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, &Page_SetAlarmClock}, { (const unsigned char*)'闹钟三', 1103, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, &Page_SetAlarmClock}, { (const unsigned char*)'闹钟四', 1104, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, &Page_SetAlarmClock}, { (const unsigned char*)'闹钟五', 1105, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, &Page_SetAlarmClock}, { (const unsigned char*)'闹钟六', 1106, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, &Page_SetAlarmClock}, { (const unsigned char*)'屏幕检测', 12, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, &Page_ScreenLed_Check}, { (const unsigned char*)'还原设置', 13, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, &Page_RestoreDefault}, { (const unsigned char*)'版本信息', 14, MENU_MODE_QUICKVIEW, MENU_STATUS_NORMAL, MENU_PAGE_ITEM_DEFAULT, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, NULL_MENU, &Page_VisionInfo},};
void Page_SetVolume (MenuRunInfo* MenuRunInfos)
{
#define PAGE_ITEM_VOLUME 1
if (MenuRunInfos->CursorMenu->Mode == MENU_MODE_QUICKVIEW)
{
return;
}
else
{
}
switch (MenuRunInfos->CursorMenu->CurrentItem)
{
case MENU_PAGE_ITEM_DEFAULT:
MenuRunInfos->CursorMenu->CurrentItem = PAGE_ITEM_VOLUME;
if (MySettings.Volume > 10)
{
MySettings.Volume = 10;
}
MenuRunInfos->CursorMenu->Status = MENU_STATUS_HAS_UPATE;
break;
case PAGE_ITEM_VOLUME:
switch (*MenuRunInfos->KeyCode)
{
case BT_KEYCODE_MENU:
case BT_KEYCODE_MENU_RMET:
MenuRunInfos->flg_CurrentPage = MENU_CURRENT_PAGE_SET_LIST;
MenuRunInfos->CursorMenu->CurrentItem = MENU_PAGE_ITEM_DEFAULT;
MenuIf_ListShow (MenuRunInfos);
return;
case BT_KEYCODE_UP:
case BT_KEYCODE_UP_RMET:
if (MySettings.Volume < 10)
{
MySettings.Volume++;
//FIXME:SLF 2018-05-30 调整音量时播放试听音
MenuRunInfos->CursorMenu->Status = MENU_STATUS_HAS_UPATE;
}
break;
case BT_KEYCODE_DOWN:
case BT_KEYCODE_DOWN_RMET:
if (MySettings.Volume > 0)
{
MySettings.Volume--;
//FIXME:SLF 2018-05-30 调整音量时播放试听音
MenuRunInfos->CursorMenu->Status = MENU_STATUS_HAS_UPATE;
}
break;
case BT_KEYCODE_OK:
case BT_KEYCODE_OK_RMET:
break;
default:
break;
}
break;
}
if (MenuRunInfos->CursorMenu->Status == MENU_STATUS_HAS_UPATE)
{
Screen_Printf (0, 0, MySettings.FontCfg.FontType_Menu, SCREEN_COLOR_RED, SCREEN_UPDATE_NORMAL, '音量调整');
Screen_Printf (16, 0, FontsType_DefaultMix, SCREEN_COLOR_RED, SCREEN_UPDATE_NORMAL, '1-10');
if (MenuRunInfos->CursorMenu->CurrentItem == PAGE_ITEM_VOLUME)
{
MenuRunInfos->CursorMenu->Status = MENU_STATUS_NORMAL;
Screen_Clear (16, 40, 24, 16, SCREEN_COLOR_GREEN, SCREEN_UPDATE_NORMAL);
Screen_Printf (16, 40, FontsType_DefaultMix, SCREEN_COLOR_GREEN, SCREEN_UPDATE_NORMAL, '%d', MySettings.Volume);
}
Screen_UpdateImmediately (SCREEN_COLOR_ALL);
}
#undef PAGE_ITEM_VOLUME
}
传入键值。Menu_If.c是接口文件,MenuIf_MenuDispatcher函数不怎么需要改, MenuIf_ListShow (MenuRunInfo* MenuRunInfos)函数需要根据自己的显示屏幕改一下。然后就是定义菜单数组和写每一项菜单的页面函数了。传上我的文件,抛砖引玉,欢迎探讨!