基于STM32F407的简易菜单设计+LCD+按键

基于STM32F407的简易多级菜单设计+LCD+按键

实现原理

主要使用 双向链表 结构实现的菜单:
结构体包含7个变量,分别是菜单中功能项的个数,当前菜单标题,菜单中各功能项标题,功能项的类型,然后是函数指针,即索引号对应的函数,最后是指向下一级的结构体指针和指向上一级的结构体指针,第一级菜单的上一级为NULL空。

//定义菜单中功能项的类型
#define TYPE_SUBMENU 101  //具有子菜单的菜单项
#define TYPE_PARAM   102  //参数项(用于执行参数设置)
typedef void (*MENU_FUN)(const char *);
//定义菜单
typedef struct menu
{
	uint8_t num;        //当前菜单功能项总数
	char * title;       //当前菜单标题
	char * label;       //功能项标题
	uint8_t type;       //当前功能项的类型
	MENU_FUN Function;  //选择某一功能后执行的功能函数
	struct menu *next;  //下一级菜单
	struct menu *prev;  //上一级菜单

} Menu;
//结构体初始化//菜单定义,在这里将每一个菜单的关联设置好
Menu menu1_main[5] = // 第1级 主菜单 
{
	{5, "主菜单", "文件          >>", TYPE_SUBMENU, NULL, menu2_file, NULL}, 
	{5, "", "编辑          >>", TYPE_SUBMENU, NULL, menu2_edit, NULL}, 
	{5, "", "视图          >>", TYPE_SUBMENU, NULL, menu2_view, NULL}, 
	{5, "", "设置          >>", TYPE_SUBMENU, NULL, menu2_set, NULL}, 
	{5, "", "帮助          --", TYPE_PARAM, NULL, NULL, NULL}, 
}; 
Menu menu2_file[4] =  // 第2级 文件菜单 
{
	{4, "文件", "open            ", TYPE_SUBMENU, NULL, NULL, menu1_main}, 
	{4, "",     "close           ", TYPE_SUBMENU, NULL, NULL, menu1_main}, 
	{4, "",     "save            ", TYPE_SUBMENU, NULL, NULL, menu1_main}, 
	{4, "",     "exit            ", TYPE_SUBMENU, NULL, NULL, menu1_main}, 
};
……  //将各级菜单设置好,并且关联好上下页的指针
//省略了,具体参见文章底部的工程百度云链接

绘制当前菜单页的函数:

void DispCrtMenu(void)//绘制当前菜单项
{
	uint8_t menu_num = cur_item[0].num;//获取当前菜单的项目数量 
	uint8_t i, num = menu_num>MENU_MAX_ROW ? MENU_MAX_ROW : menu_num;//绘制数量不能超过每一屏的最大绘制数量
	if(item_index>=MENU_MAX_ROW || item_index>=menu_num)//菜单项上下选择是否越界 
	{ 
		if(item_index==0XFF)  //此情况为 item_index=0 再按上键,0-1=0XFF
		{
			item_index = menu_num - 1;   //循环,回到最后一个功能索引值
		}
		if(item_index>=menu_num)  //此情况为到达最下面一个功能索引值
		{
			item_index = 0;  //循环,回到第一个功能索引值
		}
		if(item_index>=MENU_MAX_ROW) 
		{
			item_index = 0;
		}
	} 
	DrawTestPage((u8 *)cur_item[0].title);
	
	for (i=0; i<num; i++)//绘制某一级菜单下的功能键 
	{
		POINT_COLOR = RED;
		LCD_ShowString(144,150+(i+1)*40,200,30,24, (u8 *)cur_item[i].label,i==item_index ? 0:1);
	}
}

最终功能函数:

//定义菜单操作需要的全局变量
Menu *cur_item = menu1_main;  //初始化当前菜单为第一级(menu1_main)
Menu *prev_item = NULL;	    //初始化上一级菜单为空
uint8_t item_index = 0;//当前菜单项索引
//显示函数
void Display(uint8_t value) 
{
	if(value==KEY_UP_PRESS || value==KEY_DOWN_PRESS || value==KEY_ENTER_PRESS || value==KEY_RETURN_PRESS)
	{
		switch(value)//检测按键,进入相应动作
		{
			case KEY_UP_PRESS: 
				item_index--; 
				DispCrtMenu();
				break; 
			case KEY_DOWN_PRESS: 
				item_index++; 
				DispCrtMenu();
				break; 
			case KEY_ENTER_PRESS:
				switch(cur_item[item_index].type)//检测功能项的类型,进入相应动作
				{
					case TYPE_SUBMENU: //具有子菜单的菜单项
						if(cur_item[item_index].next != NULL)
						{ 
							prev_item = cur_item;//此级菜单变成了上一级菜单 
							cur_item = cur_item[item_index].next;//将指向的下一级菜单设置为当前菜单 
							item_index = 0;//重置子菜单项索引 
							DispCrtMenu();
						}
						else
						{
							POINT_COLOR = WHITE;
							LCD_Fill(0,lcddev.height-40,lcddev.width,lcddev.height,BLUE);
							Gui_StrCenter(0,lcddev.height-32,"待设置~~~",24,1);//居中显示
						}
						break; 
					case TYPE_PARAM:  //具有参数设置的菜单项
						if(cur_item[item_index].Function != NULL)
						{ 
							//调用相应的动作函数,并传递参数
							cur_item[item_index].Function((const char *)cur_item[item_index].label);
						}
						else
						{
							POINT_COLOR = WHITE;
							LCD_Fill(0,lcddev.height-40,lcddev.width,lcddev.height,BLUE);
							Gui_StrCenter(0,lcddev.height-32,"待设置~~~",24,1);//居中显示
						}
						break; 
					default: 
						break;
				}
				break; 
			case KEY_RETURN_PRESS: 
				if (prev_item != NULL)//返回上一级菜单的操作 
				{
					cur_item = prev_item;  //设置上一级菜单为当前菜单 
					prev_item = cur_item[0].prev;  //设置当前菜单的上一级菜单 
					item_index = 0;  //重置子菜单项索引
					DispCrtMenu();
				} 
				else
				{
					POINT_COLOR = WHITE;
					LCD_Fill(0,lcddev.height-40,lcddev.width,lcddev.height,BLUE);
					Gui_StrCenter(0,lcddev.height-32,"已是主菜单",24,1);//居中显示
				}
				break; 
		    default: 
		        break;
		}
	}
} 

主程序如下:
按键扫描函数,不断检测上下左右的按键值,当检测到按键值,将值传递到显示函数中,执行相应的switch中的case的内容。

int main(void)
{ 
	uint8_t key_value;
	uint32_t main_time,key_time;
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
	delay_init(168);      //初始化延时函数
	uart_init(115200);		//初始化串口波特率为115200
	TIMER2_Init();
	LED_Init();			  //初始化LED
	KEY_Init();
 	LCD_Init();           //初始化LCD FSMC接口
	
	POINT_COLOR=RED;      //画笔颜色:红色
	SetDefaultMenuHandler(Str_Test);//设置功能函数
	DispCrtMenu();        //将主菜单页面先显示,去掉此句进入为黑屏   	
	
  	while(1) 
	{		 
		main_time = Get_RunTime();  //获取系统时间
		if(main_time > key_time)  //按键扫描,10毫秒1次
		{
			key_time  = main_time;
			key_value = KEY_Scan(0);
			if(key_value!=0xff)
			{
				Display(key_value);
			}
		}
	} 
}

图片展示

*一级菜单
在这里插入图片描述

二级菜单-设置
在这里插入图片描述

*三级菜单-on/off test
在这里插入图片描述

使用的原子开发板实验的项目,主控为STM32F407ZGT6,4.3寸的TFT 显示屏,未加入触摸功能,只是进行了简单的多级菜单验证,有需要的可以下载参考下,可以移植到OLED、12864等屏幕中做交互界面,只是需要相应地修改显示部分。

整个工程百度云链接:
链接:https://pan.baidu.com/s/10RhsVBQ8PNir_Ey9Sf6uDQ
提取码:ke6y

猜你喜欢

转载自blog.csdn.net/y17854117512/article/details/107537554