单片机 LCD 菜单架构
参考了网上一些资料,写了一个还能用的单片机 LCD显示菜单,改起来还比较简单:
其中 MenuItem 中 itemIdx 可以不要,没有什么作用。
pSelectItem 也可以不要的。和 pCurItem 有点重复了。
实现效果如下:可以显示三级菜单。
//menu.h
#define LCD_MaxItem 4
typedef struct str_MenuItem
{
uint8_t itemIdx;
uint8_t itemCnt;
char * dispString;
void (*itemFunc)();
struct str_MenuItem *pChildMenu;
struct str_MenuItem *pParentMenu;
}MenuItem;
enum MenuItemIdxEnum{One=1,Two,Three,Four,Five,Six,One_One,One_Two,One_Three,
One_Four,One_Five,One_Six,Two_One,Two_Two,One_One_One,One_One_Two,One_One_Three,
One_One_Four,One_One_Five,One_One_Six};
void menuInit();
void showEnterMsg();
void showCurItem();
void showChildItem(); //显示下一级菜单
void showParentItem();
void showItems();
extern MenuItem * pCurItem;
extern MenuItem * pSelectItem;
extern MenuItem * pDispStartItem;
extern MenuItem * pCurMenuStartItem;
//menu.c
#include "menu.h"
#include <stdlib.h>
#define FirstMenuItemNum 6
#define SecondMenuItemNum 6
#define ThirdMenuItemNum 2
#define ForthMenuItemNum 6
#define MIN(x,y)\
( (x)<(y) ? x:y )
MenuItem firstMenu[FirstMenuItemNum];
MenuItem secondMenu[SecondMenuItemNum];
MenuItem thirdMenu[ThirdMenuItemNum];
MenuItem forthMenu[ForthMenuItemNum];
MenuItem * pCurItem;
MenuItem * pSelectItem;
MenuItem * pDispStartItem;
MenuItem * pCurMenuStartItem;
#define SaveMaxLevel 10
MenuItem * pArrCurMenuStart[SaveMaxLevel];
MenuItem * pArrDispStart[SaveMaxLevel];
uint8_t arrCntSaveMenu=0;
MenuItem firstMenu[]=
{
{One,FirstMenuItemNum,"one",showChildItem,secondMenu,NULL},
{Two,FirstMenuItemNum,"two",showChildItem,thirdMenu,NULL},
{Three,FirstMenuItemNum,"three",showEnterMsg,NULL,NULL},
{Four,FirstMenuItemNum,"four",showEnterMsg,NULL,NULL},
{Five,FirstMenuItemNum,"five",NULL,NULL,NULL},
{Six,FirstMenuItemNum,"six",NULL,NULL,NULL}
};
MenuItem secondMenu[]=
{
{One_One,SecondMenuItemNum,"oneone",NULL,NULL,firstMenu},
{One_Two,SecondMenuItemNum,"onetwo",NULL,NULL,firstMenu},
{One_Three,SecondMenuItemNum,"onethree",showChildItem,forthMenu,firstMenu},
{One_Four,SecondMenuItemNum,"onefour",NULL,NULL,firstMenu},
{One_Five,SecondMenuItemNum,"onefive",NULL,NULL,firstMenu},
{One_Six,SecondMenuItemNum,"onesix",NULL,NULL,firstMenu}
};
MenuItem thirdMenu[]=
{
{Two_One,ThirdMenuItemNum,"twoone",NULL,NULL,&firstMenu[1]},
{Two_Two,ThirdMenuItemNum,"twotwo",NULL,NULL,&firstMenu[1]}
};
MenuItem forthMenu[]=
{
{One_One_One,ForthMenuItemNum,"oneoneone",showEnterMsg,NULL,&secondMenu[2]},
{One_One_Two,ForthMenuItemNum,"oneonetwo",showEnterMsg,NULL,&secondMenu[2]},
{One_One_Three,ForthMenuItemNum,"oneonethree",showEnterMsg,NULL,&secondMenu[2]},
{One_One_Four,ForthMenuItemNum,"oneonefour",showEnterMsg,NULL,&secondMenu[2]},
{One_One_Five,ForthMenuItemNum,"oneonefive",showEnterMsg,NULL,&secondMenu[2]},
{One_One_Six,ForthMenuItemNum,"oneonesix",showEnterMsg,NULL,&secondMenu[2]}
};
void menuInit()
{
memset(pArrCurMenuStart,0,SaveMaxLevel);
memset(pArrDispStart,0,SaveMaxLevel);
arrCntSaveMenu=0;
pCurItem=&firstMenu[0];
pSelectItem=pCurItem;
pCurMenuStartItem=&firstMenu[0];
pDispStartItem=&firstMenu[0];
}
void showEnterMsg()
{
char msg[20];
lcdSerialClear();
lcdSerialSetCursor(6,0);
sprintf(msg,"item %d",pSelectItem->itemIdx);
lcdSerialWriteString(msg);
delay_ms(2000);
showCurItem();
}
void showCurItem()
{
showItems();
}
void showChildItem() //显示下一级菜单
{
// int i;
// MenuItem *pItem;
if(NULL==pCurItem->pChildMenu) //
return;
if(arrCntSaveMenu >= SaveMaxLevel)
return;
pArrCurMenuStart[arrCntSaveMenu]=pCurMenuStartItem;
pArrDispStart[arrCntSaveMenu]=pDispStartItem;
arrCntSaveMenu++;
pCurItem=pCurItem->pChildMenu;
pCurMenuStartItem=pCurItem;
pSelectItem=pCurItem;
pDispStartItem=pCurItem;
showItems();
}
void showParentItem()
{
if(NULL==pCurItem->pParentMenu)
return;
if(arrCntSaveMenu <=0)
return;
arrCntSaveMenu--;
pCurItem=pCurItem->pParentMenu;
pCurMenuStartItem=pArrCurMenuStart[arrCntSaveMenu];
pSelectItem=pCurItem;
pDispStartItem=pArrDispStart[arrCntSaveMenu];
showItems();
}
void showItems()
{
int i;
MenuItem * pItem;
lcdSerialClear();
pItem=pDispStartItem;
for(i=0;i<MIN(pSelectItem->itemCnt,LCD_MaxItem);i++)
{
lcdSerialSetCursor(2,i);
lcdSerialWriteString(pItem->dispString);
if(pSelectItem==pItem)
{
lcdSerialSetCursor(0,i);
lcdSerialWriteString("->");
}
pItem++;
}
}
//main.c
while 循环:
switch(btnCode)
{
case 0x01:
if(pCurItem - pCurMenuStartItem <(pCurItem->itemCnt-1)) // pCurItem还没有指向最后一项
{
if(pCurItem - pDispStartItem ==(LCD_MaxItem-1)) //显示项超过LCD行数,需整体上移
{
pDispStartItem++;
}
pCurItem++;
}
pSelectItem=pCurItem;
showCurItem();
printf("down\r\n");btnCode=0;
break;
case 0x02:
printf("left\r\n");btnCode=0;
if(NULL!=pCurItem->pParentMenu)
{
showParentItem();
}
break;
case 0x04:
printf("center\r\n");btnCode=0;
{
if(NULL!=(pCurItem->itemFunc))
(*(pCurItem->itemFunc))();
}
break;
case 0x08:
printf("right\r\n");btnCode=0;
if(NULL!=pCurItem->pChildMenu)
showChildItem();
break;
case 0x10:
if(pCurItem > pCurMenuStartItem)
{
if(pCurItem == pDispStartItem)
{
pDispStartItem--;
}
pCurItem--;
}
pSelectItem=pCurItem;
showCurItem();
printf("up\r\n");btnCode=0;
break;
default:
break;
}