下面我们就模仿手机菜单为例进行说明,首先需要说明几点:
1. 本例只有5个按键,所以没有退出按键,退出需要通过函数实现。
// 按键值定义
#defineKV_NO (0x00) // 无
#defineKV_UP (0x02) // 上
#define KV_DN (0x04) // 下
#define KV_MD (0x08) // 中
#defineKV_LF (0x10) // 左
#define KV_RT(0x20) // 右
2.本例液晶只可以显示4行汉子,每行显示8个汉字(128*64),所以一行显示标题,3行实现菜单显示和翻滚。
3.光标所以项反白显示。
4.对于按键与显示等,本例不在说明,只说明菜单实现方式。
菜单结构体定义:
/**************************************************************************************
* Variable definition
**************************************************************************************/
// 菜单参数
typedef struct _MENU_PRMT
{
uint8 ExitMark; // 退出菜单(0-不退出,1-退出)
uint8 Cursor; // 光标值(当前光标位置)
uint8 PageNo; // 菜单页(显示开始项)
uint8 Index; // 菜单索引(当前选择的菜单项)
uint8 DispNum; // 显示项数(每页可以现在菜单项)
uint8 MaxPage; // 最大页数(最大有多少种显示页)
} MENU_PRMT;
//------------------------------------------------------------------------------------
// 菜单执行
typedef struct _MENU_TABLE
{
uint8 *MenuName; // 菜单项目名称
void (*ItemHook)(void); // 要运行的菜单函数
} MENU_TABLE;
此处使用了两个结构体,一个是菜单项使用的参数,一个是菜单项的名称以及对应需要执行的函数。这里必须分成两个结构体,也是此菜单最大的不足之处,如果能够统一在一个结构体就好了。
----------------------------------------------------------------
网友可以对比单层菜单和这里的多层菜单的结构体,可以发现多层菜单添加了一个ExitMark,用来实现从子菜单层返回到母菜单的变量。。。
----------------------------------------------------------------
可是本人确实没有想出好的方法实现这两个结构体的统一。每个菜单对应一套参数可以理解,但是不同的菜单项却有不不同项数的菜单。例如下面我们要实现的菜单:
1. 主菜单
复制内容到剪贴板
//---------------------------------- 主菜单 ---------------------------------------
MENU_PRMT GSetPrmt; // 常规设置参数
MENU_TABLE GeneralSet[] = // 常规设置
{
{"1. 情景模式 ", Menu_Profile }, // 情景模式
{"2. 时间与日期 ", Menu_SetClock }, // 时间与日期
{"3. 语言 ", Menu_Null }, // 选择语言
{"4. 话机设置 ", Menu_Null }, // 话机设置
{"5. 语音设置 ", Menu_Null }, // 语音设置
{"6. 安全设置 ", Menu_Null }, // 安全设置
{"7. 网络设置 ", Menu_Null }, // 网络设置
{"8. 个人快捷设置 ", Menu_Null }, // 个人快捷设置
{"9. 恢复出厂设置 ", Menu_Null }, // 恢复出厂设置
{"10.退出菜单 ", Menu_Null }, // 退出菜单
};
2.二级菜单
复制内容到剪贴板
//----------------------------------- 二级菜单 -------------------------------------
MENU_PRMT ProfilePrmt; // 情景模式参数
MENU_TABLE Profile[] = // 情景模式
{
{"1. 标准 ", Menu_Null }, // 标准
{"2. 会议 ", Menu_Null }, // 会议
{"3. 户外 ", Menu_Null }, // 户外
{"4. 振动 ", Menu_Null }, // 振动
{"5. 耳机 ", Menu_Null }, // 耳机
{"6. 蓝牙模式 ", Menu_Null }, // 蓝牙模式
{"7. 离线模式 ", Menu_Null }, // 离线模式
{"8. 退出菜单 ", Menu_ExitProfile }, // 退出菜单
};
MENU_PRMT ClockPrmt; // 时间与日期参数
MENU_TABLE Clock[] = // 时间与日期
{
{"1. 时间/日期设置", Menu_Null }, // 时间/日期设置
{"2. 格式设置 ", Menu_Null }, // 格式设置
{"3. 设置城市 ", Menu_Null }, // 设置城市
{"4. 时区与时间 ", Menu_Null }, // 时区与时间
{"5. 退出时钟设置 ", Menu_ExitClockSet},// 退出时钟设置
};
上面我们列出了一个主菜单,2个二级菜单,我们可以看出,主菜单有10项,而二级菜单有8项5项等,使用导致了我们无法实现两个结构体的统一,如果那个网友能够想出统一的方法,希望能告诉一声,本人在这里先谢了。
以上菜单项中,没有实现的函数均用空函数代替,需要实现,直接填到相应想即可。
1.空函数
复制内容到剪贴板
/**************************************************************************************
* FunctionName : Menu_Null()
* Description : 空函数
* EntryParameter : None
* ReturnValue : None
**************************************************************************************/
void Menu_Null(void)
{
;
}
2. 按键函数
复制内容到剪贴板
/**************************************************************************************
* FunctionName : Menu_KeySan()
* Description : 获取按键值
* EntryParameter : None
* ReturnValue : 返回按键值
**************************************************************************************/
uint8 Menu_KeySan(void)
{
uint8 key = KV_NO;
key = KeyGetValue(); // 获取按键
if (key != KV_NO)
{
BuzzerSound(1); // 有键按下,响一声
while(KeyGetValue() != KV_NO) // 等待按键释放
{
;
}
}
return key;
}
3. 初始化函数
复制内容到剪贴板
/**************************************************************************************
* FunctionName : Menu_PrmtInit()
* Description : 初始化菜单参数
* EntryParameter : prmt - 菜单参数, num - 显示项, page - 显示页
* ReturnValue : None
**************************************************************************************/
void Menu_PrmtInit(MENU_PRMT *prmt, uint8 num, uint8 page)
{
prmt->ExitMark = 0; // 清除退出菜单标志
prmt->Cursor = 0; // 光标清零
prmt->PageNo = 0; // 页清零
prmt->Index = 0; // 索引清零
prmt->DispNum = num; // 页最多显示项目数
prmt->MaxPage = page; // 最多页数
}
4.菜单显示
复制内容到剪贴板
/**************************************************************************************
* FunctionName : Menu_Display()
* Description : 显示菜单
* EntryParameter : page - 显示页,dispNum - 每一页的显示项,cursor - 光标位置
* ReturnValue : None
**************************************************************************************/
void Menu_Display(MENU_TABLE *menu, uint8 page, uint8 dispNum, uint8 cursor)
{
uint8 i;
for (i=0; i<dispNum; i++)
{
if (cursor == i)
{
LCMDisplayStrRvs(0, (i+1)*2, menu[page+i].MenuName); // 反白显示菜单项
}
else
{
LCMDisplayStr(0, (i+1)*2, menu[page+i].MenuName); // 显示菜单项
}
}
}
5. 菜单移动
复制内容到剪贴板
/**************************************************************************************
* FunctionName : Menu_Move()
* Description : 菜单移动
* EntryParameter : prmt - 菜单参数, key - 按键值
* ReturnValue : 有确认返回0,否则返回1
**************************************************************************************/
uint8 Menu_Move(MENU_PRMT *prmt, uint8 key)
{
uint8 rValue = 1;
switch (key)
{
case KV_UP: // 向上
{
if (prmt->Cursor != 0) // 光标不在顶端
{
prmt->Cursor--; // 光标上移
}
else // 光标在顶端
{
if (prmt->PageNo != 0) // 页面没有到最小
{
prmt->PageNo--; // 向上翻
}
else
{
prmt->Cursor = prmt->DispNum-1; // 光标到底
prmt->PageNo= prmt->MaxPage-1; // 最后页
}
}
break;
}
case KV_DN: // 向下
{
if (prmt->Cursor < prmt->DispNum-1) // 光标没有到底,移动光标
{
prmt->Cursor++; // 光标向下移动
}
else // 光标到底
{
if (prmt->PageNo < prmt->MaxPage-1) // 页面没有到底,页面移动
{
prmt->PageNo++; // 下翻一页
}
else // 页面和光标都到底,返回开始页
{
prmt->Cursor = 0;
prmt->PageNo = 0;
}
}
break;
}
case KV_MD: // 确认
{
prmt->Index = prmt->Cursor + prmt->PageNo; // 计算执行项的索引
rValue = 0;
break;
}
case KV_LF: // 左键跳到顶部
{
prmt->Cursor = 0;
prmt->PageNo = 0;
break;
}
case KV_RT: // 右键跳到底部
{
prmt->Cursor = prmt->DispNum-1; // 光标到底
prmt->PageNo= prmt->MaxPage-1; // 最后页
break;
}
default:break;
}
return rValue; // 返回执行索引
}
6.菜单处理
复制内容到剪贴板
/**************************************************************************************
* FunctionName : Menu_Process()
* Description : 处理菜单项
* EntryParameter : menuName - 菜单名称,prmt - 菜单参数,table - 菜单表项, num - 菜单项数
* ReturnValue : None
**************************************************************************************/
void Menu_Process(uint8 *menuName, MENU_PRMT *prmt, MENU_TABLE *table, uint8 num)
{
uint8 key;
Menu_PrmtInit(prmt, PAGE_DISP_NUM, num-PAGE_DISP_NUM+1); // 显示项数和页数设置
do
{
LCMDisplayStr(0, 0, menuName); // 菜单标题显示
Menu_Display(table, prmt->PageNo, prmt->DispNum, prmt->Cursor); // 显示菜单项
key = Menu_KeySan(); // 获取按键
if (Menu_Move(prmt, key) == 0x00) // 菜单移动
{
LCMClearScreen(LCM_WHITE); // 清屏
table[prmt->Index].ItemHook(); // 执行相应项
}
} while (prmt->ExitMark == 0);
}
7.主菜单
复制内容到剪贴板
/**************************************************************************************
* FunctionName : MenuGeneralSet()
* Description : 常规设置
* EntryParameter : None
* ReturnValue : None
**************************************************************************************/
void MenuGeneralSet(void)
{
uint8 menuNum;
menuNum = sizeof(GeneralSet)/sizeof(GeneralSet[0]); // 菜单项数
Menu_Process(" -= 常规设置 =- ", &GSetPrmt, GeneralSet, menuNum);
}
8. 二级菜单,情景模式
/**************************************************************************************
* FunctionName : Menu_Profile()
* Description : 情景模式
* EntryParameter : None
* ReturnValue : None
**************************************************************************************/
void Menu_Profile(void)
{
uint8 menuNum;
menuNum = sizeof(Profile)/sizeof(Profile[0]); // 菜单项数
Menu_Process(" -= 情景模式 =- ", &ProfilePrmt, Profile, menuNum);
}
/**************************************************************************************
* FunctionName : Menu_ExitProfile()
* Description : 退出情景模式
* EntryParameter : None
* ReturnValue : None
**************************************************************************************/
void Menu_ExitProfile(void)
{
ProfilePrmt.ExitMark = 0x01;// 退出情景模式
LCMClearScreen(LCM_WHITE); // 清屏
}
9. 二级菜单,设置设置
/**************************************************************************************
* FunctionName : Menu_SetClock()
* Description : 时间与日期设置
* EntryParameter : None
* ReturnValue : None
**************************************************************************************/
void Menu_SetClock(void)
{
uint8 menuNum;
menuNum = sizeof(Clock)/sizeof(Clock[0]); // 菜单项数
Menu_Process(" -= 时间日期 =- ", &ClockPrmt, Clock, menuNum);
}
/**************************************************************************************
* FunctionName : Menu_ExitClockSet()
* Description : 退出日期与时钟
* EntryParameter : None
* ReturnValue : None
**************************************************************************************/
void Menu_ExitClockSet(void)
{
ClockPrmt.ExitMark = 0x01; // 退出时钟设置
LCMClearScreen(LCM_WHITE); // 清屏
}
菜单格式: