STM32+0.96OLED的多级菜单设计
硬件
硬件选型
- STM32F103C8T6最小核心板
- 0.96寸四脚OLED屏幕IIC接口
- 普通按键5个
硬件连线
SCL ---- PA1
SDA ---- PA2
KEY_UP ---- PA4
KEY_DOWN ---- PA5
KEY_LEFT ---- PA3
KEY_RIGHT ---- PA6
KEY_OK ---- PA7
代码开源链接
百度网盘
链接:https://pan.baidu.com/s/1W4dIgTYgQv7Pp4iX-QnwTg
提取码:k7p4
Gitee
屏幕使用-GUI设计: 一些单片机控制屏幕的项目,和一些GUI界面设计 stm32驱动的oled屏等等 (gitee.com)
软件开发
基础功能
Systick延时
我使用的是正点原子的代码,进行了简单的修改,但大体一样,可用正点原子代码。具体修改看我代码,不修改也可用。
5ms定时器
使用的定时器3做的一个5ms的定时器中断。定时器中断中放置按键扫描函数。
按键扫描
也是参考的正点原子的按键扫描函数,但是没有使用延时函数,使用的计数延时来达到按键消抖的目的。5ms扫描一次,大于2就延时10ms。该函数放在定时器5ms的中断函数中。这里按键扫描给一个按键按下的标志位,例如isKeyUp,按键按下就将它置1。
/*** @brief 按键扫描函数* * @param mode 模式为1就是连续扫描,为0就是单次*/
void KeyScan(u8 mode)
{static int keyCount = 0;static int keyState = 0;if(mode == 1) keyState=0;if (keyState == 0 && (KEY_UP == 0||KEY_DOWN == 0||KEY_LEFT == 0||KEY_RIGHT == 0||KEY_OK == 0)){ keyCount++;if(keyCount>2){keyState = 1;keyCount=0;if(KEY_UP == 0) KeyUp();else if(KEY_DOWN == 0) KeyDown();else if (KEY_LEFT == 0) KeyLeft();else if (KEY_RIGHT == 0) KeyRight();else if (KEY_OK == 0) KeyOk();}}else if (KEY_UP == 1 && KEY_DOWN == 1 && KEY_LEFT == 1 && KEY_RIGHT == 1 && KEY_OK == 1){keyState = 0;}
}void KeyUp()
{if(isKeyUp == 0)isKeyUp=1;LED=!LED;
}
oled的简单使用
oled使用的是中景园电子的代码,可以在项目代码中查看。
多级菜单设计
定义菜单项结构体
//菜单页参数结构体
struct MenuProperty_t
{u8 MenuLen;//当前菜单页菜单项总个数u8 scrollBarLen;//滚动条长度,由于都是用的16SIZE的字符,所以一个菜单页最多四个菜单项,五个菜单项滚动条就为1
};//菜单项结构体
struct Menu_t{struct MenuProperty_t *MenuProperty;//当前菜单项所在菜单页的参数u8 displayString[15];//当前菜单项的字符void (*func1) (void);//当前菜单项的功能函数void (*func2) (void);//当前菜单项的功能函数struct Menu_t *fatherMenu;//当前菜单项的父级菜单项struct Menu_t *childrenMenu;//当前菜单项的子级菜单项
};
定义一个菜单页
主界面菜单,算一级菜单,主界面一般可以拿来画一些好玩的UI设计,我这个项目做的是一个时钟设计。这个菜单页就只有一个菜单项,滚动条为0。由于初始化不能先填入未初始化的数据,所以他的子菜单项初始化先设定为NULL。
//主UI
struct MenuProperty_t MainUIProperty={1,0};
struct Menu_t MainUI=
{&MainUIProperty,"MainUI " ,NULL,NULL,NULL};
主菜单,算二级菜单,拿来做我想要显示的数据项分类,父菜单就是MainUI,子菜单项初始化先设定为NULL。注意字符串尽量写15个字符,用空格也要占位,使得后面数据好刷新。这个菜单页就有四个菜单项,滚动条为0。
//主菜单
struct MenuProperty_t menuMainProperty={4,0};
struct Menu_t menuMain[4]=
{{&menuMainProperty,"last menu ", NULL,NULL, &MainUI,NULL},{&menuMainProperty,"Animal ", NULL,NULL, &MainUI,NULL},{&menuMainProperty,"Pid ", NULL,NULL, &MainUI,NULL},{&menuMainProperty,"Time set ", NULL,TimeSetInit, &MainUI,NULL}
};
animal的子菜单,算3级菜单,这个就是真的想要显示的animal菜单项的数据。父菜单就是menuMain,子菜单项初始化先设定为NULL。这个菜单页就有六个菜单项,滚动条长度为2,因为一面最多显示4个,滚动一下往下移一个。
注意:要是你定义的是单个项,取地址就要加&,要是定义的数组,就可以用数组名取该数组首地址。
//animal的子菜单
struct MenuProperty_t setMenu1Property={6,2};
struct Menu_t setMenu1[6]=
{{&setMenu1Property,"last menu ",NULL,NULL,menuMain,NULL},{&setMenu1Property,"bull ",NULL,NULL,menuMain,NULL},{&setMenu1Property,"bird ",NULL,NULL,menuMain,NULL},{&setMenu1Property,"dog ",NULL,NULL,menuMain,NULL},{&setMenu1Property,"bow ",NULL,NULL,menuMain,NULL},{&setMenu1Property,"fish ",NULL,NULL,menuMain,NULL}
};
OLED页面刷新函数
刷新页面信息,要是在主页面就清空一下在画图,要是没有在主页面,使用覆盖来达到刷新的效果。
void DisplayRefreash(struct Menu_t *nowMenu,u8 selectItem,u8 scrollBar)
{int i = 0;static u8 lastSelectItem=0;//记录上次索引if(nowMenu==&MainUI)//当回到主菜单时,由于没有全占屏,所以全部清屏,再画{OLED_Clear();MainUiSet();}else { OLED_ShowChar(0,lastSelectItem*16, ' ',16,1);//清除上次索引箭头OLED_ShowChar(0,selectItem*16, '>',16,1);//画出这次索引箭头for(i=0;i<(nowMenu->MenuProperty->MenuLen-nowMenu->MenuProperty->scrollBarLen);i++){OLED_ShowString(8,i*16,nowMenu[i+scrollBar].displayString,16,1);}}OLED_Refresh();lastSelectItem = selectItem;
}
OLED数据刷新函数
当每个页面的数据要刷新时,就只需要把上一次的数据覆盖就行了,所以每次就要写满一行的字符,或者你每行的字符长度相同也可以达到相同的目的。
void DisplayRefreashData(struct Menu_t *nowMenu,u8 selectItem,u8 scrollBar)
{int i = 0;for(i=0;i<(nowMenu->MenuProperty->MenuLen-nowMenu->MenuProperty->scrollBarLen);i++){OLED_ShowString(8,i*16,nowMenu[i+scrollBar].displayString,16,1);}OLED_Refresh();
}
刷新数据前,数据的更改可以通过Sprintf函数来重新定义每个菜单项的显示字符串
void GuiDataDisplayRefresh()
{if(menuPoint == setMenu1){sprintf((char*)setMenu1[1].displayString,"bull %3d ",count1);sprintf((char*)setMenu1[2].displayString,"bird %3d ",count2);sprintf((char*)setMenu1[3].displayString,"dog %3d ",count3);sprintf((char*)setMenu1[4].displayString,"bow %3d ",count4);sprintf((char*)setMenu1[5].displayString,"fish %3d ",count5);DisplayRefreashData(menuPoint,selectItem,scrollBar);}else if(menuPoint==&MainUI){MainUiSet();OLED_Refresh();}
}
菜单初始化
主要拿来初始化一些菜单项的子菜单,以及当前菜单的指针指向。
全局变量
menuPoint 当前菜单指向地址
selectItem 当前索引 0-3
scrollBar 当前滚动条所在位置,最上处为0
void GuiInit()
{MainUI.childrenMenu = menuMain;menuMain[1].childrenMenu = setMenu1;menuMain[2].childrenMenu = setMenu2;menuMain[3].childrenMenu = setMenu3;menuPoint = &MainUI;DisplayRefreash(menuPoint,selectItem,scrollBar);
}
按键控制函数
上下按键主要拿来切换现在的索引和滚动条
左右键主要拿来实现功能函数
void GuiControl()
{if(isKeyUp==1)//上键按下{isKeyUp=0;//标志位清零selectItem--;//当前菜单在当前菜单页的索引--if(selectItem<0&&scrollBar!=0)//小于0,但是滚动条不在0,就减滚动条{selectItem = 0;scrollBar--;}else if(selectItem<0&&scrollBar==0)//小于0,滚动条也在0,就将索引移到最后,滚动条到最大{selectItem = menuPoint->MenuProperty->MenuLen-1-menuPoint->MenuProperty->scrollBarLen;scrollBar = menuPoint->MenuProperty->scrollBarLen;}DisplayRefreash(menuPoint,selectItem,scrollBar);//刷新显示}else if(isKeyDown==1)//和上键差不多{isKeyDown=0;selectItem++;//假如索引大于最大值,但是滚动条不在最大值,保持索引最大值,滚动条++if(selectItem>(menuPoint->MenuProperty->MenuLen-1-menuPoint->MenuProperty->scrollBarLen)&&scrollBar!=menuPoint->MenuProperty->scrollBarLen){selectItem = menuPoint->MenuProperty->MenuLen-1-menuPoint->MenuProperty->scrollBarLen;scrollBar++;}//假如索引大于最大值,滚动条在最大值,移动到第一个位置else if(selectItem>(menuPoint->MenuProperty->MenuLen-1-menuPoint->MenuProperty->scrollBarLen)&&scrollBar==menuPoint->MenuProperty->scrollBarLen){selectItem=0;scrollBar =0;}DisplayRefreash(menuPoint,selectItem,scrollBar);}else if(isKeyLeft==1){//假如当前菜单的func1不为空,执行相关函数if(menuPoint[selectItem+scrollBar].func1!=NULL){menuPoint[selectItem+scrollBar].func1();}isKeyLeft=0;DisplayRefreash(menuPoint,selectItem,scrollBar);}else if(isKeyRight==1){if(selectItem==0 && scrollBar==0 && menuPoint[selectItem].fatherMenu!=NULL)//假如索引为零而且父菜单不为空,指向父指针{menuPoint = menuPoint[selectItem].fatherMenu;}else if(menuPoint[selectItem+scrollBar].childrenMenu!=NULL)//假如该索引子菜单页不为空,指向子菜单{if(menuPoint[selectItem+scrollBar].func2!=NULL)//假如当前菜单的func2不为空,执行相关函数{menuPoint[selectItem+scrollBar].func2();}menuPoint = menuPoint[selectItem+scrollBar].childrenMenu;selectItem = 0;}else if(menuPoint[selectItem+scrollBar].func2!=NULL)//假如当前菜单的func2不为空,执行相关函数{menuPoint[selectItem+scrollBar].func2();}isKeyRight=0;DisplayRefreash(menuPoint,selectItem,scrollBar);}else if(isKeyOk==1){isKeyOk=0;DisplayRefreash(menuPoint,selectItem,scrollBar);}GuiDataDisplayRefresh();
}
项目参考
STM32F1多级菜单代码讲解_哔哩哔哩_bilibili
STM32+0.96OLED的多级菜单设计相关推荐
- 基于链表的多级菜单设计
基于链表的多级菜单设计 前言 主体 前言 最近在做一个简单的界面,需要用到多级菜单,一开始使用的是传统的索引法,在修改时比较乱,在网上有用链表写的,虽然一致都在用c语言,却很少使用链表,于是今天早上便 ...
- 分享一个按键液晶多级菜单设计方法,工控行业中沿用多年,屡试不爽,附带本人一个调试通过
转自:https://www.amobbs.com/forum.php?mod=viewthread&tid=4001689 typedef struct { uchar KeyStateIn ...
- 基于状态机的LCD多级菜单设计
文章地址链接1:https://blog.csdn.net/embedded_guzi/article/details/35835755 文章地址链接2:http://eeskill.com/arti ...
- 基于STM32的OLED多级菜单GUI实现(简化版智能手表)
前言:本文的OLED多级菜单UI为一个综合性的STM32小项目,使用多传感器与OLED显示屏实现智能终端的效果.项目中的多级菜单UI使用了较为常见的结构体索引法去实现功能与功能之间的来回切换,搭配DH ...
- 基于STM32F407的简易菜单设计+LCD+按键
基于STM32F407的简易多级菜单设计+LCD+按键 实现原理 主要使用 双向链表 结构实现的菜单: 结构体包含7个变量,分别是菜单中功能项的个数,当前菜单标题,菜单中各功能项标题,功能项的类型,然 ...
- STM32简易多级菜单(数组查表法)
单片机开发中,有时会用到屏幕来显示内容,当需要逐级显示内容时,就需要使用多级菜单的形式了. 1 多级菜单 多级菜单的实现,大体分为两种设计思路: 通过双向链表实现 通过数组查表实现 总体思路都是把菜单 ...
- 【STM32】基于STM32F103C8T6的水质检测系统设计(声光报警、多级菜单)
需求 1.检测参数:水温.TDS.浊度.PH 2.超出阈值声光报警 3.LCD显示目标参数的测量结果 4.测量模式:单参数测量.所有参数表同时测量 切换方式:按键切换 原理 单总线技术 单总线技术采用 ...
- django权限二(多级菜单的设计以及展示)
多级权限菜单设计级标题栏 我们现在只有数据展示,要进入其他url还需要手动的输入路径,非常的麻烦,所以我们要设计 一个导航栏以及侧边多级菜单栏,这个展示是通过stark组件的设计的增删改查页面,而 每 ...
- Vue 里,多级菜单要如何设计才显得专业?
老生常谈了! 虽然我们是 Java 猿,但是写起来前端代码也不含糊!今天我想来和大家聊聊这个前端的动态菜单,要如何设计才显得专业!还是以我们的 TienChin 项目为例,大家一起来看看. 先来一张截 ...
最新文章
- NOIP2017 列队
- 是无数像老钟叔的p8u8
- plsql 自动查询最后页_一次SQL查询优化思考过程(900W+数据,从17s到300ms)
- 北乐博客装饰分享CSS+HTML+js
- 2018-2019-1 20165319 《信息安全系统设计基础》第四周学习总结
- 如不指定存储类型c语言,总结C语言的五种存储类型
- Java——Eclipse快捷键大全
- 大数据技术原理与应用学习笔记(一)
- mac下解压war包
- unity3d meshBaker 基本的使用
- 消息中间件RabbitMQ
- 五种方法教你预防ddos攻击
- 电商运营风向标:数据分析。
- 本地生活O2O行业已经逐渐渗透到日常生活中
- python匿名函数调用_(Python) 函数、匿名函数
- linux及安全期中总结——20135227黄晓妍
- python 将函数封装成pyd或者so文件,调用该文件
- vue实现搜索框搜索新增_基于Vue.js实现简单搜索框
- VINS_FUSION
- Node 之父 Ryan Dahl说:Node 失误太多无力回天,Deno 前景明朗。NodeJS要完蛋吗?