单片机多级菜单的简单实现
多级菜单的思路
最近想把stm32上的oled显示屏做的好看一些,于是动手写了写一个多级菜单,思路很简单,每个页面用单独的函数封装好,每个功能也是用单独的函数进行封装,用了3个按键,两个切换键,一个确定键。
- 首先来看看用啥判断页面的(这偷个懒,希望没人打我)
//页面标志位
uint8_t s1 = 0; //主页面功能号
uint8_t s2 = 0; //主次页面判断标志位
uint16_t s3 = 1; //次页面功能号
- 主页面的显示函数,这是个很枯燥的一个显示函数。将功能图标显示出来,Draw_BMP()中的第一个形参判断图片是否反白,0是不反白,1是反白。
void function_primary(void) //一级界面,显示功能图标
{
Draw_BMP(0,0,0,32,4,BMP5);
Draw_BMP(0,48,0,80,4,BMP6);
Draw_BMP(0,95,0,127,4,BMP7);
Draw_BMP(0,0,4,32,8,BMP8);
Draw_BMP(0,48,4,80,8,BMP9);
Draw_BMP(0,95,4,127,8,BMP10);
switch(s1) //一级页面功能号
{
case 1: Draw_BMP(1,0,0,32,4,BMP5); break;
case 2: Draw_BMP(1,48,0,80,4,BMP6); break;
case 3: Draw_BMP(1,95,0,127,4,BMP7); break;
case 4: Draw_BMP(1,0,4,32,8,BMP8); break;
case 5: Draw_BMP(1,48,4,80,8,BMP9); break;
case 6: Draw_BMP(1,95,4,127,8,BMP10); break;
default: break;
}
}
- 二级页面的显示函数,其实和一级页面差不多,无非是换了个标志位。这里就放一个次页面的就好了,其他的想加几个就复制几个。同样OLED_P8x16Str()中的第一个形参判断文字是否反白,0是不反白,1是反白。
void function_level1(void)
{
sprintf(buf,"1.1");
OLED_P8x16Str(0u,0u,0u,(uint8_t *)buf);
sprintf(buf,"1.2");
OLED_P8x16Str(0u,0u,2u,(uint8_t *)buf);
sprintf(buf,"1.3");
OLED_P8x16Str(0u,0u,4u,(uint8_t *)buf);
sprintf(buf,"1.4 exit");
OLED_P8x16Str(0u,0u,6u,(uint8_t *)buf);
switch(s3) //次页面功能号
{
case 1:sprintf(buf,"1.1");
OLED_P8x16Str(1u,0u,0u,(uint8_t *)buf);
break;
case 2:sprintf(buf,"1.2");
OLED_P8x16Str(1u,0u,2u,(uint8_t *)buf);
break;
case 3:sprintf(buf,"1.3");
OLED_P8x16Str(1u,0u,4u,(uint8_t *)buf);
break;
case 4:sprintf(buf,"1.4 exit");
OLED_P8x16Str(1u,0u,6u,(uint8_t *)buf);
break;
default:break;
}
}
- 这里是确定按键,负责选中主界面或次界面的某个功能,按下后进行判断是哪个功能被选中并执行
void function(void) //主页面切换至次界面
{
if(HAL_GPIO_ReadPin(KEY_GPIO_Port,KEY_Pin)==RESET)
{
HAL_Delay(200);
if(HAL_GPIO_ReadPin(KEY_GPIO_Port,KEY_Pin) == SET)
{
if(s2 == 0) //位于主界面
{
switch(s1) //判断切换至哪个次界面
{
case 1: OLED_CLS();
s2 = 1;
function_level1();
break;
case 2: OLED_CLS();
s2 = 2;
function_level2();
break;
case 3: OLED_CLS();
s2 = 3;
function_level3();
break;
case 4: OLED_CLS();
s2 = 4;
function_level4();
break;
case 5: OLED_CLS();
s2 = 5;
function_level5();
break;
case 6: OLED_CLS();
s2 = 6;
function_level6();
break;
default:OLED_CLS();
break;
}
}
if(s2 != 0) //判断是否位于次界面
{
if(s3 == 4) //位于次界面时,选中次界面中功能四,执行切换回主界面
{
OLED_CLS();
function_primary();
s2 = 0; //s2恢复初值
s3 = 1; //s3恢复初值
}
}
}
}
}
- 这里则是移动按键,负责选中某个功能对应的显示名称,并且将该功能对应的显示反白。这边放的是功能号递增
void key_scan1(void) //功能切换按键1
{
if(HAL_GPIO_ReadPin(KEYS_GPIO_Port,KEYS_Pin) == RESET)
{
switch(s2) //判断是主界面中哪个功能的次界面
{
case 0:
HAL_Delay(200);
if(HAL_GPIO_ReadPin(KEYS_GPIO_Port,KEYS_Pin) == SET)
{
OLED_CLS();
s1+=1;
}
if(s1 > 6)
s1 = 1;
else
s1 = s1;
display();
break;
case 1:
HAL_Delay(200);
if(HAL_GPIO_ReadPin(KEYS_GPIO_Port,KEYS_Pin) == SET)
{
OLED_CLS();
s3+=1;
}
if(s3 > 4)
s3 = 1;
else
s3 = s3;
function_level1();
break;
case 2:
HAL_Delay(200);
if(HAL_GPIO_ReadPin(KEYS_GPIO_Port,KEYS_Pin) == SET)
{
OLED_CLS();
s3+=1;
}
if(s3 > 4)
s3 = 1;
else
s3 = s3;
function_level2();
break;
case 3:
HAL_Delay(200);
if(HAL_GPIO_ReadPin(KEYS_GPIO_Port,KEYS_Pin) == SET)
{
OLED_CLS();
s3+=1;
}
if(s3 > 4)
s3 = 1;
else
s3 = s3;
function_level3();
break;
//后面你有多少功能就加多少case语句判断,格式一样
......
default:
break;
}
}
}
- 这里是执行功能号递减的按键,只放上面对应要修改的部分,这里要说明一下,按键的函数是对所有的界面判断了功能号,即对s2进行判断,当 s2 = 0 的时候,显示的是一级界面,后面的才是二级界面的功能号。(s2作为功能号,放的是所有功能的序号。即后面有三级界面啥的,功能号都放着里面)
- 注意:每个页面可能对应的功能不同,s1,s3作为主次界面的判断标志,其界面内拥有的功能数量可能有所不同,可以自行修改。
case 0:
HAL_Delay(200);
if(HAL_GPIO_ReadPin(KEYS_GPIO_Port,KEYS_Pin) == SET)
{
OLED_CLS();
s1-=1;
}
if(s1 < 1)
s1 = 6;
else
s1 = s1;
display();
break;
- 最后在main函数这个大循环里,只要加上那三个按键的函数就行了
while (1)
{
function(); //先执行确定,显示出主页面
key_scan1(); //再进行功能选择
key_scan2();
}
- 到这边这个多级菜单也算是完成了,其实这些代码都是没啥难度,想明白思路基本就是敲完一个模板,其他的就C+V操作,改好参数就好了,要实现具体的切换功能键选中后再执行相应的功能,直接在显示的函数里加就好了。其实还有许多复杂的方法可以写多级菜单,链表啊啥的,奈何学艺不精,数据结构想从理论运用到实际还是有难度的。不过这简单的代码虽然看上去还是挺枯燥没啥味道,用起来还是可以的。最后个人认为按键最好加硬件消抖,用户体验提高不少。