方案一:简单按键扫描
硬件:独立按键,液晶;
软件架构:
全局变量;
外设(如DA)输出初始化;
液晶初始化显示;
while(1)
{
按键扫描();
}
液晶需要显示的内容数值和外设的数值放在全局变量里面。在按键扫描程序中检测独立按键是否拉低,若拉低进行去抖动延时再检测,根据相应的按键值改变全局变量并更新液晶显示和外设输出。
方案二:菜单
LCD12864_Menu.c代码如下。提示:代码中包含扫描式矩阵按键头文件和LCD12864显示头文件。本想将数据和函数分离开,写成.c加.h形式,但因此代码数据和函数混在一起,故写在一个.c文件中。
基本概念:1、菜单分层次。主菜单是第1层,为最顶层。子菜单若无子节点,则为最底层菜单。主菜单无父节点。
2、每一层次菜单菜单项称作数据节点。下列代码中每个数据节点包含信息有:当前层菜单节点总数、节点标题,节点函数,子节点(下一层菜单指针),父节点(上一层菜单指针)。
3、程序运行中,菜单更新函数依照按键值执行相应功能。若节点函数存在,则执行函数,等待按键返回菜单显示界面;若节点函数不存在则更新菜单显示。
//=================================版本信息==================================// //Ver:2011082601 //Author:XZQ //===========================================================================// //=================================头文件=============================// #include "msp430f5438.h" #include "MCU_Init.h" #include "LCD12864_Driver.h" #include "ArrayKey_Driver.h" #define Null 0 /********************** * 菜单结构体定义 * **********************/ struct MenuItem { unsigned char MenuCount; //当前层节点数 unsigned char *DisplayString; //节点标题 void (*Subs)(); //节点函数 struct MenuItem *ChildrenMenus; //子节点 struct MenuItem *ParentMenus; //父节点 }; //=================================菜单变量定义=============================// /*********************** * 全局变量声明区 * ***********************/ unsigned char DisplayStart = 0; //显示时的第一个菜单项 unsigned char UserChoose = 0; //用户所选菜单项 unsigned char DisplayPoint = 0;//显示指针 unsigned MaxItems; //同层所有节点数 unsigned char ShowCount=4; //单屏显示最多节点数 unsigned char Menu_key=0;//菜单函数中存放按键值变量 void DispVoid(void) { DispOneChar(0,0,0x30); } void NullSubs(void) //空操作函数 { } //菜单变量声明 struct MenuItem Vol_Menu[3]; struct MenuItem FFT_Menu[3]; struct MenuItem Fre_Set_Menu[3]; struct MenuItem Fre_Test_Menu[4]; struct MenuItem MainMenu[6]; struct MenuItem (*MenuPoint) = MainMenu; //结构体指针,初始化时指向主菜单 //********************第2层菜单变量******************************// struct MenuItem FFT_Menu[3]= { //MenuCount DisplayString Subs ChildrenMenus ParentMenus {3, " 【频谱显示】" , NullSubs, Null, MainMenu}, {3, "1.频率测量 ", DispVoid, Null, MainMenu}, {3, "2.返回", NullSubs, MainMenu, MainMenu}, }; struct MenuItem Vol_Menu[3]= { //MenuCount DisplayString Subs ChildrenMenus ParentMenus {3, " 【幅值测量】 " , NullSubs, Null, MainMenu}, {3, "1.频率测量 ", NullSubs, Null, MainMenu}, {3, "2.返回", NullSubs, MainMenu, MainMenu}, }; struct MenuItem Fre_Set_Menu[3]= { //MenuCount DisplayString Subs ChildrenMenus ParentMenus {3, " 【频率设置】 " , NullSubs, Null, MainMenu}, {3, "1.频率设置 ", Clear_GDRAM , Null, MainMenu}, {3, "2.Back", NullSubs, MainMenu, MainMenu}, }; struct MenuItem Fre_Test_Menu[4]= { //MenuCount DisplayString Subs ChildrenMenus ParentMenus {4, " 【测频、测相】" , NullSubs, Null, MainMenu}, {4, "1.频率测量 ", NullSubs, Null, MainMenu}, {4, "2.相位测量 ", NullSubs, Null, MainMenu}, {4, "3.返回", NullSubs, MainMenu, MainMenu}, }; //********************第1层菜单变量:主菜单******************************// struct MenuItem MainMenu[6]= {//MenuCount DisplayString Subs ChildrenMenus ParentMenus {6, " 【主菜单】 " , NullSubs, Null, Null}, {6, "1.测频、测相 " , NullSubs, Fre_Test_Menu,Null}, {6, "2.频率设置" , NullSubs, Fre_Set_Menu, Null}, {6, "3.幅值测量" , NullSubs, Vol_Menu, Null}, {6, "4.频谱显示" , NullSubs, FFT_Menu, Null}, {6, "5.返回" , NullSubs, MainMenu, MainMenu}, }; //=================================菜单函数定义=============================// //菜单界面初始化显示 void ShowMenu(void) { unsigned char n; MaxItems = MenuPoint[0].MenuCount;//得到主层菜单节点个数 DisplayPoint = DisplayStart;//指向该层菜单第一个节点 for(n=0;(DisplayPoint<MaxItems)&&(n<ShowCount);n++) { DispStr(0,n,MenuPoint[DisplayPoint++].DisplayString); } } //菜单界面显示更新 void Menu_Change(uchar KeyValue) { switch(KeyValue) //根据按键值选择相应操作 { case UP: UserChoose --; if (UserChoose ==255) { UserChoose = 0;//上翻截至,如果要回滚赋值MaxItems-1 } break; case Down: UserChoose ++; if (UserChoose == MaxItems ) { UserChoose = MaxItems-2;//下翻截至,如要回滚赋值为0 } break; case Esc: if (MenuPoint[UserChoose].ParentMenus != Null) { MenuPoint = MenuPoint[UserChoose].ParentMenus; UserChoose = 0; DisplayStart = 0; } break; case Enter: if (MenuPoint[UserChoose].Subs != NullSubs) //如果函数不为空,说明是底层菜单,执行函数 { Clear_DDRAM(); (*MenuPoint[UserChoose].Subs)(); Menu_key=KeyDefault;//改变Menu_key,确保一次按键只执行一次代码 while(Menu_key!=Esc) Menu_key=KeyScan(); } else if (MenuPoint[UserChoose].ChildrenMenus != Null)//函数为空,子层不为空,不是底层菜单,设置下一菜单显示数据 { MenuPoint = MenuPoint[UserChoose].ChildrenMenus; UserChoose = 0; DisplayStart = 0; } break; default:break; } //KEY_FLAG = 0; Clear_DDRAM(); //delay_ms(10); //液晶为慢速器件 if ((UserChoose%ShowCount==0) && (KeyValue == Down)) //一屏只能显示ShowCount行 { DisplayStart = UserChoose; // UserChoose = 4; } if (((UserChoose)%(ShowCount-1)==0) && (KeyValue == UP)) //一屏只能显示ShowCount行 { DisplayStart = 0; } ShowMenu();//显示更新后的菜单 if(UserChoose == 0) UserChoose = 1 ; //if(UserChoose != 0) DispStr(14,UserChoose%ShowCount,"←"); }