基于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/1-_59mFhxRGCGgjqvaxyu8Q
提取码:ck21
上次的链接失效了,好久没看自己写的这个文章了,非常抱歉,重新补上了。

  • 47
    点赞
  • 292
    收藏
    觉得还不错? 一键收藏
  • 26
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 26
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值