LCD1602显示多级菜单程序

时间:13-12-15 栏目:单片机, 编程之美 作者:JH单片机 评论:1 点击: 30,981 次



用LCD1602液晶显示屏显示多级功能菜单,在16*2个字符上也可以玩得淋漓尽致。在这么小的显示屏上,显示多级菜单,设计的产品看起来就更人性化,而且更方便把各项功能都统一起来,不至于那么絮乱。

使用结构体链表来完成多级菜单结构,而接着就靠用户的选择来改变现实的内容了。而写一个这样的菜单执行和菜单显示程序并不难。

 

首先,写这篇文章之前,阿浩就给自己开了一个天大的玩笑。在写这个程序之前,阿浩百度了一下,想看看别人的程序是怎么写的,结果百度到了自己网站上以前自己写的一篇文章。看本文章前,建议请戳:《LCD1602菜单设计思路》

后来,阿浩自己重新在草稿纸上整理一下显示逻辑,然后逻辑整合,最后终于写出下面的菜单显示程序了。

程序有部分是比较难理解的,毕竟每个人的思路都不一样,要知道这是阿浩第二次写的程序。而看不懂的建议看以上提到的那一篇文章,思路会清晰些。

所以建议,有兴趣的可以自己写写这个菜单显示的程序,而大体上的菜单操作程序,大同小异,这部分照抄就行了。

效果:

LCD1602多级菜单显示

程序代码:

//==================================================
//LCD1602多级菜单显示
//BY:JH单片机
//If you want to use the program or the program referenced in the article,
//please specify in which data and procedures from JHMCU.com
//
//Web:www.JHMCU.com
//==================================================
#define Null 0
void NullSubs(void)
{
}

//菜单目录结构体声明
code struct MenuItem MainMenu[7];
code struct MenuItem testMenu[6];

//菜单使用的全局变量
#define LcdMaxLine	2			//定义LCD最多显示菜单数目
struct MenuItem (*MenuPoint) = MainMenu;	//结构体菜单指针指向主菜单
unsigned char DisplayStart=0;	//显示菜单的开始位置
unsigned char UserChoose=0;		//用户的选择
unsigned char DisplayPoint=0;	//显示菜单的编号
unsigned char MaxItems=0;		//菜单的数量
bit	ShowMenuFlag=0;				//菜单运行标志

//==========================================================================
//目录结构体定义
//==========================================================================
struct MenuItem
{
	unsigned char MenuCount;    	//菜单的数目
	unsigned char *DisplayString; 	//显示的字符串
	void (*Subs)(); 				//菜单指向的功能函数
	struct MenuItem *ChildrenMenus;	//子节点
	struct MenuItem *ParentMenus;	//父节点
};

//==========================================================================
// 菜单结构体链表
//==========================================================================
//主菜单-第一级菜单
code struct MenuItem MainMenu[7] =		//结构体主菜单
{
	{7,"Set Time      ",NullSubs,testMenu,Null},		//->设置时间
	{7,"Set Alarm Time",NullSubs,Null,Null},		//->设置报警时间段
	{7,"Alarm Swtich  ",NullSubs,Null,Null},		//->报警总开关
	{7,"Check Alarm   ",NullSubs,Null,Null},		//->查看报警信息
	{7,"Alarm Init    ",NullSubs,Null,Null},		//->红外探头初始化
	{7,"Exit          ",NullSubs,MainMenu,MainMenu}, //->退出(返回主菜单)
	{7,"test          ",NullSubs,MainMenu,MainMenu}	//->退出
};

code struct MenuItem testMenu[6] =		//结构体主菜单
{
	{6,"www.jhmcu.com ",NullSubs,Null,MainMenu},
	{6,"22222222222222",NullSubs,Null,MainMenu},
	{6,"33333333333333",NullSubs,Null,MainMenu},
	{6,"44444444444444",NullSubs,Null,MainMenu},
	{6,"55555555555555",NullSubs,Null,MainMenu},
	{6,"www.jhmcu.com ",NullSubs,Null,MainMenu}
};

//==================================================
//LCD1602菜单显示通用函数
//INPUT:结构体链表,UserChoose
//OUTPUT:NONE
//FUNCTION:LCD1602液晶显示屏上显示菜单功能
//==================================================
void ShowMenu(void)
{
	unsigned char i = 0;
	MaxItems = MenuPoint[0].MenuCount;	//获取当前菜单的条目数
	DisplayPoint = DisplayStart;		//选中第一项菜单
	for(i=0;i<LcdMaxLine;i++)		//循环显示当前菜单
	{
		//如果当前显示节点小于菜单数
		if(DisplayPoint < MaxItems){
			//如果用户选中该菜单,则在菜单前打印"->"
			if(DisplayPoint==UserChoose)
				LCD_Write_String(0,i,"->");
			else
				LCD_Write_String(0,i,"  ");
			LCD_Write_String(2,i,MenuPoint[DisplayPoint++].DisplayString);
		}else	//显示最后一页菜单,并只有一项菜单时,清除最后一行
		{
			LCD_Write_String(2,LcdMaxLine-1,"                ");	//将上一次显示清屏
		}
	}
	//菜单定位逻辑
	if(UserChoose%LcdMaxLine==0){
		DisplayStart = UserChoose;
	}else{
		//逻辑:如果用户选择的菜单是奇数,则将显示定位在偶尔页
		for(i=0;i<=MaxItems;i++){
			if(UserChoose!=(i%LcdMaxLine))
				DisplayStart = UserChoose-1;	//菜单滚屏
		}
	}
}

//==================================================
//菜单执行函数
//INPUT:NONE
//OUTPUT:NONE
//==================================================
void ChangeMenu(unsigned char keynum)
{
	switch(keynum){
		case ADD:
			UserChoose--;
			if (UserChoose==255)
			{
				UserChoose = 0;	//上翻停止赋值为0,回滚赋值为MaxItems-1
			}
			break;
		case SUB:
			UserChoose++;
			if (UserChoose == MaxItems)
			{
				UserChoose = MaxItems-1;//上翻停止赋值为MaxItems-1,回滚赋值为0
			}
			break;
		case ENTER:
			//如果用户选择的菜单指向的函数不是空函数,则执行功能函数。
			if (MenuPoint[UserChoose].Subs != NullSubs)
			{
				(*MenuPoint[UserChoose].Subs)();	//执行菜单功能函数
			}
			//如果是空函数,则找子菜单,找不到子菜单,就退出
			else if (MenuPoint[UserChoose].ChildrenMenus != Null)
			{
				MenuPoint = MenuPoint[UserChoose].ChildrenMenus;
				UserChoose = 0;
				DisplayStart = 0;
			}
			break;
		case SET:
			if (MenuPoint[UserChoose].ParentMenus != Null)
			{
				MenuPoint = MenuPoint[UserChoose].ParentMenus;
				UserChoose = 0;
				DisplayStart = 0;
			}
			break;
		default:break;
	}
    ShowMenu();
}


 

 
关于本文作者

爱数电,爱模电;爱单片机,爱嵌入式;爱EDA,也爱DSP; 爱Altium Designer,也爱PCB;爱生活,同时也爱微博…… 一个自动化专业的学生,与志同道合者学习交流!!!

QQ 号码:594420349
腾讯微博:http://t.qq.com/kevin_753

项目合作