效果图:
经过调整,解决了频闪问题,菜单用得很顺畅。
最上面一行显示这一级菜单的标题。
通过STM32自带的三个按键(KEY0,KEY1,WK_UP)分别作为“下一项”,“确认”和“返回”。
OLED_MultiMenu.c为本人编写的文件,只需加入到正点原子官方OLED例程中即可使用。
通过树结构连接多级菜单的每一项。
其优点是能够非常简便快捷地制作出需要的菜单,只需要在main.c中反复调用一个函数来添加一级菜单、另一个函数来添加这一级菜单中的项目。
OLED_MultiMenu.c内容
#include "OLED_MultiMenu.h"
#include "malloc.h"
#include "string.h"
#include "key.h"
#define row 3 //由于我马上用这个模板去做中文多级菜单了,这里的两个宏定义已无法更改
#define FontSize 2
/*
多级菜单的框架建立:
1.创建根节点
2.创建新的一层(输入层名字和父节点)。
3.在该层创建一项(输入项名字和父节点)。
OLED显示:
1.层名字显示在顶上,项名字在下面。
2.一个函数控制一个可按键移动的指针,函数推动结点的遍历,每按一次执行一次结点自带的函数。
*/
//层结点的函数,列出该层所有的项目,输入的是层结点
TreeNode *OpenMenu(TreeNode *T)
{
TreeNode *H;
OLED_CLS();
OLED_ShowStr(24,0,(unsigned char *)T->Name,2);
if(T->Num==-1)
T = T->right->right;
T = T->right;
H = T;
while(T!=NULL && (T->Num-1)/row==(T->left->Num-1)/row)
{
OLED_ShowStr(8,2*(1+T->Num)-2,(unsigned char *)T->Name,FontSize);
T = T->right;
}
return H;
}
//项目结点的函数,用于移动指针,输入的是大孩子
TreeNode *Choose_Item(TreeNode *T)
{
TreeNode *H;
H = T;
if((T->Num)==1)//当要去第一个时
{
TreeNode *P;
P = T;
while(P->right!=NULL)
P = P->right;
if(P->Num%row!=0)
OLED_ShowStr(64,(P->Num%row+1)*2-2," ",FontSize);
else if(P->Num%row==0)
OLED_ShowStr(64,(row+1)*2-2," ",FontSize);
OLED_ShowStr(64,2,"<---",FontSize);
}
else if((H->Num-1)/row!=(H->left->Num-1)/row)//
{
OLED_ShowStr(64,(row+1)*2-2," ",FontSize);
OLED_ShowStr(64,2,"<---",FontSize);
}
else if(T->Num%row==0)//当要去最下面一个时
{
OLED_ShowStr(64,row*2-2," ",FontSize);
OLED_ShowStr(64,(row+1)*2-2,"<---",FontSize);
}
else
{
OLED_ShowStr(64,(T->Num%row)*2-2," ",FontSize);
OLED_ShowStr(64,((T->Num)%row+1)*2-2,"<---",FontSize);
}
if((T->left->Num)%row==0)
{
TreeNode *G;
G = T;
OLED_ShowStr(0,2," ",2);
OLED_ShowStr(0,4," ",2);
OLED_ShowStr(0,6," ",2);
while(G->Num - T->NumNum%row!=0)
OLED_ShowStr(8,(1+(G->Num)%row)*2-2,(unsigned char *)G->Name,FontSize);
else if(G->Num%row==0)
{
OLED_ShowStr(8,(row+1)*2-2,(unsigned char *)G->Name,FontSize);
break;
}
G = G->right;
}
}
return T;
}
//创建根节点
TreeNode *Root_Init(char Name[20])
{
TreeNode *R = (TreeNode *)mymalloc(sizeof(TreeNode));//R意为Root,根。
if(R == NULL)
return NULL;
R->front = NULL;
R->next = NULL;
R->left = NULL;
R->right = NULL;
strcpy(R->Name,Name);
R->current_fun = NULL;
R->Grade = 0;
R->Num = -1;
return R;
}
//创建新的一层(输入层名字和父节点)
TreeNode *New_Layer(char Name[20],TreeNode *T)
{
if(T->next!=NULL) //层只能有一个
return NULL;
TreeNode *H = (TreeNode *)mymalloc(sizeof(TreeNode));//H意为环节。
if(H == NULL)
return NULL;
H->front = T;
T->next = H;
H->next = NULL;
H->left = NULL;
H->right = NULL;
strcpy(H->Name,Name);
H->current_fun = NULL;
H->Grade = T->Grade+1;
H->Num = 0;
return H;
}
//在该层创建一项(输入项名字和父节点)。
TreeNode *New_Item(char Name[20],TreeNode *T)
{
TreeNode *H = (TreeNode *)mymalloc(sizeof(TreeNode));//H意为环节。
if(H == NULL)
return NULL;
H->front = T;
H->next = NULL;
H->left = T;
T->right = H;
H->right = NULL;
strcpy(H->Name,Name);
H->current_fun = NULL;
H->Grade = T->Grade;
H->Num = T->Num+1;
return H;
}
//按键控制OLED
TreeNode *Key_Control(TreeNode *R)
{
u8 t;
t=KEY_Scan(0); //得到键值
switch(t)
{
case KEY0_PRES: //确认按钮
if(R->next!=NULL)
{
R=R->next;
R = OpenMenu(R);
R=Choose_Item(R);
}
break;
case KEY1_PRES: //下翻按钮
if(R->Num==-1 || R->Num==0 )
break;
if(R->right==NULL)
{
while(R->Num!=1)
R = R->left;
}
else
{
R = R->right;
}
R=Choose_Item(R);
break;
case WKUP_PRES: //返回按钮
if(R->left->front->Num==-1)
break;
if(R->front!=NULL)
{
while(R->Num!=0)
R=R->left;
if(R->front->Num!=-1)
{
R=R->front;
while(R->Num!=0)
R=R->left;
}
OLED_CLS();
R = OpenMenu(R);
R=Choose_Item(R);
}
break;
default:
delay_ms(10);
}
return R;
}
OLED_MultiMenu.h内容
#ifndef __OLED_MULTIMENU_H
#define __OLED_MULTIMENU_H
#include "delay.h"
#include "OLED_I2C.h"
#include "sys.h"
typedef struct TreeNode{
u8 Grade;
int Num;
char Name[20];
struct TreeNode *front,*next,*left,*right;
void (*current_fun)(void);
}TreeNode;
#define KEY0_PRES 1 //KEY0
#define KEY1_PRES 2 //KEY1
#define WKUP_PRES 3 //WK_UP
TreeNode *Root_Init(char Name[20]);
TreeNode *New_Layer(char Name[20],TreeNode *T);
TreeNode *New_Item(char Name[20],TreeNode *T);
TreeNode *Key_Control(TreeNode *R);
#endif
main.c文件中初始化多级菜单样例:
#include "led.h"
#include "sys.h"
#include "key.h"
#include "stm32f10x.h"
#include "OLED_I2C.h"
#include "delay.h"
#include "OLED_MultiMenu.h"
int main(void)
{
TreeNode *H;
TreeNode *P;
TreeNode *P1;
TreeNode *P11;
delay_init(); //延时函数初始化
LED_Init(); //初始化与LED连接的硬件接口
KEY_Init();
I2C_Configuration();
OLED_Init();
LED0=0;
OLED_CLS();
H = Root_Init("MultiMenu");//根节点
P = H;
H = New_Layer("DIY_Window",H);//第一级菜单名字
H = New_Item("CYYL",H);
H = New_Item("LORA",H);
P1 = New_Layer("LORA-MENU",H);//第一级菜单名字
P1 = New_Item("AIRS",P1);
P1 = New_Item("COMM",P1);
P1 = New_Item("ADDR",P1);
H = New_Item("LXXN",H);
P1 = New_Layer("LXXN-MENU",H);//第二级菜单名字
P1 = New_Item("AJKZ",P1);
P1 = New_Item("KSSY",P1);
P11 = New_Layer("LAB-MENU",P1);//第三级菜单名字
P11 = New_Item("SENS",P11);
P11 = New_Item("INFO",P11);
P11 = New_Item("3S",P11);
H = New_Item("SDKA",H);
P1 = New_Layer("SDKA-MENU",H);//第一级菜单名字
P1 = New_Item("USCA",P1);
P1 = New_Item("SRAM",P1);
while(1)
{
P=Key_Control(P);
delay_ms(10);
}
}