方案一:简单按键扫描
硬件:独立按键,液晶;
软件架构:
全局变量;
外设(如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,"←");
}